summaryrefslogtreecommitdiff
path: root/pkg/api/handlers/libpod
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/api/handlers/libpod')
-rw-r--r--pkg/api/handlers/libpod/containers.go240
-rw-r--r--pkg/api/handlers/libpod/containers_create.go29
-rw-r--r--pkg/api/handlers/libpod/healthcheck.go30
-rw-r--r--pkg/api/handlers/libpod/images.go391
-rw-r--r--pkg/api/handlers/libpod/manifests.go166
-rw-r--r--pkg/api/handlers/libpod/networks.go85
-rw-r--r--pkg/api/handlers/libpod/pods.go325
-rw-r--r--pkg/api/handlers/libpod/swagger.go94
-rw-r--r--pkg/api/handlers/libpod/types.go82
-rw-r--r--pkg/api/handlers/libpod/volumes.go141
10 files changed, 1241 insertions, 342 deletions
diff --git a/pkg/api/handlers/libpod/containers.go b/pkg/api/handlers/libpod/containers.go
index df16843c7..cdc34004f 100644
--- a/pkg/api/handlers/libpod/containers.go
+++ b/pkg/api/handlers/libpod/containers.go
@@ -1,60 +1,45 @@
package libpod
import (
- "fmt"
"net/http"
+ "path/filepath"
+ "sort"
"strconv"
+ "time"
"github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod"
- "github.com/containers/libpod/pkg/api/handlers"
+ "github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/api/handlers/utils"
- "github.com/gorilla/mux"
"github.com/gorilla/schema"
"github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
)
-func StopContainer(w http.ResponseWriter, r *http.Request) {
- handlers.StopContainer(w, r)
-}
-
func ContainerExists(w http.ResponseWriter, r *http.Request) {
- // 404 no such container
- // 200 ok
runtime := r.Context().Value("runtime").(*libpod.Runtime)
- name := mux.Vars(r)["name"]
+ name := utils.GetName(r)
_, err := runtime.LookupContainer(name)
if err != nil {
- utils.ContainerNotFound(w, name, err)
+ if errors.Cause(err) == define.ErrNoSuchCtr {
+ utils.ContainerNotFound(w, name, err)
+ }
+ utils.InternalServerError(w, err)
return
+
}
utils.WriteResponse(w, http.StatusNoContent, "")
}
-func RemoveContainer(w http.ResponseWriter, r *http.Request) {
- decoder := r.Context().Value("decoder").(*schema.Decoder)
- query := struct {
- Force bool `schema:"force"`
- Vols bool `schema:"v"`
- }{
- // override any golang type defaults
- }
-
- 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
- }
- utils.RemoveContainer(w, r, query.Force, query.Vols)
-}
func ListContainers(w http.ResponseWriter, r *http.Request) {
var (
- filters []string
+ filterFuncs []libpod.ContainerFilter
+ pss []ListContainer
)
decoder := r.Context().Value("decoder").(*schema.Decoder)
query := struct {
All bool `schema:"all"`
- Filter map[string][]string `schema:"filter"`
+ Filters map[string][]string `schema:"filters"`
Last int `schema:"last"`
Namespace bool `schema:"namespace"`
Pod bool `schema:"pod"`
@@ -69,6 +54,7 @@ func ListContainers(w http.ResponseWriter, r *http.Request) {
errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
return
}
+
runtime := r.Context().Value("runtime").(*libpod.Runtime)
opts := shared.PsOptions{
All: query.All,
@@ -76,20 +62,61 @@ func ListContainers(w http.ResponseWriter, r *http.Request) {
Size: query.Size,
Sort: "",
Namespace: query.Namespace,
+ NoTrunc: true,
Pod: query.Pod,
Sync: query.Sync,
}
- if len(query.Filter) > 0 {
- for k, v := range query.Filter {
+
+ all := query.All
+ if len(query.Filters) > 0 {
+ for k, v := range query.Filters {
for _, val := range v {
- filters = append(filters, fmt.Sprintf("%s=%s", k, val))
+ generatedFunc, err := shared.GenerateContainerFilterFuncs(k, val, runtime)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ filterFuncs = append(filterFuncs, generatedFunc)
}
}
}
- pss, err := shared.GetPsContainerOutput(runtime, opts, filters, 2)
+
+ // Docker thinks that if status is given as an input, then we should override
+ // the all setting and always deal with all containers.
+ if len(query.Filters["status"]) > 0 {
+ all = true
+ }
+ if !all {
+ runningOnly, err := shared.GenerateContainerFilterFuncs("status", define.ContainerStateRunning.String(), runtime)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ filterFuncs = append(filterFuncs, runningOnly)
+ }
+
+ cons, err := runtime.GetContainers(filterFuncs...)
if err != nil {
utils.InternalServerError(w, err)
}
+ if query.Last > 0 {
+ // Sort the containers we got
+ sort.Sort(psSortCreateTime{cons})
+ // we should perform the lopping before we start getting
+ // the expensive information on containers
+ if query.Last < len(cons) {
+ cons = cons[len(cons)-query.Last:]
+ }
+ }
+ for _, con := range cons {
+ listCon, err := ListContainerBatch(runtime, con, opts)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ pss = append(pss, listCon)
+
+ }
utils.WriteResponse(w, http.StatusOK, pss)
}
@@ -107,7 +134,7 @@ func GetContainer(w http.ResponseWriter, r *http.Request) {
return
}
runtime := r.Context().Value("runtime").(*libpod.Runtime)
- name := mux.Vars(r)["name"]
+ name := utils.GetName(r)
container, err := runtime.LookupContainer(name)
if err != nil {
utils.ContainerNotFound(w, name, err)
@@ -121,39 +148,17 @@ func GetContainer(w http.ResponseWriter, r *http.Request) {
utils.WriteResponse(w, http.StatusOK, data)
}
-func KillContainer(w http.ResponseWriter, r *http.Request) {
- // /{version}/containers/(name)/kill
- _, err := utils.KillContainer(w, r)
- if err != nil {
- return
- }
- // Success
- utils.WriteResponse(w, http.StatusNoContent, "")
-}
-
func WaitContainer(w http.ResponseWriter, r *http.Request) {
exitCode, err := utils.WaitContainer(w, r)
if err != nil {
- utils.InternalServerError(w, err)
return
}
utils.WriteResponse(w, http.StatusOK, strconv.Itoa(int(exitCode)))
}
-func LogsFromContainer(w http.ResponseWriter, r *http.Request) {
- // follow
- // since
- // timestamps
- // tail string
-}
-
-func CreateContainer(w http.ResponseWriter, r *http.Request) {
-
-}
-
func UnmountContainer(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
- name := mux.Vars(r)["name"]
+ name := utils.GetName(r)
conn, err := runtime.LookupContainer(name)
if err != nil {
utils.ContainerNotFound(w, name, err)
@@ -169,7 +174,7 @@ func UnmountContainer(w http.ResponseWriter, r *http.Request) {
}
func MountContainer(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
- name := mux.Vars(r)["name"]
+ name := utils.GetName(r)
conn, err := runtime.LookupContainer(name)
if err != nil {
utils.ContainerNotFound(w, name, err)
@@ -201,3 +206,122 @@ func ShowMountedContainers(w http.ResponseWriter, r *http.Request) {
}
utils.WriteResponse(w, http.StatusOK, response)
}
+
+// BatchContainerOp is used in ps to reduce performance hits by "batching"
+// locks.
+func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts shared.PsOptions) (ListContainer, error) {
+ var (
+ conConfig *libpod.ContainerConfig
+ conState define.ContainerStatus
+ err error
+ exitCode int32
+ exited bool
+ pid int
+ size *shared.ContainerSize
+ startedTime time.Time
+ exitedTime time.Time
+ cgroup, ipc, mnt, net, pidns, user, uts string
+ )
+
+ batchErr := ctr.Batch(func(c *libpod.Container) error {
+ conConfig = c.Config()
+ conState, err = c.State()
+ if err != nil {
+ return errors.Wrapf(err, "unable to obtain container state")
+ }
+
+ exitCode, exited, err = c.ExitCode()
+ if err != nil {
+ return errors.Wrapf(err, "unable to obtain container exit code")
+ }
+ startedTime, err = c.StartedTime()
+ if err != nil {
+ logrus.Errorf("error getting started time for %q: %v", c.ID(), err)
+ }
+ exitedTime, err = c.FinishedTime()
+ if err != nil {
+ logrus.Errorf("error getting exited time for %q: %v", c.ID(), err)
+ }
+
+ if !opts.Size && !opts.Namespace {
+ return nil
+ }
+
+ if opts.Namespace {
+ pid, err = c.PID()
+ if err != nil {
+ return errors.Wrapf(err, "unable to obtain container pid")
+ }
+ ctrPID := strconv.Itoa(pid)
+ cgroup, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "cgroup"))
+ ipc, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "ipc"))
+ mnt, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "mnt"))
+ net, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "net"))
+ pidns, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "pid"))
+ user, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "user"))
+ uts, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "uts"))
+ }
+ if opts.Size {
+ size = new(shared.ContainerSize)
+
+ rootFsSize, err := c.RootFsSize()
+ if err != nil {
+ logrus.Errorf("error getting root fs size for %q: %v", c.ID(), err)
+ }
+
+ rwSize, err := c.RWSize()
+ if err != nil {
+ logrus.Errorf("error getting rw size for %q: %v", c.ID(), err)
+ }
+
+ size.RootFsSize = rootFsSize
+ size.RwSize = rwSize
+ }
+ return nil
+ })
+
+ if batchErr != nil {
+ return ListContainer{}, batchErr
+ }
+
+ ps := ListContainer{
+ Command: conConfig.Command,
+ Created: conConfig.CreatedTime.Unix(),
+ Exited: exited,
+ ExitCode: exitCode,
+ ExitedAt: exitedTime.Unix(),
+ ID: conConfig.ID,
+ Image: conConfig.RootfsImageName,
+ IsInfra: conConfig.IsInfra,
+ Labels: conConfig.Labels,
+ Mounts: ctr.UserVolumes(),
+ Names: []string{conConfig.Name},
+ Pid: pid,
+ Pod: conConfig.Pod,
+ Ports: conConfig.PortMappings,
+ Size: size,
+ StartedAt: startedTime.Unix(),
+ State: conState.String(),
+ }
+ if opts.Pod && len(conConfig.Pod) > 0 {
+ pod, err := rt.GetPod(conConfig.Pod)
+ if err != nil {
+ return ListContainer{}, err
+ }
+ ps.PodName = pod.Name()
+ }
+
+ if opts.Namespace {
+ ns := ListContainerNamespaces{
+ Cgroup: cgroup,
+ IPC: ipc,
+ MNT: mnt,
+ NET: net,
+ PIDNS: pidns,
+ User: user,
+ UTS: uts,
+ }
+ ps.Namespaces = ns
+ }
+ return ps, nil
+}
diff --git a/pkg/api/handlers/libpod/containers_create.go b/pkg/api/handlers/libpod/containers_create.go
new file mode 100644
index 000000000..ebca41151
--- /dev/null
+++ b/pkg/api/handlers/libpod/containers_create.go
@@ -0,0 +1,29 @@
+package libpod
+
+import (
+ "encoding/json"
+ "net/http"
+
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/api/handlers/utils"
+ "github.com/containers/libpod/pkg/specgen"
+ "github.com/pkg/errors"
+)
+
+// CreateContainer takes a specgenerator and makes a container. It returns
+// the new container ID on success along with any warnings.
+func CreateContainer(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ var sg specgen.SpecGenerator
+ if err := json.NewDecoder(r.Body).Decode(&sg); err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()"))
+ return
+ }
+ ctr, err := sg.MakeContainer(runtime)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ response := utils.ContainerCreateResponse{ID: ctr.ID()}
+ utils.WriteJSON(w, http.StatusCreated, response)
+}
diff --git a/pkg/api/handlers/libpod/healthcheck.go b/pkg/api/handlers/libpod/healthcheck.go
index 0d7bf3ea7..6eb2ab0e3 100644
--- a/pkg/api/handlers/libpod/healthcheck.go
+++ b/pkg/api/handlers/libpod/healthcheck.go
@@ -5,21 +5,39 @@ import (
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/api/handlers/utils"
- "github.com/gorilla/mux"
)
func RunHealthCheck(w http.ResponseWriter, r *http.Request) {
- // 200 ok
- // 404 no such
- // 500 internal
runtime := r.Context().Value("runtime").(*libpod.Runtime)
- name := mux.Vars(r)["name"]
+ name := utils.GetName(r)
status, err := runtime.HealthCheck(name)
if err != nil {
if status == libpod.HealthCheckContainerNotFound {
utils.ContainerNotFound(w, name, err)
+ return
}
+ if status == libpod.HealthCheckNotDefined {
+ utils.Error(w, "no healthcheck defined", http.StatusConflict, err)
+ return
+ }
+ if status == libpod.HealthCheckContainerStopped {
+ utils.Error(w, "container not running", http.StatusConflict, err)
+ return
+ }
+ utils.InternalServerError(w, err)
+ return
+ }
+ ctr, err := runtime.LookupContainer(name)
+ if err != nil {
utils.InternalServerError(w, err)
+ return
}
- utils.WriteResponse(w, http.StatusOK, status)
+
+ hcLog, err := ctr.GetHealthCheckLog()
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+
+ utils.WriteResponse(w, http.StatusOK, hcLog)
}
diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go
index bbc8c9346..4b24d7d9f 100644
--- a/pkg/api/handlers/libpod/images.go
+++ b/pkg/api/handlers/libpod/images.go
@@ -1,15 +1,28 @@
package libpod
import (
+ "context"
"fmt"
+ "io"
"io/ioutil"
"net/http"
"os"
+ "strconv"
+ "strings"
+ "github.com/containers/buildah"
+ "github.com/containers/image/v5/docker"
+ "github.com/containers/image/v5/docker/reference"
+ "github.com/containers/image/v5/manifest"
+ "github.com/containers/image/v5/transports/alltransports"
+ "github.com/containers/image/v5/types"
"github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/image"
+ image2 "github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/api/handlers"
"github.com/containers/libpod/pkg/api/handlers/utils"
- "github.com/gorilla/mux"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/util"
"github.com/gorilla/schema"
"github.com/pkg/errors"
)
@@ -26,11 +39,8 @@ import (
// create
func ImageExists(w http.ResponseWriter, r *http.Request) {
- // 200 ok
- // 404 no such
- // 500 internal
runtime := r.Context().Value("runtime").(*libpod.Runtime)
- name := mux.Vars(r)["name"]
+ name := utils.GetName(r)
_, err := runtime.ImageRuntime().NewFromLocal(name)
if err != nil {
@@ -41,22 +51,39 @@ func ImageExists(w http.ResponseWriter, r *http.Request) {
}
func ImageTree(w http.ResponseWriter, r *http.Request) {
- // tree is a bit of a mess ... logic is in adapter and therefore not callable from here. needs rework
-
- // name := mux.Vars(r)["name"]
- // _, layerInfoMap, _, err := s.Runtime.Tree(name)
- // if err != nil {
- // Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "Failed to find image information for %q", name))
- // return
- // }
- // it is not clear to me how to deal with this given all the processing of the image
- // is in main. we need to discuss how that really should be and return something useful.
- handlers.UnsupportedHandler(w, r)
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ name := utils.GetName(r)
+
+ img, err := runtime.ImageRuntime().NewFromLocal(name)
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusNotFound, errors.Wrapf(err, "Failed to find image %s", name))
+ return
+ }
+
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+ query := struct {
+ WhatRequires bool `schema:"whatrequires"`
+ }{
+ WhatRequires: false,
+ }
+ 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
+ }
+
+ tree, err := img.GenerateTree(query.WhatRequires)
+ if err != nil {
+ utils.Error(w, "Server error", http.StatusInternalServerError, errors.Wrapf(err, "failed to generate image tree for %s", name))
+ return
+ }
+
+ utils.WriteResponse(w, http.StatusOK, tree)
}
func GetImage(w http.ResponseWriter, r *http.Request) {
- name := mux.Vars(r)["name"]
- newImage, err := handlers.GetImage(r, name)
+ name := utils.GetName(r)
+ newImage, err := utils.GetImage(r, name)
if err != nil {
utils.Error(w, "Something went wrong.", http.StatusNotFound, errors.Wrapf(err, "Failed to find image %s", name))
return
@@ -67,15 +94,15 @@ func GetImage(w http.ResponseWriter, r *http.Request) {
return
}
utils.WriteResponse(w, http.StatusOK, inspect)
-
}
+
func GetImages(w http.ResponseWriter, r *http.Request) {
images, err := utils.GetImages(w, r)
if err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Failed get images"))
return
}
- var summaries = make([]*handlers.ImageSummary, len(images))
+ var summaries = make([]*entities.ImageSummary, len(images))
for j, img := range images {
is, err := handlers.ImageToImageSummary(img)
if err != nil {
@@ -83,7 +110,7 @@ func GetImages(w http.ResponseWriter, r *http.Request) {
return
}
// libpod has additional fields that we need to populate.
- is.CreatedTime = img.Created()
+ is.Created = img.Created().Unix()
is.ReadOnly = img.IsReadOnly()
summaries[j] = is
}
@@ -91,8 +118,9 @@ func GetImages(w http.ResponseWriter, r *http.Request) {
}
func PruneImages(w http.ResponseWriter, r *http.Request) {
- // 200 ok
- // 500 internal
+ var (
+ err error
+ )
runtime := r.Context().Value("runtime").(*libpod.Runtime)
decoder := r.Context().Value("decoder").(*schema.Decoder)
query := struct {
@@ -110,10 +138,21 @@ func PruneImages(w http.ResponseWriter, r *http.Request) {
var libpodFilters = []string{}
if _, found := r.URL.Query()["filters"]; found {
+ dangling := query.Filters["all"]
+ if len(dangling) > 0 {
+ query.All, err = strconv.ParseBool(query.Filters["all"][0])
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ }
+ // dangling is special and not implemented in the libpod side of things
+ delete(query.Filters, "dangling")
for k, v := range query.Filters {
libpodFilters = append(libpodFilters, fmt.Sprintf("%s=%s", k, v[0]))
}
}
+
cids, err := runtime.ImageRuntime().PruneImages(r.Context(), query.All, libpodFilters)
if err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
@@ -129,7 +168,7 @@ func ExportImage(w http.ResponseWriter, r *http.Request) {
Compress bool `schema:"compress"`
Format string `schema:"format"`
}{
- // override any golang type defaults
+ Format: "docker-archive",
}
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
@@ -138,11 +177,6 @@ func ExportImage(w http.ResponseWriter, r *http.Request) {
return
}
- if len(query.Format) < 1 {
- utils.InternalServerError(w, errors.New("format parameter cannot be empty."))
- return
- }
-
tmpfile, err := ioutil.TempFile("", "api.tar")
if err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to create tempfile"))
@@ -152,12 +186,13 @@ func ExportImage(w http.ResponseWriter, r *http.Request) {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to close tempfile"))
return
}
- name := mux.Vars(r)["name"]
+ name := utils.GetName(r)
newImage, err := runtime.ImageRuntime().NewFromLocal(name)
if err != nil {
utils.ImageNotFound(w, name, err)
return
}
+
if err := newImage.Save(r.Context(), name, query.Format, tmpfile.Name(), []string{}, false, query.Compress); err != nil {
utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, err)
return
@@ -171,3 +206,299 @@ func ExportImage(w http.ResponseWriter, r *http.Request) {
defer os.Remove(tmpfile.Name())
utils.WriteResponse(w, http.StatusOK, rdr)
}
+
+func ImagesLoad(w http.ResponseWriter, r *http.Request) {
+ 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
+ }
+ split := strings.Split(loadedImage, ",")
+ newImage, err := runtime.ImageRuntime().NewFromLocal(split[0])
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ // TODO this should go into libpod proper at some point.
+ if len(query.Reference) > 0 {
+ if err := newImage.TagImage(query.Reference); err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ }
+ utils.WriteResponse(w, http.StatusOK, handlers.LibpodImagesLoadReport{ID: loadedImage})
+}
+
+func ImagesImport(w http.ResponseWriter, r *http.Request) {
+ 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) {
+ 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)
+}
+
+func CommitContainer(w http.ResponseWriter, r *http.Request) {
+ var (
+ destImage string
+ mimeType string
+ )
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+
+ query := struct {
+ Author string `schema:"author"`
+ Changes []string `schema:"changes"`
+ Comment string `schema:"comment"`
+ Container string `schema:"container"`
+ Format string `schema:"format"`
+ Pause bool `schema:"pause"`
+ Repo string `schema:"repo"`
+ Tag string `schema:"tag"`
+ }{
+ Format: "oci",
+ }
+
+ if err := decoder.Decode(&query, r.URL.Query()); err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
+ return
+ }
+ rtc, err := runtime.GetConfig()
+ if err != nil {
+ utils.Error(w, "failed to get runtime config", http.StatusInternalServerError, errors.Wrap(err, "failed to get runtime config"))
+ return
+ }
+ sc := image2.GetSystemContext(rtc.Engine.SignaturePolicyPath, "", false)
+ tag := "latest"
+ options := libpod.ContainerCommitOptions{
+ Pause: true,
+ }
+ switch query.Format {
+ case "oci":
+ mimeType = buildah.OCIv1ImageManifest
+ if len(query.Comment) > 0 {
+ utils.InternalServerError(w, errors.New("messages are only compatible with the docker image format (-f docker)"))
+ return
+ }
+ case "docker":
+ mimeType = manifest.DockerV2Schema2MediaType
+ default:
+ utils.InternalServerError(w, errors.Errorf("unrecognized image format %q", query.Format))
+ return
+ }
+ options.CommitOptions = buildah.CommitOptions{
+ SignaturePolicyPath: rtc.Engine.SignaturePolicyPath,
+ ReportWriter: os.Stderr,
+ SystemContext: sc,
+ PreferredManifestType: mimeType,
+ }
+
+ if len(query.Tag) > 0 {
+ tag = query.Tag
+ }
+ options.Message = query.Comment
+ options.Author = query.Author
+ options.Pause = query.Pause
+ options.Changes = query.Changes
+ ctr, err := runtime.LookupContainer(query.Container)
+ if err != nil {
+ utils.Error(w, "failed to lookup container", http.StatusNotFound, err)
+ return
+ }
+
+ // I know mitr hates this ... but doing for now
+ if len(query.Repo) > 1 {
+ destImage = fmt.Sprintf("%s:%s", query.Repo, tag)
+ }
+
+ commitImage, err := ctr.Commit(r.Context(), destImage, options)
+ if err != nil && !strings.Contains(err.Error(), "is not running") {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "CommitFailure"))
+ return
+ }
+ utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: commitImage.ID()}) // nolint
+}
diff --git a/pkg/api/handlers/libpod/manifests.go b/pkg/api/handlers/libpod/manifests.go
new file mode 100644
index 000000000..d87ed7eba
--- /dev/null
+++ b/pkg/api/handlers/libpod/manifests.go
@@ -0,0 +1,166 @@
+package libpod
+
+import (
+ "encoding/json"
+ "net/http"
+
+ "github.com/containers/buildah/manifests"
+ copy2 "github.com/containers/image/v5/copy"
+ "github.com/containers/image/v5/transports/alltransports"
+ "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/gorilla/schema"
+ "github.com/opencontainers/go-digest"
+ "github.com/pkg/errors"
+)
+
+func ManifestCreate(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+ query := struct {
+ Name []string `schema:"name"`
+ Image []string `schema:"image"`
+ All bool `schema:"all"`
+ }{
+ // 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
+ }
+ rtc, err := runtime.GetConfig()
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ sc := image.GetSystemContext(rtc.Engine.SignaturePolicyPath, "", false)
+ manID, err := image.CreateManifestList(runtime.ImageRuntime(), *sc, query.Name, query.Image, query.All)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: manID})
+}
+
+func ManifestInspect(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ name := utils.GetName(r)
+ newImage, err := runtime.ImageRuntime().NewFromLocal(name)
+ if err != nil {
+ utils.ImageNotFound(w, name, err)
+ return
+ }
+ data, err := newImage.InspectManifest()
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusOK, data)
+}
+
+func ManifestAdd(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ var manifestInput image.ManifestAddOpts
+ if err := json.NewDecoder(r.Body).Decode(&manifestInput); err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()"))
+ return
+ }
+ name := utils.GetName(r)
+ newImage, err := runtime.ImageRuntime().NewFromLocal(name)
+ if err != nil {
+ utils.ImageNotFound(w, name, err)
+ return
+ }
+ rtc, err := runtime.GetConfig()
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ sc := image.GetSystemContext(rtc.Engine.SignaturePolicyPath, "", false)
+ newID, err := newImage.AddManifest(*sc, manifestInput)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: newID})
+}
+
+func ManifestRemove(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+ query := struct {
+ Digest string `schema:"digest"`
+ }{
+ // Add defaults here once needed.
+ }
+ name := utils.GetName(r)
+ 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
+ }
+ newImage, err := runtime.ImageRuntime().NewFromLocal(name)
+ if err != nil {
+ utils.ImageNotFound(w, name, err)
+ return
+ }
+ d, err := digest.Parse(query.Digest)
+ if err != nil {
+ utils.Error(w, "invalid digest", http.StatusBadRequest, err)
+ return
+ }
+ newID, err := newImage.RemoveManifest(d)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: newID})
+}
+func ManifestPush(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+ query := struct {
+ All bool `schema:"all"`
+ Destination string `schema:"destination"`
+ }{
+ // 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
+ }
+ name := utils.GetName(r)
+ newImage, err := runtime.ImageRuntime().NewFromLocal(name)
+ if err != nil {
+ utils.ImageNotFound(w, name, err)
+ return
+ }
+ dest, err := alltransports.ParseImageName(query.Destination)
+ if err != nil {
+ utils.Error(w, "invalid destination parameter", http.StatusBadRequest, errors.Errorf("invalid destination parameter %q", query.Destination))
+ return
+ }
+ rtc, err := runtime.GetConfig()
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ sc := image.GetSystemContext(rtc.Engine.SignaturePolicyPath, "", false)
+ opts := manifests.PushOptions{
+ ImageListSelection: copy2.CopySpecificImages,
+ SystemContext: sc,
+ }
+ if query.All {
+ opts.ImageListSelection = copy2.CopyAllImages
+ }
+ newD, err := newImage.PushManifest(dest, opts)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusOK, newD.String())
+}
diff --git a/pkg/api/handlers/libpod/networks.go b/pkg/api/handlers/libpod/networks.go
new file mode 100644
index 000000000..e8a92e93e
--- /dev/null
+++ b/pkg/api/handlers/libpod/networks.go
@@ -0,0 +1,85 @@
+package libpod
+
+import (
+ "net/http"
+
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/api/handlers/utils"
+ "github.com/containers/libpod/pkg/network"
+ "github.com/gorilla/schema"
+ "github.com/pkg/errors"
+)
+
+func CreateNetwork(w http.ResponseWriter, r *http.Request) {}
+func ListNetworks(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ config, err := runtime.GetConfig()
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ configDir := config.Network.NetworkConfigDir
+ if len(configDir) < 1 {
+ configDir = network.CNIConfigDir
+ }
+ networks, err := network.LoadCNIConfsFromDir(configDir)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusOK, networks)
+}
+
+func RemoveNetwork(w http.ResponseWriter, r *http.Request) {
+ // 200 ok
+ // 404 no such
+ // 500 internal
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+ query := struct {
+ Force bool `schema:"force"`
+ }{
+ // override any golang type defaults
+ }
+ 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
+ }
+ name := utils.GetName(r)
+ if err := network.RemoveNetwork(name); err != nil {
+ // If the network cannot be found, we return a 404.
+ if errors.Cause(err) == network.ErrNetworkNotFound {
+ utils.Error(w, "Something went wrong", http.StatusNotFound, err)
+ return
+ }
+ utils.InternalServerError(w, err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusOK, "")
+}
+
+func InspectNetwork(w http.ResponseWriter, r *http.Request) {
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+ query := struct {
+ Force bool `schema:"force"`
+ }{
+ // override any golang type defaults
+ }
+ 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
+ }
+ name := utils.GetName(r)
+ n, err := network.InspectNetwork(name)
+ if err != nil {
+ // If the network cannot be found, we return a 404.
+ if errors.Cause(err) == network.ErrNetworkNotFound {
+ utils.Error(w, "Something went wrong", http.StatusNotFound, err)
+ return
+ }
+ utils.InternalServerError(w, err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusOK, n)
+}
diff --git a/pkg/api/handlers/libpod/pods.go b/pkg/api/handlers/libpod/pods.go
index 656a75646..7e9c2e2c0 100644
--- a/pkg/api/handlers/libpod/pods.go
+++ b/pkg/api/handlers/libpod/pods.go
@@ -4,108 +4,41 @@ import (
"encoding/json"
"fmt"
"net/http"
- "strings"
- "github.com/containers/libpod/cmd/podman/shared"
- "github.com/containers/libpod/cmd/podman/shared/parse"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/api/handlers"
"github.com/containers/libpod/pkg/api/handlers/utils"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/specgen"
"github.com/containers/libpod/pkg/util"
- "github.com/gorilla/mux"
"github.com/gorilla/schema"
"github.com/pkg/errors"
)
func PodCreate(w http.ResponseWriter, r *http.Request) {
- // 200 ok
- // 500 internal
var (
runtime = r.Context().Value("runtime").(*libpod.Runtime)
- options []libpod.PodCreateOption
err error
)
- labels := make(map[string]string)
- input := handlers.PodCreateConfig{}
- if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()"))
+ var psg specgen.PodSpecGenerator
+ if err := json.NewDecoder(r.Body).Decode(&psg); err != nil {
+ utils.Error(w, "Failed to decode specgen", http.StatusInternalServerError, errors.Wrap(err, "failed to decode specgen"))
return
}
- if len(input.InfraCommand) > 0 || len(input.InfraImage) > 0 {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError,
- errors.New("infra-command and infra-image are not implemented yet"))
- return
- }
- // TODO long term we should break the following out of adapter and into libpod proper
- // so that the cli and api can share the creation of a pod with the same options
- if len(input.CGroupParent) > 0 {
- options = append(options, libpod.WithPodCgroupParent(input.CGroupParent))
- }
-
- if len(input.Labels) > 0 {
- if err := parse.ReadKVStrings(labels, []string{}, input.Labels); err != nil {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
- return
- }
- }
-
- if len(labels) != 0 {
- options = append(options, libpod.WithPodLabels(labels))
- }
-
- if len(input.Name) > 0 {
- options = append(options, libpod.WithPodName(input.Name))
- }
-
- if len(input.Hostname) > 0 {
- options = append(options, libpod.WithPodHostname(input.Hostname))
- }
-
- if input.Infra {
- // TODO infra-image and infra-command are not supported in the libpod API yet. Will fix
- // when implemented in libpod
- options = append(options, libpod.WithInfraContainer())
- sharedNamespaces := shared.DefaultKernelNamespaces
- if len(input.Share) > 0 {
- sharedNamespaces = input.Share
- }
- nsOptions, err := shared.GetNamespaceOptions(strings.Split(sharedNamespaces, ","))
- if err != nil {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
- return
- }
- options = append(options, nsOptions...)
- }
-
- if len(input.Publish) > 0 {
- portBindings, err := shared.CreatePortBindings(input.Publish)
- if err != nil {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
- return
- }
- options = append(options, libpod.WithInfraContainerPorts(portBindings))
-
- }
- // always have containers use pod cgroups
- // User Opt out is not yet supported
- options = append(options, libpod.WithPodCgroups())
-
- pod, err := runtime.NewPod(r.Context(), options...)
+ pod, err := psg.MakePod(runtime)
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()})
+ utils.WriteResponse(w, http.StatusCreated, handlers.IDResponse{ID: pod.ID()})
}
func Pods(w http.ResponseWriter, r *http.Request) {
- // 200 ok
- // 500 internal
- var (
- runtime = r.Context().Value("runtime").(*libpod.Runtime)
- podInspectData []*libpod.PodInspect
- )
decoder := r.Context().Value("decoder").(*schema.Decoder)
query := struct {
Filters map[string][]string `schema:"filters"`
@@ -118,30 +51,17 @@ func Pods(w http.ResponseWriter, r *http.Request) {
return
}
- if _, found := r.URL.Query()["filters"]; found {
- utils.Error(w, "filters are not implemented yet", http.StatusInternalServerError, define.ErrNotImplemented)
- return
- }
-
- pods, err := runtime.GetAllPods()
+ pods, err := utils.GetPods(w, r)
if err != nil {
utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
return
}
- for _, pod := range pods {
- data, err := pod.Inspect()
- if err != nil {
- utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
- return
- }
- podInspectData = append(podInspectData, data)
- }
- utils.WriteResponse(w, http.StatusOK, podInspectData)
+ utils.WriteResponse(w, http.StatusOK, pods)
}
func PodInspect(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
- name := mux.Vars(r)["name"]
+ name := utils.GetName(r)
pod, err := runtime.LookupPod(name)
if err != nil {
utils.PodNotFound(w, name, err)
@@ -156,14 +76,12 @@ func PodInspect(w http.ResponseWriter, r *http.Request) {
}
func PodStop(w http.ResponseWriter, r *http.Request) {
- // 200
- // 304 not modified
- // 404 no such
- // 500 internal
var (
stopError error
runtime = r.Context().Value("runtime").(*libpod.Runtime)
decoder = r.Context().Value("decoder").(*schema.Decoder)
+ responses map[string]error
+ errs []error
)
query := struct {
Timeout int `schema:"t"`
@@ -176,90 +94,75 @@ func PodStop(w http.ResponseWriter, r *http.Request) {
errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
return
}
- allContainersStopped := true
- name := mux.Vars(r)["name"]
+ name := utils.GetName(r)
pod, err := runtime.LookupPod(name)
if err != nil {
utils.PodNotFound(w, name, err)
return
}
- // TODO we need to implement a pod.State/Status in libpod internal so libpod api
- // users dont have to run through all containers.
- podContainers, err := pod.AllContainers()
+ status, err := pod.GetPodStatus()
if err != nil {
utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
return
}
-
- for _, con := range podContainers {
- containerState, err := con.State()
- if err != nil {
- utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
- return
- }
- if containerState == define.ContainerStateRunning {
- allContainersStopped = false
- break
- }
- }
- if allContainersStopped {
- alreadyStopped := errors.Errorf("pod %s is already stopped", pod.ID())
- utils.Error(w, "Something went wrong", http.StatusNotModified, alreadyStopped)
+ if status != define.PodStateRunning {
+ utils.WriteResponse(w, http.StatusNotModified, "")
return
}
if query.Timeout > 0 {
- _, stopError = pod.StopWithTimeout(r.Context(), false, query.Timeout)
+ responses, stopError = pod.StopWithTimeout(r.Context(), false, query.Timeout)
} else {
- _, stopError = pod.Stop(r.Context(), false)
+ responses, stopError = pod.Stop(r.Context(), false)
}
if stopError != nil {
utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
return
}
- utils.WriteResponse(w, http.StatusOK, "")
+ for _, err := range responses {
+ errs = append(errs, err)
+ }
+ report := entities.PodStopReport{
+ Errs: errs,
+ Id: pod.ID(),
+ }
+ utils.WriteResponse(w, http.StatusOK, report)
}
func PodStart(w http.ResponseWriter, r *http.Request) {
+ var (
+ errs []error
+ )
runtime := r.Context().Value("runtime").(*libpod.Runtime)
- allContainersRunning := true
- name := mux.Vars(r)["name"]
+ name := utils.GetName(r)
pod, err := runtime.LookupPod(name)
if err != nil {
utils.PodNotFound(w, name, err)
return
}
-
- // TODO we need to implement a pod.State/Status in libpod internal so libpod api
- // users dont have to run through all containers.
- podContainers, err := pod.AllContainers()
+ status, err := pod.GetPodStatus()
if err != nil {
utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
return
}
-
- for _, con := range podContainers {
- containerState, err := con.State()
- if err != nil {
- utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
- return
- }
- if containerState != define.ContainerStateRunning {
- allContainersRunning = false
- break
- }
- }
- if allContainersRunning {
- alreadyRunning := errors.Errorf("pod %s is already running", pod.ID())
- utils.Error(w, "Something went wrong", http.StatusNotModified, alreadyRunning)
+ if status == define.PodStateRunning {
+ utils.WriteResponse(w, http.StatusNotModified, "")
return
}
- if _, err := pod.Start(r.Context()); err != nil {
+ responses, err := pod.Start(r.Context())
+ if err != nil {
utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
return
}
- utils.WriteResponse(w, http.StatusOK, "")
+ for _, err := range responses {
+ errs = append(errs, err)
+ }
+ report := entities.PodStartReport{
+ Errs: errs,
+ Id: pod.ID(),
+ }
+ utils.WriteResponse(w, http.StatusOK, report)
}
func PodDelete(w http.ResponseWriter, r *http.Request) {
@@ -268,7 +171,7 @@ func PodDelete(w http.ResponseWriter, r *http.Request) {
decoder = r.Context().Value("decoder").(*schema.Decoder)
)
query := struct {
- force bool `schema:"force"`
+ Force bool `schema:"force"`
}{
// override any golang type defaults
}
@@ -278,109 +181,110 @@ func PodDelete(w http.ResponseWriter, r *http.Request) {
errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
return
}
- name := mux.Vars(r)["name"]
+ name := utils.GetName(r)
pod, err := runtime.LookupPod(name)
if err != nil {
utils.PodNotFound(w, name, err)
return
}
- if err := runtime.RemovePod(r.Context(), pod, true, query.force); err != nil {
+ if err := runtime.RemovePod(r.Context(), pod, true, query.Force); err != nil {
utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
return
}
- utils.WriteResponse(w, http.StatusNoContent, "")
+ report := entities.PodRmReport{
+ Id: pod.ID(),
+ }
+ utils.WriteResponse(w, http.StatusOK, report)
}
func PodRestart(w http.ResponseWriter, r *http.Request) {
+ var (
+ errs []error
+ )
runtime := r.Context().Value("runtime").(*libpod.Runtime)
- name := mux.Vars(r)["name"]
+ name := utils.GetName(r)
pod, err := runtime.LookupPod(name)
if err != nil {
utils.PodNotFound(w, name, err)
return
}
- _, err = pod.Restart(r.Context())
+ responses, err := pod.Restart(r.Context())
if err != nil {
utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
return
}
- utils.WriteResponse(w, http.StatusOK, "")
+ for _, err := range responses {
+ errs = append(errs, err)
+ }
+ report := entities.PodRestartReport{
+ Errs: errs,
+ Id: pod.ID(),
+ }
+ utils.WriteResponse(w, http.StatusOK, report)
}
func PodPrune(w http.ResponseWriter, r *http.Request) {
var (
- err error
- pods []*libpod.Pod
runtime = r.Context().Value("runtime").(*libpod.Runtime)
- decoder = r.Context().Value("decoder").(*schema.Decoder)
)
- query := struct {
- force bool `schema:"force"`
- }{
- // override any golang type defaults
- }
-
- 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 query.force {
- pods, err = runtime.GetAllPods()
- if err != nil {
- utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
- return
- }
- } else {
- // TODO We need to make a libpod.PruneVolumes or this code will be a mess. Volumes
- // already does this right. It will also help clean this code path up with less
- // conditionals. We do this when we integrate with libpod again.
- utils.Error(w, "not implemented", http.StatusInternalServerError, errors.New("not implemented"))
+ pruned, err := runtime.PrunePods()
+ if err != nil {
+ utils.InternalServerError(w, err)
return
}
- for _, p := range pods {
- if err := runtime.RemovePod(r.Context(), p, true, query.force); err != nil {
- utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
- return
- }
- }
- utils.WriteResponse(w, http.StatusNoContent, "")
+ utils.WriteResponse(w, http.StatusOK, pruned)
}
func PodPause(w http.ResponseWriter, r *http.Request) {
+ var (
+ errs []error
+ )
runtime := r.Context().Value("runtime").(*libpod.Runtime)
- name := mux.Vars(r)["name"]
+ name := utils.GetName(r)
pod, err := runtime.LookupPod(name)
if err != nil {
utils.PodNotFound(w, name, err)
return
}
- _, err = pod.Pause()
+ responses, err := pod.Pause()
if err != nil {
utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
return
}
- utils.WriteResponse(w, http.StatusNoContent, "")
+ for _, v := range responses {
+ errs = append(errs, v)
+ }
+ report := entities.PodPauseReport{
+ Errs: errs,
+ Id: pod.ID(),
+ }
+ utils.WriteResponse(w, http.StatusOK, report)
}
func PodUnpause(w http.ResponseWriter, r *http.Request) {
- // 200 ok
- // 404 no such
- // 500 internal
+ var (
+ errs []error
+ )
runtime := r.Context().Value("runtime").(*libpod.Runtime)
- name := mux.Vars(r)["name"]
+ name := utils.GetName(r)
pod, err := runtime.LookupPod(name)
if err != nil {
utils.PodNotFound(w, name, err)
return
}
- _, err = pod.Unpause()
+ responses, err := pod.Unpause()
if err != nil {
- utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
+ utils.Error(w, "failed to pause pod", http.StatusInternalServerError, err)
return
}
- utils.WriteResponse(w, http.StatusOK, "")
+ for _, v := range responses {
+ errs = append(errs, v)
+ }
+ report := entities.PodUnpauseReport{
+ Errs: errs,
+ Id: pod.ID(),
+ }
+ utils.WriteResponse(w, http.StatusOK, &report)
}
func PodKill(w http.ResponseWriter, r *http.Request) {
@@ -388,9 +292,10 @@ func PodKill(w http.ResponseWriter, r *http.Request) {
runtime = r.Context().Value("runtime").(*libpod.Runtime)
decoder = r.Context().Value("decoder").(*schema.Decoder)
signal = "SIGKILL"
+ errs []error
)
query := struct {
- signal string `schema:"signal"`
+ Signal string `schema:"signal"`
}{
// override any golang type defaults
}
@@ -399,16 +304,15 @@ func PodKill(w http.ResponseWriter, r *http.Request) {
errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
return
}
- muxVars := mux.Vars(r)
- if _, found := muxVars["signal"]; found {
- signal = query.signal
+ if _, found := r.URL.Query()["signal"]; found {
+ signal = query.Signal
}
sig, err := util.ParseSignal(signal)
if err != nil {
utils.InternalServerError(w, errors.Wrapf(err, "unable to parse signal value"))
}
- name := mux.Vars(r)["name"]
+ name := utils.GetName(r)
pod, err := runtime.LookupPod(name)
if err != nil {
utils.PodNotFound(w, name, err)
@@ -431,21 +335,32 @@ func PodKill(w http.ResponseWriter, r *http.Request) {
utils.Error(w, msg, http.StatusConflict, errors.Errorf("cannot kill a pod with no running containers: %s", pod.ID()))
return
}
- _, err = pod.Kill(uint(sig))
+
+ responses, err := pod.Kill(uint(sig))
if err != nil {
- utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
+ utils.Error(w, "failed to kill pod", http.StatusInternalServerError, err)
return
}
- utils.WriteResponse(w, http.StatusOK, "")
+
+ for _, v := range responses {
+ if v != nil {
+ errs = append(errs, v)
+ }
+ }
+ report := &entities.PodKillReport{
+ Errs: errs,
+ Id: pod.ID(),
+ }
+ utils.WriteResponse(w, http.StatusOK, report)
}
func PodExists(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
- name := mux.Vars(r)["name"]
+ name := utils.GetName(r)
_, err := runtime.LookupPod(name)
if err != nil {
utils.PodNotFound(w, name, err)
return
}
- utils.WriteResponse(w, http.StatusOK, "")
+ utils.WriteResponse(w, http.StatusNoContent, "")
}
diff --git a/pkg/api/handlers/libpod/swagger.go b/pkg/api/handlers/libpod/swagger.go
new file mode 100644
index 000000000..1fad2dd1a
--- /dev/null
+++ b/pkg/api/handlers/libpod/swagger.go
@@ -0,0 +1,94 @@
+package libpod
+
+import (
+ "net/http"
+ "os"
+
+ "github.com/containers/image/v5/manifest"
+ "github.com/containers/libpod/pkg/api/handlers/utils"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/pkg/errors"
+)
+
+// DefaultPodmanSwaggerSpec provides the default path to the podman swagger spec file
+const DefaultPodmanSwaggerSpec = "/usr/share/containers/podman/swagger.yaml"
+
+// List Containers
+// swagger:response ListContainers
+type swagInspectPodResponse struct {
+ // in:body
+ Body []ListContainer
+}
+
+// Inspect Manifest
+// swagger:response InspectManifest
+type swagInspectManifestResponse struct {
+ // in:body
+ Body manifest.List
+}
+
+// Kill Pod
+// swagger:response PodKillReport
+type swagKillPodResponse struct {
+ // in:body
+ Body entities.PodKillReport
+}
+
+// Pause pod
+// swagger:response PodPauseReport
+type swagPausePodResponse struct {
+ // in:body
+ Body entities.PodPauseReport
+}
+
+// Unpause pod
+// swagger:response PodUnpauseReport
+type swagUnpausePodResponse struct {
+ // in:body
+ Body entities.PodUnpauseReport
+}
+
+// Stop pod
+// swagger:response PodStopReport
+type swagStopPodResponse struct {
+ // in:body
+ Body entities.PodStopReport
+}
+
+// Restart pod
+// swagger:response PodRestartReport
+type swagRestartPodResponse struct {
+ // in:body
+ Body entities.PodRestartReport
+}
+
+// Start pod
+// swagger:response PodStartReport
+type swagStartPodResponse struct {
+ // in:body
+ Body entities.PodStartReport
+}
+
+// Rm pod
+// swagger:response PodRmReport
+type swagRmPodResponse struct {
+ // in:body
+ Body entities.PodRmReport
+}
+
+func ServeSwagger(w http.ResponseWriter, r *http.Request) {
+ path := DefaultPodmanSwaggerSpec
+ if p, found := os.LookupEnv("PODMAN_SWAGGER_SPEC"); found {
+ path = p
+ }
+ if _, err := os.Stat(path); err != nil {
+ if os.IsNotExist(err) {
+ utils.InternalServerError(w, errors.Errorf("file %q does not exist", path))
+ return
+ }
+ utils.InternalServerError(w, err)
+ return
+ }
+ w.Header().Set("Content-Type", "text/yaml")
+ http.ServeFile(w, r, path)
+}
diff --git a/pkg/api/handlers/libpod/types.go b/pkg/api/handlers/libpod/types.go
new file mode 100644
index 000000000..0949b2a72
--- /dev/null
+++ b/pkg/api/handlers/libpod/types.go
@@ -0,0 +1,82 @@
+package libpod
+
+import (
+ "github.com/containers/libpod/cmd/podman/shared"
+ "github.com/containers/libpod/libpod"
+ "github.com/cri-o/ocicni/pkg/ocicni"
+)
+
+// Listcontainer describes a container suitable for listing
+type ListContainer struct {
+ // Container command
+ Command []string
+ // Container creation time
+ Created int64
+ // If container has exited/stopped
+ Exited bool
+ // Time container exited
+ ExitedAt int64
+ // If container has exited, the return code from the command
+ ExitCode int32
+ // The unique identifier for the container
+ ID string `json:"Id"`
+ // Container image
+ Image string
+ // If this container is a Pod infra container
+ IsInfra bool
+ // Labels for container
+ Labels map[string]string
+ // User volume mounts
+ Mounts []string
+ // The names assigned to the container
+ Names []string
+ // Namespaces the container belongs to. Requires the
+ // namespace boolean to be true
+ Namespaces ListContainerNamespaces
+ // The process id of the container
+ Pid int
+ // If the container is part of Pod, the Pod ID. Requires the pod
+ // boolean to be set
+ Pod string
+ // If the container is part of Pod, the Pod name. Requires the pod
+ // boolean to be set
+ PodName string
+ // Port mappings
+ Ports []ocicni.PortMapping
+ // Size of the container rootfs. Requires the size boolean to be true
+ Size *shared.ContainerSize
+ // Time when container started
+ StartedAt int64
+ // State of container
+ State string
+}
+
+// ListContainer Namespaces contains the identifiers of the container's Linux namespaces
+type ListContainerNamespaces struct {
+ // Mount namespace
+ MNT string `json:"Mnt,omitempty"`
+ // Cgroup namespace
+ Cgroup string `json:"Cgroup,omitempty"`
+ // IPC namespace
+ IPC string `json:"Ipc,omitempty"`
+ // Network namespace
+ NET string `json:"Net,omitempty"`
+ // PID namespace
+ PIDNS string `json:"Pidns,omitempty"`
+ // UTS namespace
+ UTS string `json:"Uts,omitempty"`
+ // User namespace
+ User string `json:"User,omitempty"`
+}
+
+// sortContainers helps us set-up ability to sort by createTime
+type sortContainers []*libpod.Container
+
+func (a sortContainers) Len() int { return len(a) }
+func (a sortContainers) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+
+type psSortCreateTime struct{ sortContainers }
+
+func (a psSortCreateTime) Less(i, j int) bool {
+ return a.sortContainers[i].CreatedTime().Before(a.sortContainers[j].CreatedTime())
+}
diff --git a/pkg/api/handlers/libpod/volumes.go b/pkg/api/handlers/libpod/volumes.go
index 3e0e597c6..5a6fc021e 100644
--- a/pkg/api/handlers/libpod/volumes.go
+++ b/pkg/api/handlers/libpod/volumes.go
@@ -6,17 +6,15 @@ import (
"github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod"
- "github.com/containers/libpod/pkg/api/handlers"
+ "github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/api/handlers/utils"
- "github.com/gorilla/mux"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/domain/filters"
"github.com/gorilla/schema"
"github.com/pkg/errors"
- log "github.com/sirupsen/logrus"
)
func CreateVolume(w http.ResponseWriter, r *http.Request) {
- // 200 ok
- // 500 internal
var (
volumeOptions []libpod.VolumeCreateOption
runtime = r.Context().Value("runtime").(*libpod.Runtime)
@@ -26,13 +24,12 @@ func CreateVolume(w http.ResponseWriter, r *http.Request) {
}{
// override any golang type defaults
}
- input := handlers.VolumeCreateConfig{}
+ input := entities.VolumeCreateOptions{}
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
}
-
// decode params from body
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()"))
@@ -48,72 +45,124 @@ func CreateVolume(w http.ResponseWriter, r *http.Request) {
if len(input.Label) > 0 {
volumeOptions = append(volumeOptions, libpod.WithVolumeLabels(input.Label))
}
- if len(input.Opts) > 0 {
- parsedOptions, err := shared.ParseVolumeOptions(input.Opts)
+ if len(input.Options) > 0 {
+ parsedOptions, err := shared.ParseVolumeOptions(input.Options)
if err != nil {
utils.InternalServerError(w, err)
+ return
}
volumeOptions = append(volumeOptions, parsedOptions...)
}
vol, err := runtime.NewVolume(r.Context(), volumeOptions...)
if err != nil {
utils.InternalServerError(w, err)
+ return
+ }
+ config, err := vol.Config()
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
}
- utils.WriteResponse(w, http.StatusOK, vol.Name())
+ volResponse := entities.VolumeConfigResponse{
+ Name: config.Name,
+ Driver: config.Driver,
+ Mountpoint: config.MountPoint,
+ CreatedAt: config.CreatedTime,
+ Labels: config.Labels,
+ Options: config.Options,
+ UID: config.UID,
+ GID: config.GID,
+ }
+ utils.WriteResponse(w, http.StatusOK, volResponse)
}
func InspectVolume(w http.ResponseWriter, r *http.Request) {
var (
runtime = r.Context().Value("runtime").(*libpod.Runtime)
)
- name := mux.Vars(r)["name"]
+ name := utils.GetName(r)
vol, err := runtime.GetVolume(name)
if err != nil {
utils.VolumeNotFound(w, name, err)
+ return
}
- inspect, err := vol.Inspect()
- if err != nil {
- utils.InternalServerError(w, err)
+ volResponse := entities.VolumeConfigResponse{
+ Name: vol.Name(),
+ Driver: vol.Driver(),
+ Mountpoint: vol.MountPoint(),
+ CreatedAt: vol.CreatedTime(),
+ Labels: vol.Labels(),
+ Scope: vol.Scope(),
+ Options: vol.Options(),
+ UID: vol.UID(),
+ GID: vol.GID(),
}
- utils.WriteResponse(w, http.StatusOK, inspect)
+ utils.WriteResponse(w, http.StatusOK, volResponse)
}
func ListVolumes(w http.ResponseWriter, r *http.Request) {
- //var (
- // runtime = r.Context().Value("runtime").(*libpod.Runtime)
- // decoder = r.Context().Value("decoder").(*schema.Decoder)
- //)
- //query := struct {
- // Filter string `json:"filter"`
- //}{
- // // override any golang type defaults
- //}
- //
- //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
- //}
- /*
- This is all in main in cmd and needs to be extracted from there first.
- */
+ var (
+ decoder = r.Context().Value("decoder").(*schema.Decoder)
+ runtime = r.Context().Value("runtime").(*libpod.Runtime)
+ volumeConfigs []*entities.VolumeListReport
+ )
+ query := struct {
+ Filters map[string][]string `schema:"filters"`
+ }{
+ // override any golang type defaults
+ }
+ 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
+ }
+
+ volumeFilters, err := filters.GenerateVolumeFilters(query.Filters)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+
+ vols, err := runtime.Volumes(volumeFilters...)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ for _, v := range vols {
+ config := entities.VolumeConfigResponse{
+ Name: v.Name(),
+ Driver: v.Driver(),
+ Mountpoint: v.MountPoint(),
+ CreatedAt: v.CreatedTime(),
+ Labels: v.Labels(),
+ Scope: v.Scope(),
+ Options: v.Options(),
+ UID: v.UID(),
+ GID: v.GID(),
+ }
+ volumeConfigs = append(volumeConfigs, &entities.VolumeListReport{VolumeConfigResponse: config})
+ }
+ utils.WriteResponse(w, http.StatusOK, volumeConfigs)
}
func PruneVolumes(w http.ResponseWriter, r *http.Request) {
var (
runtime = r.Context().Value("runtime").(*libpod.Runtime)
+ reports []*entities.VolumePruneReport
)
- pruned, errs := runtime.PruneVolumes(r.Context())
- if errs != nil {
- if len(errs) > 1 {
- for _, err := range errs {
- log.Infof("Request Failed(%s): %s", http.StatusText(http.StatusInternalServerError), err.Error())
- }
- }
- utils.InternalServerError(w, errs[len(errs)-1])
+ pruned, err := runtime.PruneVolumes(r.Context())
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ for k, v := range pruned {
+ reports = append(reports, &entities.VolumePruneReport{
+ Err: v,
+ Id: k,
+ })
}
- utils.WriteResponse(w, http.StatusOK, pruned)
+ utils.WriteResponse(w, http.StatusOK, reports)
}
func RemoveVolume(w http.ResponseWriter, r *http.Request) {
@@ -132,13 +181,19 @@ func RemoveVolume(w http.ResponseWriter, r *http.Request) {
errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
return
}
- name := mux.Vars(r)["name"]
+ name := utils.GetName(r)
vol, err := runtime.LookupVolume(name)
if err != nil {
utils.VolumeNotFound(w, name, err)
+ return
}
if err := runtime.RemoveVolume(r.Context(), vol, query.Force); err != nil {
+ if errors.Cause(err) == define.ErrVolumeBeingUsed {
+ utils.Error(w, "volumes being used", http.StatusConflict, err)
+ return
+ }
utils.InternalServerError(w, err)
+ return
}
utils.WriteResponse(w, http.StatusNoContent, "")
}