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
75 changes: 75 additions & 0 deletions cmd/podman/pods/prune.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package pods

import (
"bufio"
"context"
"fmt"
"os"
"strings"

"github.com/containers/libpod/cmd/podman/registry"
"github.com/containers/libpod/cmd/podman/utils"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)

var (
pruneOptions = entities.PodPruneOptions{}
)

var (
pruneDescription = fmt.Sprintf(`podman pod prune Removes all exited pods`)

pruneCommand = &cobra.Command{
Use: "prune [flags]",
Short: "Remove all stopped pods and their containers",
Long: pruneDescription,
RunE: prune,
Example: `podman pod prune`,
}
)

func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
Command: pruneCommand,
Parent: podCmd,
})
flags := pruneCommand.Flags()
flags.BoolVarP(&pruneOptions.Force, "force", "f", false, "Do not prompt for confirmation. The default is false")
}

func prune(cmd *cobra.Command, args []string) error {
var (
errs utils.OutputErrors
)
if len(args) > 0 {
return errors.Errorf("`%s` takes no arguments", cmd.CommandPath())
}
if !pruneOptions.Force {
reader := bufio.NewReader(os.Stdin)
fmt.Println("WARNING! This will remove all stopped/exited pods..")
fmt.Print("Are you sure you want to continue? [y/N] ")
answer, err := reader.ReadString('\n')
if err != nil {
return errors.Wrapf(err, "error reading input")
}
if strings.ToLower(answer)[0] != 'y' {
return nil
}
}
responses, err := registry.ContainerEngine().PodPrune(context.Background(), pruneOptions)

if err != nil {
return err
}
for _, r := range responses {
if r.Err == nil {
fmt.Println(r.Id)
} else {
errs = append(errs, r.Err)
}
}
return errs.PrintErrors()
}
3 changes: 1 addition & 2 deletions libpod/runtime_pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,7 @@ func (r *Runtime) GetRunningPods() ([]*Pod, error) {
}

