From 2a6d8b3bd69fe2aedf9a30744befed1aead5e2b9 Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Fri, 18 Jan 2019 14:30:45 +0100 Subject: podman-inspect: don't ignore errors Return errors when executing the --format templates. Otherwise, Podman will just silently ignore them and not print any output that could guide user into solving the issue. Fixes: #2159 Signed-off-by: Valentin Rothberg --- cmd/podman/formats/formats.go | 13 +++++++------ cmd/podman/inspect.go | 6 ++++-- 2 files changed, 11 insertions(+), 8 deletions(-) (limited to 'cmd') diff --git a/cmd/podman/formats/formats.go b/cmd/podman/formats/formats.go index 3da0ea385..c454c39bd 100644 --- a/cmd/podman/formats/formats.go +++ b/cmd/podman/formats/formats.go @@ -20,6 +20,8 @@ const ( JSONString = "json" // IDString const to save on duplicates for Go templates IDString = "{{.ID}}" + + parsingErrorStr = "Template parsing error" ) // Writer interface for outputs @@ -96,7 +98,7 @@ func (t StdoutTemplateArray) Out() error { t.Template = strings.Replace(strings.TrimSpace(t.Template[5:]), " ", "\t", -1) headerTmpl, err := template.New("header").Funcs(headerFunctions).Parse(t.Template) if err != nil { - return errors.Wrapf(err, "Template parsing error") + return errors.Wrapf(err, parsingErrorStr) } err = headerTmpl.Execute(w, t.Fields) if err != nil { @@ -107,13 +109,12 @@ func (t StdoutTemplateArray) Out() error { t.Template = strings.Replace(t.Template, " ", "\t", -1) tmpl, err := template.New("image").Funcs(basicFunctions).Parse(t.Template) if err != nil { - return errors.Wrapf(err, "Template parsing error") + return errors.Wrapf(err, parsingErrorStr) } - for i, img := range t.Output { + for i, raw := range t.Output { basicTmpl := tmpl.Funcs(basicFunctions) - err = basicTmpl.Execute(w, img) - if err != nil { - return err + if err := basicTmpl.Execute(w, raw); err != nil { + return errors.Wrapf(err, parsingErrorStr) } if i != len(t.Output)-1 { fmt.Fprintln(w, "") diff --git a/cmd/podman/inspect.go b/cmd/podman/inspect.go index 6ffcde55f..2f1e97c6c 100644 --- a/cmd/podman/inspect.go +++ b/cmd/podman/inspect.go @@ -87,6 +87,9 @@ func inspectCmd(c *cli.Context) error { } inspectedObjects, iterateErr := iterateInput(getContext(), c, args, runtime, inspectType) + if iterateErr != nil { + return iterateErr + } var out formats.Writer if outputFormat != "" && outputFormat != formats.JSONString { @@ -97,8 +100,7 @@ func inspectCmd(c *cli.Context) error { out = formats.JSONStructArray{Output: inspectedObjects} } - formats.Writer(out).Out() - return iterateErr + return formats.Writer(out).Out() } // func iterateInput iterates the images|containers the user has requested and returns the inspect data and error -- cgit v1.2.3-54-g00ecf From 14eaca337af0fe3c66624a1a808157b6de8bff68 Mon Sep 17 00:00:00 2001 From: Daniel J Walsh Date: Fri, 18 Jan 2019 15:01:53 -0500 Subject: Vendor in latest opencontainers/selinux This will now verify labels passed in by the user. Will also prevent users from accidently relabeling their homedir. podman run -ti -v ~/home/user:Z fedora sh Is not a good idea. Signed-off-by: Daniel J Walsh --- cmd/podman/create.go | 12 ++++- libpod/oci.go | 17 ++++-- vendor.conf | 2 +- .../selinux/go-selinux/label/label.go | 4 +- .../selinux/go-selinux/label/label_selinux.go | 62 ++++++++++++++++++++-- .../selinux/go-selinux/selinux_linux.go | 36 +++++++++---- .../selinux/go-selinux/selinux_stub.go | 8 +-- 7 files changed, 111 insertions(+), 30 deletions(-) (limited to 'cmd') diff --git a/cmd/podman/create.go b/cmd/podman/create.go index d98b78bd4..93f38d2db 100644 --- a/cmd/podman/create.go +++ b/cmd/podman/create.go @@ -172,7 +172,11 @@ func parseSecurityOpt(config *cc.CreateConfig, securityOpts []string) error { if err != nil { return errors.Wrapf(err, "container %q not found", config.PidMode.Container()) } - labelOpts = append(labelOpts, label.DupSecOpt(ctr.ProcessLabel())...) + secopts, err := label.DupSecOpt(ctr.ProcessLabel()) + if err != nil { + return errors.Wrapf(err, "failed to duplicate label %q ", ctr.ProcessLabel()) + } + labelOpts = append(labelOpts, secopts...) } if config.IpcMode.IsHost() { @@ -182,7 +186,11 @@ func parseSecurityOpt(config *cc.CreateConfig, securityOpts []string) error { if err != nil { return errors.Wrapf(err, "container %q not found", config.IpcMode.Container()) } - labelOpts = append(labelOpts, label.DupSecOpt(ctr.ProcessLabel())...) + secopts, err := label.DupSecOpt(ctr.ProcessLabel()) + if err != nil { + return errors.Wrapf(err, "failed to duplicate label %q ", ctr.ProcessLabel()) + } + labelOpts = append(labelOpts, secopts...) } for _, opt := range securityOpts { diff --git a/libpod/oci.go b/libpod/oci.go index 31c1a7e85..a1894b52f 100644 --- a/libpod/oci.go +++ b/libpod/oci.go @@ -357,18 +357,25 @@ func (r *OCIRuntime) createOCIContainer(ctr *Container, cgroupParent string, res // Set the label of the conmon process to be level :s0 // This will allow the container processes to talk to fifo-files // passed into the container by conmon - var plabel string + var ( + plabel string + con selinux.Context + ) plabel, err = selinux.CurrentLabel() if err != nil { childPipe.Close() return errors.Wrapf(err, "Failed to get current SELinux label") } - c := selinux.NewContext(plabel) + con, err = selinux.NewContext(plabel) + if err != nil { + return errors.Wrapf(err, "Failed to get new context from SELinux label") + } + runtime.LockOSThread() - if c["level"] != "s0" && c["level"] != "" { - c["level"] = "s0" - if err = label.SetProcessLabel(c.Get()); err != nil { + if con["level"] != "s0" && con["level"] != "" { + con["level"] = "s0" + if err = label.SetProcessLabel(con.Get()); err != nil { runtime.UnlockOSThread() return err } diff --git a/vendor.conf b/vendor.conf index 18283cae6..82ae1260e 100644 --- a/vendor.conf +++ b/vendor.conf @@ -51,7 +51,7 @@ github.com/opencontainers/image-spec v1.0.0 github.com/opencontainers/runc bbb17efcb4c0ab986407812a31ba333a7450064c github.com/opencontainers/runtime-spec d810dbc60d8c5aeeb3d054bd1132fab2121968ce github.com/opencontainers/runtime-tools master -github.com/opencontainers/selinux 51c6c0a5dbc675792e953298cb9871819d6f9bb8 +github.com/opencontainers/selinux v1.1 github.com/ostreedev/ostree-go master github.com/pkg/errors v0.8.0 github.com/pmezard/go-difflib 792786c7400a136282c1664665ae0a8db921c6c2 diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/label/label.go b/vendor/github.com/opencontainers/selinux/go-selinux/label/label.go index bb27ac936..4e9a8c54f 100644 --- a/vendor/github.com/opencontainers/selinux/go-selinux/label/label.go +++ b/vendor/github.com/opencontainers/selinux/go-selinux/label/label.go @@ -75,8 +75,8 @@ func ReleaseLabel(label string) error { // DupSecOpt takes a process label and returns security options that // can be used to set duplicate labels on future container processes -func DupSecOpt(src string) []string { - return nil +func DupSecOpt(src string) ([]string, error) { + return nil, nil } // DisableSecOpt returns a security opt that can disable labeling diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/label/label_selinux.go b/vendor/github.com/opencontainers/selinux/go-selinux/label/label_selinux.go index de214b2d5..d4e26909d 100644 --- a/vendor/github.com/opencontainers/selinux/go-selinux/label/label_selinux.go +++ b/vendor/github.com/opencontainers/selinux/go-selinux/label/label_selinux.go @@ -4,6 +4,8 @@ package label import ( "fmt" + "os" + "os/user" "strings" "github.com/opencontainers/selinux/go-selinux" @@ -35,8 +37,15 @@ func InitLabels(options []string) (plabel string, mlabel string, Err error) { ReleaseLabel(mountLabel) } }() - pcon := selinux.NewContext(processLabel) - mcon := selinux.NewContext(mountLabel) + pcon, err := selinux.NewContext(processLabel) + if err != nil { + return "", "", err + } + + mcon, err := selinux.NewContext(mountLabel) + if err != nil { + return "", "", err + } for _, opt := range options { if opt == "disable" { return "", mountLabel, nil @@ -146,13 +155,56 @@ func Relabel(path string, fileLabel string, shared bool) error { return nil } - exclude_paths := map[string]bool{"/": true, "/usr": true, "/etc": true, "/tmp": true, "/home": true, "/run": true, "/var": true, "/root": true} + exclude_paths := map[string]bool{ + "/": true, + "/bin": true, + "/boot": true, + "/dev": true, + "/etc": true, + "/etc/passwd": true, + "/etc/pki": true, + "/etc/shadow": true, + "/home": true, + "/lib": true, + "/lib64": true, + "/media": true, + "/opt": true, + "/proc": true, + "/root": true, + "/run": true, + "/sbin": true, + "/srv": true, + "/sys": true, + "/tmp": true, + "/usr": true, + "/var": true, + "/var/lib": true, + "/var/log": true, + } + + if home := os.Getenv("HOME"); home != "" { + exclude_paths[home] = true + } + + if sudoUser := os.Getenv("SUDO_USER"); sudoUser != "" { + if usr, err := user.Lookup(sudoUser); err == nil { + exclude_paths[usr.HomeDir] = true + } + } + + if path != "/" { + path = strings.TrimSuffix(path, "/") + } if exclude_paths[path] { return fmt.Errorf("SELinux relabeling of %s is not allowed", path) } if shared { - c := selinux.NewContext(fileLabel) + c, err := selinux.NewContext(fileLabel) + if err != nil { + return err + } + c["level"] = "s0" fileLabel = c.Get() } @@ -195,7 +247,7 @@ func ReleaseLabel(label string) error { // DupSecOpt takes a process label and returns security options that // can be used to set duplicate labels on future container processes -func DupSecOpt(src string) []string { +func DupSecOpt(src string) ([]string, error) { return selinux.DupSecOpt(src) } diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go b/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go index 7832f7497..5adafd317 100644 --- a/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go +++ b/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go @@ -52,6 +52,8 @@ var ( ErrMCSAlreadyExists = errors.New("MCS label already exists") // ErrEmptyPath is returned when an empty path has been specified. ErrEmptyPath = errors.New("empty path") + // InvalidLabel is returned when an invalid label is specified. + InvalidLabel = errors.New("Invalid Label") assignRegex = regexp.MustCompile(`^([^=]+)=(.*)$`) roFileLabel string @@ -405,11 +407,14 @@ func (c Context) Get() string { } // NewContext creates a new Context struct from the specified label -func NewContext(label string) Context { +func NewContext(label string) (Context, error) { c := make(Context) if len(label) != 0 { con := strings.SplitN(label, ":", 4) + if len(con) < 3 { + return c, InvalidLabel + } c["user"] = con[0] c["role"] = con[1] c["type"] = con[2] @@ -417,7 +422,7 @@ func NewContext(label string) Context { c["level"] = con[3] } } - return c + return c, nil } // ClearLabels clears all reserved labels @@ -630,12 +635,12 @@ func ContainerLabels() (processLabel string, fileLabel string) { roFileLabel = fileLabel } exit: - scon := NewContext(processLabel) + scon, _ := NewContext(processLabel) if scon["level"] != "" { mcs := uniqMcs(1024) scon["level"] = mcs processLabel = scon.Get() - scon = NewContext(fileLabel) + scon, _ = NewContext(fileLabel) scon["level"] = mcs fileLabel = scon.Get() } @@ -661,8 +666,14 @@ func CopyLevel(src, dest string) (string, error) { if err := SecurityCheckContext(dest); err != nil { return "", err } - scon := NewContext(src) - tcon := NewContext(dest) + scon, err := NewContext(src) + if err != nil { + return "", err + } + tcon, err := NewContext(dest) + if err != nil { + return "", err + } mcsDelete(tcon["level"]) mcsAdd(scon["level"]) tcon["level"] = scon["level"] @@ -714,15 +725,18 @@ func Chcon(fpath string, label string, recurse bool) error { // DupSecOpt takes an SELinux process label and returns security options that // can be used to set the SELinux Type and Level for future container processes. -func DupSecOpt(src string) []string { +func DupSecOpt(src string) ([]string, error) { if src == "" { - return nil + return nil, nil + } + con, err := NewContext(src) + if err != nil { + return nil, err } - con := NewContext(src) if con["user"] == "" || con["role"] == "" || con["type"] == "" { - return nil + return nil, nil } dup := []string{"user:" + con["user"], "role:" + con["role"], @@ -733,7 +747,7 @@ func DupSecOpt(src string) []string { dup = append(dup, "level:"+con["level"]) } - return dup + return dup, nil } // DisableSecOpt returns a security opt that can be used to disable SELinux diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/selinux_stub.go b/vendor/github.com/opencontainers/selinux/go-selinux/selinux_stub.go index 99efa155a..9497acbd0 100644 --- a/vendor/github.com/opencontainers/selinux/go-selinux/selinux_stub.go +++ b/vendor/github.com/opencontainers/selinux/go-selinux/selinux_stub.go @@ -115,9 +115,9 @@ func (c Context) Get() string { } // NewContext creates a new Context struct from the specified label -func NewContext(label string) Context { +func NewContext(label string) (Context, error) { c := make(Context) - return c + return c, nil } // ClearLabels clears all reserved MLS/MCS levels @@ -195,8 +195,8 @@ func Chcon(fpath string, label string, recurse bool) error { // DupSecOpt takes an SELinux process label and returns security options that // can be used to set the SELinux Type and Level for future container processes. -func DupSecOpt(src string) []string { - return nil +func DupSecOpt(src string) ([]string, error) { + return nil, nil } // DisableSecOpt returns a security opt that can be used to disable SELinux -- cgit v1.2.3-54-g00ecf From 9dfefed334cda999b55820368507209f8c6721b0 Mon Sep 17 00:00:00 2001 From: Daniel J Walsh Date: Tue, 29 Jan 2019 16:10:51 +0000 Subject: Make --quiet work in podman create/run The --queit option is supposed to suppress the pulling messages when a new image is being pulled down. This patch fixes this issue. Signed-off-by: Daniel J Walsh --- cmd/podman/create.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'cmd') diff --git a/cmd/podman/create.go b/cmd/podman/create.go index 93f38d2db..e10009971 100644 --- a/cmd/podman/create.go +++ b/cmd/podman/create.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "io" "os" "path/filepath" "strconv" @@ -127,7 +128,12 @@ func createContainer(c *cli.Context, runtime *libpod.Runtime) (*libpod.Container var data *inspect.ImageData = nil if rootfs == "" && !rootless.SkipStorageSetup() { - newImage, err := runtime.ImageRuntime().New(ctx, c.Args()[0], rtc.SignaturePolicyPath, "", os.Stderr, nil, image.SigningOptions{}, false) + var writer io.Writer + if !c.Bool("quiet") { + writer = os.Stderr + } + + newImage, err := runtime.ImageRuntime().New(ctx, c.Args()[0], rtc.SignaturePolicyPath, "", writer, nil, image.SigningOptions{}, false) if err != nil { return nil, nil, err } -- cgit v1.2.3-54-g00ecf From 28f5d25e8dabb6647699c2ccab8621cefac37d3b Mon Sep 17 00:00:00 2001 From: baude Date: Thu, 10 Jan 2019 14:39:39 -0600 Subject: Add varlink support for prune Add the ability to prune unused images using the varlink API. Signed-off-by: baude --- API.md | 8 ++++++++ cmd/podman/images_prune.go | 15 +++++++++++++-- cmd/podman/shared/prune.go | 24 ------------------------ cmd/podman/varlink/io.podman.varlink | 4 ++++ pkg/varlinkapi/images.go | 18 ++++++++++++++++++ 5 files changed, 43 insertions(+), 26 deletions(-) delete mode 100644 cmd/podman/shared/prune.go (limited to 'cmd') diff --git a/API.md b/API.md index 3722c2864..0cbdffea4 100755 --- a/API.md +++ b/API.md @@ -57,6 +57,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in [func ImageExists(name: string) int](#ImageExists) +[func ImagesPrune() []string](#ImagesPrune) + [func ImportImage(source: string, reference: string, message: string, changes: []string) string](#ImportImage) [func InspectContainer(name: string) string](#InspectContainer) @@ -543,6 +545,12 @@ $ varlink call -m unix:/run/podman/io.podman/io.podman.ImageExists '{"name": "im "exists": 1 } ~~~ +### func ImagesPrune +
+ +method ImagesPrune() [[]string](#[]string)
+ImagesPrune removes all unused images from the local store. Upon successful pruning, +the IDs of the removed images are returned. ### func ImportImage
diff --git a/cmd/podman/images_prune.go b/cmd/podman/images_prune.go index cb72a498f..06879e02d 100644 --- a/cmd/podman/images_prune.go +++ b/cmd/podman/images_prune.go @@ -1,8 +1,8 @@ package main import ( + "fmt" "github.com/containers/libpod/cmd/podman/libpodruntime" - "github.com/containers/libpod/cmd/podman/shared" "github.com/pkg/errors" "github.com/urfave/cli" ) @@ -30,5 +30,16 @@ func pruneImagesCmd(c *cli.Context) error { } defer runtime.Shutdown(false) - return shared.Prune(runtime.ImageRuntime()) + pruneImages, err := runtime.ImageRuntime().GetPruneImages() + if err != nil { + return err + } + + for _, i := range pruneImages { + if err := i.Remove(true); err != nil { + return errors.Wrapf(err, "failed to remove %s", i.ID()) + } + fmt.Println(i.ID()) + } + return nil } diff --git a/cmd/podman/shared/prune.go b/cmd/podman/shared/prune.go deleted file mode 100644 index 90cfe4475..000000000 --- a/cmd/podman/shared/prune.go +++ /dev/null @@ -1,24 +0,0 @@ -package shared - -import ( - "fmt" - "github.com/pkg/errors" - - "github.com/containers/libpod/libpod/image" -) - -// Prune removes all unnamed and unused images from the local store -func Prune(ir *image.Runtime) error { - pruneImages, err := ir.GetPruneImages() - if err != nil { - return err - } - - for _, i := range pruneImages { - if err := i.Remove(true); err != nil { - return errors.Wrapf(err, "failed to remove %s", i.ID()) - } - fmt.Println(i.ID()) - } - return nil -} diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink index 4e8b69faf..7cd6c16b2 100644 --- a/cmd/podman/varlink/io.podman.varlink +++ b/cmd/podman/varlink/io.podman.varlink @@ -1015,6 +1015,10 @@ method MountContainer(name: string) -> (path: string) # ~~~ method UnmountContainer(name: string, force: bool) -> () +# ImagesPrune removes all unused images from the local store. Upon successful pruning, +# the IDs of the removed images are returned. +method ImagesPrune() -> (pruned: []string) + # This function is not implemented yet. method ListContainerPorts(name: string) -> (notimplemented: NotImplemented) diff --git a/pkg/varlinkapi/images.go b/pkg/varlinkapi/images.go index 8f8934025..4a4f20031 100644 --- a/pkg/varlinkapi/images.go +++ b/pkg/varlinkapi/images.go @@ -620,3 +620,21 @@ func (i *LibpodAPI) ContainerRunlabel(call iopodman.VarlinkCall, input iopodman. } return call.ReplyContainerRunlabel() } + +// ImagesPrune .... +func (i *LibpodAPI) ImagesPrune(call iopodman.VarlinkCall) error { + var ( + pruned []string + ) + pruneImages, err := i.Runtime.ImageRuntime().GetPruneImages() + if err != nil { + return err + } + for _, i := range pruneImages { + if err := i.Remove(true); err != nil { + return call.ReplyErrorOccurred(err.Error()) + } + pruned = append(pruned, i.ID()) + } + return call.ReplyImagesPrune(pruned) +} -- cgit v1.2.3-54-g00ecf From 431459caf946c1645da31b4fc6c953ad77f9bc85 Mon Sep 17 00:00:00 2001 From: baude Date: Tue, 22 Jan 2019 09:17:34 -0600 Subject: podman image prune -- implement all flag we now, by default, only prune dangling images. if --all is passed, we prune dangling images AND images that do not have an associated containers. also went ahead and enabled the podman-remote image prune side of things. Fixes: #2192 Signed-off-by: baude MH: Removed dependence on remote-client adapter work to limit scale of changes Signed-off-by: Matthew Heon --- API.md | 4 ++-- cmd/podman/images_prune.go | 26 ++++++++++++++---------- cmd/podman/varlink/io.podman.varlink | 2 +- completions/bash/podman | 2 ++ docs/podman-image-prune.1.md | 21 ++++++++++++++++--- libpod/image/prune.go | 39 +++++++++++++++++++++++++++--------- pkg/varlinkapi/images.go | 17 ++++------------ test/e2e/prune_test.go | 5 ++++- 8 files changed, 76 insertions(+), 40 deletions(-) (limited to 'cmd') diff --git a/API.md b/API.md index 0cbdffea4..e4576850b 100755 --- a/API.md +++ b/API.md @@ -57,7 +57,7 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in [func ImageExists(name: string) int](#ImageExists) -[func ImagesPrune() []string](#ImagesPrune) +[func ImagesPrune(all: bool) []string](#ImagesPrune) [func ImportImage(source: string, reference: string, message: string, changes: []string) string](#ImportImage) @@ -548,7 +548,7 @@ $ varlink call -m unix:/run/podman/io.podman/io.podman.ImageExists '{"name": "im ### func ImagesPrune
-method ImagesPrune() [[]string](#[]string)
+method ImagesPrune(all: [bool](https://godoc.org/builtin#bool)) [[]string](#[]string)
ImagesPrune removes all unused images from the local store. Upon successful pruning, the IDs of the removed images are returned. ### func ImportImage diff --git a/cmd/podman/images_prune.go b/cmd/podman/images_prune.go index 06879e02d..7310137e7 100644 --- a/cmd/podman/images_prune.go +++ b/cmd/podman/images_prune.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/pkg/errors" "github.com/urfave/cli" @@ -13,13 +14,19 @@ var ( Removes all unnamed images from local storage ` - + pruneImageFlags = []cli.Flag{ + cli.BoolFlag{ + Name: "all, a", + Usage: "remove all unused images, not just dangling ones", + }, + } pruneImagesCommand = cli.Command{ Name: "prune", Usage: "Remove unused images", Description: pruneImagesDescription, Action: pruneImagesCmd, OnUsageError: usageErrorHandler, + Flags: pruneImageFlags, } ) @@ -30,16 +37,13 @@ func pruneImagesCmd(c *cli.Context) error { } defer runtime.Shutdown(false) - pruneImages, err := runtime.ImageRuntime().GetPruneImages() - if err != nil { - return err - } - - for _, i := range pruneImages { - if err := i.Remove(true); err != nil { - return errors.Wrapf(err, "failed to remove %s", i.ID()) + // Call prune; if any cids are returned, print them and then + // return err in case an error also came up + pruneCids, err := runtime.ImageRuntime().PruneImages(c.Bool("all")) + if len(pruneCids) > 0 { + for _, cid := range pruneCids { + fmt.Println(cid) } - fmt.Println(i.ID()) } - return nil + return err } diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink index 7cd6c16b2..6f3bf34a2 100644 --- a/cmd/podman/varlink/io.podman.varlink +++ b/cmd/podman/varlink/io.podman.varlink @@ -1017,7 +1017,7 @@ method UnmountContainer(name: string, force: bool) -> () # ImagesPrune removes all unused images from the local store. Upon successful pruning, # the IDs of the removed images are returned. -method ImagesPrune() -> (pruned: []string) +method ImagesPrune(all: bool) -> (pruned: []string) # This function is not implemented yet. method ListContainerPorts(name: string) -> (notimplemented: NotImplemented) diff --git a/completions/bash/podman b/completions/bash/podman index 6333dfdf2..410180638 100644 --- a/completions/bash/podman +++ b/completions/bash/podman @@ -2453,6 +2453,8 @@ _podman_images_prune() { " local boolean_options=" + -a + --all -h --help " diff --git a/docs/podman-image-prune.1.md b/docs/podman-image-prune.1.md index db76b26e0..df912c380 100644 --- a/docs/podman-image-prune.1.md +++ b/docs/podman-image-prune.1.md @@ -6,23 +6,38 @@ podman-image-prune - Remove all unused images # SYNOPSIS **podman image prune** +[**-a**|**--all**] [**-h**|**--help**] # DESCRIPTION -**podman image prune** removes all unused images from local storage. An unused image -is defined as an image that does not have any containers based on it. +**podman image prune** removes all dangling images from local storage. With the `all` option, +you can delete all unused images. Unused images are dangling images as well as any image that +does not have any containers based on it. + +## OPTIONS +**--all, -a** + +Remove dangling images and images that have no associated containers. ## Examples ## -Remove all unused images from local storage +Remove all dangling images from local storage ``` $ sudo podman image prune f3e20dc537fb04cb51672a5cb6fdf2292e61d411315549391a0d1f64e4e3097e 324a7a3b2e0135f4226ffdd473e4099fd9e477a74230cdc35de69e84c0f9d907 +``` + +Remove all unused images from local storage +``` +$ sudo podman image prune -a +f3e20dc537fb04cb51672a5cb6fdf2292e61d411315549391a0d1f64e4e3097e +324a7a3b2e0135f4226ffdd473e4099fd9e477a74230cdc35de69e84c0f9d907 6125002719feb1ddf3030acab1df6156da7ce0e78e571e9b6e9c250424d6220c 91e732da5657264c6f4641b8d0c4001c218ae6c1adb9dcef33ad00cafd37d8b6 e4e5109420323221f170627c138817770fb64832da7d8fe2babd863148287fca 77a57fa8285e9656dbb7b23d9efa837a106957409ddd702f995605af27a45ebe + ``` ## SEE ALSO diff --git a/libpod/image/prune.go b/libpod/image/prune.go index 6a1f160d5..8602c222c 100644 --- a/libpod/image/prune.go +++ b/libpod/image/prune.go @@ -1,9 +1,11 @@ package image +import "github.com/pkg/errors" + // GetPruneImages returns a slice of images that have no names/unused -func (ir *Runtime) GetPruneImages() ([]*Image, error) { +func (ir *Runtime) GetPruneImages(all bool) ([]*Image, error) { var ( - unamedImages []*Image + pruneImages []*Image ) allImages, err := ir.GetImages() if err != nil { @@ -11,16 +13,35 @@ func (ir *Runtime) GetPruneImages() ([]*Image, error) { } for _, i := range allImages { if len(i.Names()) == 0 { - unamedImages = append(unamedImages, i) + pruneImages = append(pruneImages, i) continue } - containers, err := i.Containers() - if err != nil { - return nil, err + if all { + containers, err := i.Containers() + if err != nil { + return nil, err + } + if len(containers) < 1 { + pruneImages = append(pruneImages, i) + } } - if len(containers) < 1 { - unamedImages = append(unamedImages, i) + } + return pruneImages, nil +} + +// PruneImages prunes dangling and optionally all unused images from the local +// image store +func (ir *Runtime) PruneImages(all bool) ([]string, error) { + var prunedCids []string + pruneImages, err := ir.GetPruneImages(all) + if err != nil { + return nil, errors.Wrap(err, "unable to get images to prune") + } + for _, p := range pruneImages { + if err := p.Remove(true); err != nil { + return nil, errors.Wrap(err, "failed to prune image") } + prunedCids = append(prunedCids, p.ID()) } - return unamedImages, nil + return prunedCids, nil } diff --git a/pkg/varlinkapi/images.go b/pkg/varlinkapi/images.go index 4a4f20031..cfcdde6ef 100644 --- a/pkg/varlinkapi/images.go +++ b/pkg/varlinkapi/images.go @@ -622,19 +622,10 @@ func (i *LibpodAPI) ContainerRunlabel(call iopodman.VarlinkCall, input iopodman. } // ImagesPrune .... -func (i *LibpodAPI) ImagesPrune(call iopodman.VarlinkCall) error { - var ( - pruned []string - ) - pruneImages, err := i.Runtime.ImageRuntime().GetPruneImages() +func (i *LibpodAPI) ImagesPrune(call iopodman.VarlinkCall, all bool) error { + prunedImages, err := i.Runtime.ImageRuntime().PruneImages(all) if err != nil { - return err - } - for _, i := range pruneImages { - if err := i.Remove(true); err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - pruned = append(pruned, i.ID()) + return call.ReplyErrorOccurred(err.Error()) } - return call.ReplyImagesPrune(pruned) + return call.ReplyImagesPrune(prunedImages) } diff --git a/test/e2e/prune_test.go b/test/e2e/prune_test.go index 6679a676c..81fb82b20 100644 --- a/test/e2e/prune_test.go +++ b/test/e2e/prune_test.go @@ -39,6 +39,7 @@ var _ = Describe("Podman rm", func() { }) It("podman container prune containers", func() { + SkipIfRemote() top := podmanTest.RunTopContainer("") top.WaitWithDefaultTimeout() Expect(top.ExitCode()).To(Equal(0)) @@ -55,6 +56,7 @@ var _ = Describe("Podman rm", func() { }) It("podman image prune none images", func() { + SkipIfRemote() podmanTest.BuildImage(pruneImage, "alpine_bash:latest", "true") none := podmanTest.Podman([]string{"images", "-a"}) @@ -72,10 +74,11 @@ var _ = Describe("Podman rm", func() { Expect(none.ExitCode()).To(Equal(0)) hasNoneAfter, _ := after.GrepString("") Expect(hasNoneAfter).To(BeFalse()) + Expect(len(after.OutputToStringArray()) > 1).To(BeTrue()) }) It("podman image prune unused images", func() { - prune := podmanTest.Podman([]string{"image", "prune"}) + prune := podmanTest.Podman([]string{"image", "prune", "-a"}) prune.WaitWithDefaultTimeout() Expect(prune.ExitCode()).To(Equal(0)) -- cgit v1.2.3-54-g00ecf From c62efd08f75e97ba27d9e34068bcc2b3bf122896 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 11 Jan 2019 11:23:19 +0100 Subject: rootless: create the userns immediately when creating a new pod Closes: https://github.com/containers/libpod/issues/2124 Signed-off-by: Giuseppe Scrivano --- cmd/podman/create.go | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'cmd') diff --git a/cmd/podman/create.go b/cmd/podman/create.go index e10009971..a66603360 100644 --- a/cmd/podman/create.go +++ b/cmd/podman/create.go @@ -435,6 +435,16 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim } if c.IsSet("pod") { if strings.HasPrefix(originalPodName, "new:") { + if rootless.IsRootless() { + // To create a new pod, we must immediately create the userns. + became, ret, err := rootless.BecomeRootInUserNS() + if err != nil { + return nil, err + } + if became { + os.Exit(ret) + } + } // pod does not exist; lets make it var podOptions []libpod.PodCreateOption podOptions = append(podOptions, libpod.WithPodName(podName), libpod.WithInfraContainer(), libpod.WithPodCgroups()) -- cgit v1.2.3-54-g00ecf From b478f42656115eb83ff42a4a65afc10ec8864ae4 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 11 Jan 2019 11:51:21 +0100 Subject: rootless: join both userns and mount namespace with --pod When --pod is specified then join both the user and mount namespace for the pod so we can initialize the storage. Signed-off-by: Giuseppe Scrivano --- cmd/podman/create.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'cmd') diff --git a/cmd/podman/create.go b/cmd/podman/create.go index a66603360..8c45b568a 100644 --- a/cmd/podman/create.go +++ b/cmd/podman/create.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "io" + "io/ioutil" "os" "path/filepath" "strconv" @@ -809,11 +810,15 @@ func joinOrCreateRootlessUserNamespace(createConfig *cc.CreateConfig, runtime *l if s != libpod.ContainerStateRunning && s != libpod.ContainerStatePaused { continue } - pid, err := prevCtr.PID() + data, err := ioutil.ReadFile(prevCtr.Config().ConmonPidFile) if err != nil { - return false, -1, err + return false, -1, errors.Wrapf(err, "cannot read conmon PID file %q", prevCtr.Config().ConmonPidFile) } - return rootless.JoinNS(uint(pid)) + conmonPid, err := strconv.Atoi(string(data)) + if err != nil { + return false, -1, errors.Wrapf(err, "cannot parse PID %q", data) + } + return rootless.JoinDirectUserAndMountNS(uint(conmonPid)) } } -- cgit v1.2.3-54-g00ecf