From 09ff62429a324e01ad2c584afe9a5f66f580ae78 Mon Sep 17 00:00:00 2001 From: Jhon Honce Date: Tue, 5 Mar 2019 16:11:28 -0700 Subject: Implement podman-remote rm * refactor command output to use one function * Add new worker pool parallel operations * Implement podman-remote umount * Refactored podman wait to use printCmdOutput() Signed-off-by: Jhon Honce --- pkg/adapter/containers.go | 127 ++++++++++++++++++++++++++++++++----- pkg/adapter/containers_remote.go | 59 +++++++++++++++-- pkg/adapter/runtime.go | 65 +++++++++++++++++++ pkg/adapter/shortcuts/shortcuts.go | 39 ++++++------ 4 files changed, 246 insertions(+), 44 deletions(-) (limited to 'pkg/adapter') diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go index 1bca99cec..19ff2270c 100644 --- a/pkg/adapter/containers.go +++ b/pkg/adapter/containers.go @@ -18,6 +18,7 @@ import ( "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/adapter/shortcuts" + "github.com/containers/storage" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -62,52 +63,144 @@ func (r *LocalRuntime) StopContainers(ctx context.Context, cli *cliconfig.StopVa timeout = &t } - var ( - ok = []string{} - failures = map[string]error{} - ) + maxWorkers := shared.DefaultPoolSize("stop") + if cli.GlobalIsSet("max-workers") { + maxWorkers = cli.GlobalFlags.MaxWorks + } + logrus.Debugf("Setting maximum stop workers to %d", maxWorkers) ctrs, err := shortcuts.GetContainersByContext(cli.All, cli.Latest, cli.InputArgs, r.Runtime) if err != nil { - return ok, failures, err + return nil, nil, err } + pool := shared.NewPool("stop", maxWorkers, len(ctrs)) for _, c := range ctrs { + c := c + if timeout == nil { t := c.StopTimeout() timeout = &t logrus.Debugf("Set timeout to container %s default (%d)", c.ID(), *timeout) } - if err := c.StopWithTimeout(*timeout); err == nil { - ok = append(ok, c.ID()) - } else if errors.Cause(err) == libpod.ErrCtrStopped { - ok = append(ok, c.ID()) - logrus.Debugf("Container %s is already stopped", c.ID()) - } else { - failures[c.ID()] = err - } + + pool.Add(shared.Job{ + c.ID(), + func() error { + err := c.StopWithTimeout(*timeout) + if err != nil { + if errors.Cause(err) == libpod.ErrCtrStopped { + logrus.Debugf("Container %s is already stopped", c.ID()) + return nil + } + logrus.Debugf("Failed to stop container %s: %s", c.ID(), err.Error()) + } + return err + }, + }) } - return ok, failures, nil + return pool.Run() } // KillContainers sends signal to container(s) based on CLI inputs. // Returns list of successful id(s), map of failed id(s) + error, or error not from container func (r *LocalRuntime) KillContainers(ctx context.Context, cli *cliconfig.KillValues, signal syscall.Signal) ([]string, map[string]error, error) { + maxWorkers := shared.DefaultPoolSize("kill") + if cli.GlobalIsSet("max-workers") { + maxWorkers = cli.GlobalFlags.MaxWorks + } + logrus.Debugf("Setting maximum kill workers to %d", maxWorkers) + + ctrs, err := shortcuts.GetContainersByContext(cli.All, cli.Latest, cli.InputArgs, r.Runtime) + if err != nil { + return nil, nil, err + } + + pool := shared.NewPool("kill", maxWorkers, len(ctrs)) + for _, c := range ctrs { + c := c + + pool.Add(shared.Job{ + c.ID(), + func() error { + return c.Kill(uint(signal)) + }, + }) + } + return pool.Run() +} + +// RemoveContainers removes container(s) based on CLI inputs. +func (r *LocalRuntime) RemoveContainers(ctx context.Context, cli *cliconfig.RmValues) ([]string, map[string]error, error) { var ( ok = []string{} failures = map[string]error{} ) + maxWorkers := shared.DefaultPoolSize("rm") + if cli.GlobalIsSet("max-workers") { + maxWorkers = cli.GlobalFlags.MaxWorks + } + logrus.Debugf("Setting maximum rm workers to %d", maxWorkers) + ctrs, err := shortcuts.GetContainersByContext(cli.All, cli.Latest, cli.InputArgs, r.Runtime) if err != nil { + // Force may be used to remove containers no longer found in the database + if cli.Force && len(cli.InputArgs) > 0 && errors.Cause(err) == libpod.ErrNoSuchCtr { + r.RemoveContainersFromStorage(cli.InputArgs) + } return ok, failures, err } + pool := shared.NewPool("rm", maxWorkers, len(ctrs)) for _, c := range ctrs { - if err := c.Kill(uint(signal)); err == nil { - ok = append(ok, c.ID()) + c := c + + pool.Add(shared.Job{ + c.ID(), + func() error { + err := r.RemoveContainer(ctx, c, cli.Force, cli.Volumes) + if err != nil { + logrus.Debugf("Failed to remove container %s: %s", c.ID(), err.Error()) + } + return err + }, + }) + } + return pool.Run() +} + +// UmountRootFilesystems removes container(s) based on CLI inputs. +func (r *LocalRuntime) UmountRootFilesystems(ctx context.Context, cli *cliconfig.UmountValues) ([]string, map[string]error, error) { + var ( + ok = []string{} + failures = map[string]error{} + ) + + ctrs, err := shortcuts.GetContainersByContext(cli.All, cli.Latest, cli.InputArgs, r.Runtime) + if err != nil { + return ok, failures, err + } + + for _, ctr := range ctrs { + state, err := ctr.State() + if err != nil { + logrus.Debugf("Error umounting container %s state: %s", ctr.ID(), err.Error()) + continue + } + if state == libpod.ContainerStateRunning { + logrus.Debugf("Error umounting container %s, is running", ctr.ID()) + continue + } + + if err := ctr.Unmount(cli.Force); err != nil { + if cli.All && errors.Cause(err) == storage.ErrLayerNotMounted { + logrus.Debugf("Error umounting container %s, storage.ErrLayerNotMounted", ctr.ID()) + continue + } + failures[ctr.ID()] = errors.Wrapf(err, "error unmounting continaner %s", ctr.ID()) } else { - failures[c.ID()] = err + ok = append(ok, ctr.ID()) } } return ok, failures, nil diff --git a/pkg/adapter/containers_remote.go b/pkg/adapter/containers_remote.go index 3730827c7..1892629ea 100644 --- a/pkg/adapter/containers_remote.go +++ b/pkg/adapter/containers_remote.go @@ -12,11 +12,12 @@ import ( "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/shared" - "github.com/containers/libpod/cmd/podman/varlink" - "github.com/containers/libpod/libpod" - "github.com/containers/libpod/pkg/inspect" "github.com/pkg/errors" "github.com/sirupsen/logrus" + + iopodman "github.com/containers/libpod/cmd/podman/varlink" + "github.com/containers/libpod/libpod" + "github.com/containers/libpod/pkg/inspect" "github.com/varlink/go/varlink" ) @@ -128,7 +129,7 @@ func (c *Container) Name() string { return c.config.Name } -// StopContainers stops requested containers using CLI inputs. +// StopContainers stops requested containers using varlink. // Returns the list of stopped container ids, map of failed to stop container ids + errors, or any non-container error func (r *LocalRuntime) StopContainers(ctx context.Context, cli *cliconfig.StopValues) ([]string, map[string]error, error) { var ( @@ -152,7 +153,7 @@ func (r *LocalRuntime) StopContainers(ctx context.Context, cli *cliconfig.StopVa return ok, failures, nil } -// KillContainers sends signal to container(s) based on CLI inputs. +// KillContainers sends signal to container(s) based on varlink. // Returns list of successful id(s), map of failed id(s) + error, or error not from container func (r *LocalRuntime) KillContainers(ctx context.Context, cli *cliconfig.KillValues, signal syscall.Signal) ([]string, map[string]error, error) { var ( @@ -176,6 +177,52 @@ func (r *LocalRuntime) KillContainers(ctx context.Context, cli *cliconfig.KillVa return ok, failures, nil } +// RemoveContainer removes container(s) based on varlink inputs. +func (r *LocalRuntime) RemoveContainers(ctx context.Context, cli *cliconfig.RmValues) ([]string, map[string]error, error) { + ids, err := iopodman.GetContainersByContext().Call(r.Conn, cli.All, cli.Latest, cli.InputArgs) + if err != nil { + return nil, nil, err + } + + var ( + ok = []string{} + failures = map[string]error{} + ) + + for _, id := range ids { + _, err := iopodman.RemoveContainer().Call(r.Conn, id, cli.Force, cli.Volumes) + if err != nil { + failures[id] = err + } else { + ok = append(ok, id) + } + } + return ok, failures, nil +} + +// UmountRootFilesystems umounts container(s) root filesystems based on varlink inputs +func (r *LocalRuntime) UmountRootFilesystems(ctx context.Context, cli *cliconfig.UmountValues) ([]string, map[string]error, error) { + ids, err := iopodman.GetContainersByContext().Call(r.Conn, cli.All, cli.Latest, cli.InputArgs) + if err != nil { + return nil, nil, err + } + + var ( + ok = []string{} + failures = map[string]error{} + ) + + for _, id := range ids { + err := iopodman.UnmountContainer().Call(r.Conn, id, cli.Force) + if err != nil { + failures[id] = err + } else { + ok = append(ok, id) + } + } + return ok, failures, nil +} + // WaitOnContainers waits for all given container(s) to stop. // interval is currently ignored. func (r *LocalRuntime) WaitOnContainers(ctx context.Context, cli *cliconfig.WaitValues, interval time.Duration) ([]string, map[string]error, error) { @@ -227,7 +274,7 @@ func BatchContainerOp(ctr *Container, opts shared.PsOptions) (shared.BatchContai // Logs one or more containers over a varlink connection func (r *LocalRuntime) Log(c *cliconfig.LogsValues, options *libpod.LogOptions) error { - //GetContainersLogs + // GetContainersLogs reply, err := iopodman.GetContainersLogs().Send(r.Conn, uint64(varlink.More), c.InputArgs, c.Follow, c.Latest, options.Since.Format(time.RFC3339Nano), int64(c.Tail), c.Timestamps) if err != nil { return errors.Wrapf(err, "failed to get container logs") diff --git a/pkg/adapter/runtime.go b/pkg/adapter/runtime.go index 182a04044..d45bdb56d 100644 --- a/pkg/adapter/runtime.go +++ b/pkg/adapter/runtime.go @@ -310,6 +310,46 @@ func (r *LocalRuntime) HealthCheck(c *cliconfig.HealthCheckValues) (libpod.Healt return r.Runtime.HealthCheck(c.InputArgs[0]) } +// JoinOrCreateRootlessPod joins the specified pod if it is running or it creates a new user namespace +// if the pod is stopped +// func (r *LocalRuntime) JoinOrCreateRootlessPod(pod *Pod) (bool, int, error) { +// if os.Geteuid() == 0 { +// return false, 0, nil +// } +// opts := rootless.Opts{ +// Argument: pod.ID(), +// } +// +// inspect, err := pod.Inspect() +// if err != nil { +// return false, 0, err +// } +// for _, ctr := range inspect.Containers { +// prevCtr, err := r.LookupContainer(ctr.ID) +// if err != nil { +// return false, -1, err +// } +// s, err := prevCtr.State() +// if err != nil { +// return false, -1, err +// } +// if s != libpod.ContainerStateRunning && s != libpod.ContainerStatePaused { +// continue +// } +// data, err := ioutil.ReadFile(prevCtr.Config().ConmonPidFile) +// if err != nil { +// return false, -1, errors.Wrapf(err, "cannot read conmon PID file %q", prevCtr.Config().ConmonPidFile) +// } +// conmonPid, err := strconv.Atoi(string(data)) +// if err != nil { +// return false, -1, errors.Wrapf(err, "cannot parse PID %q", data) +// } +// return rootless.JoinDirectUserAndMountNSWithOpts(uint(conmonPid), &opts) +// } +// +// return rootless.BecomeRootInUserNSWithOpts(&opts) +// } + // Events is a wrapper to libpod to obtain libpod/podman events func (r *LocalRuntime) Events(c *cliconfig.EventValues) error { var ( @@ -363,3 +403,28 @@ func (r *LocalRuntime) Events(c *cliconfig.EventValues) error { func (r *LocalRuntime) Diff(c *cliconfig.DiffValues, to string) ([]archive.Change, error) { return r.Runtime.GetDiff("", to) } + +// func (r *LocalRuntime) joinContainerOrCreateRootlessUserNS(ctr *libpod.Container) (bool, int, error) { +// if os.Geteuid() == 0 { +// return false, 0, nil +// } +// s, err := ctr.State() +// if err != nil { +// return false, -1, err +// } +// opts := rootless.Opts{ +// Argument: ctr.ID(), +// } +// if s == libpod.ContainerStateRunning || s == libpod.ContainerStatePaused { +// data, err := ioutil.ReadFile(ctr.Config().ConmonPidFile) +// if err != nil { +// return false, -1, errors.Wrapf(err, "Container %s cannot read conmon PID file %q", ctr.ID(), ctr.Config().ConmonPidFile) +// } +// conmonPid, err := strconv.Atoi(string(data)) +// if err != nil { +// return false, -1, errors.Wrapf(err, "Container %s cannot parse PID %q", ctr.ID(), data) +// } +// return rootless.JoinDirectUserAndMountNSWithOpts(uint(conmonPid), &opts) +// } +// return rootless.BecomeRootInUserNSWithOpts(&opts) +// } diff --git a/pkg/adapter/shortcuts/shortcuts.go b/pkg/adapter/shortcuts/shortcuts.go index 677d88457..3e4eff555 100644 --- a/pkg/adapter/shortcuts/shortcuts.go +++ b/pkg/adapter/shortcuts/shortcuts.go @@ -1,6 +1,8 @@ package shortcuts -import "github.com/containers/libpod/libpod" +import ( + "github.com/containers/libpod/libpod" +) // GetPodsByContext gets pods whether all, latest, or a slice of names/ids func GetPodsByContext(all, latest bool, pods []string, runtime *libpod.Runtime) ([]*libpod.Pod, error) { @@ -27,28 +29,23 @@ func GetPodsByContext(all, latest bool, pods []string, runtime *libpod.Runtime) } // GetContainersByContext gets pods whether all, latest, or a slice of names/ids -func GetContainersByContext(all, latest bool, names []string, runtime *libpod.Runtime) ([]*libpod.Container, error) { - var ctrs = []*libpod.Container{} +func GetContainersByContext(all, latest bool, names []string, runtime *libpod.Runtime) (ctrs []*libpod.Container, err error) { + var ctr *libpod.Container + ctrs = []*libpod.Container{} if all { - return runtime.GetAllContainers() - } - - if latest { - c, err := runtime.GetLatestContainer() - if err != nil { - return nil, err - } - ctrs = append(ctrs, c) - return ctrs, nil - } - - for _, c := range names { - ctr, err := runtime.LookupContainer(c) - if err != nil { - return nil, err - } + ctrs, err = runtime.GetAllContainers() + } else if latest { + ctr, err = runtime.GetLatestContainer() ctrs = append(ctrs, ctr) + } else { + for _, n := range names { + ctr, e := runtime.LookupContainer(n) + if e != nil && err == nil { + err = e + } + ctrs = append(ctrs, ctr) + } } - return ctrs, nil + return } -- cgit v1.2.3-54-g00ecf