Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions libpod/runtime_ctr.go
Original file line number Diff line number Diff line change
Expand Up @@ -836,3 +836,44 @@ func (r *Runtime) GetLatestContainer() (*Container, error) {
}
return ctrs[lastCreatedIndex], nil
}

// PruneContainers removes stopped and exited containers from localstorage. A set of optional filters
// can be provided to be more granular.
func (r *Runtime) PruneContainers(filterFuncs []ContainerFilter) (map[string]int64, map[string]error, error) {
pruneErrors := make(map[string]error)
prunedContainers := make(map[string]int64)
// We add getting the exited and stopped containers via a filter
containerStateFilter := func(c *Container) bool {
if c.PodID() != "" {
return false
}
state, err := c.State()
if err != nil {
logrus.Error(err)
return false
}
if state == define.ContainerStateStopped || state == define.ContainerStateExited {
return true
}
return false
}
filterFuncs = append(filterFuncs, containerStateFilter)
delContainers, err := r.GetContainers(filterFuncs...)
if err != nil {
return nil, nil, err
}
for _, c := range delContainers {
ctr := c
size, err := ctr.RWSize()
if err != nil {
pruneErrors[ctr.ID()] = err
continue
}
err = r.RemoveContainer(context.Background(), ctr, false, false)
pruneErrors[ctr.ID()] = err
if err != nil {
prunedContainers[ctr.ID()] = size
}
}
return prunedContainers, pruneErrors, nil
}
53 changes: 53 additions & 0 deletions pkg/api/handlers/containers.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package handlers

import (
"fmt"
"github.com/docker/docker/api/types"
"net/http"

"github.com/containers/libpod/libpod"
Expand Down Expand Up @@ -192,3 +193,55 @@ func RestartContainer(w http.ResponseWriter, r *http.Request) {
// 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)
}
80 changes: 29 additions & 51 deletions pkg/api/handlers/generic/containers.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package generic

import (
"context"
"encoding/binary"
"fmt"
"net/http"
Expand All @@ -11,12 +10,10 @@ import (
"time"

"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/libpod/logs"
"github.com/containers/libpod/pkg/api/handlers"
"github.com/containers/libpod/pkg/api/handlers/utils"
"github.com/containers/libpod/pkg/util"
"github.com/docker/docker/api/types"
"github.com/gorilla/mux"
"github.com/gorilla/schema"
"github.com/pkg/errors"
Expand Down Expand Up @@ -47,14 +44,40 @@ func RemoveContainer(w http.ResponseWriter, r *http.Request) {
}

func ListContainers(w http.ResponseWriter, r *http.Request) {
var (
containers []*libpod.Container
err error
)
runtime := r.Context().Value("runtime").(*libpod.Runtime)

containers, err := runtime.GetAllContainers()
decoder := r.Context().Value("decoder").(*schema.Decoder)
query := struct {
All bool `schema:"all"`
Limit int `schema:"limit"`
Size bool `schema:"size"`
Filters map[string][]string `schema:"filters"`
}{
// override any golang type defaults
}
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 query.All {
containers, err = runtime.GetAllContainers()
} else {
containers, err = runtime.GetRunningContainers()
}
if err != nil {
utils.InternalServerError(w, err)
return
}

if _, found := mux.Vars(r)["limit"]; found {
last := query.Limit
if len(containers) > last {
containers = containers[len(containers)-last:]
}
}
// TODO filters still need to be applied
infoData, err := runtime.Info()
if err != nil {
utils.InternalServerError(w, errors.Wrapf(err, "Failed to obtain system info"))
Expand Down Expand Up @@ -125,51 +148,6 @@ func WaitContainer(w http.ResponseWriter, r *http.Request) {
})
}

func PruneContainers(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)

containers, err := runtime.GetAllContainers()
if err != nil {
utils.InternalServerError(w, err)
return
}

deletedContainers := []string{}
var spaceReclaimed uint64
for _, ctnr := range containers {
// Only remove stopped or exit'ed containers.
state, err := ctnr.State()
if err != nil {
utils.InternalServerError(w, err)
return
}
switch state {
case define.ContainerStateStopped, define.ContainerStateExited:
default:
continue
}

deletedContainers = append(deletedContainers, ctnr.ID())
cSize, err := ctnr.RootFsSize()
if err != nil {
utils.InternalServerError(w, err)
return
}
spaceReclaimed += uint64(cSize)

err = runtime.RemoveContainer(context.Background(), ctnr, false, false)
if err != nil && !(errors.Cause(err) == define.ErrCtrRemoved) {
utils.InternalServerError(w, err)
return
}
}
report := types.ContainersPruneReport{
ContainersDeleted: deletedContainers,
SpaceReclaimed: spaceReclaimed,
}
utils.WriteResponse(w, http.StatusOK, report)
}

func LogsFromContainer(w http.ResponseWriter, r *http.Request) {
decoder := r.Context().Value("decoder").(*schema.Decoder)
runtime := r.Context().Value("runtime").(*libpod.Runtime)
Expand Down
4 changes: 3 additions & 1 deletion pkg/api/handlers/generic/containers_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()"))
return
}

if len(input.HostConfig.Links) > 0 {
utils.Error(w, utils.ErrLinkNotSupport.Error(), http.StatusBadRequest, errors.Wrapf(utils.ErrLinkNotSupport, "bad parameter"))
}
newImage, err := runtime.ImageRuntime().NewFromLocal(input.Image)
if err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "NewFromLocal()"))
Expand Down
42 changes: 25 additions & 17 deletions pkg/api/handlers/libpod/containers.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package libpod

import (
"fmt"
"net/http"
"strconv"

"github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod"
Expand Down Expand Up @@ -46,12 +48,18 @@ func RemoveContainer(w http.ResponseWriter, r *http.Request) {
utils.RemoveContainer(w, r, query.Force, query.Vols)
}
func ListContainers(w http.ResponseWriter, r *http.Request) {
var (
filters []string
)
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"`
All bool `schema:"all"`
Filter map[string][]string `schema:"filter"`
Last int `schema:"last"`
Namespace bool `schema:"namespace"`
Pod bool `schema:"pod"`
Size bool `schema:"size"`
Sync bool `schema:"sync"`
}{
// override any golang type defaults
}
Expand All @@ -63,15 +71,22 @@ func ListContainers(w http.ResponseWriter, r *http.Request) {
}
runtime := r.Context().Value("runtime").(*libpod.Runtime)
opts := shared.PsOptions{
All: true,
All: query.All,
Last: query.Last,
Size: query.Size,
Sort: "",
Namespace: true,
Namespace: query.Namespace,
Pod: query.Pod,
Sync: query.Sync,
}

pss, err := shared.GetPsContainerOutput(runtime, opts, query.Filter, 2)
if len(query.Filter) > 0 {
for k, v := range query.Filter {
for _, val := range v {
filters = append(filters, fmt.Sprintf("%s=%s", k, val))
}
}
}
pss, err := shared.GetPsContainerOutput(runtime, opts, filters, 2)
if err != nil {
utils.InternalServerError(w, err)
}
Expand Down Expand Up @@ -117,19 +132,12 @@ func KillContainer(w http.ResponseWriter, r *http.Request) {
}

func WaitContainer(w http.ResponseWriter, r *http.Request) {
_, err := utils.WaitContainer(w, r)
exitCode, 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
utils.WriteResponse(w, http.StatusOK, strconv.Itoa(int(exitCode)))
}

func LogsFromContainer(w http.ResponseWriter, r *http.Request) {
Expand Down
7 changes: 7 additions & 0 deletions pkg/api/handlers/swagger.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ type swagContainerPruneReport struct {
Body []ContainersPruneReport
}

// Prune containers
// swagger:response DocsLibpodPruneResponse
type swagLibpodContainerPruneReport struct {
// in: body
Body []LibpodContainersPruneReport
}

// Inspect container
// swagger:response DocsContainerInspectResponse
type swagContainerInspectResponse struct {
Expand Down
6 changes: 6 additions & 0 deletions pkg/api/handlers/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ type ContainersPruneReport struct {
docker.ContainersPruneReport
}

type LibpodContainersPruneReport struct {
ID string `json:"id"`
SpaceReclaimed int64 `json:"space"`
PruneError string `json:"error"`
}

type Info struct {
docker.Info
BuildahVersion string
Expand Down
21 changes: 20 additions & 1 deletion pkg/api/handlers/utils/containers.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"syscall"
"time"

"github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
"github.com/gorilla/mux"
Expand Down Expand Up @@ -83,7 +84,7 @@ func WaitContainer(w http.ResponseWriter, r *http.Request) (int32, error) {
}

if len(query.Condition) > 0 {
return 0, errors.Errorf("the condition parameter is not supported")
UnSupportedParameter("condition")
}

name := mux.Vars(r)["name"]
Expand All @@ -101,3 +102,21 @@ func WaitContainer(w http.ResponseWriter, r *http.Request) (int32, error) {
}
return con.Wait()
}

// GenerateFilterFuncsFromMap is used to generate un-executed functions that can be used to filter
// containers. It is specifically designed for the RESTFUL API input.
func GenerateFilterFuncsFromMap(r *libpod.Runtime, filters map[string][]string) ([]libpod.ContainerFilter, error) {
var (
filterFuncs []libpod.ContainerFilter
)
for k, v := range filters {
for _, val := range v {
f, err := shared.GenerateContainerFilterFuncs(k, val, r)
if err != nil {
return filterFuncs, err
}
filterFuncs = append(filterFuncs, f)
}
}
return filterFuncs, nil
}
8 changes: 8 additions & 0 deletions pkg/api/handlers/utils/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,11 @@ func WriteJSON(w http.ResponseWriter, code int, value interface{}) {
logrus.Errorf("unable to write json: %q", err)
}
}

func FilterMapToString(filters map[string][]string) (string, error) {
f, err := json.Marshal(filters)
if err != nil {
return "", err
}
return string(f), nil
}
Loading