// PrunePods removes unused pods and their containers from local storage.
// If force is given, then running pods are also included in the pruning.
func (r *Runtime) PrunePods() (map[string]error, error) {
func (r *Runtime) PrunePods(ctx context.Context) (map[string]error, error) {
response := make(map[string]error)
states := []string{define.PodStateStopped, define.PodStateExited}
filterFunc := func(p *Pod) bool {
Expand Down
11 changes: 9 additions & 2 deletions pkg/api/handlers/libpod/pods.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,13 +232,20 @@ func PodRestart(w http.ResponseWriter, r *http.Request) {
func PodPrune(w http.ResponseWriter, r *http.Request) {
var (
runtime = r.Context().Value("runtime").(*libpod.Runtime)
reports []*entities.PodPruneReport
)
pruned, err := runtime.PrunePods()
responses, err := runtime.PrunePods(r.Context())
if err != nil {
utils.InternalServerError(w, err)
return
}
utils.WriteResponse(w, http.StatusOK, pruned)
for k, v := range responses {
reports = append(reports, &entities.PodPruneReport{
Err: v,
Id: k,
})
}
utils.WriteResponse(w, http.StatusOK, reports)
}

func PodPause(w http.ResponseWriter, r *http.Request) {
Expand Down
7 changes: 7 additions & 0 deletions pkg/api/handlers/libpod/swagger.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ type swagStartPodResponse struct {
Body entities.PodStartReport
}

// Prune pod
// swagger:response PodPruneReport
type swagPrunePodResponse struct {
// in:body
Body entities.PodPruneReport
}

// Rm pod
// swagger:response PodRmReport
type swagRmPodResponse struct {
Expand Down
6 changes: 1 addition & 5 deletions pkg/api/server/register_pods.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,7 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
// - application/json
// responses:
// 200:
// description: tbd
// schema:
// type: object
// additionalProperties:
// type: string
// $ref: '#/responses/PodPruneReport'
// 400:
// $ref: "#/responses/BadParamError"
// 409:
Expand Down
12 changes: 7 additions & 5 deletions pkg/bindings/pods/pods.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,17 +98,19 @@ func Pause(ctx context.Context, nameOrID string) (*entities.PodPauseReport, erro
return &report, response.Process(&report)
}

// Prune removes all non-running pods in local storage.
func Prune(ctx context.Context) error {
// Prune by default removes all non-running pods in local storage.
// And with force set true removes all pods.
func Prune(ctx context.Context) ([]*entities.PodPruneReport, error) {
var reports []*entities.PodPruneReport
conn, err := bindings.GetClient(ctx)
if err != nil {
return err
return nil, err
}
response, err := conn.DoRequest(nil, http.MethodPost, "/pods/prune", nil)
if err != nil {
return err
return nil, err
}
return response.Process(nil)
return reports, response.Process(&reports)
}

// List returns all pods in local storage. The optional filters parameter can
Expand Down
14 changes: 10 additions & 4 deletions pkg/bindings/test/pods_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ var _ = Describe("Podman pods", func() {
var newpod2 string = "newpod2"
bt.Podcreate(&newpod2)
// No pods pruned since no pod in exited state
err = pods.Prune(bt.conn)
pruneResponse, err := pods.Prune(bt.conn)
Expect(err).To(BeNil())
podSummary, err := pods.List(bt.conn, nil)
Expect(err).To(BeNil())
Expand All @@ -279,13 +279,19 @@ var _ = Describe("Podman pods", func() {
Expect(err).To(BeNil())
// FIXME sujil please fix this
//Expect(response.State.Status).To(Equal(define.PodStateExited))
err = pods.Prune(bt.conn)
pruneResponse, err = pods.Prune(bt.conn)
Expect(err).To(BeNil())
// Validate status and record pod id of pod to be pruned
//Expect(response.State.Status).To(Equal(define.PodStateExited))
//podID := response.Config.ID
// Check if right pod was pruned
Expect(len(pruneResponse)).To(Equal(1))
// One pod is pruned hence only one pod should be active.
podSummary, err = pods.List(bt.conn, nil)
Expect(err).To(BeNil())
Expect(len(podSummary)).To(Equal(1))

// Test prune all pods in exited state.
// Test prune multiple pods.
bt.Podcreate(&newpod)
_, err = pods.Start(bt.conn, newpod)
Expect(err).To(BeNil())
Expand All @@ -311,7 +317,7 @@ var _ = Describe("Podman pods", func() {
Expect(define.StringToContainerStatus(i.State)).
To(Equal(define.ContainerStateStopped))
}
err = pods.Prune(bt.conn)
_, err = pods.Prune(bt.conn)
Expect(err).To(BeNil())
podSummary, err = pods.List(bt.conn, nil)
Expect(err).To(BeNil())
Expand Down
1 change: 1 addition & 0 deletions pkg/domain/entities/engine_container.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ type ContainerEngine interface {
PodPs(ctx context.Context, options PodPSOptions) ([]*ListPodsReport, error)
PodRestart(ctx context.Context, namesOrIds []string, options PodRestartOptions) ([]*PodRestartReport, error)
PodRm(ctx context.Context, namesOrIds []string, options PodRmOptions) ([]*PodRmReport, error)
PodPrune(ctx context.Context, options PodPruneOptions) ([]*PodPruneReport, error)
PodStart(ctx context.Context, namesOrIds []string, options PodStartOptions) ([]*PodStartReport, error)
PodStop(ctx context.Context, namesOrIds []string, options PodStopOptions) ([]*PodStopReport, error)
PodTop(ctx context.Context, options PodTopOptions) (*StringSliceReport, error)
Expand Down
9 changes: 9 additions & 0 deletions pkg/domain/entities/pods.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,15 @@ func (p PodCreateOptions) ToPodSpecGen(s *specgen.PodSpecGenerator) {
s.CgroupParent = p.CGroupParent
}

type PodPruneOptions struct {
Force bool `json:"force" schema:"force"`
}

type PodPruneReport struct {
Err error
Id string
}

type PodTopOptions struct {
// CLI flags.
ListDescriptors bool
Expand Down
2 changes: 0 additions & 2 deletions pkg/domain/entities/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@ type Report struct {
}

type PodDeleteReport struct{ Report }
type PodPruneOptions struct{}

type PodPruneReport struct{ Report }
type VolumeDeleteOptions struct{}
type VolumeDeleteReport struct{ Report }

Expand Down
17 changes: 17 additions & 0 deletions pkg/domain/infra/abi/pods.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,23 @@ func (ic *ContainerEngine) PodRm(ctx context.Context, namesOrIds []string, optio
return reports, nil
}

func (ic *ContainerEngine) PodPrune(ctx context.Context, options entities.PodPruneOptions) ([]*entities.PodPruneReport, error) {
var (
reports []*entities.PodPruneReport
)
response, err := ic.Libpod.PrunePods(ctx)
if err != nil {
return nil, err
}
for k, v := range response {
reports = append(reports, &entities.PodPruneReport{
Err: v,
Id: k,
})
}
return reports, nil
}

func (ic *ContainerEngine) PodCreate(ctx context.Context, opts entities.PodCreateOptions) (*entities.PodCreateReport, error) {
podSpec := specgen.NewPodSpecGenerator()
opts.ToPodSpecGen(podSpec)
Expand Down
4 changes: 4 additions & 0 deletions pkg/domain/infra/tunnel/pods.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,10 @@ func (ic *ContainerEngine) PodRm(ctx context.Context, namesOrIds []string, optio
return reports, nil
}

func (ic *ContainerEngine) PodPrune(ctx context.Context, opts entities.PodPruneOptions) ([]*entities.PodPruneReport, error) {
return pods.Prune(ic.ClientCxt)
}

func (ic *ContainerEngine) PodCreate(ctx context.Context, opts entities.PodCreateOptions) (*entities.PodCreateReport, error) {
podSpec := specgen.NewPodSpecGenerator()
opts.ToPodSpecGen(podSpec)
Expand Down
23 changes: 3 additions & 20 deletions test/e2e/pod_prune_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ var _ = Describe("Podman pod prune", func() {
_, ec, _ := podmanTest.CreatePod("")
Expect(ec).To(Equal(0))

result := podmanTest.Podman([]string{"pod", "prune"})
result := podmanTest.Podman([]string{"pod", "prune", "--force"})
result.WaitWithDefaultTimeout()
Expect(result.ExitCode()).To(Equal(0))
})
Expand All @@ -49,7 +49,7 @@ var _ = Describe("Podman pod prune", func() {
ec2.WaitWithDefaultTimeout()
Expect(ec2.ExitCode()).To(Equal(0))

result := podmanTest.Podman([]string{"pod", "prune"})
result := podmanTest.Podman([]string{"pod", "prune", "-f"})
result.WaitWithDefaultTimeout()
Expect(result.ExitCode()).To((Equal(0)))

Expand All @@ -65,29 +65,12 @@ var _ = Describe("Podman pod prune", func() {
_, ec2, _ := podmanTest.RunLsContainerInPod("", podid)
Expect(ec2).To(Equal(0))

result := podmanTest.Podman([]string{"pod", "prune"})
result := podmanTest.Podman([]string{"pod", "prune", "-f"})
result.WaitWithDefaultTimeout()
Expect(result.ExitCode()).To(Equal(0))

result = podmanTest.Podman([]string{"ps", "-qa"})
result.WaitWithDefaultTimeout()
Expect(len(result.OutputToStringArray())).To(Equal(0))
})

It("podman pod prune -f does remove a running container", func() {
_, ec, podid := podmanTest.CreatePod("")
Expect(ec).To(Equal(0))

session := podmanTest.RunTopContainerInPod("", podid)
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))

result := podmanTest.Podman([]string{"pod", "prune", "-f"})
result.WaitWithDefaultTimeout()
Expect(result.ExitCode()).To(Equal(0))

result = podmanTest.Podman([]string{"ps", "-q"})
result.WaitWithDefaultTimeout()
Expect(result.OutputToString()).To(BeEmpty())
})
})