diff options
Diffstat (limited to 'cmd/podman')
-rw-r--r-- | cmd/podman/attach.go | 48 | ||||
-rw-r--r-- | cmd/podman/commands.go | 7 | ||||
-rw-r--r-- | cmd/podman/commands_remoteclient.go | 2 | ||||
-rw-r--r-- | cmd/podman/container.go | 1 | ||||
-rw-r--r-- | cmd/podman/healthcheck.go | 2 | ||||
-rw-r--r-- | cmd/podman/kill.go | 21 | ||||
-rw-r--r-- | cmd/podman/main.go | 131 | ||||
-rw-r--r-- | cmd/podman/main_local.go | 155 | ||||
-rw-r--r-- | cmd/podman/main_remote.go | 43 | ||||
-rw-r--r-- | cmd/podman/ps.go | 187 | ||||
-rw-r--r-- | cmd/podman/pull.go | 10 | ||||
-rw-r--r-- | cmd/podman/push.go | 12 | ||||
-rw-r--r-- | cmd/podman/rm.go | 72 | ||||
-rw-r--r-- | cmd/podman/shared/container.go | 171 | ||||
-rw-r--r-- | cmd/podman/shared/workers.go | 133 | ||||
-rw-r--r-- | cmd/podman/stop.go | 21 | ||||
-rw-r--r-- | cmd/podman/umount.go | 47 | ||||
-rw-r--r-- | cmd/podman/utils.go | 49 | ||||
-rw-r--r-- | cmd/podman/varlink/io.podman.varlink | 51 | ||||
-rw-r--r-- | cmd/podman/wait.go | 20 |
20 files changed, 666 insertions, 517 deletions
diff --git a/cmd/podman/attach.go b/cmd/podman/attach.go index f326f53c3..2fa05a3b1 100644 --- a/cmd/podman/attach.go +++ b/cmd/podman/attach.go @@ -1,11 +1,7 @@ package main import ( - "os" - "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/cmd/podman/libpodruntime" - "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/adapter" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -39,49 +35,21 @@ func init() { flags.BoolVar(&attachCommand.SigProxy, "sig-proxy", true, "Proxy received signals to the process") flags.BoolVarP(&attachCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") markFlagHiddenForRemoteClient("latest", flags) + // TODO allow for passing of a new deatch keys + markFlagHiddenForRemoteClient("detach-keys", flags) } func attachCmd(c *cliconfig.AttachValues) error { - args := c.InputArgs - var ctr *libpod.Container - if len(c.InputArgs) > 1 || (len(c.InputArgs) == 0 && !c.Latest) { return errors.Errorf("attach requires the name or id of one running container or the latest flag") } - - runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) - if err != nil { - return errors.Wrapf(err, "error creating libpod runtime") - } - defer runtime.Shutdown(false) - - if c.Latest { - ctr, err = runtime.GetLatestContainer() - } else { - ctr, err = runtime.LookupContainer(args[0]) - } - - if err != nil { - return errors.Wrapf(err, "unable to exec into %s", args[0]) + if remoteclient && len(c.InputArgs) != 1 { + return errors.Errorf("attach requires the name or id of one running container") } - - conState, err := ctr.State() + runtime, err := adapter.GetRuntime(&c.PodmanCommand) if err != nil { - return errors.Wrapf(err, "unable to determine state of %s", args[0]) - } - if conState != libpod.ContainerStateRunning { - return errors.Errorf("you can only attach to running containers") - } - - inputStream := os.Stdin - if c.NoStdin { - inputStream = nil + return errors.Wrapf(err, "error creating runtime") } - - // If the container is in a pod, also set to recursively start dependencies - if err := adapter.StartAttachCtr(getContext(), ctr, os.Stdout, os.Stderr, inputStream, c.DetachKeys, c.SigProxy, false, ctr.PodID() != ""); err != nil && errors.Cause(err) != libpod.ErrDetach { - return errors.Wrapf(err, "error attaching to container %s", ctr.ID()) - } - - return nil + defer runtime.Shutdown(false) + return runtime.Attach(getContext(), c) } diff --git a/cmd/podman/commands.go b/cmd/podman/commands.go index e9afcbc06..9fea1494b 100644 --- a/cmd/podman/commands.go +++ b/cmd/podman/commands.go @@ -11,12 +11,10 @@ const remoteclient = false // Commands that the local client implements func getMainCommands() []*cobra.Command { rootCommands := []*cobra.Command{ - _attachCommand, _commitCommand, _execCommand, _generateCommand, _playCommand, - &_psCommand, _loginCommand, _logoutCommand, _mountCommand, @@ -24,12 +22,10 @@ func getMainCommands() []*cobra.Command { _portCommand, _refreshCommand, _restartCommand, - _rmCommand, _searchCommand, _startCommand, _statsCommand, _topCommand, - _umountCommand, _unpauseCommand, } @@ -50,7 +46,6 @@ func getImageSubCommands() []*cobra.Command { func getContainerSubCommands() []*cobra.Command { return []*cobra.Command{ - _attachCommand, _checkpointCommand, _cleanupCommand, _commitCommand, @@ -107,7 +102,7 @@ func getSystemSubCommands() []*cobra.Command { } // Commands that the local client implements -func getHealtcheckSubCommands() []*cobra.Command { +func getHealthcheckSubCommands() []*cobra.Command { return []*cobra.Command{ _healthcheckrunCommand, } diff --git a/cmd/podman/commands_remoteclient.go b/cmd/podman/commands_remoteclient.go index 9b09e7dbc..278fe229c 100644 --- a/cmd/podman/commands_remoteclient.go +++ b/cmd/podman/commands_remoteclient.go @@ -49,6 +49,6 @@ func getSystemSubCommands() []*cobra.Command { } // Commands that the remoteclient implements -func getHealtcheckSubCommands() []*cobra.Command { +func getHealthcheckSubCommands() []*cobra.Command { return []*cobra.Command{} } diff --git a/cmd/podman/container.go b/cmd/podman/container.go index d1c42f673..380d1f250 100644 --- a/cmd/podman/container.go +++ b/cmd/podman/container.go @@ -50,6 +50,7 @@ var ( // Commands that are universally implemented. containerCommands = []*cobra.Command{ + _attachCommand, _containerExistsCommand, _contInspectSubCommand, _diffCommand, diff --git a/cmd/podman/healthcheck.go b/cmd/podman/healthcheck.go index 48d6b6bbf..9fb099ffa 100644 --- a/cmd/podman/healthcheck.go +++ b/cmd/podman/healthcheck.go @@ -20,7 +20,7 @@ var healthcheckCommands []*cobra.Command func init() { healthcheckCommand.AddCommand(healthcheckCommands...) - healthcheckCommand.AddCommand(getHealtcheckSubCommands()...) + healthcheckCommand.AddCommand(getHealthcheckSubCommands()...) healthcheckCommand.SetUsageTemplate(UsageTemplate()) rootCmd.AddCommand(healthcheckCommand.Command) } diff --git a/cmd/podman/kill.go b/cmd/podman/kill.go index 6019fbfec..20142e0bf 100644 --- a/cmd/podman/kill.go +++ b/cmd/podman/kill.go @@ -1,9 +1,6 @@ package main import ( - "fmt" - "reflect" - "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/pkg/adapter" "github.com/docker/docker/pkg/signal" @@ -71,21 +68,5 @@ func killCmd(c *cliconfig.KillValues) error { if err != nil { return err } - - for _, id := range ok { - fmt.Println(id) - } - - if len(failures) > 0 { - keys := reflect.ValueOf(failures).MapKeys() - lastKey := keys[len(keys)-1].String() - lastErr := failures[lastKey] - delete(failures, lastKey) - - for _, err := range failures { - outputError(err) - } - return lastErr - } - return nil + return printCmdResults(ok, failures) } diff --git a/cmd/podman/main.go b/cmd/podman/main.go index b44cf9f0a..35a94b3db 100644 --- a/cmd/podman/main.go +++ b/cmd/podman/main.go @@ -3,26 +3,18 @@ package main import ( "context" "io" - "io/ioutil" - "log/syslog" "os" - "runtime/pprof" - "strconv" - "strings" "syscall" "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/libpod" _ "github.com/containers/libpod/pkg/hooks/0.1.0" "github.com/containers/libpod/pkg/rootless" - "github.com/containers/libpod/pkg/tracing" "github.com/containers/libpod/version" "github.com/containers/storage/pkg/reexec" "github.com/opentracing/opentracing-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" - lsyslog "github.com/sirupsen/logrus/hooks/syslog" "github.com/spf13/cobra" ) @@ -38,6 +30,7 @@ var ( // Commands that the remote and local client have // implemented. var mainCommands = []*cobra.Command{ + _attachCommand, _buildCommand, _diffCommand, _createCommand, @@ -52,13 +45,16 @@ var mainCommands = []*cobra.Command{ _loadCommand, _logsCommand, podCommand.Command, + &_psCommand, _pullCommand, _pushCommand, + _rmCommand, &_rmiCommand, _runCommand, _saveCommand, _stopCommand, _tagCommand, + _umountCommand, _versionCommand, _waitCommand, imageCommand.Command, @@ -85,40 +81,13 @@ func init() { cobra.OnInitialize(initConfig) rootCmd.TraverseChildren = true rootCmd.Version = version.Version - rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.CGroupManager, "cgroup-manager", "", "Cgroup manager to use (cgroupfs or systemd, default systemd)") - // -c is deprecated due to conflict with -c on subcommands - rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.CpuProfile, "cpu-profile", "", "Path for the cpu profiling results") - rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Config, "config", "", "Path of a libpod config file detailing container server configuration options") - rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.ConmonPath, "conmon", "", "Path of the conmon binary") - rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.NetworkCmdPath, "network-cmd-path", "", "Path to the command for configuring the network") - rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.CniConfigDir, "cni-config-dir", "", "Path of the configuration directory for CNI networks") - rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.DefaultMountsFile, "default-mounts-file", "", "Path to default mounts file") - rootCmd.PersistentFlags().MarkHidden("defaults-mount-file") - // Override default --help information of `--help` global flag - var dummyHelp bool - rootCmd.PersistentFlags().BoolVar(&dummyHelp, "help", false, "Help for podman") - rootCmd.PersistentFlags().StringSliceVar(&MainGlobalOpts.HooksDir, "hooks-dir", []string{}, "Set the OCI hooks directory path (may be set multiple times)") - rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.LogLevel, "log-level", "error", "Log messages above specified level: debug, info, warn, error, fatal or panic") - rootCmd.PersistentFlags().IntVar(&MainGlobalOpts.MaxWorks, "max-workers", 0, "The maximum number of workers for parallel operations") - rootCmd.PersistentFlags().MarkHidden("max-workers") - rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Namespace, "namespace", "", "Set the libpod namespace, used to create separate views of the containers and pods on the system") - rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Root, "root", "", "Path to the root directory in which data, including images, is stored") - rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Runroot, "runroot", "", "Path to the 'run directory' where all state information is stored") - rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Runtime, "runtime", "", "Path to the OCI-compatible binary used to run containers, default is /usr/bin/runc") - // -s is depracated due to conflict with -s on subcommands - rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.StorageDriver, "storage-driver", "", "Select which storage driver is used to manage storage of images and containers (default is overlay)") - rootCmd.PersistentFlags().StringSliceVar(&MainGlobalOpts.StorageOpts, "storage-opt", []string{}, "Used to pass an option to the storage driver") - rootCmd.PersistentFlags().BoolVar(&MainGlobalOpts.Syslog, "syslog", false, "Output logging information to syslog as well as the console") - - rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.TmpDir, "tmpdir", "", "Path to the tmp directory") - rootCmd.PersistentFlags().BoolVar(&MainGlobalOpts.Trace, "trace", false, "Enable opentracing output") // Override default --help information of `--version` global flag var dummyVersion bool rootCmd.PersistentFlags().BoolVar(&dummyVersion, "version", false, "Version for podman") rootCmd.AddCommand(mainCommands...) rootCmd.AddCommand(getMainCommands()...) - } + func initConfig() { // we can do more stuff in here. } @@ -128,63 +97,16 @@ func before(cmd *cobra.Command, args []string) error { logrus.Errorf(err.Error()) os.Exit(1) } - if os.Geteuid() != 0 && cmd != _searchCommand && cmd != _versionCommand && !strings.HasPrefix(cmd.Use, "help") { - podmanCmd := cliconfig.PodmanCommand{ - cmd, - args, - MainGlobalOpts, - } - runtime, err := libpodruntime.GetRuntime(&podmanCmd) - if err != nil { - return errors.Wrapf(err, "could not get runtime") - } - defer runtime.Shutdown(false) - - ctrs, err := runtime.GetRunningContainers() - if err != nil { - logrus.Errorf(err.Error()) - os.Exit(1) - } - var became bool - var ret int - if len(ctrs) == 0 { - became, ret, err = rootless.BecomeRootInUserNS() - } else { - for _, ctr := range ctrs { - data, err := ioutil.ReadFile(ctr.Config().ConmonPidFile) - if err != nil { - logrus.Errorf(err.Error()) - os.Exit(1) - } - conmonPid, err := strconv.Atoi(string(data)) - if err != nil { - logrus.Errorf(err.Error()) - os.Exit(1) - } - became, ret, err = rootless.JoinUserAndMountNS(uint(conmonPid)) - if err == nil { - break - } - } - } - if err != nil { - logrus.Errorf(err.Error()) - os.Exit(1) - } - if became { - os.Exit(ret) - } + if err := setupRootless(cmd, args); err != nil { + return err } - if MainGlobalOpts.Syslog { - hook, err := lsyslog.NewSyslogHook("", "", syslog.LOG_INFO, "") - if err == nil { - logrus.AddHook(hook) - } + // Set log level; if not log-level is provided, default to error + logLevel := MainGlobalOpts.LogLevel + if logLevel == "" { + logLevel = "error" } - - // Set log level - level, err := logrus.ParseLevel(MainGlobalOpts.LogLevel) + level, err := logrus.ParseLevel(logLevel) if err != nil { return err } @@ -209,36 +131,11 @@ func before(cmd *cobra.Command, args []string) error { // Be sure we can create directories with 0755 mode. syscall.Umask(0022) - - if cmd.Flag("cpu-profile").Changed { - f, err := os.Create(MainGlobalOpts.CpuProfile) - if err != nil { - return errors.Wrapf(err, "unable to create cpu profiling file %s", - MainGlobalOpts.CpuProfile) - } - pprof.StartCPUProfile(f) - } - if cmd.Flag("trace").Changed { - var tracer opentracing.Tracer - tracer, closer = tracing.Init("podman") - opentracing.SetGlobalTracer(tracer) - - span = tracer.StartSpan("before-context") - - Ctx = opentracing.ContextWithSpan(context.Background(), span) - } - return nil + return profileOn(cmd) } func after(cmd *cobra.Command, args []string) error { - if cmd.Flag("cpu-profile").Changed { - pprof.StopCPUProfile() - } - if cmd.Flag("trace").Changed { - span.Finish() - closer.Close() - } - return nil + return profileOff(cmd) } func main() { diff --git a/cmd/podman/main_local.go b/cmd/podman/main_local.go new file mode 100644 index 000000000..e008a4617 --- /dev/null +++ b/cmd/podman/main_local.go @@ -0,0 +1,155 @@ +// +build !remoteclient + +package main + +import ( + "context" + "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/containers/libpod/cmd/podman/libpodruntime" + "github.com/containers/libpod/pkg/rootless" + "io/ioutil" + "log/syslog" + "os" + "runtime/pprof" + "strconv" + "strings" + + "github.com/containers/libpod/pkg/tracing" + "github.com/opentracing/opentracing-go" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + lsyslog "github.com/sirupsen/logrus/hooks/syslog" + "github.com/spf13/cobra" +) + +const remote = false + +func init() { + + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.CGroupManager, "cgroup-manager", "", "Cgroup manager to use (cgroupfs or systemd, default systemd)") + // -c is deprecated due to conflict with -c on subcommands + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.CpuProfile, "cpu-profile", "", "Path for the cpu profiling results") + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Config, "config", "", "Path of a libpod config file detailing container server configuration options") + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.ConmonPath, "conmon", "", "Path of the conmon binary") + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.NetworkCmdPath, "network-cmd-path", "", "Path to the command for configuring the network") + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.CniConfigDir, "cni-config-dir", "", "Path of the configuration directory for CNI networks") + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.DefaultMountsFile, "default-mounts-file", "", "Path to default mounts file") + rootCmd.PersistentFlags().MarkHidden("defaults-mount-file") + // Override default --help information of `--help` global flag + var dummyHelp bool + rootCmd.PersistentFlags().BoolVar(&dummyHelp, "help", false, "Help for podman") + rootCmd.PersistentFlags().StringSliceVar(&MainGlobalOpts.HooksDir, "hooks-dir", []string{}, "Set the OCI hooks directory path (may be set multiple times)") + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.LogLevel, "log-level", "error", "Log messages above specified level: debug, info, warn, error, fatal or panic") + rootCmd.PersistentFlags().IntVar(&MainGlobalOpts.MaxWorks, "max-workers", 0, "The maximum number of workers for parallel operations") + rootCmd.PersistentFlags().MarkHidden("max-workers") + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Namespace, "namespace", "", "Set the libpod namespace, used to create separate views of the containers and pods on the system") + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Root, "root", "", "Path to the root directory in which data, including images, is stored") + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Runroot, "runroot", "", "Path to the 'run directory' where all state information is stored") + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Runtime, "runtime", "", "Path to the OCI-compatible binary used to run containers, default is /usr/bin/runc") + // -s is depracated due to conflict with -s on subcommands + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.StorageDriver, "storage-driver", "", "Select which storage driver is used to manage storage of images and containers (default is overlay)") + rootCmd.PersistentFlags().StringSliceVar(&MainGlobalOpts.StorageOpts, "storage-opt", []string{}, "Used to pass an option to the storage driver") + rootCmd.PersistentFlags().BoolVar(&MainGlobalOpts.Syslog, "syslog", false, "Output logging information to syslog as well as the console") + + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.TmpDir, "tmpdir", "", "Path to the tmp directory") + rootCmd.PersistentFlags().BoolVar(&MainGlobalOpts.Trace, "trace", false, "Enable opentracing output") +} + +func setSyslog() error { + if MainGlobalOpts.Syslog { + hook, err := lsyslog.NewSyslogHook("", "", syslog.LOG_INFO, "") + if err == nil { + logrus.AddHook(hook) + return nil + } + return err + } + return nil +} + +func profileOn(cmd *cobra.Command) error { + if cmd.Flag("cpu-profile").Changed { + f, err := os.Create(MainGlobalOpts.CpuProfile) + if err != nil { + return errors.Wrapf(err, "unable to create cpu profiling file %s", + MainGlobalOpts.CpuProfile) + } + if err := pprof.StartCPUProfile(f); err != nil { + return err + } + } + + if cmd.Flag("trace").Changed { + var tracer opentracing.Tracer + tracer, closer = tracing.Init("podman") + opentracing.SetGlobalTracer(tracer) + + span = tracer.StartSpan("before-context") + + Ctx = opentracing.ContextWithSpan(context.Background(), span) + } + return nil +} + +func profileOff(cmd *cobra.Command) error { + if cmd.Flag("cpu-profile").Changed { + pprof.StopCPUProfile() + } + if cmd.Flag("trace").Changed { + span.Finish() + closer.Close() + } + return nil +} + +func setupRootless(cmd *cobra.Command, args []string) error { + if os.Geteuid() == 0 || cmd == _searchCommand || cmd == _versionCommand || strings.HasPrefix(cmd.Use, "help") { + return nil + } + podmanCmd := cliconfig.PodmanCommand{ + cmd, + args, + MainGlobalOpts, + } + runtime, err := libpodruntime.GetRuntime(&podmanCmd) + if err != nil { + return errors.Wrapf(err, "could not get runtime") + } + defer runtime.Shutdown(false) + + ctrs, err := runtime.GetRunningContainers() + if err != nil { + logrus.Errorf(err.Error()) + os.Exit(1) + } + var became bool + var ret int + if len(ctrs) == 0 { + became, ret, err = rootless.BecomeRootInUserNS() + } else { + for _, ctr := range ctrs { + data, err := ioutil.ReadFile(ctr.Config().ConmonPidFile) + if err != nil { + logrus.Errorf(err.Error()) + os.Exit(1) + } + conmonPid, err := strconv.Atoi(string(data)) + if err != nil { + logrus.Errorf(err.Error()) + os.Exit(1) + } + became, ret, err = rootless.JoinUserAndMountNS(uint(conmonPid)) + if err == nil { + break + } + } + } + if err != nil { + logrus.Errorf(err.Error()) + os.Exit(1) + } + if became { + os.Exit(ret) + } + return nil +} diff --git a/cmd/podman/main_remote.go b/cmd/podman/main_remote.go new file mode 100644 index 000000000..2a7d184cd --- /dev/null +++ b/cmd/podman/main_remote.go @@ -0,0 +1,43 @@ +// +build remoteclient + +package main + +import ( + "os" + + "github.com/containers/libpod/pkg/rootless" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +const remote = true + +func init() { + // remote client specific flags can go here. +} + +func setSyslog() error { + return nil +} + +func profileOn(cmd *cobra.Command) error { + return nil +} + +func profileOff(cmd *cobra.Command) error { + return nil +} + +func setupRootless(cmd *cobra.Command, args []string) error { + if rootless.IsRootless() { + became, ret, err := rootless.BecomeRootInUserNS() + if err != nil { + logrus.Errorf(err.Error()) + os.Exit(1) + } + if became { + os.Exit(ret) + } + } + return nil +} diff --git a/cmd/podman/ps.go b/cmd/podman/ps.go index 759a03b86..5bb88f227 100644 --- a/cmd/podman/ps.go +++ b/cmd/podman/ps.go @@ -6,7 +6,6 @@ import ( "os" "reflect" "sort" - "strconv" "strings" "text/tabwriter" "time" @@ -14,15 +13,12 @@ import ( tm "github.com/buger/goterm" "github.com/containers/buildah/pkg/formats" "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/cmd/podman/shared" - "github.com/containers/libpod/libpod" - "github.com/containers/libpod/pkg/util" + "github.com/containers/libpod/pkg/adapter" "github.com/cri-o/ocicni/pkg/ocicni" "github.com/docker/go-units" "github.com/opentracing/opentracing-go" "github.com/pkg/errors" - "github.com/sirupsen/logrus" "github.com/spf13/cobra" "k8s.io/apimachinery/pkg/fields" ) @@ -205,10 +201,6 @@ func psCmd(c *cliconfig.PsValues) error { span, _ := opentracing.StartSpanFromContext(Ctx, "psCmd") defer span.Finish() } - // TODO disable when single rootless userns merges - if c.Bool("size") && os.Geteuid() != 0 { - return errors.New("the --size option is not presently supported without root") - } var watch bool @@ -224,7 +216,7 @@ func psCmd(c *cliconfig.PsValues) error { return errors.Wrapf(err, "error with flags passed") } - runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) + runtime, err := adapter.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } @@ -279,128 +271,6 @@ func checkFlagsPassed(c *cliconfig.PsValues) error { return nil } -func generateContainerFilterFuncs(filter, filterValue string, runtime *libpod.Runtime) (func(container *libpod.Container) bool, error) { - switch filter { - case "id": - return func(c *libpod.Container) bool { - return strings.Contains(c.ID(), filterValue) - }, nil - case "label": - var filterArray []string = strings.SplitN(filterValue, "=", 2) - var filterKey string = filterArray[0] - if len(filterArray) > 1 { - filterValue = filterArray[1] - } else { - filterValue = "" - } - return func(c *libpod.Container) bool { - for labelKey, labelValue := range c.Labels() { - if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) { - return true - } - } - return false - }, nil - case "name": - return func(c *libpod.Container) bool { - return strings.Contains(c.Name(), filterValue) - }, nil - case "exited": - exitCode, err := strconv.ParseInt(filterValue, 10, 32) - if err != nil { - return nil, errors.Wrapf(err, "exited code out of range %q", filterValue) - } - return func(c *libpod.Container) bool { - ec, exited, err := c.ExitCode() - if ec == int32(exitCode) && err == nil && exited == true { - return true - } - return false - }, nil - case "status": - if !util.StringInSlice(filterValue, []string{"created", "running", "paused", "stopped", "exited", "unknown"}) { - return nil, errors.Errorf("%s is not a valid status", filterValue) - } - return func(c *libpod.Container) bool { - status, err := c.State() - if err != nil { - return false - } - if filterValue == "stopped" { - filterValue = "exited" - } - state := status.String() - if status == libpod.ContainerStateConfigured { - state = "created" - } else if status == libpod.ContainerStateStopped { - state = "exited" - } - return state == filterValue - }, nil - case "ancestor": - // This needs to refine to match docker - // - ancestor=(<image-name>[:tag]|<image-id>| ⟨image@digest⟩) - containers created from an image or a descendant. - return func(c *libpod.Container) bool { - containerConfig := c.Config() - if strings.Contains(containerConfig.RootfsImageID, filterValue) || strings.Contains(containerConfig.RootfsImageName, filterValue) { - return true - } - return false - }, nil - case "before": - ctr, err := runtime.LookupContainer(filterValue) - if err != nil { - return nil, errors.Errorf("unable to find container by name or id of %s", filterValue) - } - containerConfig := ctr.Config() - createTime := containerConfig.CreatedTime - return func(c *libpod.Container) bool { - cc := c.Config() - return createTime.After(cc.CreatedTime) - }, nil - case "since": - ctr, err := runtime.LookupContainer(filterValue) - if err != nil { - return nil, errors.Errorf("unable to find container by name or id of %s", filterValue) - } - containerConfig := ctr.Config() - createTime := containerConfig.CreatedTime - return func(c *libpod.Container) bool { - cc := c.Config() - return createTime.Before(cc.CreatedTime) - }, nil - case "volume": - //- volume=(<volume-name>|<mount-point-destination>) - return func(c *libpod.Container) bool { - containerConfig := c.Config() - var dest string - arr := strings.Split(filterValue, ":") - source := arr[0] - if len(arr) == 2 { - dest = arr[1] - } - for _, mount := range containerConfig.Spec.Mounts { - if dest != "" && (mount.Source == source && mount.Destination == dest) { - return true - } - if dest == "" && mount.Source == source { - return true - } - } - return false - }, nil - case "health": - return func(c *libpod.Container) bool { - hcStatus, err := c.HealthCheckStatus() - if err != nil { - return false - } - return hcStatus == filterValue - }, nil - } - return nil, errors.Errorf("%s is an invalid filter", filter) -} - // generate the accurate header based on template given func (p *psTemplateParams) headerMap() map[string]string { v := reflect.Indirect(reflect.ValueOf(p)) @@ -550,11 +420,9 @@ func dumpJSON(containers []shared.PsContainerOutput) error { return nil } -func psDisplay(c *cliconfig.PsValues, runtime *libpod.Runtime) error { +func psDisplay(c *cliconfig.PsValues, runtime *adapter.LocalRuntime) error { var ( - filterFuncs []libpod.ContainerFilter - outputContainers []*libpod.Container - err error + err error ) opts := shared.PsOptions{ All: c.All, @@ -570,51 +438,8 @@ func psDisplay(c *cliconfig.PsValues, runtime *libpod.Runtime) error { Sync: c.Sync, } - maxWorkers := shared.Parallelize("ps") - if c.GlobalIsSet("max-workers") { - maxWorkers = c.GlobalFlags.MaxWorks - } - logrus.Debugf("Setting maximum workers to %d", maxWorkers) - - filters := c.Filter - if len(filters) > 0 { - for _, f := range filters { - filterSplit := strings.SplitN(f, "=", 2) - if len(filterSplit) < 2 { - return errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f) - } - generatedFunc, err := generateContainerFilterFuncs(filterSplit[0], filterSplit[1], runtime) - if err != nil { - return errors.Wrapf(err, "invalid filter") - } - filterFuncs = append(filterFuncs, generatedFunc) - } - } - if !opts.Latest { - // Get all containers - containers, err := runtime.GetContainers(filterFuncs...) - if err != nil { - return err - } - - // We only want the last few containers - if opts.Last > 0 && opts.Last <= len(containers) { - return errors.Errorf("--last not yet supported") - } else { - outputContainers = containers - } - } else { - // Get just the latest container - // Ignore filters - latestCtr, err := runtime.GetLatestContainer() - if err != nil { - return err - } - - outputContainers = []*libpod.Container{latestCtr} - } - - pss := shared.PBatch(outputContainers, maxWorkers, opts) + pss, err := runtime.Ps(c, opts) + // Here and down if opts.Sort != "" { pss, err = sortPsOutput(opts.Sort, pss) if err != nil { diff --git a/cmd/podman/pull.go b/cmd/podman/pull.go index 2aac28642..491d3a8c2 100644 --- a/cmd/podman/pull.go +++ b/cmd/podman/pull.go @@ -46,12 +46,16 @@ func init() { pullCommand.SetUsageTemplate(UsageTemplate()) flags := pullCommand.Flags() flags.BoolVar(&pullCommand.AllTags, "all-tags", false, "All tagged images inthe repository will be pulled") - flags.StringVar(&pullCommand.Authfile, "authfile", "", "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override") flags.StringVar(&pullCommand.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys") flags.StringVar(&pullCommand.Creds, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry") flags.BoolVarP(&pullCommand.Quiet, "quiet", "q", false, "Suppress output information when pulling images") - flags.StringVar(&pullCommand.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)") - flags.BoolVar(&pullCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") + + // Disabled flags for the remote client + if !remote { + flags.StringVar(&pullCommand.Authfile, "authfile", "", "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override") + flags.StringVar(&pullCommand.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)") + flags.BoolVar(&pullCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") + } } diff --git a/cmd/podman/push.go b/cmd/podman/push.go index a1dac24ae..a5638a698 100644 --- a/cmd/podman/push.go +++ b/cmd/podman/push.go @@ -45,16 +45,20 @@ func init() { pushCommand.SetUsageTemplate(UsageTemplate()) flags := pushCommand.Flags() flags.MarkHidden("signature-policy") - flags.StringVar(&pushCommand.Authfile, "authfile", "", "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override") flags.StringVar(&pushCommand.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys") - flags.BoolVar(&pushCommand.Compress, "compress", false, "Compress tarball image layers when pushing to a directory using the 'dir' transport. (default is same compression type as source)") flags.StringVar(&pushCommand.Creds, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry") flags.StringVarP(&pushCommand.Format, "format", "f", "", "Manifest type (oci, v2s1, or v2s2) to use when pushing an image using the 'dir:' transport (default is manifest type of source)") flags.BoolVarP(&pushCommand.Quiet, "quiet", "q", false, "Don't output progress information when pushing images") flags.BoolVar(&pushCommand.RemoveSignatures, "remove-signatures", false, "Discard any pre-existing signatures in the image") - flags.StringVar(&pushCommand.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)") flags.StringVar(&pushCommand.SignBy, "sign-by", "", "Add a signature at the destination using the specified key") - flags.BoolVar(&pushCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") + + // Disabled flags for the remote client + if !remote { + flags.StringVar(&pushCommand.Authfile, "authfile", "", "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override") + flags.BoolVar(&pushCommand.Compress, "compress", false, "Compress tarball image layers when pushing to a directory using the 'dir' transport. (default is same compression type as source)") + flags.StringVar(&pushCommand.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)") + flags.BoolVar(&pushCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") + } } func pushCmd(c *cliconfig.PushValues) error { diff --git a/cmd/podman/rm.go b/cmd/podman/rm.go index 52e281402..66f70a36f 100644 --- a/cmd/podman/rm.go +++ b/cmd/podman/rm.go @@ -4,12 +4,9 @@ import ( "fmt" "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/cmd/podman/libpodruntime" - "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod" - "github.com/containers/libpod/libpod/image" + "github.com/containers/libpod/pkg/adapter" "github.com/pkg/errors" - "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -48,78 +45,29 @@ func init() { markFlagHiddenForRemoteClient("latest", flags) } -// saveCmd saves the image to either docker-archive or oci +// rmCmd removes one or more containers func rmCmd(c *cliconfig.RmValues) error { - var ( - deleteFuncs []shared.ParallelWorkerInput - ) - - ctx := getContext() - runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) + runtime, err := adapter.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } defer runtime.Shutdown(false) - failureCnt := 0 - delContainers, err := getAllOrLatestContainers(&c.PodmanCommand, runtime, -1, "all") + ok, failures, err := runtime.RemoveContainers(getContext(), c) if err != nil { - if c.Force && len(c.InputArgs) > 0 { - if errors.Cause(err) == libpod.ErrNoSuchCtr { - err = nil + if errors.Cause(err) == libpod.ErrNoSuchCtr { + if len(c.InputArgs) > 1 { + exitCode = 125 } else { - failureCnt++ - } - runtime.RemoveContainersFromStorage(c.InputArgs) - } - if len(delContainers) == 0 { - if err != nil && failureCnt == 0 { exitCode = 1 } - return err - } - if err != nil { - if errors.Cause(err) == libpod.ErrNoSuchCtr { - exitCode = 1 - } - fmt.Println(err.Error()) - } - } - - for _, container := range delContainers { - con := container - f := func() error { - return runtime.RemoveContainer(ctx, con, c.Force, c.Volumes) - } - - deleteFuncs = append(deleteFuncs, shared.ParallelWorkerInput{ - ContainerID: con.ID(), - ParallelFunc: f, - }) - } - maxWorkers := shared.Parallelize("rm") - if c.GlobalIsSet("max-workers") { - maxWorkers = c.GlobalFlags.MaxWorks - } - logrus.Debugf("Setting maximum workers to %d", maxWorkers) - - // Run the parallel funcs - deleteErrors, errCount := shared.ParallelExecuteWorkerPool(maxWorkers, deleteFuncs) - err = printParallelOutput(deleteErrors, errCount) - if err != nil { - for _, result := range deleteErrors { - if result != nil && errors.Cause(result) != image.ErrNoSuchCtr { - failureCnt++ - } - } - if failureCnt == 0 { - exitCode = 1 } + return err } - if failureCnt > 0 { + if len(failures) > 0 { exitCode = 125 } - return err + return printCmdResults(ok, failures) } diff --git a/cmd/podman/shared/container.go b/cmd/podman/shared/container.go index 6826191c5..7bef62355 100644 --- a/cmd/podman/shared/container.go +++ b/cmd/podman/shared/container.go @@ -44,7 +44,6 @@ type PsOptions struct { Quiet bool Size bool Sort string - Label string Namespace bool Sync bool } @@ -274,6 +273,176 @@ func worker(wg *sync.WaitGroup, jobs <-chan workerInput, results chan<- PsContai } } +func generateContainerFilterFuncs(filter, filterValue string, r *libpod.Runtime) (func(container *libpod.Container) bool, error) { + switch filter { + case "id": + return func(c *libpod.Container) bool { + return strings.Contains(c.ID(), filterValue) + }, nil + case "label": + var filterArray []string = strings.SplitN(filterValue, "=", 2) + var filterKey string = filterArray[0] + if len(filterArray) > 1 { + filterValue = filterArray[1] + } else { + filterValue = "" + } + return func(c *libpod.Container) bool { + for labelKey, labelValue := range c.Labels() { + if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) { + return true + } + } + return false + }, nil + case "name": + return func(c *libpod.Container) bool { + return strings.Contains(c.Name(), filterValue) + }, nil + case "exited": + exitCode, err := strconv.ParseInt(filterValue, 10, 32) + if err != nil { + return nil, errors.Wrapf(err, "exited code out of range %q", filterValue) + } + return func(c *libpod.Container) bool { + ec, exited, err := c.ExitCode() + if ec == int32(exitCode) && err == nil && exited == true { + return true + } + return false + }, nil + case "status": + if !util.StringInSlice(filterValue, []string{"created", "running", "paused", "stopped", "exited", "unknown"}) { + return nil, errors.Errorf("%s is not a valid status", filterValue) + } + return func(c *libpod.Container) bool { + status, err := c.State() + if err != nil { + return false + } + if filterValue == "stopped" { + filterValue = "exited" + } + state := status.String() + if status == libpod.ContainerStateConfigured { + state = "created" + } else if status == libpod.ContainerStateStopped { + state = "exited" + } + return state == filterValue + }, nil + case "ancestor": + // This needs to refine to match docker + // - ancestor=(<image-name>[:tag]|<image-id>| ⟨image@digest⟩) - containers created from an image or a descendant. + return func(c *libpod.Container) bool { + containerConfig := c.Config() + if strings.Contains(containerConfig.RootfsImageID, filterValue) || strings.Contains(containerConfig.RootfsImageName, filterValue) { + return true + } + return false + }, nil + case "before": + ctr, err := r.LookupContainer(filterValue) + if err != nil { + return nil, errors.Errorf("unable to find container by name or id of %s", filterValue) + } + containerConfig := ctr.Config() + createTime := containerConfig.CreatedTime + return func(c *libpod.Container) bool { + cc := c.Config() + return createTime.After(cc.CreatedTime) + }, nil + case "since": + ctr, err := r.LookupContainer(filterValue) + if err != nil { + return nil, errors.Errorf("unable to find container by name or id of %s", filterValue) + } + containerConfig := ctr.Config() + createTime := containerConfig.CreatedTime + return func(c *libpod.Container) bool { + cc := c.Config() + return createTime.Before(cc.CreatedTime) + }, nil + case "volume": + //- volume=(<volume-name>|<mount-point-destination>) + return func(c *libpod.Container) bool { + containerConfig := c.Config() + var dest string + arr := strings.Split(filterValue, ":") + source := arr[0] + if len(arr) == 2 { + dest = arr[1] + } + for _, mount := range containerConfig.Spec.Mounts { + if dest != "" && (mount.Source == source && mount.Destination == dest) { + return true + } + if dest == "" && mount.Source == source { + return true + } + } + return false + }, nil + case "health": + return func(c *libpod.Container) bool { + hcStatus, err := c.HealthCheckStatus() + if err != nil { + return false + } + return hcStatus == filterValue + }, nil + } + return nil, errors.Errorf("%s is an invalid filter", filter) +} + +// GetPsContainerOutput returns a slice of containers specifically for ps output +func GetPsContainerOutput(r *libpod.Runtime, opts PsOptions, filters []string, maxWorkers int) ([]PsContainerOutput, error) { + var ( + filterFuncs []libpod.ContainerFilter + outputContainers []*libpod.Container + ) + + if len(filters) > 0 { + for _, f := range filters { + filterSplit := strings.SplitN(f, "=", 2) + if len(filterSplit) < 2 { + return nil, errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f) + } + generatedFunc, err := generateContainerFilterFuncs(filterSplit[0], filterSplit[1], r) + if err != nil { + return nil, errors.Wrapf(err, "invalid filter") + } + filterFuncs = append(filterFuncs, generatedFunc) + } + } + if !opts.Latest { + // Get all containers + containers, err := r.GetContainers(filterFuncs...) + if err != nil { + return nil, err + } + + // We only want the last few containers + if opts.Last > 0 && opts.Last <= len(containers) { + return nil, errors.Errorf("--last not yet supported") + } else { + outputContainers = containers + } + } else { + // Get just the latest container + // Ignore filters + latestCtr, err := r.GetLatestContainer() + if err != nil { + return nil, err + } + + outputContainers = []*libpod.Container{latestCtr} + } + + pss := PBatch(outputContainers, maxWorkers, opts) + return pss, nil +} + // PBatch is performs batch operations on a container in parallel. It spawns the number of workers // relative to the the number of parallel operations desired. func PBatch(containers []*libpod.Container, workers int, opts PsOptions) []PsContainerOutput { diff --git a/cmd/podman/shared/workers.go b/cmd/podman/shared/workers.go new file mode 100644 index 000000000..112af89cc --- /dev/null +++ b/cmd/podman/shared/workers.go @@ -0,0 +1,133 @@ +package shared + +import ( + "reflect" + "runtime" + "strings" + "sync" + + "github.com/sirupsen/logrus" +) + +// JobFunc provides the function signature for the pool'ed functions +type JobFunc func() error + +// Job defines the function to run +type Job struct { + ID string + Fn JobFunc +} + +// JobResult defines the results from the function ran +type JobResult struct { + Job Job + Err error +} + +// Pool defines the worker pool and queues +type Pool struct { + id string + wg *sync.WaitGroup + jobs chan Job + results chan JobResult + size int + capacity int +} + +// NewPool creates and initializes a new Pool +func NewPool(id string, size int, capacity int) *Pool { + var wg sync.WaitGroup + + // min for int... + s := size + if s > capacity { + s = capacity + } + + return &Pool{ + id, + &wg, + make(chan Job, capacity), + make(chan JobResult, capacity), + s, + capacity, + } +} + +// Add Job to pool for parallel processing +func (p *Pool) Add(job Job) { + p.wg.Add(1) + p.jobs <- job +} + +// Run the Job's in the pool, gather and return results +func (p *Pool) Run() ([]string, map[string]error, error) { + var ( + ok = []string{} + failures = map[string]error{} + ) + + for w := 0; w < p.size; w++ { + w := w + go p.newWorker(w) + } + close(p.jobs) + p.wg.Wait() + + close(p.results) + for r := range p.results { + if r.Err == nil { + ok = append(ok, r.Job.ID) + } else { + failures[r.Job.ID] = r.Err + } + } + + if logrus.GetLevel() == logrus.DebugLevel { + for i, f := range failures { + logrus.Debugf("Pool[%s, %s: %s]", p.id, i, f.Error()) + } + } + + return ok, failures, nil +} + +// newWorker creates new parallel workers to monitor jobs channel from Pool +func (p *Pool) newWorker(slot int) { + for job := range p.jobs { + err := job.Fn() + p.results <- JobResult{job, err} + if logrus.GetLevel() == logrus.DebugLevel { + n := strings.Split(runtime.FuncForPC(reflect.ValueOf(job.Fn).Pointer()).Name(), ".") + logrus.Debugf("Worker#%d finished job %s/%s (%v)", slot, n[2:], job.ID, err) + } + p.wg.Done() + } +} + +// DefaultPoolSize provides the maximum number of parallel workers (int) as calculated by a basic +// heuristic. This can be overriden by the --max-workers primary switch to podman. +func DefaultPoolSize(name string) int { + numCpus := runtime.NumCPU() + switch name { + case "kill": + case "pause": + case "rm": + case "unpause": + if numCpus <= 3 { + return numCpus * 3 + } + return numCpus * 4 + case "ps": + return 8 + case "restart": + return numCpus * 2 + case "stop": + if numCpus <= 2 { + return 4 + } else { + return numCpus * 3 + } + } + return 3 +} diff --git a/cmd/podman/stop.go b/cmd/podman/stop.go index e27be64f6..38d90fe81 100644 --- a/cmd/podman/stop.go +++ b/cmd/podman/stop.go @@ -1,9 +1,6 @@ package main import ( - "fmt" - "reflect" - "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/adapter" @@ -68,21 +65,5 @@ func stopCmd(c *cliconfig.StopValues) error { if err != nil { return err } - - for _, id := range ok { - fmt.Println(id) - } - - if len(failures) > 0 { - keys := reflect.ValueOf(failures).MapKeys() - lastKey := keys[len(keys)-1].String() - lastErr := failures[lastKey] - delete(failures, lastKey) - - for _, err := range failures { - outputError(err) - } - return lastErr - } - return nil + return printCmdResults(ok, failures) } diff --git a/cmd/podman/umount.go b/cmd/podman/umount.go index a938c7c38..914e37cfa 100644 --- a/cmd/podman/umount.go +++ b/cmd/podman/umount.go @@ -1,20 +1,16 @@ package main import ( - "fmt" - "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/cmd/podman/libpodruntime" - "github.com/containers/libpod/libpod" - "github.com/containers/storage" + "github.com/containers/libpod/pkg/adapter" "github.com/pkg/errors" - "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) var ( umountCommand cliconfig.UmountValues - description = `Container storage increments a mount counter each time a container is mounted. + + description = `Container storage increments a mount counter each time a container is mounted. When a container is unmounted, the mount counter is decremented. The container's root filesystem is physically unmounted only when the mount counter reaches zero indicating no other processes are using the mount. @@ -51,42 +47,15 @@ func init() { } func umountCmd(c *cliconfig.UmountValues) error { - runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) + runtime, err := adapter.GetRuntime(&c.PodmanCommand) if err != nil { - return errors.Wrapf(err, "could not get runtime") + return errors.Wrapf(err, "error creating runtime") } defer runtime.Shutdown(false) - force := c.Force - umountAll := c.All - - containers, err := getAllOrLatestContainers(&c.PodmanCommand, runtime, -1, "all") + ok, failures, err := runtime.UmountRootFilesystems(getContext(), c) if err != nil { - if len(containers) == 0 { - return err - } - fmt.Println(err.Error()) - } - - umountContainerErrStr := "error unmounting container" - var lastError error - for _, ctr := range containers { - ctrState, err := ctr.State() - if ctrState == libpod.ContainerStateRunning || err != nil { - continue - } - - if err = ctr.Unmount(force); err != nil { - if umountAll && errors.Cause(err) == storage.ErrLayerNotMounted { - continue - } - if lastError != nil { - logrus.Error(lastError) - } - lastError = errors.Wrapf(err, "%s %s", umountContainerErrStr, ctr.ID()) - continue - } - fmt.Printf("%s\n", ctr.ID()) + return err } - return lastError + return printCmdResults(ok, failures) } diff --git a/cmd/podman/utils.go b/cmd/podman/utils.go index c763940db..81bd02faa 100644 --- a/cmd/podman/utils.go +++ b/cmd/podman/utils.go @@ -2,11 +2,12 @@ package main import ( "fmt" + "reflect" "github.com/spf13/pflag" ) -//printParallelOutput takes the map of parallel worker results and outputs them +// printParallelOutput takes the map of parallel worker results and outputs them // to stdout func printParallelOutput(m map[string]error, errCount int) error { var lastError error @@ -23,6 +24,26 @@ func printParallelOutput(m map[string]error, errCount int) error { return lastError } +// print results from CLI command +func printCmdResults(ok []string, failures map[string]error) error { + for _, id := range ok { + fmt.Println(id) + } + + if len(failures) > 0 { + keys := reflect.ValueOf(failures).MapKeys() + lastKey := keys[len(keys)-1].String() + lastErr := failures[lastKey] + delete(failures, lastKey) + + for _, err := range failures { + outputError(err) + } + return lastErr + } + return nil +} + // markFlagHiddenForRemoteClient makes the flag not appear as part of the CLI // on the remote-client func markFlagHiddenForRemoteClient(flagName string, flags *pflag.FlagSet) { @@ -30,3 +51,29 @@ func markFlagHiddenForRemoteClient(flagName string, flags *pflag.FlagSet) { flags.MarkHidden(flagName) } } + +// TODO: remove when adapter package takes over this functionality +// func joinContainerOrCreateRootlessUserNS(runtime *libpod.Runtime, 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, "cannot read conmon PID file %q", ctr.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) +// } diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink index 9098a9297..ae830f3e6 100644 --- a/cmd/podman/varlink/io.podman.varlink +++ b/cmd/podman/varlink/io.podman.varlink @@ -133,6 +133,47 @@ type ContainerStats ( pids: int ) +type PsOpts ( + all: bool, + filters: ?[]string, + last: ?int, + latest: ?bool, + noTrunc: ?bool, + pod: ?bool, + quiet: ?bool, + sort: ?string, + sync: ?bool +) + +type PsContainer ( + id: string, + image: string, + command: string, + created: string, + ports: string, + names: string, + isInfra: bool, + status: string, + state: string, + pidNum: int, + rootFsSize: int, + rwSize: int, + pod: string, + createdAt: string, + exitedAt: string, + startedAt: string, + labels: [string]string, + nsPid: string, + cgroup: string, + ipc: string, + mnt: string, + net: string, + pidNs: string, + user: string, + uts: string, + mounts: string +) + # ContainerMount describes the struct for mounts in a container type ContainerMount ( destination: string, @@ -474,6 +515,8 @@ method GetInfo() -> (info: PodmanInfo) # See also [GetContainer](#GetContainer). method ListContainers() -> (containers: []Container) +method Ps(opts: PsOpts) -> (containers: []PsContainer) + # GetContainer returns information about a single container. If a container # with the given id doesn't exist, a [ContainerNotFound](#ContainerNotFound) # error will be returned. See also [ListContainers](ListContainers) and @@ -615,8 +658,9 @@ method PauseContainer(name: string) -> (container: string) # See also [PauseContainer](#PauseContainer). method UnpauseContainer(name: string) -> (container: string) -# This method has not be implemented yet. -# method AttachToContainer() -> (notimplemented: NotImplemented) +method Attach(name: string) -> () + +method AttachControl(name: string) -> () # GetAttachSockets takes the name or ID of an existing container. It returns file paths for two sockets needed # to properly communicate with a container. The first is the actual I/O socket that the container uses. The @@ -1111,6 +1155,9 @@ method PodStateData(name: string) -> (config: string) # This call is for the development of Podman only and should not be used. method CreateFromCC(in: []string) -> (id: string) +# Spec returns the oci spec for a container. This call is for development of Podman only and generally should not be used. +method Spec(name: string) -> (config: string) + # Sendfile allows a remote client to send a file to the host method SendFile(type: string, length: int) -> (file_handle: string) diff --git a/cmd/podman/wait.go b/cmd/podman/wait.go index 4449898a0..827ac6826 100644 --- a/cmd/podman/wait.go +++ b/cmd/podman/wait.go @@ -1,8 +1,6 @@ package main import ( - "fmt" - "reflect" "time" "github.com/containers/libpod/cmd/podman/cliconfig" @@ -62,21 +60,5 @@ func waitCmd(c *cliconfig.WaitValues) error { if err != nil { return err } - - for _, id := range ok { - fmt.Println(id) - } - - if len(failures) > 0 { - keys := reflect.ValueOf(failures).MapKeys() - lastKey := keys[len(keys)-1].String() - lastErr := failures[lastKey] - delete(failures, lastKey) - - for _, err := range failures { - outputError(err) - } - return lastErr - } - return nil + return printCmdResults(ok, failures) } |