Merge pull request #5733 from sujil02/v2-pod-prune

Add pod prune for api v2
This commit is contained in:
OpenShift Merge Robot
2020-04-18 06:02:51 -04:00
committed by GitHub
13 changed files with 144 additions and 40 deletions

75
cmd/podman/pods/prune.go Normal file
View File

@ -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()
}

View File

@ -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 {

View File

@ -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) {

View File

@ -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 {

View File

@ -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:

View File

@ -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

View File

@ -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())
@ -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())
@ -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())

View File

@ -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)

View File

@ -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

View File

@ -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 }

View File

@ -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)

View File

@ -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)

View File

@ -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))
})
@ -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)))
@ -65,7 +65,7 @@ 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))
@ -73,21 +73,4 @@ var _ = Describe("Podman pod prune", func() {
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())
})
})