mirror of
https://github.com/containers/podman.git
synced 2025-06-05 22:31:06 +08:00

during container creation, if no network is provided, we need to add a default value so the container can be later started. use apiv2 container creation for RunTopContainer instead of an exec to the system podman. RunTopContainer now also returns the container id and an error. added a libpod commit endpoint. also, changed the use of the connections and bindings slightly to make it more convenient to write tests. Fixes: 5366 Signed-off-by: Brent Baude <bbaude@redhat.com>
244 lines
6.6 KiB
Go
244 lines
6.6 KiB
Go
package handlers
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"github.com/containers/libpod/libpod"
|
|
"github.com/containers/libpod/libpod/define"
|
|
"github.com/containers/libpod/pkg/api/handlers/utils"
|
|
"github.com/docker/docker/api/types"
|
|
"github.com/gorilla/schema"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
func StopContainer(w http.ResponseWriter, r *http.Request) {
|
|
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
|
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
|
|
|
// /{version}/containers/(name)/stop
|
|
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
|
|
}
|
|
|
|
name := utils.GetName(r)
|
|
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))
|
|
return
|
|
}
|
|
// If the Container is stopped already, send a 304
|
|
if state == define.ContainerStateStopped || state == define.ContainerStateExited {
|
|
utils.WriteResponse(w, http.StatusNotModified, "")
|
|
return
|
|
}
|
|
|
|
var stopError error
|
|
if query.Timeout > 0 {
|
|
stopError = con.StopWithTimeout(uint(query.Timeout))
|
|
} else {
|
|
stopError = con.Stop()
|
|
}
|
|
if stopError != nil {
|
|
utils.InternalServerError(w, errors.Wrapf(stopError, "failed to stop %s", name))
|
|
return
|
|
}
|
|
|
|
// Success
|
|
utils.WriteResponse(w, http.StatusNoContent, "")
|
|
}
|
|
|
|
func UnpauseContainer(w http.ResponseWriter, r *http.Request) {
|
|
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
|
|
|
// /{version}/containers/(name)/unpause
|
|
name := utils.GetName(r)
|
|
con, err := runtime.LookupContainer(name)
|
|
if err != nil {
|
|
utils.ContainerNotFound(w, name, err)
|
|
return
|
|
}
|
|
|
|
if err := con.Unpause(); err != nil {
|
|
utils.InternalServerError(w, err)
|
|
return
|
|
}
|
|
|
|
// Success
|
|
utils.WriteResponse(w, http.StatusNoContent, "")
|
|
}
|
|
|
|
func PauseContainer(w http.ResponseWriter, r *http.Request) {
|
|
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
|
|
|
// /{version}/containers/(name)/pause
|
|
name := utils.GetName(r)
|
|
con, err := runtime.LookupContainer(name)
|
|
if err != nil {
|
|
utils.ContainerNotFound(w, name, err)
|
|
return
|
|
}
|
|
|
|
// the api does not error if the Container is already paused, so just into it
|
|
if err := con.Pause(); err != nil {
|
|
utils.InternalServerError(w, err)
|
|
return
|
|
}
|
|
// Success
|
|
utils.WriteResponse(w, http.StatusNoContent, "")
|
|
}
|
|
|
|
func StartContainer(w http.ResponseWriter, r *http.Request) {
|
|
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
|
query := struct {
|
|
DetachKeys string `schema:"detachKeys"`
|
|
}{
|
|
// Override golang default values for types
|
|
}
|
|
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
|
|
}
|
|
if len(query.DetachKeys) > 0 {
|
|
// TODO - start does not support adding detach keys
|
|
utils.Error(w, "Something went wrong", http.StatusBadRequest, errors.New("the detachKeys parameter is not supported yet"))
|
|
return
|
|
}
|
|
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
|
name := utils.GetName(r)
|
|
con, err := runtime.LookupContainer(name)
|
|
if err != nil {
|
|
utils.ContainerNotFound(w, name, err)
|
|
return
|
|
}
|
|
|
|
state, err := con.State()
|
|
if err != nil {
|
|
utils.InternalServerError(w, err)
|
|
return
|
|
}
|
|
if state == define.ContainerStateRunning {
|
|
utils.WriteResponse(w, http.StatusNotModified, "")
|
|
return
|
|
}
|
|
if err := con.Start(r.Context(), false); err != nil {
|
|
utils.InternalServerError(w, err)
|
|
return
|
|
}
|
|
utils.WriteResponse(w, http.StatusNoContent, "")
|
|
}
|
|
|
|
func RestartContainer(w http.ResponseWriter, r *http.Request) {
|
|
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
|
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
|
// /{version}/containers/(name)/restart
|
|
query := struct {
|
|
Timeout int `schema:"t"`
|
|
}{
|
|
// Override golang default values for types
|
|
}
|
|
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
|
|
}
|
|
|
|
name := utils.GetName(r)
|
|
con, err := runtime.LookupContainer(name)
|
|
if err != nil {
|
|
utils.ContainerNotFound(w, name, err)
|
|
return
|
|
}
|
|
|
|
state, err := con.State()
|
|
if err != nil {
|
|
utils.InternalServerError(w, err)
|
|
return
|
|
}
|
|
|
|
// FIXME: This is not in the swagger.yml...
|
|
// If the Container is stopped already, send a 409
|
|
if state == define.ContainerStateStopped || state == define.ContainerStateExited {
|
|
msg := fmt.Sprintf("Container %s is not running", name)
|
|
utils.Error(w, msg, http.StatusConflict, errors.New(msg))
|
|
return
|
|
}
|
|
|
|
timeout := con.StopTimeout()
|
|
if _, found := r.URL.Query()["t"]; found {
|
|
timeout = uint(query.Timeout)
|
|
}
|
|
|
|
if err := con.RestartWithTimeout(r.Context(), timeout); err != nil {
|
|
utils.InternalServerError(w, err)
|
|
return
|
|
}
|
|
|
|
// Success
|
|
utils.WriteResponse(w, http.StatusNoContent, "")
|
|
}
|
|
|
|
func PruneContainers(w http.ResponseWriter, r *http.Request) {
|
|
var (
|
|
delContainers []string
|
|
space int64
|
|
)
|
|
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
|
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
|
|
|
query := struct {
|
|
Filters map[string][]string `schema:"filter"`
|
|
}{}
|
|
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
|
|
}
|
|
|
|
filterFuncs, err := utils.GenerateFilterFuncsFromMap(runtime, query.Filters)
|
|
if err != nil {
|
|
utils.InternalServerError(w, err)
|
|
return
|
|
}
|
|
prunedContainers, pruneErrors, err := runtime.PruneContainers(filterFuncs)
|
|
if err != nil {
|
|
utils.InternalServerError(w, err)
|
|
return
|
|
}
|
|
|
|
// Libpod response differs
|
|
if utils.IsLibpodRequest(r) {
|
|
var response []LibpodContainersPruneReport
|
|
for ctrID, size := range prunedContainers {
|
|
response = append(response, LibpodContainersPruneReport{ID: ctrID, SpaceReclaimed: size})
|
|
}
|
|
for ctrID, err := range pruneErrors {
|
|
response = append(response, LibpodContainersPruneReport{ID: ctrID, PruneError: err.Error()})
|
|
}
|
|
utils.WriteResponse(w, http.StatusOK, response)
|
|
return
|
|
}
|
|
for ctrID, size := range prunedContainers {
|
|
if pruneErrors[ctrID] == nil {
|
|
space += size
|
|
delContainers = append(delContainers, ctrID)
|
|
}
|
|
}
|
|
report := types.ContainersPruneReport{
|
|
ContainersDeleted: delContainers,
|
|
SpaceReclaimed: uint64(space),
|
|
}
|
|
utils.WriteResponse(w, http.StatusOK, report)
|
|
}
|