diff options
24 files changed, 443 insertions, 105 deletions
diff --git a/cmd/podman/README.md b/cmd/podman/README.md index c1b8f48a7..da92d0216 100644 --- a/cmd/podman/README.md +++ b/cmd/podman/README.md @@ -1,23 +1,16 @@ -# Adding a podman V2 commands +# Podman CLI -## Build podman V2 +The following is an example of how to add a new primary command (`manifest`) and a sub-command (`inspect`) to the Podman CLI. +This is example code, the production code has additional error checking and the business logic provided. -```shell script -$ cd $GOPATH/src/github.com/containers/libpod/cmd/podmanV2 -``` -If you wish to include the libpod library in your program, -```shell script -$ go build -tags 'ABISupport' . -``` -The `--remote` flag may be used to connect to the Podman service using the API. -Otherwise, direct calls will be made to the Libpod library. -```shell script -$ go build -tags '!ABISupport' . -``` -The Libpod library is not linked into the executable. -All calls are made via the API and `--remote=False` is an error condition. +See items below for details on building, installing, contributing to Podman: + - [Readme](README.md) + - [Contributing](CONTRIBUTING.md) + - [Podman Usage](transfer.md) + - [Trouble Shooting](troubleshooting.md) + - [Code Of Conduct](CODE-OF-CONDUCT.md) -## Adding a new command `podman manifests` +## Adding a new command `podman manifest` ```shell script $ mkdir -p $GOPATH/src/github.com/containers/libpod/cmd/podmanV2/manifests ``` @@ -27,6 +20,7 @@ package manifests import ( "github.com/containers/libpod/cmd/podman/registry" + "github.com/containers/libpod/cmd/podman/validate" "github.com/containers/libpod/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -36,11 +30,11 @@ var ( manifestCmd = &cobra.Command{ Use: "manifest", Short: "Manage manifests", + Args: cobra.ExactArgs(1), Long: "Manage manifests", - Example: "podman manifests IMAGE", + Example: "podman manifest IMAGE", TraverseChildren: true, - PersistentPreRunE: preRunE, - RunE: registry.SubCommandExists, // Report error if there is no sub command given + RunE: validate.SubCommandExists, // Report error if there is no sub command given } ) func init() { @@ -51,15 +45,6 @@ func init() { // The definition for this command Command: manifestCmd, }) - // Setup cobra templates, sub commands will inherit - manifestCmd.SetHelpTemplate(registry.HelpTemplate()) - manifestCmd.SetUsageTemplate(registry.UsageTemplate()) -} - -// preRunE populates the image engine for sub commands -func preRunE(cmd *cobra.Command, args []string) error { - _, err := registry.NewImageEngine(cmd, args) - return err } ``` To "wire" in the `manifest` command, edit the file ```$GOPATH/src/github.com/containers/libpod/cmd/podmanV2/main.go``` to add: @@ -87,7 +72,11 @@ var ( Short: "Display manifest from image", Long: "Displays the low-level information on a manifest identified by image name or ID", RunE: inspect, - Example: "podman manifest DEADBEEF", + Annotations: map[string]string{ + // Add this annotation if this command cannot be run rootless + // registry.ParentNSRequired: "", + }, + Example: "podman manifest inspect DEADBEEF", } ) @@ -98,6 +87,7 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, // The definition for this command Command: inspectCmd, + // The parent command to proceed this command on the CLI Parent: manifestCmd, }) @@ -106,8 +96,22 @@ func init() { // Business logic: cmd is inspectCmd, args is the positional arguments from os.Args func inspect(cmd *cobra.Command, args []string) error { - // Business logic using registry.ImageEngine + // Business logic using registry.ImageEngine() // Do not pull from libpod directly use the domain objects and types return nil } ``` + +## Helper functions + +The complete set can be found in the `validate` package, here are some examples: + + - `cobra.Command{ Args: validate.NoArgs }` used when the command does not accept errors + - `cobra.Command{ Args: validate.IdOrLatestArgs }` used to ensure either a list of ids given or the --latest flag + - `cobra.Command{ RunE: validate.SubCommandExists }` used to validate a subcommand given to a command + - `validate.ChoiceValue` used to create a `pflag.Value` that validate user input against a provided slice of values. For example: + ```go + flags := cobraCommand.Flags() + created := validate.ChoiceValue(&opts.Sort, "command", "created", "id", "image", "names", "runningfor", "size", "status") + flags.Var(created, "sort", "Sort output by: "+created.Choices()) + ``` diff --git a/cmd/podman/containers/prune.go b/cmd/podman/containers/prune.go index df627259c..d4bea48f9 100644 --- a/cmd/podman/containers/prune.go +++ b/cmd/podman/containers/prune.go @@ -43,7 +43,6 @@ func init() { func prune(cmd *cobra.Command, args []string) error { var ( - errs utils.OutputErrors pruneOptions = entities.ContainerPruneOptions{} ) if len(args) > 0 { @@ -76,11 +75,5 @@ func prune(cmd *cobra.Command, args []string) error { if err != nil { return err } - for k := range responses.ID { - fmt.Println(k) - } - for _, v := range responses.Err { - errs = append(errs, v) - } - return errs.PrintErrors() + return utils.PrintContainerPruneResults(responses) } diff --git a/cmd/podman/containers/ps.go b/cmd/podman/containers/ps.go index c5696a158..e9146bda7 100644 --- a/cmd/podman/containers/ps.go +++ b/cmd/podman/containers/ps.go @@ -67,8 +67,8 @@ func listFlagSet(flags *pflag.FlagSet) { flags.BoolVar(&listOpts.Sync, "sync", false, "Sync container state with OCI runtime") flags.UintVarP(&listOpts.Watch, "watch", "w", 0, "Watch the ps output on an interval in seconds") - created := validate.ChoiceValue(&listOpts.Sort, "command", "created", "id", "image", "names", "runningfor", "size", "status") - flags.Var(created, "sort", "Sort output by: "+created.Choices()) + sort := validate.ChoiceValue(&listOpts.Sort, "command", "created", "id", "image", "names", "runningfor", "size", "status") + flags.Var(sort, "sort", "Sort output by: "+sort.Choices()) if registry.IsRemote() { _ = flags.MarkHidden("latest") diff --git a/cmd/podman/images/prune.go b/cmd/podman/images/prune.go index 53a1966c1..7c9e3eb61 100644 --- a/cmd/podman/images/prune.go +++ b/cmd/podman/images/prune.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/containers/libpod/cmd/podman/registry" + "github.com/containers/libpod/cmd/podman/utils" "github.com/containers/libpod/cmd/podman/validate" "github.com/containers/libpod/pkg/domain/entities" "github.com/pkg/errors" @@ -71,17 +72,5 @@ Are you sure you want to continue? [y/N] `) return err } - for _, i := range results.Report.Id { - fmt.Println(i) - } - - for _, e := range results.Report.Err { - fmt.Fprint(os.Stderr, e.Error()+"\n") - } - - if results.Size > 0 { - fmt.Fprintf(os.Stdout, "Size: %d\n", results.Size) - } - - return nil + return utils.PrintImagePruneResults(results) } diff --git a/cmd/podman/login.go b/cmd/podman/login.go index 9de805d15..dc57758ab 100644 --- a/cmd/podman/login.go +++ b/cmd/podman/login.go @@ -63,6 +63,6 @@ func login(cmd *cobra.Command, args []string) error { DockerCertPath: loginOptions.CertDir, DockerInsecureSkipTLSVerify: skipTLS, } - + loginOptions.GetLoginSet = cmd.Flag("get-login").Changed return auth.Login(context.Background(), &sysCtx, &loginOptions.LoginOptions, args[0]) } diff --git a/cmd/podman/pods/prune.go b/cmd/podman/pods/prune.go index 091e3375b..bc15d8035 100644 --- a/cmd/podman/pods/prune.go +++ b/cmd/podman/pods/prune.go @@ -41,9 +41,6 @@ func init() { } 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()) } @@ -60,16 +57,8 @@ func prune(cmd *cobra.Command, args []string) error { } } 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() + return utils.PrintPodPruneResults(responses) } diff --git a/cmd/podman/system/prune.go b/cmd/podman/system/prune.go new file mode 100644 index 000000000..cc9b473f1 --- /dev/null +++ b/cmd/podman/system/prune.go @@ -0,0 +1,103 @@ +package system + +import ( + "bufio" + "context" + "fmt" + "os" + "strings" + + "github.com/containers/libpod/cmd/podman/registry" + "github.com/containers/libpod/cmd/podman/utils" + "github.com/containers/libpod/cmd/podman/validate" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +var ( + pruneOptions = entities.SystemPruneOptions{} + + pruneDescription = fmt.Sprintf(` + podman system prune + + Remove unused data +`) + + pruneCommand = &cobra.Command{ + Use: "prune [flags]", + Short: "Remove unused data", + Args: validate.NoArgs, + Long: pruneDescription, + RunE: prune, + Example: `podman system prune`, + } + force bool +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: pruneCommand, + Parent: systemCmd, + }) + flags := pruneCommand.Flags() + flags.BoolVarP(&force, "force", "f", false, "Do not prompt for confirmation. The default is false") + flags.BoolVarP(&pruneOptions.All, "all", "a", false, "Remove all unused data") + flags.BoolVar(&pruneOptions.Volume, "volumes", false, "Prune volumes") + +} + +func prune(cmd *cobra.Command, args []string) error { + // Prompt for confirmation if --force is not set + if !force { + reader := bufio.NewReader(os.Stdin) + volumeString := "" + if pruneOptions.Volume { + volumeString = ` + - all volumes not used by at least one container` + } + fmt.Printf(` +WARNING! This will remove: + - all stopped containers%s + - all stopped pods + - all dangling images + - all build cache +Are you sure you want to continue? [y/N] `, volumeString) + answer, err := reader.ReadString('\n') + if err != nil { + return errors.Wrapf(err, "error reading input") + } + if strings.ToLower(answer)[0] != 'y' { + return nil + } + } + // TODO: support for filters in system prune + response, err := registry.ContainerEngine().SystemPrune(context.Background(), pruneOptions) + if err != nil { + return err + } + // Print pod prune results + fmt.Println("Deleted Pods") + err = utils.PrintPodPruneResults(response.PodPruneReport) + if err != nil { + return err + } + // Print container prune results + fmt.Println("Deleted Containers") + err = utils.PrintContainerPruneResults(response.ContainerPruneReport) + if err != nil { + return err + } + // Print Volume prune results + if pruneOptions.Volume { + fmt.Println("Deleted Volumes") + err = utils.PrintVolumePruneResults(response.VolumePruneReport) + if err != nil { + return err + } + } + // Print Images prune results + fmt.Println("Deleted Images") + return utils.PrintImagePruneResults(response.ImagePruneReport) +} diff --git a/cmd/podman/utils/utils.go b/cmd/podman/utils/utils.go index c7d105ba4..f4c704628 100644 --- a/cmd/podman/utils/utils.go +++ b/cmd/podman/utils/utils.go @@ -1,6 +1,11 @@ package utils -import "os" +import ( + "fmt" + "os" + + "github.com/containers/libpod/pkg/domain/entities" +) // IsDir returns true if the specified path refers to a directory. func IsDir(path string) bool { @@ -20,3 +25,51 @@ func FileExists(path string) bool { } return !file.IsDir() } + +func PrintPodPruneResults(podPruneReports []*entities.PodPruneReport) error { + var errs OutputErrors + for _, r := range podPruneReports { + if r.Err == nil { + fmt.Println(r.Id) + } else { + errs = append(errs, r.Err) + } + } + return errs.PrintErrors() +} + +func PrintContainerPruneResults(containerPruneReport *entities.ContainerPruneReport) error { + var errs OutputErrors + for k := range containerPruneReport.ID { + fmt.Println(k) + } + for _, v := range containerPruneReport.Err { + errs = append(errs, v) + } + return errs.PrintErrors() +} + +func PrintVolumePruneResults(volumePruneReport []*entities.VolumePruneReport) error { + var errs OutputErrors + for _, r := range volumePruneReport { + if r.Err == nil { + fmt.Println(r.Id) + } else { + errs = append(errs, r.Err) + } + } + return errs.PrintErrors() +} + +func PrintImagePruneResults(imagePruneReport *entities.ImagePruneReport) error { + for _, i := range imagePruneReport.Report.Id { + fmt.Println(i) + } + for _, e := range imagePruneReport.Report.Err { + fmt.Fprint(os.Stderr, e.Error()+"\n") + } + if imagePruneReport.Size > 0 { + fmt.Fprintf(os.Stdout, "Size: %d\n", imagePruneReport.Size) + } + return nil +} diff --git a/cmd/podman/volumes/prune.go b/cmd/podman/volumes/prune.go index 2c3ed88f3..57344385b 100644 --- a/cmd/podman/volumes/prune.go +++ b/cmd/podman/volumes/prune.go @@ -44,9 +44,6 @@ func init() { } func prune(cmd *cobra.Command, args []string) error { - var ( - errs utils.OutputErrors - ) // Prompt for confirmation if --force is not set if !pruneOptions.Force { reader := bufio.NewReader(os.Stdin) @@ -64,12 +61,5 @@ func prune(cmd *cobra.Command, args []string) error { 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() + return utils.PrintVolumePruneResults(responses) } diff --git a/contrib/podmanimage/stable/Dockerfile b/contrib/podmanimage/stable/Dockerfile index 912d16e1c..fdf461f72 100644 --- a/contrib/podmanimage/stable/Dockerfile +++ b/contrib/podmanimage/stable/Dockerfile @@ -6,7 +6,7 @@ # This image can be used to create a secured container # that runs safely with privileges within the container. # -FROM fedora:latest +FROM registry.fedoraproject.org/fedora:latest # Don't include container-selinux and remove # directories used by yum that are just taking diff --git a/contrib/podmanimage/stable/manual/Containerfile b/contrib/podmanimage/stable/manual/Containerfile index 4375ea4f4..79ff95956 100644 --- a/contrib/podmanimage/stable/manual/Containerfile +++ b/contrib/podmanimage/stable/manual/Containerfile @@ -17,7 +17,7 @@ # `podman push quay.io/stable:v1.7.0 docker://quay.io/podman/stable:v1.7.0` # # Start Build Process using the latest Fedora -FROM fedora:latest +FROM registry.fedoraproject.org/fedora:latest # Don't include container-selinux and remove # directories used by dnf that are just taking diff --git a/contrib/podmanimage/testing/Dockerfile b/contrib/podmanimage/testing/Dockerfile index 31265a0ea..0124879e0 100644 --- a/contrib/podmanimage/testing/Dockerfile +++ b/contrib/podmanimage/testing/Dockerfile @@ -8,7 +8,7 @@ # This image can be used to create a secured container # that runs safely with privileges within the container. # -FROM fedora:latest +FROM registry.fedoraproject.org/fedora:latest # Don't include container-selinux and remove # directories used by yum that are just taking diff --git a/contrib/podmanimage/upstream/Dockerfile b/contrib/podmanimage/upstream/Dockerfile index 541670aa2..6787cfcc3 100644 --- a/contrib/podmanimage/upstream/Dockerfile +++ b/contrib/podmanimage/upstream/Dockerfile @@ -8,7 +8,7 @@ # The containers created by this image also come with a # Podman development environment in /root/podman. # -FROM fedora:latest +FROM registry.fedoraproject.org/fedora:latest ENV GOPATH=/root/podman # Install the software required to build Podman. diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go index 729a00be8..276429b11 100644 --- a/libpod/container_inspect.go +++ b/libpod/container_inspect.go @@ -661,7 +661,7 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named if ns.Path != "" { networkMode = fmt.Sprintf("ns:%s", ns.Path) } else { - networkMode = "none" + networkMode = "private" } break } @@ -743,22 +743,23 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named ipcMode := "" if c.config.IPCNsCtr != "" { ipcMode = fmt.Sprintf("container:%s", c.config.IPCNsCtr) - } else { + } else if ctrSpec.Linux != nil { // Locate the spec's IPC namespace. // If there is none, it's ipc=host. // If there is one and it has a path, it's "ns:". // If no path, it's default - the empty string. - foundIPCNS := false + for _, ns := range ctrSpec.Linux.Namespaces { if ns.Type == spec.IPCNamespace { - foundIPCNS = true if ns.Path != "" { ipcMode = fmt.Sprintf("ns:%s", ns.Path) + } else { + ipcMode = "private" } break } } - if !foundIPCNS { + if ipcMode == "" { ipcMode = "host" } } @@ -781,22 +782,22 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named pidMode := "" if c.config.PIDNsCtr != "" { pidMode = fmt.Sprintf("container:%s", c.config.PIDNsCtr) - } else { + } else if ctrSpec.Linux != nil { // Locate the spec's PID namespace. // If there is none, it's pid=host. // If there is one and it has a path, it's "ns:". // If there is no path, it's default - the empty string. - foundPIDNS := false for _, ns := range ctrSpec.Linux.Namespaces { if ns.Type == spec.PIDNamespace { - foundPIDNS = true if ns.Path != "" { pidMode = fmt.Sprintf("ns:%s", ns.Path) + } else { + pidMode = "private" } break } } - if !foundPIDNS { + if pidMode == "" { pidMode = "host" } } @@ -806,22 +807,23 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named utsMode := "" if c.config.UTSNsCtr != "" { utsMode = fmt.Sprintf("container:%s", c.config.UTSNsCtr) - } else { + } else if ctrSpec.Linux != nil { + // Locate the spec's UTS namespace. // If there is none, it's uts=host. // If there is one and it has a path, it's "ns:". // If there is no path, it's default - the empty string. - foundUTSNS := false for _, ns := range ctrSpec.Linux.Namespaces { if ns.Type == spec.UTSNamespace { - foundUTSNS = true if ns.Path != "" { utsMode = fmt.Sprintf("ns:%s", ns.Path) + } else { + utsMode = "private" } break } } - if !foundUTSNS { + if utsMode == "" { utsMode = "host" } } @@ -831,11 +833,12 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named usernsMode := "" if c.config.UserNsCtr != "" { usernsMode = fmt.Sprintf("container:%s", c.config.UserNsCtr) - } else { + } else if ctrSpec.Linux != nil { // Locate the spec's user namespace. // If there is none, it's default - the empty string. // If there is one, it's "private" if no path, or "ns:" if // there's a path. + for _, ns := range ctrSpec.Linux.Namespaces { if ns.Type == spec.UserNamespace { if ns.Path != "" { diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go index da4b85067..d59ff18ca 100644 --- a/libpod/oci_conmon_linux.go +++ b/libpod/oci_conmon_linux.go @@ -731,7 +731,7 @@ func (r *ConmonOCIRuntime) ExecContainer(c *Container, sessionID string, options args = append(args, "-t") } - if options.Streams.AttachInput { + if options.Streams != nil && options.Streams.AttachInput { args = append(args, "-i") } diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index b11932de6..734f72e5f 100644 --- a/pkg/domain/entities/engine_container.go +++ b/pkg/domain/entities/engine_container.go @@ -42,6 +42,7 @@ type ContainerEngine interface { ContainerWait(ctx context.Context, namesOrIds []string, options WaitOptions) ([]WaitReport, error) Events(ctx context.Context, opts EventsOptions) error GenerateSystemd(ctx context.Context, nameOrID string, opts GenerateSystemdOptions) (*GenerateSystemdReport, error) + SystemPrune(ctx context.Context, options SystemPruneOptions) (*SystemPruneReport, error) HealthCheckRun(ctx context.Context, nameOrId string, options HealthCheckOptions) (*define.HealthCheckResults, error) Info(ctx context.Context) (*define.Info, error) NetworkCreate(ctx context.Context, name string, options NetworkCreateOptions) (*NetworkCreateReport, error) diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index f4996583a..244fbc5cd 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -193,6 +193,10 @@ func (ic *ContainerEngine) ContainerPrune(ctx context.Context, options entities. filterFuncs = append(filterFuncs, generatedFunc) } } + return ic.pruneContainersHelper(ctx, filterFuncs) +} + +func (ic *ContainerEngine) pruneContainersHelper(ctx context.Context, filterFuncs []libpod.ContainerFilter) (*entities.ContainerPruneReport, error) { prunedContainers, pruneErrors, err := ic.Libpod.PruneContainers(filterFuncs) if err != nil { return nil, err @@ -523,7 +527,8 @@ func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrId string, } // If the container is in a pod, also set to recursively start dependencies - if err := terminal.StartAttachCtr(ctx, ctr, options.Stdout, options.Stderr, options.Stdin, options.DetachKeys, options.SigProxy, false, ctr.PodID() != ""); err != nil && errors.Cause(err) != define.ErrDetach { + err = terminal.StartAttachCtr(ctx, ctr, options.Stdout, options.Stderr, options.Stdin, options.DetachKeys, options.SigProxy, false, ctr.PodID() != "") + if err != nil && errors.Cause(err) != define.ErrDetach { return errors.Wrapf(err, "error attaching to container %s", ctr.ID()) } return nil diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index be788b2bf..0af06fb89 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -36,7 +36,11 @@ func (ir *ImageEngine) Exists(_ context.Context, nameOrId string) (*entities.Boo } func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOptions) (*entities.ImagePruneReport, error) { - results, err := ir.Libpod.ImageRuntime().PruneImages(ctx, opts.All, opts.Filter) + return ir.pruneImagesHelper(ctx, opts.All, opts.Filter) +} + +func (ir *ImageEngine) pruneImagesHelper(ctx context.Context, all bool, filters []string) (*entities.ImagePruneReport, error) { + results, err := ir.Libpod.ImageRuntime().PruneImages(ctx, all, filters) if err != nil { return nil, err } diff --git a/pkg/domain/infra/abi/pods.go b/pkg/domain/infra/abi/pods.go index b286bcf0d..16c222cbd 100644 --- a/pkg/domain/infra/abi/pods.go +++ b/pkg/domain/infra/abi/pods.go @@ -243,6 +243,10 @@ func (ic *ContainerEngine) PodRm(ctx context.Context, namesOrIds []string, optio } func (ic *ContainerEngine) PodPrune(ctx context.Context, options entities.PodPruneOptions) ([]*entities.PodPruneReport, error) { + return ic.prunePodHelper(ctx) +} + +func (ic *ContainerEngine) prunePodHelper(ctx context.Context) ([]*entities.PodPruneReport, error) { var ( reports []*entities.PodPruneReport ) diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go index e5c109ee6..ab1b282d8 100644 --- a/pkg/domain/infra/abi/system.go +++ b/pkg/domain/infra/abi/system.go @@ -175,3 +175,41 @@ func setUMask() { // nolint:deadcode,unused func checkInput() error { // nolint:deadcode,unused return nil } + +// SystemPrune removes unsed data from the system. Pruning pods, containers, volumes and images. +func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.SystemPruneOptions) (*entities.SystemPruneReport, error) { + var systemPruneReport = new(entities.SystemPruneReport) + podPruneReport, err := ic.prunePodHelper(ctx) + if err != nil { + return nil, err + } + systemPruneReport.PodPruneReport = podPruneReport + + containerPruneReport, err := ic.pruneContainersHelper(ctx, nil) + if err != nil { + return nil, err + } + systemPruneReport.ContainerPruneReport = containerPruneReport + + results, err := ic.Libpod.ImageRuntime().PruneImages(ctx, options.All, nil) + if err != nil { + return nil, err + } + report := entities.ImagePruneReport{ + Report: entities.Report{ + Id: results, + Err: nil, + }, + } + + systemPruneReport.ImagePruneReport = &report + + if options.Volume { + volumePruneReport, err := ic.pruneVolumesHelper(ctx) + if err != nil { + return nil, err + } + systemPruneReport.VolumePruneReport = volumePruneReport + } + return systemPruneReport, nil +} diff --git a/pkg/domain/infra/abi/volumes.go b/pkg/domain/infra/abi/volumes.go index bdae4359d..91b2440df 100644 --- a/pkg/domain/infra/abi/volumes.go +++ b/pkg/domain/infra/abi/volumes.go @@ -1,5 +1,3 @@ -// +build ABISupport - package abi import ( @@ -113,6 +111,10 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin } func (ic *ContainerEngine) VolumePrune(ctx context.Context, opts entities.VolumePruneOptions) ([]*entities.VolumePruneReport, error) { + return ic.pruneVolumesHelper(ctx) +} + +func (ic *ContainerEngine) pruneVolumesHelper(ctx context.Context) ([]*entities.VolumePruneReport, error) { var ( reports []*entities.VolumePruneReport ) diff --git a/pkg/domain/infra/tunnel/system.go b/pkg/domain/infra/tunnel/system.go index 97bf885e7..18cb6c75a 100644 --- a/pkg/domain/infra/tunnel/system.go +++ b/pkg/domain/infra/tunnel/system.go @@ -3,6 +3,7 @@ package tunnel import ( "context" "errors" + "fmt" "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/bindings/system" @@ -21,3 +22,9 @@ func (ic *ContainerEngine) VarlinkService(_ context.Context, _ entities.ServiceO func (ic *ContainerEngine) SetupRootless(_ context.Context, cmd *cobra.Command) error { panic(errors.New("rootless engine mode is not supported when tunneling")) } + +// SystemPrune prunes unused data from the system. +func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.SystemPruneOptions) (*entities.SystemPruneReport, error) { + fmt.Println("in tunnel") + return system.Prune(ic.ClientCxt, &options.All, &options.Volume) +} diff --git a/test/e2e/prune_test.go b/test/e2e/prune_test.go index 466a4f739..a09b6f37a 100644 --- a/test/e2e/prune_test.go +++ b/test/e2e/prune_test.go @@ -148,7 +148,6 @@ var _ = Describe("Podman prune", func() { It("podman system image prune unused images", func() { SkipIfRemote() - Skip(v2fail) podmanTest.RestoreAllArtifacts() podmanTest.BuildImage(pruneImage, "alpine_bash:latest", "true") prune := podmanTest.PodmanNoCache([]string{"system", "prune", "-a", "--force"}) @@ -162,7 +161,6 @@ var _ = Describe("Podman prune", func() { }) It("podman system prune pods", func() { - Skip(v2fail) session := podmanTest.Podman([]string{"pod", "create"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -193,4 +191,160 @@ var _ = Describe("Podman prune", func() { Expect(pods.ExitCode()).To(Equal(0)) Expect(len(pods.OutputToStringArray())).To(Equal(2)) }) + + It("podman system prune - pod,container stopped", func() { + session := podmanTest.Podman([]string{"pod", "create"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + // Start and stop a pod to get it in exited state. + session = podmanTest.Podman([]string{"pod", "start", "-l"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"pod", "stop", "-l"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + // Create a container. This container should be pruned. + create := podmanTest.Podman([]string{"create", "--name", "test", BB}) + create.WaitWithDefaultTimeout() + Expect(create.ExitCode()).To(Equal(0)) + + prune := podmanTest.Podman([]string{"system", "prune", "-f"}) + prune.WaitWithDefaultTimeout() + Expect(prune.ExitCode()).To(Equal(0)) + + pods := podmanTest.Podman([]string{"pod", "ps"}) + pods.WaitWithDefaultTimeout() + Expect(pods.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfPods()).To(Equal(0)) + + Expect(podmanTest.NumberOfContainers()).To(Equal(0)) + }) + + It("podman system prune with running, exited pod and volume prune set true", func() { + + // Start and stop a pod to get it in exited state. + session := podmanTest.Podman([]string{"pod", "create"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"pod", "start", "-l"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"pod", "stop", "-l"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + // Start a pod and leave it running + session = podmanTest.Podman([]string{"pod", "create"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + session = podmanTest.Podman([]string{"pod", "start", "-l"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + // Number of pod should be 2. One exited one running. + Expect(podmanTest.NumberOfPods()).To(Equal(2)) + + // Create a container. This container should be pruned. + _, ec, _ := podmanTest.RunLsContainer("test1") + Expect(ec).To(Equal(0)) + + // Number of containers should be three now. + // Two as pods infra container and one newly created. + Expect(podmanTest.NumberOfContainers()).To(Equal(3)) + + // image list current count should not be pruned if all flag isnt enabled + session = podmanTest.Podman([]string{"images"}) + session.WaitWithDefaultTimeout() + numberOfImages := len(session.OutputToStringArray()) + + // Adding unused volume should be pruned + session = podmanTest.Podman([]string{"volume", "create"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"create", "-v", "myvol:/myvol", ALPINE, "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"volume", "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(len(session.OutputToStringArray())).To(Equal(3)) + + session = podmanTest.Podman([]string{"system", "prune", "--force", "--volumes"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + // Volumes should be pruned. + session = podmanTest.Podman([]string{"volume", "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(len(session.OutputToStringArray())).To(Equal(0)) + + // One Pod should not be pruned as it was running + Expect(podmanTest.NumberOfPods()).To(Equal(1)) + + // Running pods infra container should not be pruned. + Expect(podmanTest.NumberOfContainers()).To(Equal(1)) + + // Image should not be pruned and number should be same. + images := podmanTest.Podman([]string{"images"}) + images.WaitWithDefaultTimeout() + Expect(len(images.OutputToStringArray())).To(Equal(numberOfImages)) + }) + + It("podman system prune - with dangling images true", func() { + session := podmanTest.Podman([]string{"pod", "create"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + // Start and stop a pod to get it in exited state. + session = podmanTest.Podman([]string{"pod", "start", "-l"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"pod", "stop", "-l"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + // Create a container. This container should be pruned. + create := podmanTest.Podman([]string{"create", "--name", "test", BB}) + create.WaitWithDefaultTimeout() + Expect(create.ExitCode()).To(Equal(0)) + + // Adding images should be pruned + podmanTest.BuildImage(pruneImage, "alpine_bash:latest", "true") + + // Adding unused volume should not be pruned as volumes not set + session = podmanTest.Podman([]string{"volume", "create"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + prune := podmanTest.Podman([]string{"system", "prune", "-f", "-a"}) + prune.WaitWithDefaultTimeout() + Expect(prune.ExitCode()).To(Equal(0)) + + pods := podmanTest.Podman([]string{"pod", "ps"}) + pods.WaitWithDefaultTimeout() + Expect(pods.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfPods()).To(Equal(0)) + + Expect(podmanTest.NumberOfContainers()).To(Equal(0)) + + // Volumes should not be pruned + session = podmanTest.Podman([]string{"volume", "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(len(session.OutputToStringArray())).To(Equal(2)) + + images := podmanTest.PodmanNoCache([]string{"images", "-aq"}) + images.WaitWithDefaultTimeout() + // all images are unused, so they all should be deleted! + Expect(len(images.OutputToStringArray())).To(Equal(0)) + }) }) diff --git a/test/e2e/volume_prune_test.go b/test/e2e/volume_prune_test.go index b9ea90568..3049646b0 100644 --- a/test/e2e/volume_prune_test.go +++ b/test/e2e/volume_prune_test.go @@ -65,7 +65,6 @@ var _ = Describe("Podman volume prune", func() { }) It("podman system prune --volume", func() { - Skip(v2fail) session := podmanTest.Podman([]string{"volume", "create"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) |