diff options
Diffstat (limited to 'cmd')
-rw-r--r-- | cmd/podman/commands.go | 2 | ||||
-rw-r--r-- | cmd/podman/container.go | 1 | ||||
-rw-r--r-- | cmd/podman/generate_kube.go | 4 | ||||
-rw-r--r-- | cmd/podman/load.go | 71 | ||||
-rw-r--r-- | cmd/podman/logs.go | 58 | ||||
-rw-r--r-- | cmd/podman/main.go | 2 | ||||
-rw-r--r-- | cmd/podman/mount.go | 2 | ||||
-rw-r--r-- | cmd/podman/pod_inspect.go | 9 | ||||
-rw-r--r-- | cmd/podman/pod_stats.go | 2 | ||||
-rw-r--r-- | cmd/podman/pod_top.go | 26 | ||||
-rw-r--r-- | cmd/podman/rm.go | 5 | ||||
-rw-r--r-- | cmd/podman/search.go | 19 | ||||
-rw-r--r-- | cmd/podman/shared/container.go | 80 | ||||
-rw-r--r-- | cmd/podman/tree.go | 2 | ||||
-rw-r--r-- | cmd/podman/umount.go | 2 | ||||
-rw-r--r-- | cmd/podman/varlink/io.podman.varlink | 10 |
16 files changed, 204 insertions, 91 deletions
diff --git a/cmd/podman/commands.go b/cmd/podman/commands.go index d37af70c1..810c5a6f6 100644 --- a/cmd/podman/commands.go +++ b/cmd/podman/commands.go @@ -21,7 +21,6 @@ func getMainCommands() []*cobra.Command { &_psCommand, _loginCommand, _logoutCommand, - _logsCommand, _mountCommand, _pauseCommand, _portCommand, @@ -63,7 +62,6 @@ func getContainerSubCommands() []*cobra.Command { _execCommand, _exportCommand, _killCommand, - _logsCommand, _mountCommand, _pauseCommand, _portCommand, diff --git a/cmd/podman/container.go b/cmd/podman/container.go index ce6ad8883..2e9cedbaa 100644 --- a/cmd/podman/container.go +++ b/cmd/podman/container.go @@ -53,6 +53,7 @@ var ( _containerExistsCommand, _contInspectSubCommand, _listSubCommand, + _logsCommand, } ) diff --git a/cmd/podman/generate_kube.go b/cmd/podman/generate_kube.go index e3db14af3..42cfba8d8 100644 --- a/cmd/podman/generate_kube.go +++ b/cmd/podman/generate_kube.go @@ -57,8 +57,8 @@ func generateKubeYAMLCmd(c *cliconfig.GenerateKubeValues) error { return errors.Wrapf(libpod.ErrNotImplemented, "rootless users") } args := c.InputArgs - if len(args) > 1 || (len(args) < 1 && !c.Bool("latest")) { - return errors.Errorf("you must provide one container|pod ID or name or --latest") + if len(args) != 1 { + return errors.Errorf("you must provide exactly one container|pod ID or name") } runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) diff --git a/cmd/podman/load.go b/cmd/podman/load.go index 46add699e..04ff9fcca 100644 --- a/cmd/podman/load.go +++ b/cmd/podman/load.go @@ -5,21 +5,24 @@ import ( "io" "io/ioutil" "os" + "strings" "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/shared/parse" "github.com/containers/libpod/pkg/adapter" "github.com/pkg/errors" "github.com/spf13/cobra" + "golang.org/x/crypto/ssh/terminal" ) var ( loadCommand cliconfig.LoadValues - loadDescription = "Loads the image from docker-archive stored on the local machine." - _loadCommand = &cobra.Command{ - Use: "load [flags] [PATH]", - Short: "Load an image from docker archive", + loadDescription = "Loads an image from a locally stored archive (tar file) into container storage." + + _loadCommand = &cobra.Command{ + Use: "load [flags] [NAME[:TAG]]", + Short: "Load an image from container archive", Long: loadDescription, RunE: func(cmd *cobra.Command, args []string) error { loadCommand.InputArgs = args @@ -60,49 +63,43 @@ func loadCmd(c *cliconfig.LoadValues) error { } defer runtime.Shutdown(false) - input := c.Input - if runtime.Remote && len(input) == 0 { - return errors.New("the remote client requires you to load via -i and a tarball") - } - if len(input) == 0 { - input = "/dev/stdin" - c.Input = input - - fi, err := os.Stdin.Stat() - if err != nil { + if len(c.Input) > 0 { + if err := parse.ValidateFileName(c.Input); err != nil { return err } - // checking if loading from pipe - if !fi.Mode().IsRegular() { - outFile, err := ioutil.TempFile("/var/tmp", "podman") - if err != nil { - return errors.Errorf("error creating file %v", err) - } - defer os.Remove(outFile.Name()) - defer outFile.Close() - - inFile, err := os.OpenFile(input, 0, 0666) - if err != nil { - return errors.Errorf("error reading file %v", err) - } - defer inFile.Close() - - _, err = io.Copy(outFile, inFile) - if err != nil { - return errors.Errorf("error copying file %v", err) - } + } else { + if terminal.IsTerminal(int(os.Stdin.Fd())) { + return errors.Errorf("cannot read from terminal. Use command-line redirection or the --input flag.") + } + outFile, err := ioutil.TempFile("/var/tmp", "podman") + if err != nil { + return errors.Errorf("error creating file %v", err) + } + defer os.Remove(outFile.Name()) + defer outFile.Close() - input = outFile.Name() + _, err = io.Copy(outFile, os.Stdin) + if err != nil { + return errors.Errorf("error copying file %v", err) } - } - if err := parse.ValidateFileName(input); err != nil { - return err + + c.Input = outFile.Name() } names, err := runtime.LoadImage(getContext(), imageName, c) if err != nil { return err } + if len(imageName) > 0 { + split := strings.Split(names, ",") + newImage, err := runtime.NewImageFromLocal(split[0]) + if err != nil { + return err + } + if err := newImage.TagImage(imageName); err != nil { + return errors.Wrapf(err, "error adding '%s' to image %q", imageName, newImage.InputName) + } + } fmt.Println("Loaded image(s): " + names) return nil } diff --git a/cmd/podman/logs.go b/cmd/podman/logs.go index c3416fe57..a1b5fb4cc 100644 --- a/cmd/podman/logs.go +++ b/cmd/podman/logs.go @@ -1,27 +1,24 @@ package main import ( - "os" "time" "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/libpod" - "github.com/containers/libpod/pkg/logs" + "github.com/containers/libpod/pkg/adapter" "github.com/containers/libpod/pkg/util" "github.com/pkg/errors" - "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) var ( logsCommand cliconfig.LogsValues - logsDescription = `Retrieves logs for a container. + logsDescription = `Retrieves logs for one or more containers. This does not guarantee execution order when combined with podman run (i.e. your run may not have generated any logs at the time you execute podman logs. ` _logsCommand = &cobra.Command{ - Use: "logs [flags] CONTAINER", + Use: "logs [flags] CONTAINER [CONTAINER...]", Short: "Fetch the logs of a container", Long: logsDescription, RunE: func(cmd *cobra.Command, args []string) error { @@ -29,9 +26,19 @@ var ( logsCommand.GlobalFlags = MainGlobalOpts return logsCmd(&logsCommand) }, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) > 0 && logsCommand.Latest { + return errors.New("no containers can be specified when using 'latest'") + } + if !logsCommand.Latest && len(args) < 1 { + return errors.New("specify at least one container name or ID to log") + } + return nil + }, Example: `podman logs ctrID podman logs --tail 2 mywebserver - podman logs --follow=true --since 10m ctrID`, + podman logs --follow=true --since 10m ctrID + podman logs mywebserver mydbserver`, } ) @@ -54,20 +61,14 @@ func init() { } func logsCmd(c *cliconfig.LogsValues) error { - var ctr *libpod.Container var err error - 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) - args := c.InputArgs - if len(args) != 1 && !c.Latest { - return errors.Errorf("'podman logs' requires exactly one container name/ID") - } - sinceTime := time.Time{} if c.Flag("since").Changed { // parse time, error out if something is wrong @@ -78,7 +79,7 @@ func logsCmd(c *cliconfig.LogsValues) error { sinceTime = since } - opts := &logs.LogOptions{ + opts := &libpod.LogOptions{ Details: c.Details, Follow: c.Follow, Since: sinceTime, @@ -86,30 +87,5 @@ func logsCmd(c *cliconfig.LogsValues) error { Timestamps: c.Timestamps, } - if c.Latest { - ctr, err = runtime.GetLatestContainer() - } else { - ctr, err = runtime.LookupContainer(args[0]) - } - if err != nil { - return err - } - - logPath := ctr.LogPath() - - state, err := ctr.State() - if err != nil { - return err - } - - // If the log file does not exist yet and the container is in the - // Configured state, it has never been started before and no logs exist - // Exit cleanly in this case - if _, err := os.Stat(logPath); err != nil { - if state == libpod.ContainerStateConfigured { - logrus.Debugf("Container has not been started, no logs exist yet") - return nil - } - } - return logs.ReadLogs(logPath, ctr, opts) + return runtime.Log(c, opts) } diff --git a/cmd/podman/main.go b/cmd/podman/main.go index 1717e0624..ef300ef75 100644 --- a/cmd/podman/main.go +++ b/cmd/podman/main.go @@ -45,6 +45,7 @@ var mainCommands = []*cobra.Command{ &_inspectCommand, _killCommand, _loadCommand, + _logsCommand, podCommand.Command, _pullCommand, _pushCommand, @@ -75,6 +76,7 @@ var cmdsNotRequiringRootless = map[*cobra.Command]bool{ _podKillCommand: true, _podStatsCommand: true, _podStopCommand: true, + _podTopCommand: true, _restartCommand: true, _rmCommand: true, _runCommand: true, diff --git a/cmd/podman/mount.go b/cmd/podman/mount.go index 4381074ab..d074551ce 100644 --- a/cmd/podman/mount.go +++ b/cmd/podman/mount.go @@ -25,7 +25,7 @@ var ( ` _mountCommand = &cobra.Command{ - Use: "mount [flags] CONTAINER", + Use: "mount [flags] [CONTAINER]", Short: "Mount a working container's root filesystem", Long: mountDescription, RunE: func(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/pod_inspect.go b/cmd/podman/pod_inspect.go index 79ffe2e6f..851f39aa0 100644 --- a/cmd/podman/pod_inspect.go +++ b/cmd/podman/pod_inspect.go @@ -14,7 +14,7 @@ var ( podInspectCommand cliconfig.PodInspectValues podInspectDescription = `Display the configuration for a pod by name or id - By default, this will render all results in a JSON array. If the container and image have the same name, this command returns the container JSON.` + By default, this will render all results in a JSON array.` _podInspectCommand = &cobra.Command{ Use: "inspect [flags] POD", @@ -34,7 +34,7 @@ func init() { podInspectCommand.SetHelpTemplate(HelpTemplate()) podInspectCommand.SetUsageTemplate(UsageTemplate()) flags := podInspectCommand.Flags() - flags.BoolVarP(&podInspectCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") + flags.BoolVarP(&podInspectCommand.Latest, "latest", "l", false, "Act on the latest pod podman is aware of") markFlagHiddenForRemoteClient("latest", flags) } @@ -44,6 +44,11 @@ func podInspectCmd(c *cliconfig.PodInspectValues) error { pod *adapter.Pod ) args := c.InputArgs + + if len(args) < 1 && !c.Latest { + return errors.Errorf("you must provide the name or id of a pod") + } + runtime, err := adapter.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") diff --git a/cmd/podman/pod_stats.go b/cmd/podman/pod_stats.go index 701051938..e8ff322ce 100644 --- a/cmd/podman/pod_stats.go +++ b/cmd/podman/pod_stats.go @@ -25,7 +25,7 @@ var ( podStatsDescription = `For each specified pod this command will display percentage of CPU, memory, network I/O, block I/O and PIDs for containers in one the pods.` _podStatsCommand = &cobra.Command{ - Use: "stats [flags] POD [POD...]", + Use: "stats [flags] [POD...]", Short: "Display a live stream of resource usage statistics for the containers in one or more pods", Long: podStatsDescription, RunE: func(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/pod_top.go b/cmd/podman/pod_top.go index c9a6d8822..f65d66df6 100644 --- a/cmd/podman/pod_top.go +++ b/cmd/podman/pod_top.go @@ -9,6 +9,7 @@ import ( "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/libpod" + "github.com/containers/libpod/pkg/rootless" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -53,6 +54,10 @@ func podTopCmd(c *cliconfig.PodTopValues) error { ) args := c.InputArgs + if os.Geteuid() != 0 { + rootless.SetSkipStorageSetup(true) + } + if c.ListDescriptors { descriptors, err := libpod.GetContainerPidInformationDescriptors() if err != nil { @@ -77,6 +82,27 @@ func podTopCmd(c *cliconfig.PodTopValues) error { } else { descriptors = args[1:] } + + if os.Geteuid() != 0 { + var pod *adapter.Pod + var err error + if c.Latest { + pod, err = runtime.GetLatestPod() + } else { + pod, err = runtime.LookupPod(c.InputArgs[0]) + } + if err != nil { + return errors.Wrapf(err, "unable to lookup requested container") + } + became, ret, err := runtime.JoinOrCreateRootlessPod(pod) + if err != nil { + return err + } + if became { + os.Exit(ret) + } + } + w := tabwriter.NewWriter(os.Stdout, 5, 1, 3, ' ', 0) psOutput, err := runtime.PodTop(c, descriptors) if err != nil { diff --git a/cmd/podman/rm.go b/cmd/podman/rm.go index 56aaae9eb..299420bb6 100644 --- a/cmd/podman/rm.go +++ b/cmd/podman/rm.go @@ -195,5 +195,10 @@ func rmCmd(c *cliconfig.RmValues) error { exitCode = 1 } } + + if failureCnt > 0 { + exitCode = 125 + } + return err } diff --git a/cmd/podman/search.go b/cmd/podman/search.go index 5997e144a..a10b9d419 100644 --- a/cmd/podman/search.go +++ b/cmd/podman/search.go @@ -1,6 +1,7 @@ package main import ( + "reflect" "strings" "github.com/containers/buildah/pkg/formats" @@ -79,7 +80,10 @@ func searchCmd(c *cliconfig.SearchValues) error { return err } format := genSearchFormat(c.Format) - out := formats.StdoutTemplateArray{Output: searchToGeneric(results), Template: format, Fields: results[0].HeaderMap()} + if len(results) == 0 { + return nil + } + out := formats.StdoutTemplateArray{Output: searchToGeneric(results), Template: format, Fields: genSearchOutputMap()} formats.Writer(out).Out() return nil } @@ -99,3 +103,16 @@ func searchToGeneric(params []image.SearchResult) (genericParams []interface{}) } return genericParams } + +func genSearchOutputMap() map[string]string { + io := image.SearchResult{} + v := reflect.Indirect(reflect.ValueOf(io)) + values := make(map[string]string) + + for i := 0; i < v.NumField(); i++ { + key := v.Type().Field(i).Name + value := key + values[key] = strings.ToUpper(splitCamelCase(value)) + } + return values +} diff --git a/cmd/podman/shared/container.go b/cmd/podman/shared/container.go index 41950928e..6826191c5 100644 --- a/cmd/podman/shared/container.go +++ b/cmd/podman/shared/container.go @@ -3,11 +3,11 @@ package shared import ( "context" "fmt" - "github.com/google/shlex" "io" "os" "path/filepath" "regexp" + "sort" "strconv" "strings" "sync" @@ -21,6 +21,7 @@ import ( "github.com/containers/libpod/pkg/util" "github.com/cri-o/ocicni/pkg/ocicni" "github.com/docker/go-units" + "github.com/google/shlex" "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -583,18 +584,93 @@ func getCgroup(spec *specs.Spec) string { return cgroup } +func comparePorts(i, j ocicni.PortMapping) bool { + if i.ContainerPort != j.ContainerPort { + return i.ContainerPort < j.ContainerPort + } + + if i.HostIP != j.HostIP { + return i.HostIP < j.HostIP + } + + if i.HostPort != j.HostPort { + return i.HostPort < j.HostPort + } + + return i.Protocol < j.Protocol +} + +// returns the group as <IP:startPort:lastPort->startPort:lastPort/Proto> +// e.g 0.0.0.0:1000-1006->1000-1006/tcp +func formatGroup(key string, start, last int32) string { + parts := strings.Split(key, "/") + groupType := parts[0] + var ip string + if len(parts) > 1 { + ip = parts[0] + groupType = parts[1] + } + group := strconv.Itoa(int(start)) + if start != last { + group = fmt.Sprintf("%s-%d", group, last) + } + if ip != "" { + group = fmt.Sprintf("%s:%s->%s", ip, group, group) + } + return fmt.Sprintf("%s/%s", group, groupType) +} + // portsToString converts the ports used to a string of the from "port1, port2" +// also groups continuous list of ports in readable format. func portsToString(ports []ocicni.PortMapping) string { + type portGroup struct { + first int32 + last int32 + } var portDisplay []string if len(ports) == 0 { return "" } + //Sort the ports, so grouping continuous ports become easy. + sort.Slice(ports, func(i, j int) bool { + return comparePorts(ports[i], ports[j]) + }) + + // portGroupMap is used for grouping continuous ports + portGroupMap := make(map[string]*portGroup) + var groupKeyList []string + for _, v := range ports { + hostIP := v.HostIP if hostIP == "" { hostIP = "0.0.0.0" } - portDisplay = append(portDisplay, fmt.Sprintf("%s:%d->%d/%s", hostIP, v.HostPort, v.ContainerPort, v.Protocol)) + // if hostPort and containerPort are not same, consider as individual port. + if v.ContainerPort != v.HostPort { + portDisplay = append(portDisplay, fmt.Sprintf("%s:%d->%d/%s", hostIP, v.HostPort, v.ContainerPort, v.Protocol)) + continue + } + + portMapKey := fmt.Sprintf("%s/%s", hostIP, v.Protocol) + + portgroup, ok := portGroupMap[portMapKey] + if !ok { + portGroupMap[portMapKey] = &portGroup{first: v.ContainerPort, last: v.ContainerPort} + // this list is required to travese portGroupMap + groupKeyList = append(groupKeyList, portMapKey) + continue + } + + if portgroup.last == (v.ContainerPort - 1) { + portgroup.last = v.ContainerPort + continue + } + } + // for each portMapKey, format group list and appned to output string + for _, portKey := range groupKeyList { + group := portGroupMap[portKey] + portDisplay = append(portDisplay, formatGroup(portKey, group.first, group.last)) } return strings.Join(portDisplay, ", ") } diff --git a/cmd/podman/tree.go b/cmd/podman/tree.go index ebda18cdb..c56e35aef 100644 --- a/cmd/podman/tree.go +++ b/cmd/podman/tree.go @@ -23,7 +23,7 @@ var ( treeDescription = "Prints layer hierarchy of an image in a tree format" _treeCommand = &cobra.Command{ - Use: "tree", + Use: "tree [flags] IMAGE", Short: treeDescription, Long: treeDescription, RunE: func(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/umount.go b/cmd/podman/umount.go index c57d5794c..a938c7c38 100644 --- a/cmd/podman/umount.go +++ b/cmd/podman/umount.go @@ -31,7 +31,7 @@ var ( return umountCmd(&umountCommand) }, Args: func(cmd *cobra.Command, args []string) error { - return checkAllAndLatest(cmd, args, true) + return checkAllAndLatest(cmd, args, false) }, Example: `podman umount ctrID podman umount ctrID1 ctrID2 ctrID3 diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink index 791790e2e..517a7a2a1 100644 --- a/cmd/podman/varlink/io.podman.varlink +++ b/cmd/podman/varlink/io.podman.varlink @@ -19,6 +19,14 @@ type StringResponse ( message: string ) +type LogLine ( + device: string, + parseLogType : string, + time: string, + msg: string, + cid: string +) + # ContainerChanges describes the return struct for ListContainerChanges type ContainerChanges ( changed: []string, @@ -522,6 +530,8 @@ method ListContainerProcesses(name: string, opts: []string) -> (container: []str # capability of varlink if the client invokes it. method GetContainerLogs(name: string) -> (container: []string) +method GetContainersLogs(names: []string, follow: bool, latest: bool, since: string, tail: int, timestamps: bool) -> (log: LogLine) + # ListContainerChanges takes a name or ID of a container and returns changes between the container and # its base image. It returns a struct of changed, deleted, and added path names. method ListContainerChanges(name: string) -> (container: ContainerChanges) |