summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/README.md66
-rw-r--r--cmd/podman/common/specgen.go1
-rw-r--r--cmd/podman/containers/prune.go9
-rw-r--r--cmd/podman/containers/ps.go4
-rw-r--r--cmd/podman/images/prune.go15
-rw-r--r--cmd/podman/images/search.go4
-rw-r--r--cmd/podman/main.go1
-rw-r--r--cmd/podman/networks/create.go81
-rw-r--r--cmd/podman/networks/inspect.go46
-rw-r--r--cmd/podman/networks/list.go133
-rw-r--r--cmd/podman/networks/network.go9
-rw-r--r--cmd/podman/networks/rm.go63
-rw-r--r--cmd/podman/pods/prune.go13
-rw-r--r--cmd/podman/system/prune.go103
-rw-r--r--cmd/podman/utils/utils.go55
-rw-r--r--cmd/podman/volumes/prune.go12
-rw-r--r--docs/source/markdown/podman-search.1.md2
-rw-r--r--pkg/domain/entities/engine_container.go5
-rw-r--r--pkg/domain/entities/network.go52
-rw-r--r--pkg/domain/infra/abi/containers.go7
-rw-r--r--pkg/domain/infra/abi/images.go6
-rw-r--r--pkg/domain/infra/abi/network.go258
-rw-r--r--pkg/domain/infra/abi/pods.go4
-rw-r--r--pkg/domain/infra/abi/system.go38
-rw-r--r--pkg/domain/infra/abi/volumes.go6
-rw-r--r--pkg/domain/infra/tunnel/network.go23
-rw-r--r--pkg/domain/infra/tunnel/system.go7
-rw-r--r--test/e2e/network_create_test.go1
-rw-r--r--test/e2e/network_test.go10
-rw-r--r--test/e2e/prune_test.go158
-rw-r--r--test/e2e/volume_prune_test.go1
31 files changed, 1091 insertions, 102 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/common/specgen.go b/cmd/podman/common/specgen.go
index 7250f88bb..33cba30cd 100644
--- a/cmd/podman/common/specgen.go
+++ b/cmd/podman/common/specgen.go
@@ -403,6 +403,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string
}
s.ShmSize = &shmSize
}
+ s.CNINetworks = c.Net.CNINetworks
s.HostAdd = c.Net.AddHosts
s.UseImageResolvConf = c.Net.UseImageResolvConf
s.DNSServers = c.Net.DNSServers
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/images/search.go b/cmd/podman/images/search.go
index a8abfb339..a259b2419 100644
--- a/cmd/podman/images/search.go
+++ b/cmd/podman/images/search.go
@@ -105,6 +105,10 @@ func imageSearch(cmd *cobra.Command, args []string) error {
return errors.Errorf("search requires exactly one argument")
}
+ if searchOptions.Limit > 100 {
+ return errors.Errorf("Limit %d is outside the range of [1, 100]", searchOptions.Limit)
+ }
+
// TLS verification in c/image is controlled via a `types.OptionalBool`
// which allows for distinguishing among set-true, set-false, unspecified
// which is important to implement a sane way of dealing with defaults of
diff --git a/cmd/podman/main.go b/cmd/podman/main.go
index 3a8958b6d..422dee90b 100644
--- a/cmd/podman/main.go
+++ b/cmd/podman/main.go
@@ -9,6 +9,7 @@ import (
_ "github.com/containers/libpod/cmd/podman/healthcheck"
_ "github.com/containers/libpod/cmd/podman/images"
_ "github.com/containers/libpod/cmd/podman/manifest"
+ _ "github.com/containers/libpod/cmd/podman/networks"
_ "github.com/containers/libpod/cmd/podman/pods"
"github.com/containers/libpod/cmd/podman/registry"
_ "github.com/containers/libpod/cmd/podman/system"
diff --git a/cmd/podman/networks/create.go b/cmd/podman/networks/create.go
new file mode 100644
index 000000000..2bb75ea9e
--- /dev/null
+++ b/cmd/podman/networks/create.go
@@ -0,0 +1,81 @@
+package network
+
+import (
+ "fmt"
+ "net"
+
+ "github.com/containers/libpod/cmd/podman/registry"
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/network"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+ "github.com/spf13/pflag"
+)
+
+var (
+ networkCreateDescription = `create CNI networks for containers and pods`
+ networkCreateCommand = &cobra.Command{
+ Use: "create [flags] [NETWORK]",
+ Short: "network create",
+ Long: networkCreateDescription,
+ RunE: networkCreate,
+ Example: `podman network create podman1`,
+ Annotations: map[string]string{
+ registry.ParentNSRequired: "",
+ },
+ }
+)
+
+var (
+ networkCreateOptions entities.NetworkCreateOptions
+)
+
+func networkCreateFlags(flags *pflag.FlagSet) {
+ flags.StringVarP(&networkCreateOptions.Driver, "driver", "d", "bridge", "driver to manage the network")
+ flags.IPVar(&networkCreateOptions.Gateway, "gateway", nil, "IPv4 or IPv6 gateway for the subnet")
+ flags.BoolVar(&networkCreateOptions.Internal, "internal", false, "restrict external access from this network")
+ flags.IPNetVar(&networkCreateOptions.Range, "ip-range", net.IPNet{}, "allocate container IP from range")
+ flags.StringVar(&networkCreateOptions.MacVLAN, "macvlan", "", "create a Macvlan connection based on this device")
+ // TODO not supported yet
+ //flags.StringVar(&networkCreateOptions.IPamDriver, "ipam-driver", "", "IP Address Management Driver")
+ // TODO enable when IPv6 is working
+ //flags.BoolVar(&networkCreateOptions.IPV6, "IPv6", false, "enable IPv6 networking")
+ flags.IPNetVar(&networkCreateOptions.Subnet, "subnet", net.IPNet{}, "subnet in CIDR format")
+ flags.BoolVar(&networkCreateOptions.DisableDNS, "disable-dns", false, "disable dns plugin")
+}
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode},
+ Command: networkCreateCommand,
+ Parent: networkCmd,
+ })
+ flags := networkCreateCommand.Flags()
+ networkCreateFlags(flags)
+
+}
+
+func networkCreate(cmd *cobra.Command, args []string) error {
+ var (
+ name string
+ )
+ if err := network.IsSupportedDriver(networkCreateOptions.Driver); err != nil {
+ return err
+ }
+ if len(args) > 1 {
+ return errors.Errorf("only one network can be created at a time")
+ }
+ if len(args) > 0 && !libpod.NameRegex.MatchString(args[0]) {
+ return libpod.RegexError
+ }
+
+ if len(args) > 0 {
+ name = args[0]
+ }
+ response, err := registry.ContainerEngine().NetworkCreate(registry.Context(), name, networkCreateOptions)
+ if err != nil {
+ return err
+ }
+ fmt.Println(response.Filename)
+ return nil
+}
diff --git a/cmd/podman/networks/inspect.go b/cmd/podman/networks/inspect.go
new file mode 100644
index 000000000..0bc73579a
--- /dev/null
+++ b/cmd/podman/networks/inspect.go
@@ -0,0 +1,46 @@
+package network
+
+import (
+ "encoding/json"
+ "fmt"
+
+ "github.com/containers/libpod/cmd/podman/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ networkinspectDescription = `Inspect network`
+ networkinspectCommand = &cobra.Command{
+ Use: "inspect NETWORK [NETWORK...] [flags] ",
+ Short: "network inspect",
+ Long: networkinspectDescription,
+ RunE: networkInspect,
+ Example: `podman network inspect podman`,
+ Args: cobra.MinimumNArgs(1),
+ Annotations: map[string]string{
+ registry.ParentNSRequired: "",
+ },
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode},
+ Command: networkinspectCommand,
+ Parent: networkCmd,
+ })
+}
+
+func networkInspect(cmd *cobra.Command, args []string) error {
+ responses, err := registry.ContainerEngine().NetworkInspect(registry.Context(), args, entities.NetworkInspectOptions{})
+ if err != nil {
+ return err
+ }
+ b, err := json.MarshalIndent(responses, "", " ")
+ if err != nil {
+ return err
+ }
+ fmt.Println(string(b))
+ return nil
+}
diff --git a/cmd/podman/networks/list.go b/cmd/podman/networks/list.go
new file mode 100644
index 000000000..e27062255
--- /dev/null
+++ b/cmd/podman/networks/list.go
@@ -0,0 +1,133 @@
+package network
+
+import (
+ "encoding/json"
+ "fmt"
+ "html/template"
+ "io"
+ "os"
+ "strings"
+
+ "github.com/containers/libpod/cmd/podman/validate"
+
+ "github.com/containers/libpod/cmd/podman/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/network"
+ "github.com/spf13/cobra"
+ "github.com/spf13/pflag"
+)
+
+var (
+ networklistDescription = `List networks`
+ networklistCommand = &cobra.Command{
+ Use: "ls",
+ Args: validate.NoArgs,
+ Short: "network list",
+ Long: networklistDescription,
+ RunE: networkList,
+ Example: `podman network list`,
+ Annotations: map[string]string{
+ registry.ParentNSRequired: "",
+ },
+ }
+)
+
+var (
+ networkListOptions entities.NetworkListOptions
+ headers string = "NAME\tVERSION\tPLUGINS\n"
+ defaultListRow string = "{{.Name}}\t{{.Version}}\t{{.Plugins}}\n"
+)
+
+func networkListFlags(flags *pflag.FlagSet) {
+ // TODO enable filters based on something
+ //flags.StringSliceVarP(&networklistCommand.Filter, "filter", "f", []string{}, "Pause all running containers")
+ flags.StringVarP(&networkListOptions.Format, "format", "f", "", "Pretty-print containers to JSON or using a Go template")
+ flags.BoolVarP(&networkListOptions.Quiet, "quiet", "q", false, "display only names")
+}
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode},
+ Command: networklistCommand,
+ Parent: networkCmd,
+ })
+ flags := networklistCommand.Flags()
+ networkListFlags(flags)
+}
+
+func networkList(cmd *cobra.Command, args []string) error {
+ var (
+ w io.Writer = os.Stdout
+ nlprs []NetworkListPrintReports
+ )
+
+ responses, err := registry.ContainerEngine().NetworkList(registry.Context(), networkListOptions)
+ if err != nil {
+ return err
+ }
+
+ // quiet means we only print the network names
+ if networkListOptions.Quiet {
+ return quietOut(responses)
+ }
+
+ if networkListOptions.Format == "json" {
+ return jsonOut(responses)
+ }
+
+ for _, r := range responses {
+ nlprs = append(nlprs, NetworkListPrintReports{r})
+ }
+
+ row := networkListOptions.Format
+ if len(row) < 1 {
+ row = defaultListRow
+ }
+ if !strings.HasSuffix(row, "\n") {
+ row += "\n"
+ }
+
+ format := "{{range . }}" + row + "{{end}}"
+ if !cmd.Flag("format").Changed {
+ format = headers + format
+ }
+ tmpl, err := template.New("listNetworks").Parse(format)
+ if err != nil {
+ return err
+ }
+ if err := tmpl.Execute(w, nlprs); err != nil {
+ return err
+ }
+ if flusher, ok := w.(interface{ Flush() error }); ok {
+ return flusher.Flush()
+ }
+ return nil
+}
+
+func quietOut(responses []*entities.NetworkListReport) error {
+ for _, r := range responses {
+ fmt.Println(r.Name)
+ }
+ return nil
+}
+
+func jsonOut(responses []*entities.NetworkListReport) error {
+ b, err := json.MarshalIndent(responses, "", " ")
+ if err != nil {
+ return err
+ }
+ fmt.Println(string(b))
+ return nil
+}
+
+type NetworkListPrintReports struct {
+ *entities.NetworkListReport
+}
+
+func (n NetworkListPrintReports) Version() string {
+ return n.CNIVersion
+}
+
+func (n NetworkListPrintReports) Plugins() string {
+ return network.GetCNIPlugins(n.NetworkConfigList)
+}
diff --git a/cmd/podman/networks/network.go b/cmd/podman/networks/network.go
index e2a928312..56dd390ea 100644
--- a/cmd/podman/networks/network.go
+++ b/cmd/podman/networks/network.go
@@ -1,4 +1,4 @@
-package images
+package network
import (
"github.com/containers/libpod/cmd/podman/registry"
@@ -9,7 +9,7 @@ import (
var (
// Command: podman _network_
- cmd = &cobra.Command{
+ networkCmd = &cobra.Command{
Use: "network",
Short: "Manage networks",
Long: "Manage networks",
@@ -18,12 +18,9 @@ var (
}
)
-// TODO add the following to main.go to get networks back onto the
-// command list.
-// _ "github.com/containers/libpod/cmd/podman/networks"
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Mode: []entities.EngineMode{entities.ABIMode},
- Command: cmd,
+ Command: networkCmd,
})
}
diff --git a/cmd/podman/networks/rm.go b/cmd/podman/networks/rm.go
new file mode 100644
index 000000000..dc1eb9909
--- /dev/null
+++ b/cmd/podman/networks/rm.go
@@ -0,0 +1,63 @@
+package network
+
+import (
+ "fmt"
+
+ "github.com/containers/libpod/cmd/podman/registry"
+ "github.com/containers/libpod/cmd/podman/utils"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+ "github.com/spf13/pflag"
+)
+
+var (
+ networkrmDescription = `Remove networks`
+ networkrmCommand = &cobra.Command{
+ Use: "rm [flags] NETWORK [NETWORK...]",
+ Short: "network rm",
+ Long: networkrmDescription,
+ RunE: networkRm,
+ Example: `podman network rm podman`,
+ Args: cobra.MinimumNArgs(1),
+ Annotations: map[string]string{
+ registry.ParentNSRequired: "",
+ },
+ }
+)
+
+var (
+ networkRmOptions entities.NetworkRmOptions
+)
+
+func networkRmFlags(flags *pflag.FlagSet) {
+ flags.BoolVarP(&networkRmOptions.Force, "force", "f", false, "remove any containers using network")
+}
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode},
+ Command: networkrmCommand,
+ Parent: networkCmd,
+ })
+ flags := networkrmCommand.Flags()
+ networkRmFlags(flags)
+}
+
+func networkRm(cmd *cobra.Command, args []string) error {
+ var (
+ errs utils.OutputErrors
+ )
+
+ responses, err := registry.ContainerEngine().NetworkRm(registry.Context(), args, networkRmOptions)
+ if err != nil {
+ return err
+ }
+ for _, r := range responses {
+ if r.Err == nil {
+ fmt.Println(r.Name)
+ } else {
+ errs = append(errs, r.Err)
+ }
+ }
+ return errs.PrintErrors()
+}
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/docs/source/markdown/podman-search.1.md b/docs/source/markdown/podman-search.1.md
index 31de6f839..75bfeb058 100644
--- a/docs/source/markdown/podman-search.1.md
+++ b/docs/source/markdown/podman-search.1.md
@@ -59,7 +59,7 @@ Valid placeholders for the Go template are listed below:
**--limit**=*limit*
-Limit the number of results
+Limit the number of results. This value can be in the range between 1 and 100. The default number of results is 25.
Note: The results from each registry will be limited to this value.
Example if limit is 10 and two registries are being searched, the total
number of results will be 20, 10 from each (if there are at least 10 matches in each).
diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go
index eebf4c033..734f72e5f 100644
--- a/pkg/domain/entities/engine_container.go
+++ b/pkg/domain/entities/engine_container.go
@@ -42,8 +42,13 @@ 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)
+ NetworkInspect(ctx context.Context, namesOrIds []string, options NetworkInspectOptions) ([]NetworkInspectReport, error)
+ NetworkList(ctx context.Context, options NetworkListOptions) ([]*NetworkListReport, error)
+ NetworkRm(ctx context.Context, namesOrIds []string, options NetworkRmOptions) ([]*NetworkRmReport, error)
PodCreate(ctx context.Context, opts PodCreateOptions) (*PodCreateReport, error)
PodExists(ctx context.Context, nameOrId string) (*BoolReport, error)
PodInspect(ctx context.Context, options PodInspectOptions) (*PodInspectReport, error)
diff --git a/pkg/domain/entities/network.go b/pkg/domain/entities/network.go
new file mode 100644
index 000000000..cffd40899
--- /dev/null
+++ b/pkg/domain/entities/network.go
@@ -0,0 +1,52 @@
+package entities
+
+import (
+ "net"
+
+ "github.com/containernetworking/cni/libcni"
+)
+
+// NetworkListOptions describes options for listing networks in cli
+type NetworkListOptions struct {
+ Format string
+ Quiet bool
+}
+
+// NetworkListReport describes the results from listing networks
+type NetworkListReport struct {
+ *libcni.NetworkConfigList
+}
+
+// NetworkInspectOptions describes options for inspect networks
+type NetworkInspectOptions struct {
+}
+
+// NetworkInspectReport describes the results from inspect networks
+type NetworkInspectReport map[string]interface{}
+
+// NetworkRmOptions describes options for removing networks
+type NetworkRmOptions struct {
+ Force bool
+}
+
+//NetworkRmReport describes the results of network removal
+type NetworkRmReport struct {
+ Name string
+ Err error
+}
+
+// NetworkCreateOptions describes options to create a network
+type NetworkCreateOptions struct {
+ DisableDNS bool
+ Driver string
+ Gateway net.IP
+ Internal bool
+ MacVLAN string
+ Range net.IPNet
+ Subnet net.IPNet
+}
+
+// NetworkCreateReport describes a created network for the cli
+type NetworkCreateReport struct {
+ Filename string
+}
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/network.go b/pkg/domain/infra/abi/network.go
new file mode 100644
index 000000000..5c39b5374
--- /dev/null
+++ b/pkg/domain/infra/abi/network.go
@@ -0,0 +1,258 @@
+package abi
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "path/filepath"
+
+ cniversion "github.com/containernetworking/cni/pkg/version"
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/network"
+ "github.com/containers/libpod/pkg/util"
+ "github.com/pkg/errors"
+)
+
+func getCNIConfDir(r *libpod.Runtime) (string, error) {
+ config, err := r.GetConfig()
+ if err != nil {
+ return "", err
+ }
+ configPath := config.Network.NetworkConfigDir
+
+ if len(config.Network.NetworkConfigDir) < 1 {
+ configPath = network.CNIConfigDir
+ }
+ return configPath, nil
+}
+
+func (ic *ContainerEngine) NetworkList(ctx context.Context, options entities.NetworkListOptions) ([]*entities.NetworkListReport, error) {
+ var reports []*entities.NetworkListReport
+ cniConfigPath, err := getCNIConfDir(ic.Libpod)
+ if err != nil {
+ return nil, err
+ }
+ networks, err := network.LoadCNIConfsFromDir(cniConfigPath)
+ if err != nil {
+ return nil, err
+ }
+
+ for _, n := range networks {
+ reports = append(reports, &entities.NetworkListReport{NetworkConfigList: n})
+ }
+ return reports, nil
+}
+
+func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []string, options entities.NetworkInspectOptions) ([]entities.NetworkInspectReport, error) {
+ var (
+ rawCNINetworks []entities.NetworkInspectReport
+ )
+ for _, name := range namesOrIds {
+ rawList, err := network.InspectNetwork(name)
+ if err != nil {
+ return nil, err
+ }
+ rawCNINetworks = append(rawCNINetworks, rawList)
+ }
+ return rawCNINetworks, nil
+}
+
+func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, options entities.NetworkRmOptions) ([]*entities.NetworkRmReport, error) {
+ var reports []*entities.NetworkRmReport
+ for _, name := range namesOrIds {
+ report := entities.NetworkRmReport{Name: name}
+ containers, err := ic.Libpod.GetAllContainers()
+ if err != nil {
+ return reports, err
+ }
+ // We need to iterate containers looking to see if they belong to the given network
+ for _, c := range containers {
+ if util.StringInSlice(name, c.Config().Networks) {
+ // if user passes force, we nuke containers
+ if !options.Force {
+ // Without the force option, we return an error
+ return reports, errors.Errorf("%q has associated containers with it. Use -f to forcibly delete containers", name)
+ }
+ if err := ic.Libpod.RemoveContainer(ctx, c, true, true); err != nil {
+ return reports, err
+ }
+ }
+ }
+ if err := network.RemoveNetwork(name); err != nil {
+ report.Err = err
+ }
+ reports = append(reports, &report)
+ }
+ return reports, nil
+}
+
+func (ic *ContainerEngine) NetworkCreate(ctx context.Context, name string, options entities.NetworkCreateOptions) (*entities.NetworkCreateReport, error) {
+ var (
+ err error
+ fileName string
+ )
+ if len(options.MacVLAN) > 0 {
+ fileName, err = createMacVLAN(ic.Libpod, name, options)
+ } else {
+ fileName, err = createBridge(ic.Libpod, name, options)
+ }
+ if err != nil {
+ return nil, err
+ }
+ return &entities.NetworkCreateReport{Filename: fileName}, nil
+}
+
+// createBridge creates a CNI network
+func createBridge(r *libpod.Runtime, name string, options entities.NetworkCreateOptions) (string, error) {
+ isGateway := true
+ ipMasq := true
+ subnet := &options.Subnet
+ ipRange := options.Range
+ runtimeConfig, err := r.GetConfig()
+ if err != nil {
+ return "", err
+ }
+ // if range is provided, make sure it is "in" network
+ if subnet.IP != nil {
+ // if network is provided, does it conflict with existing CNI or live networks
+ err = network.ValidateUserNetworkIsAvailable(subnet)
+ } else {
+ // if no network is provided, figure out network
+ subnet, err = network.GetFreeNetwork()
+ }
+ if err != nil {
+ return "", err
+ }
+ gateway := options.Gateway
+ if gateway == nil {
+ // if no gateway is provided, provide it as first ip of network
+ gateway = network.CalcGatewayIP(subnet)
+ }
+ // if network is provided and if gateway is provided, make sure it is "in" network
+ if options.Subnet.IP != nil && options.Gateway != nil {
+ if !subnet.Contains(gateway) {
+ return "", errors.Errorf("gateway %s is not in valid for subnet %s", gateway.String(), subnet.String())
+ }
+ }
+ if options.Internal {
+ isGateway = false
+ ipMasq = false
+ }
+
+ // if a range is given, we need to ensure it is "in" the network range.
+ if options.Range.IP != nil {
+ if options.Subnet.IP == nil {
+ return "", errors.New("you must define a subnet range to define an ip-range")
+ }
+ firstIP, err := network.FirstIPInSubnet(&options.Range)
+ if err != nil {
+ return "", err
+ }
+ lastIP, err := network.LastIPInSubnet(&options.Range)
+ if err != nil {
+ return "", err
+ }
+ if !subnet.Contains(firstIP) || !subnet.Contains(lastIP) {
+ return "", errors.Errorf("the ip range %s does not fall within the subnet range %s", options.Range.String(), subnet.String())
+ }
+ }
+ bridgeDeviceName, err := network.GetFreeDeviceName()
+ if err != nil {
+ return "", err
+ }
+
+ if len(name) > 0 {
+ netNames, err := network.GetNetworkNamesFromFileSystem()
+ if err != nil {
+ return "", err
+ }
+ if util.StringInSlice(name, netNames) {
+ return "", errors.Errorf("the network name %s is already used", name)
+ }
+ } else {
+ // If no name is given, we give the name of the bridge device
+ name = bridgeDeviceName
+ }
+
+ ncList := network.NewNcList(name, cniversion.Current())
+ var plugins []network.CNIPlugins
+ var routes []network.IPAMRoute
+
+ defaultRoute, err := network.NewIPAMDefaultRoute()
+ if err != nil {
+ return "", err
+ }
+ routes = append(routes, defaultRoute)
+ ipamConfig, err := network.NewIPAMHostLocalConf(subnet, routes, ipRange, gateway)
+ if err != nil {
+ return "", err
+ }
+
+ // TODO need to iron out the role of isDefaultGW and IPMasq
+ bridge := network.NewHostLocalBridge(bridgeDeviceName, isGateway, false, ipMasq, ipamConfig)
+ plugins = append(plugins, bridge)
+ plugins = append(plugins, network.NewPortMapPlugin())
+ plugins = append(plugins, network.NewFirewallPlugin())
+ // if we find the dnsname plugin, we add configuration for it
+ if network.HasDNSNamePlugin(runtimeConfig.Network.CNIPluginDirs) && !options.DisableDNS {
+ // Note: in the future we might like to allow for dynamic domain names
+ plugins = append(plugins, network.NewDNSNamePlugin(network.DefaultPodmanDomainName))
+ }
+ ncList["plugins"] = plugins
+ b, err := json.MarshalIndent(ncList, "", " ")
+ if err != nil {
+ return "", err
+ }
+ cniConfigPath, err := getCNIConfDir(r)
+ if err != nil {
+ return "", err
+ }
+ cniPathName := filepath.Join(cniConfigPath, fmt.Sprintf("%s.conflist", name))
+ err = ioutil.WriteFile(cniPathName, b, 0644)
+ return cniPathName, err
+}
+
+func createMacVLAN(r *libpod.Runtime, name string, options entities.NetworkCreateOptions) (string, error) {
+ var (
+ plugins []network.CNIPlugins
+ )
+ liveNetNames, err := network.GetLiveNetworkNames()
+ if err != nil {
+ return "", err
+ }
+ // Make sure the host-device exists
+ if !util.StringInSlice(options.MacVLAN, liveNetNames) {
+ return "", errors.Errorf("failed to find network interface %q", options.MacVLAN)
+ }
+ if len(name) > 0 {
+ netNames, err := network.GetNetworkNamesFromFileSystem()
+ if err != nil {
+ return "", err
+ }
+ if util.StringInSlice(name, netNames) {
+ return "", errors.Errorf("the network name %s is already used", name)
+ }
+ } else {
+ name, err = network.GetFreeDeviceName()
+ if err != nil {
+ return "", err
+ }
+ }
+ ncList := network.NewNcList(name, cniversion.Current())
+ macvlan := network.NewMacVLANPlugin(options.MacVLAN)
+ plugins = append(plugins, macvlan)
+ ncList["plugins"] = plugins
+ b, err := json.MarshalIndent(ncList, "", " ")
+ if err != nil {
+ return "", err
+ }
+ cniConfigPath, err := getCNIConfDir(r)
+ if err != nil {
+ return "", err
+ }
+ cniPathName := filepath.Join(cniConfigPath, fmt.Sprintf("%s.conflist", name))
+ err = ioutil.WriteFile(cniPathName, b, 0644)
+ return cniPathName, 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/network.go b/pkg/domain/infra/tunnel/network.go
new file mode 100644
index 000000000..4ff72dcfc
--- /dev/null
+++ b/pkg/domain/infra/tunnel/network.go
@@ -0,0 +1,23 @@
+package tunnel
+
+import (
+ "context"
+ "errors"
+
+ "github.com/containers/libpod/pkg/domain/entities"
+)
+
+func (ic *ContainerEngine) NetworkList(ctx context.Context, options entities.NetworkListOptions) ([]*entities.NetworkListReport, error) {
+ return nil, errors.New("not implemented")
+}
+
+func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []string, options entities.NetworkInspectOptions) ([]entities.NetworkInspectReport, error) {
+ return nil, errors.New("not implemented")
+}
+func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, options entities.NetworkRmOptions) ([]*entities.NetworkRmReport, error) {
+ return nil, errors.New("not implemented")
+}
+
+func (ic *ContainerEngine) NetworkCreate(ctx context.Context, name string, options entities.NetworkCreateOptions) (*entities.NetworkCreateReport, error) {
+ return nil, errors.New("not implemented")
+}
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/network_create_test.go b/test/e2e/network_create_test.go
index 19dabced7..7eccaa9ab 100644
--- a/test/e2e/network_create_test.go
+++ b/test/e2e/network_create_test.go
@@ -76,7 +76,6 @@ var _ = Describe("Podman network create", func() {
)
BeforeEach(func() {
- Skip(v2fail)
SkipIfRootless()
tempdir, err = CreateTempDirInTempDir()
if err != nil {
diff --git a/test/e2e/network_test.go b/test/e2e/network_test.go
index 2cb7eb144..8d575d7f9 100644
--- a/test/e2e/network_test.go
+++ b/test/e2e/network_test.go
@@ -34,7 +34,7 @@ var _ = Describe("Podman network", func() {
)
BeforeEach(func() {
- Skip(v2fail)
+ SkipIfRootless()
tempdir, err = CreateTempDirInTempDir()
if err != nil {
os.Exit(1)
@@ -80,7 +80,6 @@ var _ = Describe("Podman network", func() {
)
It("podman network list", func() {
- SkipIfRootless()
// Setup, use uuid to prevent conflict with other tests
uuid := stringid.GenerateNonCryptoID()
secondPath := filepath.Join(cniPath, fmt.Sprintf("%s.conflist", uuid))
@@ -94,7 +93,6 @@ var _ = Describe("Podman network", func() {
})
It("podman network list -q", func() {
- SkipIfRootless()
// Setup, use uuid to prevent conflict with other tests
uuid := stringid.GenerateNonCryptoID()
secondPath := filepath.Join(cniPath, fmt.Sprintf("%s.conflist", uuid))
@@ -108,14 +106,12 @@ var _ = Describe("Podman network", func() {
})
It("podman network rm no args", func() {
- SkipIfRootless()
session := podmanTest.Podman([]string{"network", "rm"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).ToNot(BeZero())
})
It("podman network rm", func() {
- SkipIfRootless()
// Setup, use uuid to prevent conflict with other tests
uuid := stringid.GenerateNonCryptoID()
secondPath := filepath.Join(cniPath, fmt.Sprintf("%s.conflist", uuid))
@@ -138,14 +134,12 @@ var _ = Describe("Podman network", func() {
})
It("podman network inspect no args", func() {
- SkipIfRootless()
session := podmanTest.Podman([]string{"network", "inspect"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).ToNot(BeZero())
})
It("podman network inspect", func() {
- SkipIfRootless()
// Setup, use uuid to prevent conflict with other tests
uuid := stringid.GenerateNonCryptoID()
secondPath := filepath.Join(cniPath, fmt.Sprintf("%s.conflist", uuid))
@@ -159,7 +153,6 @@ var _ = Describe("Podman network", func() {
})
It("podman inspect container single CNI network", func() {
- SkipIfRootless()
netName := "testNetSingleCNI"
network := podmanTest.Podman([]string{"network", "create", "--subnet", "10.50.50.0/24", netName})
network.WaitWithDefaultTimeout()
@@ -190,7 +183,6 @@ var _ = Describe("Podman network", func() {
})
It("podman inspect container two CNI networks", func() {
- SkipIfRootless()
netName1 := "testNetTwoCNI1"
network1 := podmanTest.Podman([]string{"network", "create", "--subnet", "10.50.51.0/25", netName1})
network1.WaitWithDefaultTimeout()
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))