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.go186
-rw-r--r--pkg/api/handlers/libpod/healthcheck.go25
-rw-r--r--pkg/api/handlers/libpod/images.go165
-rw-r--r--pkg/api/handlers/libpod/pods.go440
-rw-r--r--pkg/api/handlers/libpod/volumes.go144
5 files changed, 960 insertions, 0 deletions
diff --git a/pkg/api/handlers/libpod/containers.go b/pkg/api/handlers/libpod/containers.go
new file mode 100644
index 000000000..bfb028b1b
--- /dev/null
+++ b/pkg/api/handlers/libpod/containers.go
@@ -0,0 +1,186 @@
+package libpod
+
+import (
+ "net/http"
+
+ "github.com/containers/libpod/cmd/podman/shared"
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/api/handlers"
+ "github.com/containers/libpod/pkg/api/handlers/utils"
+ "github.com/gorilla/mux"
+ "github.com/gorilla/schema"
+ "github.com/pkg/errors"
+)
+
+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"]
+ _, err := runtime.LookupContainer(name)
+ if err != nil {
+ utils.ContainerNotFound(w, name, err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusNoContent, "")
+}
+
+func RemoveContainer(w http.ResponseWriter, r *http.Request) {
+ // 204 no error
+ // 400 bad param
+ // 404 no such container
+ // 409 conflict
+ // 500 internal error
+ 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) {
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+ query := struct {
+ Filter []string `schema:"filter"`
+ Last int `schema:"last"`
+ Size bool `schema:"size"`
+ Sync bool `schema:"sync"`
+ }{
+ // 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
+ }
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ opts := shared.PsOptions{
+ All: true,
+ Last: query.Last,
+ Size: query.Size,
+ Sort: "",
+ Namespace: true,
+ Sync: query.Sync,
+ }
+
+ pss, err := shared.GetPsContainerOutput(runtime, opts, query.Filter, 2)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ }
+ utils.WriteResponse(w, http.StatusOK, pss)
+}
+
+func GetContainer(w http.ResponseWriter, r *http.Request) {
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+ query := struct {
+ Size bool `schema:"size"`
+ }{
+ // 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
+ }
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ name := mux.Vars(r)["name"]
+ container, err := runtime.LookupContainer(name)
+ if err != nil {
+ utils.ContainerNotFound(w, name, err)
+ return
+ }
+ data, err := container.Inspect(query.Size)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ 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) {
+ _, err := utils.WaitContainer(w, r)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusNoContent, "")
+}
+
+func PruneContainers(w http.ResponseWriter, r *http.Request) {
+ // TODO Needs rebase to get filers; Also would be handy to define
+ // an actual libpod container prune method.
+ // force
+ // filters
+}
+
+func LogsFromContainer(w http.ResponseWriter, r *http.Request) {
+ // follow
+ // since
+ // timestamps
+ // tail string
+}
+func StatsContainer(w http.ResponseWriter, r *http.Request) {
+ //stream
+}
+func CreateContainer(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"]
+ conn, err := runtime.LookupContainer(name)
+ if err != nil {
+ utils.ContainerNotFound(w, name, err)
+ return
+ }
+ m, err := conn.Mount()
+ if err != nil {
+ utils.InternalServerError(w, err)
+ }
+ utils.WriteResponse(w, http.StatusOK, m)
+}
+
+func ShowMountedContainers(w http.ResponseWriter, r *http.Request) {
+ response := make(map[string]string)
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ conns, err := runtime.GetAllContainers()
+ if err != nil {
+ utils.InternalServerError(w, err)
+ }
+ for _, conn := range conns {
+ mounted, mountPoint, err := conn.Mounted()
+ if err != nil {
+ utils.InternalServerError(w, err)
+ }
+ if !mounted {
+ continue
+ }
+ response[conn.ID()] = mountPoint
+ }
+ utils.WriteResponse(w, http.StatusOK, response)
+}
diff --git a/pkg/api/handlers/libpod/healthcheck.go b/pkg/api/handlers/libpod/healthcheck.go
new file mode 100644
index 000000000..0d7bf3ea7
--- /dev/null
+++ b/pkg/api/handlers/libpod/healthcheck.go
@@ -0,0 +1,25 @@
+package libpod
+
+import (
+ "net/http"
+
+ "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"]
+ status, err := runtime.HealthCheck(name)
+ if err != nil {
+ if status == libpod.HealthCheckContainerNotFound {
+ utils.ContainerNotFound(w, name, err)
+ }
+ utils.InternalServerError(w, err)
+ }
+ utils.WriteResponse(w, http.StatusOK, status)
+}
diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go
new file mode 100644
index 000000000..0d4e220a8
--- /dev/null
+++ b/pkg/api/handlers/libpod/images.go
@@ -0,0 +1,165 @@
+package libpod
+
+import (
+ "io/ioutil"
+ "net/http"
+ "os"
+
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/api/handlers"
+ "github.com/containers/libpod/pkg/api/handlers/utils"
+ "github.com/gorilla/mux"
+ "github.com/gorilla/schema"
+ "github.com/pkg/errors"
+)
+
+// Commit
+// author string
+// "container"
+// repo string
+// tag string
+// message
+// pause bool
+// changes []string
+
+// 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"]
+
+ _, 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
+ }
+ utils.WriteResponse(w, http.StatusNoContent, "")
+}
+
+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)
+}
+
+func GetImage(w http.ResponseWriter, r *http.Request) {
+ name := mux.Vars(r)["name"]
+ newImage, err := handlers.GetImage(r, name)
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusNotFound, errors.Wrapf(err, "Failed to find image %s", name))
+ return
+ }
+ inspect, err := newImage.Inspect(r.Context())
+ if err != nil {
+ utils.Error(w, "Server error", http.StatusInternalServerError, errors.Wrapf(err, "failed in inspect image %s", inspect.ID))
+ 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))
+ for j, img := range images {
+ is, err := handlers.ImageToImageSummary(img)
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Failed transform image summaries"))
+ return
+ }
+ // libpod has additional fields that we need to populate.
+ is.CreatedTime = img.Created()
+ is.ReadOnly = img.IsReadOnly()
+ summaries[j] = is
+ }
+ utils.WriteResponse(w, http.StatusOK, summaries)
+}
+
+func PruneImages(w http.ResponseWriter, r *http.Request) {
+ // 200 ok
+ // 500 internal
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+ query := struct {
+ All bool `schema:"all"`
+ Filters []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
+ }
+ cids, err := runtime.ImageRuntime().PruneImages(r.Context(), query.All, query.Filters)
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusOK, cids)
+}
+
+func ExportImage(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+ query := struct {
+ Compress bool `schema:"compress"`
+ Format string `schema:"format"`
+ }{
+ // 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 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"))
+ return
+ }
+ if err := tmpfile.Close(); err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to close tempfile"))
+ return
+ }
+ name := mux.Vars(r)["name"]
+ 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
+ }
+ rdr, err := os.Open(tmpfile.Name())
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to read the exported tarfile"))
+ return
+ }
+ defer rdr.Close()
+ defer os.Remove(tmpfile.Name())
+ utils.WriteResponse(w, http.StatusOK, rdr)
+}
diff --git a/pkg/api/handlers/libpod/pods.go b/pkg/api/handlers/libpod/pods.go
new file mode 100644
index 000000000..daaf9d018
--- /dev/null
+++ b/pkg/api/handlers/libpod/pods.go
@@ -0,0 +1,440 @@
+package libpod
+
+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/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()"))
+ 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...)
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusCreated, handlers.IDResponse{ID: pod.CgroupParent()})
+}
+
+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 []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
+ }
+ if len(query.filters) > 0 {
+ utils.Error(w, "filters are not implemented yet", http.StatusInternalServerError, define.ErrNotImplemented)
+ return
+ }
+ pods, err := runtime.GetAllPods()
+ 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)
+}
+
+func PodInspect(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ name := mux.Vars(r)["name"]
+ pod, err := runtime.LookupPod(name)
+ if err != nil {
+ utils.PodNotFound(w, name, err)
+ return
+ }
+ podData, err := pod.Inspect()
+ if err != nil {
+ utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusOK, podData)
+}
+
+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)
+ )
+ query := struct {
+ timeout int `schema:"t"`
+ }{
+ // 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
+ }
+ allContainersStopped := true
+ name := mux.Vars(r)["name"]
+ 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()
+ 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)
+ return
+ }
+
+ if query.timeout > 0 {
+ _, stopError = pod.StopWithTimeout(r.Context(), false, query.timeout)
+ } else {
+ _, stopError = pod.Stop(r.Context(), false)
+ }
+ if stopError != nil {
+ utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusOK, "")
+}
+
+func PodStart(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ allContainersRunning := true
+ name := mux.Vars(r)["name"]
+ 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()
+ 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)
+ return
+ }
+ if _, err := pod.Start(r.Context()); err != nil {
+ utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusOK, "")
+}
+
+func PodDelete(w http.ResponseWriter, r *http.Request) {
+ var (
+ 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
+ }
+ name := mux.Vars(r)["name"]
+ 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 {
+ utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusNoContent, "")
+}
+
+func PodRestart(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ name := mux.Vars(r)["name"]
+ pod, err := runtime.LookupPod(name)
+ if err != nil {
+ utils.PodNotFound(w, name, err)
+ return
+ }
+ _, err = pod.Restart(r.Context())
+ if err != nil {
+ utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusOK, "")
+}
+
+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"))
+ 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, "")
+}
+
+func PodPause(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ name := mux.Vars(r)["name"]
+ pod, err := runtime.LookupPod(name)
+ if err != nil {
+ utils.PodNotFound(w, name, err)
+ return
+ }
+ _, err = pod.Pause()
+ if err != nil {
+ utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusNoContent, "")
+}
+
+func PodUnpause(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"]
+ pod, err := runtime.LookupPod(name)
+ if err != nil {
+ utils.PodNotFound(w, name, err)
+ return
+ }
+ _, err = pod.Unpause()
+ if err != nil {
+ utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusOK, "")
+}
+
+func PodKill(w http.ResponseWriter, r *http.Request) {
+ var (
+ runtime = r.Context().Value("runtime").(*libpod.Runtime)
+ decoder = r.Context().Value("decoder").(*schema.Decoder)
+ )
+ query := struct {
+ signal int `schema:"signal"`
+ }{
+ // 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 := mux.Vars(r)["name"]
+ pod, err := runtime.LookupPod(name)
+ if err != nil {
+ utils.PodNotFound(w, name, err)
+ return
+ }
+ podStates, err := pod.Status()
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
+ return
+ }
+ hasRunning := false
+ for _, s := range podStates {
+ if s == define.ContainerStateRunning {
+ hasRunning = true
+ break
+ }
+ }
+ if !hasRunning {
+ msg := fmt.Sprintf("Container %s is not running", pod.ID())
+ utils.Error(w, msg, http.StatusConflict, errors.Errorf("cannot kill a pod with no running containers: %s", pod.ID()))
+ return
+ }
+ // TODO How do we differentiate if a signal was sent vs accepting the pod/container default?
+ _, err = pod.Kill(uint(query.signal))
+ if err != nil {
+ utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusOK, "")
+}
+
+func PodExists(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ name := mux.Vars(r)["name"]
+ _, err := runtime.LookupPod(name)
+ if err != nil {
+ utils.PodNotFound(w, name, err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusOK, "")
+}
diff --git a/pkg/api/handlers/libpod/volumes.go b/pkg/api/handlers/libpod/volumes.go
new file mode 100644
index 000000000..3e0e597c6
--- /dev/null
+++ b/pkg/api/handlers/libpod/volumes.go
@@ -0,0 +1,144 @@
+package libpod
+
+import (
+ "encoding/json"
+ "net/http"
+
+ "github.com/containers/libpod/cmd/podman/shared"
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/api/handlers"
+ "github.com/containers/libpod/pkg/api/handlers/utils"
+ "github.com/gorilla/mux"
+ "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)
+ decoder = r.Context().Value("decoder").(*schema.Decoder)
+ )
+ query := struct {
+ }{
+ // override any golang type defaults
+ }
+ input := handlers.VolumeCreateConfig{}
+ 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()"))
+ return
+ }
+
+ if len(input.Name) > 0 {
+ volumeOptions = append(volumeOptions, libpod.WithVolumeName(input.Name))
+ }
+ if len(input.Driver) > 0 {
+ volumeOptions = append(volumeOptions, libpod.WithVolumeDriver(input.Driver))
+ }
+ if len(input.Label) > 0 {
+ volumeOptions = append(volumeOptions, libpod.WithVolumeLabels(input.Label))
+ }
+ if len(input.Opts) > 0 {
+ parsedOptions, err := shared.ParseVolumeOptions(input.Opts)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ }
+ volumeOptions = append(volumeOptions, parsedOptions...)
+ }
+ vol, err := runtime.NewVolume(r.Context(), volumeOptions...)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ }
+ utils.WriteResponse(w, http.StatusOK, vol.Name())
+}
+
+func InspectVolume(w http.ResponseWriter, r *http.Request) {
+ var (
+ runtime = r.Context().Value("runtime").(*libpod.Runtime)
+ )
+ name := mux.Vars(r)["name"]
+ vol, err := runtime.GetVolume(name)
+ if err != nil {
+ utils.VolumeNotFound(w, name, err)
+ }
+ inspect, err := vol.Inspect()
+ if err != nil {
+ utils.InternalServerError(w, err)
+ }
+ utils.WriteResponse(w, http.StatusOK, inspect)
+}
+
+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.
+ */
+
+}
+
+func PruneVolumes(w http.ResponseWriter, r *http.Request) {
+ var (
+ runtime = r.Context().Value("runtime").(*libpod.Runtime)
+ )
+ 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])
+ }
+ utils.WriteResponse(w, http.StatusOK, pruned)
+}
+
+func RemoveVolume(w http.ResponseWriter, r *http.Request) {
+ var (
+ 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
+ }
+ name := mux.Vars(r)["name"]
+ vol, err := runtime.LookupVolume(name)
+ if err != nil {
+ utils.VolumeNotFound(w, name, err)
+ }
+ if err := runtime.RemoveVolume(r.Context(), vol, query.Force); err != nil {
+ utils.InternalServerError(w, err)
+ }
+ utils.WriteResponse(w, http.StatusNoContent, "")
+}