diff options
Diffstat (limited to 'cmd')
-rw-r--r-- | cmd/podman/cliconfig/config.go | 3 | ||||
-rw-r--r-- | cmd/podman/commands.go | 2 | ||||
-rw-r--r-- | cmd/podman/container.go | 1 | ||||
-rw-r--r-- | cmd/podman/logs.go | 58 | ||||
-rw-r--r-- | cmd/podman/main.go | 2 | ||||
-rw-r--r-- | cmd/podman/pod_top.go | 26 | ||||
-rw-r--r-- | cmd/podman/runlabel.go | 28 | ||||
-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/varlink/io.podman.varlink | 10 |
11 files changed, 182 insertions, 49 deletions
diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go index cb9d9a338..1461c9f03 100644 --- a/cmd/podman/cliconfig/config.go +++ b/cmd/podman/cliconfig/config.go @@ -421,14 +421,15 @@ type RmiValues struct { type RunlabelValues struct { PodmanCommand Authfile string - Display bool CertDir string Creds string + Display bool Name string Opt1 string Opt2 string Opt3 string Quiet bool + Replace bool SignaturePolicy string TlsVerify bool } 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/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/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/runlabel.go b/cmd/podman/runlabel.go index 229ff1201..f79aa8b0e 100644 --- a/cmd/podman/runlabel.go +++ b/cmd/podman/runlabel.go @@ -10,9 +10,11 @@ import ( "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/utils" "github.com/pkg/errors" + "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -45,6 +47,7 @@ func init() { flags.StringVar(&runlabelCommand.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys") flags.StringVar(&runlabelCommand.Creds, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry") flags.BoolVar(&runlabelCommand.Display, "display", false, "Preview the command that the label would run") + flags.BoolVar(&runlabelCommand.Replace, "replace", false, "Replace existing container with a new one from the image") flags.StringVar(&runlabelCommand.Name, "name", "", "Assign a name to the container") flags.StringVar(&runlabelCommand.Opt1, "opt1", "", "Optional parameter to pass for install") @@ -146,10 +149,33 @@ func runlabelCmd(c *cliconfig.RunlabelValues) error { return err } if !c.Quiet { - fmt.Printf("Command: %s\n", strings.Join(cmd, " ")) + fmt.Printf("command: %s\n", strings.Join(cmd, " ")) if c.Display { return nil } } + + // If container already exists && --replace given -- Nuke it + if c.Replace { + for i, entry := range cmd { + if entry == "--name" { + name := cmd[i+1] + ctr, err := runtime.LookupContainer(name) + if err != nil { + if errors.Cause(err) != libpod.ErrNoSuchCtr { + logrus.Debugf("Error occurred searching for container %s: %s", name, err.Error()) + return err + } + } else { + logrus.Debugf("Runlabel --replace option given. Container %s will be deleted. The new container will be named %s", ctr.ID(), name) + if err := runtime.RemoveContainer(ctx, ctr, true, false); err != nil { + return err + } + } + break + } + } + } + return utils.ExecCmdWithStdStreams(stdIn, stdOut, stdErr, env, cmd[0], cmd[1:]...) } 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/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) |