summaryrefslogtreecommitdiff
path: root/pkg/api
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/api')
-rw-r--r--pkg/api/handlers/compat/containers.go125
-rw-r--r--pkg/api/handlers/compat/containers_restart.go42
-rw-r--r--pkg/api/handlers/compat/containers_stop.go44
-rw-r--r--pkg/api/handlers/libpod/containers.go6
-rw-r--r--pkg/api/handlers/utils/containers.go33
-rw-r--r--pkg/api/server/register_containers.go23
6 files changed, 195 insertions, 78 deletions
diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go
index 0f91a4362..b41987800 100644
--- a/pkg/api/handlers/compat/containers.go
+++ b/pkg/api/handlers/compat/containers.go
@@ -22,6 +22,7 @@ import (
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/go-connections/nat"
+ "github.com/docker/go-units"
"github.com/gorilla/mux"
"github.com/gorilla/schema"
"github.com/pkg/errors"
@@ -31,11 +32,11 @@ import (
func RemoveContainer(w http.ResponseWriter, r *http.Request) {
decoder := r.Context().Value("decoder").(*schema.Decoder)
query := struct {
- All bool `schema:"all"`
- Force bool `schema:"force"`
- Ignore bool `schema:"ignore"`
- Link bool `schema:"link"`
- Volumes bool `schema:"v"`
+ Force bool `schema:"force"`
+ Ignore bool `schema:"ignore"`
+ Link bool `schema:"link"`
+ DockerVolumes bool `schema:"v"`
+ LibpodVolumes bool `schema:"volumes"`
}{
// override any golang type defaults
}
@@ -46,10 +47,19 @@ func RemoveContainer(w http.ResponseWriter, r *http.Request) {
return
}
- if query.Link && !utils.IsLibpodRequest(r) {
- utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
- utils.ErrLinkNotSupport)
- return
+ options := entities.RmOptions{
+ Force: query.Force,
+ Ignore: query.Ignore,
+ }
+ if utils.IsLibpodRequest(r) {
+ options.Volumes = query.LibpodVolumes
+ } else {
+ if query.Link {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ utils.ErrLinkNotSupport)
+ return
+ }
+ options.Volumes = query.DockerVolumes
}
runtime := r.Context().Value("runtime").(*libpod.Runtime)
@@ -57,12 +67,6 @@ func RemoveContainer(w http.ResponseWriter, r *http.Request) {
// code.
containerEngine := abi.ContainerEngine{Libpod: runtime}
name := utils.GetName(r)
- options := entities.RmOptions{
- All: query.All,
- Force: query.Force,
- Volumes: query.Volumes,
- Ignore: query.Ignore,
- }
report, err := containerEngine.ContainerRm(r.Context(), []string{name}, options)
if err != nil {
if errors.Cause(err) == define.ErrNoSuchCtr {
@@ -193,45 +197,48 @@ func KillContainer(w http.ResponseWriter, r *http.Request) {
return
}
- sig, err := signal.ParseSignalNameOrNumber(query.Signal)
- if err != nil {
- utils.InternalServerError(w, err)
- return
- }
+ // Now use the ABI implementation to prevent us from having duplicate
+ // code.
+ containerEngine := abi.ContainerEngine{Libpod: runtime}
name := utils.GetName(r)
- con, err := runtime.LookupContainer(name)
- if err != nil {
- utils.ContainerNotFound(w, name, err)
- return
+ options := entities.KillOptions{
+ Signal: query.Signal,
}
-
- state, err := con.State()
+ report, err := containerEngine.ContainerKill(r.Context(), []string{name}, options)
if err != nil {
- utils.InternalServerError(w, err)
- return
- }
+ if errors.Cause(err) == define.ErrCtrStateInvalid ||
+ errors.Cause(err) == define.ErrCtrStopped {
+ utils.Error(w, fmt.Sprintf("Container %s is not running", name), http.StatusConflict, err)
+ return
+ }
+ if errors.Cause(err) == define.ErrNoSuchCtr {
+ utils.ContainerNotFound(w, name, err)
+ return
+ }
- // If the Container is stopped already, send a 409
- if state == define.ContainerStateStopped || state == define.ContainerStateExited {
- utils.Error(w, fmt.Sprintf("Container %s is not running", name), http.StatusConflict, errors.New(fmt.Sprintf("Cannot kill Container %s, it is not running", name)))
+ utils.InternalServerError(w, err)
return
}
- signal := uint(sig)
-
- err = con.Kill(signal)
- if err != nil {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "unable to kill Container %s", name))
+ if len(report) > 0 && report[0].Err != nil {
+ utils.InternalServerError(w, report[0].Err)
return
}
-
// Docker waits for the container to stop if the signal is 0 or
// SIGKILL.
- if !utils.IsLibpodRequest(r) && (signal == 0 || syscall.Signal(signal) == syscall.SIGKILL) {
- if _, err = con.Wait(); err != nil {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "failed to wait for Container %s", con.ID()))
+ if !utils.IsLibpodRequest(r) {
+ sig, err := signal.ParseSignalNameOrNumber(query.Signal)
+ if err != nil {
+ utils.InternalServerError(w, err)
return
}
+ if sig == 0 || syscall.Signal(sig) == syscall.SIGKILL {
+ var opts entities.WaitOptions
+ if _, err := containerEngine.ContainerWait(r.Context(), []string{name}, opts); err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
+ return
+ }
+ }
}
// Success
utils.WriteResponse(w, http.StatusNoContent, nil)
@@ -242,6 +249,10 @@ func WaitContainer(w http.ResponseWriter, r *http.Request) {
// /{version}/containers/(name)/wait
exitCode, err := utils.WaitContainer(w, r)
if err != nil {
+ if errors.Cause(err) == define.ErrNoSuchCtr {
+ logrus.Warnf("container not found %q: %v", utils.GetName(r), err)
+ return
+ }
logrus.Warnf("failed to wait on container %q: %v", mux.Vars(r)["name"], err)
return
}
@@ -264,6 +275,7 @@ func LibpodToContainer(l *libpod.Container, sz bool) (*handlers.Container, error
sizeRootFs int64
sizeRW int64
state define.ContainerStatus
+ status string
)
if state, err = l.State(); err != nil {
@@ -274,6 +286,35 @@ func LibpodToContainer(l *libpod.Container, sz bool) (*handlers.Container, error
stateStr = "created"
}
+ if state == define.ContainerStateConfigured || state == define.ContainerStateCreated {
+ status = "Created"
+ } else if state == define.ContainerStateStopped || state == define.ContainerStateExited {
+ exitCode, _, err := l.ExitCode()
+ if err != nil {
+ return nil, err
+ }
+ finishedTime, err := l.FinishedTime()
+ if err != nil {
+ return nil, err
+ }
+ status = fmt.Sprintf("Exited (%d) %s ago", exitCode, units.HumanDuration(time.Since(finishedTime)))
+ } else if state == define.ContainerStateRunning || state == define.ContainerStatePaused {
+ startedTime, err := l.StartedTime()
+ if err != nil {
+ return nil, err
+ }
+ status = fmt.Sprintf("Up %s", units.HumanDuration(time.Since(startedTime)))
+ if state == define.ContainerStatePaused {
+ status += " (Paused)"
+ }
+ } else if state == define.ContainerStateRemoving {
+ status = "Removal In Progress"
+ } else if state == define.ContainerStateStopping {
+ status = "Stopping"
+ } else {
+ status = "Unknown"
+ }
+
if sz {
if sizeRW, err = l.RWSize(); err != nil {
return nil, err
@@ -295,7 +336,7 @@ func LibpodToContainer(l *libpod.Container, sz bool) (*handlers.Container, error
SizeRootFs: sizeRootFs,
Labels: l.Labels(),
State: stateStr,
- Status: "",
+ Status: status,
HostConfig: struct {
NetworkMode string `json:",omitempty"`
}{
diff --git a/pkg/api/handlers/compat/containers_restart.go b/pkg/api/handlers/compat/containers_restart.go
index e8928596a..70edfcbb3 100644
--- a/pkg/api/handlers/compat/containers_restart.go
+++ b/pkg/api/handlers/compat/containers_restart.go
@@ -4,7 +4,10 @@ import (
"net/http"
"github.com/containers/podman/v2/libpod"
+ "github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/pkg/api/handlers/utils"
+ "github.com/containers/podman/v2/pkg/domain/entities"
+ "github.com/containers/podman/v2/pkg/domain/infra/abi"
"github.com/gorilla/schema"
"github.com/pkg/errors"
)
@@ -12,34 +15,49 @@ import (
func RestartContainer(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
decoder := r.Context().Value("decoder").(*schema.Decoder)
+ // Now use the ABI implementation to prevent us from having duplicate
+ // code.
+ containerEngine := abi.ContainerEngine{Libpod: runtime}
+
// /{version}/containers/(name)/restart
query := struct {
- Timeout int `schema:"t"`
+ All bool `schema:"all"`
+ DockerTimeout uint `schema:"t"`
+ LibpodTimeout uint `schema:"timeout"`
}{
- // Override golang default values for types
+ // override any golang type defaults
}
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
- utils.BadRequest(w, "url", r.URL.String(), errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
+ 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)
- con, err := runtime.LookupContainer(name)
- if err != nil {
- utils.ContainerNotFound(w, name, err)
- return
- }
- timeout := con.StopTimeout()
- if _, found := r.URL.Query()["t"]; found {
- timeout = uint(query.Timeout)
+ options := entities.RestartOptions{
+ All: query.All,
+ Timeout: &query.DockerTimeout,
+ }
+ if utils.IsLibpodRequest(r) {
+ options.Timeout = &query.LibpodTimeout
}
+ report, err := containerEngine.ContainerRestart(r.Context(), []string{name}, options)
+ if err != nil {
+ if errors.Cause(err) == define.ErrNoSuchCtr {
+ utils.ContainerNotFound(w, name, err)
+ return
+ }
- if err := con.RestartWithTimeout(r.Context(), timeout); err != nil {
utils.InternalServerError(w, err)
return
}
+ if len(report) > 0 && report[0].Err != nil {
+ utils.InternalServerError(w, report[0].Err)
+ return
+ }
+
// Success
utils.WriteResponse(w, http.StatusNoContent, nil)
}
diff --git a/pkg/api/handlers/compat/containers_stop.go b/pkg/api/handlers/compat/containers_stop.go
index 8bc58cf59..000685aa0 100644
--- a/pkg/api/handlers/compat/containers_stop.go
+++ b/pkg/api/handlers/compat/containers_stop.go
@@ -6,6 +6,8 @@ import (
"github.com/containers/podman/v2/libpod"
"github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/pkg/api/handlers/utils"
+ "github.com/containers/podman/v2/pkg/domain/entities"
+ "github.com/containers/podman/v2/pkg/domain/infra/abi"
"github.com/gorilla/schema"
"github.com/pkg/errors"
)
@@ -13,10 +15,15 @@ import (
func StopContainer(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
decoder := r.Context().Value("decoder").(*schema.Decoder)
+ // Now use the ABI implementation to prevent us from having duplicate
+ // code.
+ containerEngine := abi.ContainerEngine{Libpod: runtime}
// /{version}/containers/(name)/stop
query := struct {
- Timeout int `schema:"t"`
+ Ignore bool `schema:"ignore"`
+ DockerTimeout uint `schema:"t"`
+ LibpodTimeout uint `schema:"timeout"`
}{
// override any golang type defaults
}
@@ -27,31 +34,46 @@ func StopContainer(w http.ResponseWriter, r *http.Request) {
}
name := utils.GetName(r)
+
+ options := entities.StopOptions{
+ Ignore: query.Ignore,
+ }
+ if utils.IsLibpodRequest(r) {
+ if query.LibpodTimeout > 0 {
+ options.Timeout = &query.LibpodTimeout
+ }
+ } else {
+ if query.DockerTimeout > 0 {
+ options.Timeout = &query.DockerTimeout
+ }
+ }
con, err := runtime.LookupContainer(name)
if err != nil {
utils.ContainerNotFound(w, name, err)
return
}
-
state, err := con.State()
if err != nil {
- utils.InternalServerError(w, errors.Wrapf(err, "unable to get state for Container %s", name))
+ utils.InternalServerError(w, err)
return
}
- // If the Container is stopped already, send a 304
if state == define.ContainerStateStopped || state == define.ContainerStateExited {
utils.WriteResponse(w, http.StatusNotModified, nil)
return
}
+ report, err := containerEngine.ContainerStop(r.Context(), []string{name}, options)
+ if err != nil {
+ if errors.Cause(err) == define.ErrNoSuchCtr {
+ utils.ContainerNotFound(w, name, err)
+ return
+ }
- var stopError error
- if query.Timeout > 0 {
- stopError = con.StopWithTimeout(uint(query.Timeout))
- } else {
- stopError = con.Stop()
+ utils.InternalServerError(w, err)
+ return
}
- if stopError != nil {
- utils.InternalServerError(w, errors.Wrapf(stopError, "failed to stop %s", name))
+
+ if len(report) > 0 && report[0].Err != nil {
+ utils.InternalServerError(w, report[0].Err)
return
}
diff --git a/pkg/api/handlers/libpod/containers.go b/pkg/api/handlers/libpod/containers.go
index a0cb1d49e..f6e348cef 100644
--- a/pkg/api/handlers/libpod/containers.go
+++ b/pkg/api/handlers/libpod/containers.go
@@ -148,6 +148,12 @@ func GetContainer(w http.ResponseWriter, r *http.Request) {
func WaitContainer(w http.ResponseWriter, r *http.Request) {
exitCode, err := utils.WaitContainer(w, r)
if err != nil {
+ name := utils.GetName(r)
+ if errors.Cause(err) == define.ErrNoSuchCtr {
+ utils.ContainerNotFound(w, name, err)
+ return
+ }
+ logrus.Warnf("failed to wait on container %q: %v", name, err)
return
}
utils.WriteResponse(w, http.StatusOK, strconv.Itoa(int(exitCode)))
diff --git a/pkg/api/handlers/utils/containers.go b/pkg/api/handlers/utils/containers.go
index 1439a3a75..fac237f87 100644
--- a/pkg/api/handlers/utils/containers.go
+++ b/pkg/api/handlers/utils/containers.go
@@ -6,6 +6,8 @@ import (
"github.com/containers/podman/v2/libpod"
"github.com/containers/podman/v2/libpod/define"
+ "github.com/containers/podman/v2/pkg/domain/entities"
+ "github.com/containers/podman/v2/pkg/domain/infra/abi"
"github.com/gorilla/schema"
"github.com/pkg/errors"
)
@@ -16,10 +18,13 @@ func WaitContainer(w http.ResponseWriter, r *http.Request) (int32, error) {
interval time.Duration
)
runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ // Now use the ABI implementation to prevent us from having duplicate
+ // code.
+ containerEngine := abi.ContainerEngine{Libpod: runtime}
decoder := r.Context().Value("decoder").(*schema.Decoder)
query := struct {
- Interval string `schema:"interval"`
- Condition string `schema:"condition"`
+ Interval string `schema:"interval"`
+ Condition define.ContainerStatus `schema:"condition"`
}{
// Override golang default values for types
}
@@ -27,6 +32,10 @@ func WaitContainer(w http.ResponseWriter, r *http.Request) (int32, error) {
Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
return 0, err
}
+ options := entities.WaitOptions{
+ Condition: define.ContainerStateStopped,
+ }
+ name := GetName(r)
if _, found := r.URL.Query()["interval"]; found {
interval, err = time.ParseDuration(query.Interval)
if err != nil {
@@ -40,19 +49,19 @@ func WaitContainer(w http.ResponseWriter, r *http.Request) (int32, error) {
return 0, err
}
}
- condition := define.ContainerStateStopped
+ options.Interval = interval
+
if _, found := r.URL.Query()["condition"]; found {
- condition, err = define.StringToContainerStatus(query.Condition)
- if err != nil {
- InternalServerError(w, err)
- return 0, err
- }
+ options.Condition = query.Condition
}
- name := GetName(r)
- con, err := runtime.LookupContainer(name)
+
+ report, err := containerEngine.ContainerWait(r.Context(), []string{name}, options)
if err != nil {
- ContainerNotFound(w, name, err)
return 0, err
}
- return con.WaitForConditionWithInterval(interval, condition)
+ if len(report) == 0 {
+ InternalServerError(w, errors.New("No reports returned"))
+ return 0, err
+ }
+ return report[0].ExitCode, report[0].Error
}
diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go
index e30747800..ff1781d1e 100644
--- a/pkg/api/server/register_containers.go
+++ b/pkg/api/server/register_containers.go
@@ -199,6 +199,11 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
// required: true
// description: the name or ID of the container
// - in: query
+ // name: all
+ // type: boolean
+ // default: false
+ // description: Send kill signal to all containers
+ // - in: query
// name: signal
// type: string
// default: TERM
@@ -486,6 +491,11 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
// - paused
// - running
// - stopped
+ // - in: query
+ // name: interval
+ // type: string
+ // default: "250ms"
+ // description: Time Interval to wait before polling for completion.
// produces:
// - application/json
// responses:
@@ -1219,9 +1229,20 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
// required: true
// description: the name or ID of the container
// - in: query
- // name: t
+ // name: all
+ // type: boolean
+ // default: false
+ // description: Stop all containers
+ // - in: query
+ // name: timeout
// type: integer
+ // default: 10
// description: number of seconds to wait before killing container
+ // - in: query
+ // name: Ignore
+ // type: boolean
+ // default: false
+ // description: do not return error if container is already stopped
// produces:
// - application/json
// responses: