diff options
-rw-r--r-- | cmd/podman/containers/ps.go | 160 | ||||
-rw-r--r-- | cmd/podman/containers/wait.go | 14 | ||||
-rw-r--r-- | cmd/podman/images/build.go | 4 | ||||
-rw-r--r-- | docs/source/markdown/podman-wait.1.md | 7 | ||||
-rw-r--r-- | go.mod | 2 | ||||
-rw-r--r-- | go.sum | 4 | ||||
-rw-r--r-- | test/e2e/ps_test.go | 33 | ||||
-rw-r--r-- | test/e2e/wait_test.go | 45 | ||||
-rw-r--r-- | vendor/modules.txt | 2 |
9 files changed, 211 insertions, 60 deletions
diff --git a/cmd/podman/containers/ps.go b/cmd/podman/containers/ps.go index 8c3e2e3b7..80a50f4ef 100644 --- a/cmd/podman/containers/ps.go +++ b/cmd/podman/containers/ps.go @@ -371,12 +371,6 @@ func (l psReporter) CreatedHuman() string { // portsToString converts the ports used to a string of the from "port1, port2" // and also groups a continuous list of ports into a readable format. func portsToString(ports []ocicni.PortMapping) string { - type portGroup struct { - first int32 - last int32 - } - portDisplay := []string{} - if len(ports) == 0 { return "" } @@ -385,41 +379,124 @@ func portsToString(ports []ocicni.PortMapping) string { return comparePorts(ports[i], ports[j]) }) - // portGroupMap is used for grouping continuous ports. - portGroupMap := make(map[string]*portGroup) - var groupKeyList []string + portGroups := [][]ocicni.PortMapping{} + currentGroup := []ocicni.PortMapping{} + for i, v := range ports { + var prevPort, nextPort *int32 + if i > 0 { + prevPort = &ports[i-1].ContainerPort + } + if i+1 < len(ports) { + nextPort = &ports[i+1].ContainerPort + } - for _, v := range ports { + port := v.ContainerPort - hostIP := v.HostIP - if hostIP == "" { - hostIP = "0.0.0.0" + // Helper functions + addToCurrentGroup := func(x ocicni.PortMapping) { + currentGroup = append(currentGroup, x) } - // 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 + + addToPortGroup := func(x ocicni.PortMapping) { + portGroups = append(portGroups, []ocicni.PortMapping{x}) + } + + finishCurrentGroup := func() { + portGroups = append(portGroups, currentGroup) + currentGroup = []ocicni.PortMapping{} } - portMapKey := fmt.Sprintf("%s/%s", hostIP, v.Protocol) + // Single entry slice + if prevPort == nil && nextPort == nil { + addToPortGroup(v) + } + + // Start of the slice with len > 0 + if prevPort == nil && nextPort != nil { + isGroup := *nextPort-1 == port + + if isGroup { + // Start with a group + addToCurrentGroup(v) + } else { + // Start with single item + addToPortGroup(v) + } - portgroup, ok := portGroupMap[portMapKey] - if !ok { - portGroupMap[portMapKey] = &portGroup{first: v.ContainerPort, last: v.ContainerPort} - // This list is required to traverse portGroupMap. - groupKeyList = append(groupKeyList, portMapKey) continue } - if portgroup.last == (v.ContainerPort - 1) { - portgroup.last = v.ContainerPort + // Middle of the slice with len > 0 + if prevPort != nil && nextPort != nil { + currentIsGroup := *prevPort+1 == port + nextIsGroup := *nextPort-1 == port + + if currentIsGroup { + // Maybe in the middle of a group + addToCurrentGroup(v) + + if !nextIsGroup { + // End of a group + finishCurrentGroup() + } + } else if nextIsGroup { + // Start of a new group + addToCurrentGroup(v) + } else { + // No group at all + addToPortGroup(v) + } + continue } + + // End of the slice with len > 0 + if prevPort != nil && nextPort == nil { + isGroup := *prevPort+1 == port + + if isGroup { + // End group + addToCurrentGroup(v) + finishCurrentGroup() + } else { + // End single item + addToPortGroup(v) + } + } } - // For each portMapKey, format group list and append to output string. - for _, portKey := range groupKeyList { - group := portGroupMap[portKey] - portDisplay = append(portDisplay, formatGroup(portKey, group.first, group.last)) + + portDisplay := []string{} + for _, group := range portGroups { + if len(group) == 0 { + // Usually should not happen, but better do not crash. + continue + } + + first := group[0] + + hostIP := first.HostIP + if hostIP == "" { + hostIP = "0.0.0.0" + } + + // Single mappings + if len(group) == 1 { + portDisplay = append(portDisplay, + fmt.Sprintf( + "%s:%d->%d/%s", + hostIP, first.HostPort, first.ContainerPort, first.Protocol, + ), + ) + continue + } + + // Group mappings + last := group[len(group)-1] + portDisplay = append(portDisplay, formatGroup( + fmt.Sprintf("%s/%s", hostIP, first.Protocol), + first.HostPort, last.HostPort, + first.ContainerPort, last.ContainerPort, + )) } return strings.Join(portDisplay, ", ") } @@ -440,9 +517,10 @@ func comparePorts(i, j ocicni.PortMapping) bool { return i.Protocol < j.Protocol } -// formatGroup 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 { +// formatGroup returns the group in the format: +// <IP:firstHost:lastHost->firstCtr:lastCtr/Proto> +// e.g 0.0.0.0:1000-1006->2000-2006/tcp. +func formatGroup(key string, firstHost, lastHost, firstCtr, lastCtr int32) string { parts := strings.Split(key, "/") groupType := parts[0] var ip string @@ -450,12 +528,16 @@ func formatGroup(key string, start, last int32) string { 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) + + group := func(first, last int32) string { + group := strconv.Itoa(int(first)) + if first != last { + group = fmt.Sprintf("%s-%d", group, last) + } + return group } - return fmt.Sprintf("%s/%s", group, groupType) + hostGroup := group(firstHost, lastHost) + ctrGroup := group(firstCtr, lastCtr) + + return fmt.Sprintf("%s:%s->%s/%s", ip, hostGroup, ctrGroup, groupType) } diff --git a/cmd/podman/containers/wait.go b/cmd/podman/containers/wait.go index 0f9304558..b4986143b 100644 --- a/cmd/podman/containers/wait.go +++ b/cmd/podman/containers/wait.go @@ -23,7 +23,7 @@ var ( Short: "Block on one or more containers", Long: waitDescription, RunE: wait, - Example: `podman wait --interval 5000 ctrID + Example: `podman wait --interval 5s ctrID podman wait ctrID1 ctrID2`, } @@ -32,7 +32,7 @@ var ( Short: waitCommand.Short, Long: waitCommand.Long, RunE: waitCommand.RunE, - Example: `podman container wait --interval 5000 ctrID + Example: `podman container wait --interval 5s ctrID podman container wait ctrID1 ctrID2`, } ) @@ -40,10 +40,11 @@ var ( var ( waitOptions = entities.WaitOptions{} waitCondition string + waitInterval string ) func waitFlags(flags *pflag.FlagSet) { - flags.DurationVarP(&waitOptions.Interval, "interval", "i", time.Duration(250), "Milliseconds to wait before polling for completion") + flags.StringVarP(&waitInterval, "interval", "i", "250ns", "Time Interval to wait before polling for completion") flags.StringVar(&waitCondition, "condition", "stopped", "Condition to wait on") } @@ -70,8 +71,11 @@ func wait(cmd *cobra.Command, args []string) error { err error errs utils.OutputErrors ) - if waitOptions.Interval == 0 { - return errors.New("interval must be greater then 0") + if waitOptions.Interval, err = time.ParseDuration(waitInterval); err != nil { + var err1 error + if waitOptions.Interval, err1 = time.ParseDuration(waitInterval + "ms"); err1 != nil { + return err + } } if !waitOptions.Latest && len(args) == 0 { diff --git a/cmd/podman/images/build.go b/cmd/podman/images/build.go index f8ea4754f..18c31313b 100644 --- a/cmd/podman/images/build.go +++ b/cmd/podman/images/build.go @@ -282,8 +282,7 @@ func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *buil flags.Layers = false } - var stdin, stdout, stderr, reporter *os.File - stdin = os.Stdin + var stdout, stderr, reporter *os.File stdout = os.Stdout stderr = os.Stderr reporter = os.Stderr @@ -422,7 +421,6 @@ func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *buil ForceRmIntermediateCtrs: flags.ForceRm, IDMappingOptions: idmappingOptions, IIDFile: flags.Iidfile, - In: stdin, Isolation: isolation, Labels: flags.Label, Layers: flags.Layers, diff --git a/docs/source/markdown/podman-wait.1.md b/docs/source/markdown/podman-wait.1.md index 1d85e9af0..f0ccb1f9e 100644 --- a/docs/source/markdown/podman-wait.1.md +++ b/docs/source/markdown/podman-wait.1.md @@ -23,8 +23,8 @@ Condition to wait on (default "stopped") Print usage statement -**--interval**, **-i**=*microseconds* - Microseconds to wait before polling for completion +**--interval**, **-i**=*duration* + Time interval to wait before polling for completion. A duration string is a sequence of decimal numbers, each with optional fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m". Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". Time unit defaults to "ms". **--latest**, **-l** @@ -42,6 +42,9 @@ $ podman wait mywebserver $ podman wait --latest 0 +$ podman wait --interval 2s +0 + $ podman wait 860a4b23 1 @@ -72,6 +72,6 @@ require ( gopkg.in/square/go-jose.v2 v2.5.1 // indirect gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect k8s.io/api v0.0.0-20190620084959-7cf5895f2711 - k8s.io/apimachinery v0.19.2 + k8s.io/apimachinery v0.19.3 k8s.io/client-go v0.0.0-20190620085101-78d2af792bab ) @@ -815,8 +815,8 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt k8s.io/api v0.0.0-20190620084959-7cf5895f2711 h1:BblVYz/wE5WtBsD/Gvu54KyBUTJMflolzc5I2DTvh50= k8s.io/api v0.0.0-20190620084959-7cf5895f2711/go.mod h1:TBhBqb1AWbBQbW3XRusr7n7E4v2+5ZY8r8sAMnyFC5A= k8s.io/apimachinery v0.0.0-20190612205821-1799e75a0719/go.mod h1:I4A+glKBHiTgiEjQiCCQfCAIcIMFGt291SmsvcrFzJA= -k8s.io/apimachinery v0.19.2 h1:5Gy9vQpAGTKHPVOh5c4plE274X8D/6cuEiTO2zve7tc= -k8s.io/apimachinery v0.19.2/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA= +k8s.io/apimachinery v0.19.3 h1:bpIQXlKjB4cB/oNpnNnV+BybGPR7iP5oYpsOTEJ4hgc= +k8s.io/apimachinery v0.19.3/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA= k8s.io/client-go v0.0.0-20190620085101-78d2af792bab h1:E8Fecph0qbNsAbijJJQryKu4Oi9QTp5cVpjTE+nqg6g= k8s.io/client-go v0.0.0-20190620085101-78d2af792bab/go.mod h1:E95RaSlHr79aHaX0aGSwcPNfygDiPKOVXdmivCIZT0k= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= diff --git a/test/e2e/ps_test.go b/test/e2e/ps_test.go index 48ef566ce..c65738993 100644 --- a/test/e2e/ps_test.go +++ b/test/e2e/ps_test.go @@ -411,18 +411,43 @@ var _ = Describe("Podman ps", func() { Expect(output).To(ContainSubstring(podName)) }) - It("podman ps test with port range", func() { - session := podmanTest.RunTopContainer("") + It("podman ps test with single port range", func() { + session := podmanTest.Podman([]string{"run", "-dt", "-p", "2000-2006:2000-2006", ALPINE, "top"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session = podmanTest.Podman([]string{"run", "-dt", "-p", "2000-2006:2000-2006", ALPINE, "top"}) + session = podmanTest.Podman([]string{"ps", "--format", "{{.Ports}}"}) + session.WaitWithDefaultTimeout() + Expect(session.OutputToString()).To(ContainSubstring("0.0.0.0:2000-2006")) + }) + + It("podman ps test with invalid port range", func() { + session := podmanTest.Podman([]string{ + "run", "-p", "1000-2000:2000-3000", "-p", "1999-2999:3001-4001", ALPINE, + }) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(125)) + Expect(session.ErrorToString()).To(ContainSubstring("conflicting port mappings for host port 1999")) + }) + + It("podman ps test with multiple port range", func() { + session := podmanTest.Podman([]string{ + "run", "-dt", + "-p", "3000-3001:3000-3001", + "-p", "3100-3102:4000-4002", + "-p", "30080:30080", + "-p", "30443:30443", + "-p", "8000:8080", + ALPINE, "top"}, + ) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) session = podmanTest.Podman([]string{"ps", "--format", "{{.Ports}}"}) session.WaitWithDefaultTimeout() - Expect(session.OutputToString()).To(ContainSubstring("0.0.0.0:2000-2006")) + Expect(session.OutputToString()).To(ContainSubstring( + "0.0.0.0:3000-3001->3000-3001/tcp, 0.0.0.0:3100-3102->4000-4002/tcp, 0.0.0.0:8000->8080/tcp, 0.0.0.0:30080->30080/tcp, 0.0.0.0:30443->30443/tcp", + )) }) It("podman ps sync flag", func() { diff --git a/test/e2e/wait_test.go b/test/e2e/wait_test.go index 4f0129a47..aa8a1f245 100644 --- a/test/e2e/wait_test.go +++ b/test/e2e/wait_test.go @@ -46,6 +46,7 @@ var _ = Describe("Podman wait", func() { Expect(session.ExitCode()).To(Equal(0)) session = podmanTest.Podman([]string{"wait", cid}) session.Wait() + Expect(session.ExitCode()).To(Equal(0)) }) It("podman wait on a sleeping container", func() { @@ -55,22 +56,60 @@ var _ = Describe("Podman wait", func() { Expect(session.ExitCode()).To(Equal(0)) session = podmanTest.Podman([]string{"wait", cid}) session.Wait(20) + Expect(session.ExitCode()).To(Equal(0)) }) It("podman wait on latest container", func() { session := podmanTest.Podman([]string{"run", "-d", ALPINE, "sleep", "1"}) session.Wait(20) Expect(session.ExitCode()).To(Equal(0)) - session = podmanTest.Podman([]string{"wait", "-l"}) - session.Wait(20) + if IsRemote() { + session = podmanTest.Podman([]string{"wait", session.OutputToString()}) + } else { + session = podmanTest.Podman([]string{"wait", "-l"}) + } + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) }) It("podman container wait on latest container", func() { session := podmanTest.Podman([]string{"container", "run", "-d", ALPINE, "sleep", "1"}) session.Wait(20) Expect(session.ExitCode()).To(Equal(0)) - session = podmanTest.Podman([]string{"container", "wait", "-l"}) + if IsRemote() { + session = podmanTest.Podman([]string{"container", "wait", session.OutputToString()}) + } else { + session = podmanTest.Podman([]string{"container", "wait", "-l"}) + } + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + + It("podman container wait on latest container with --interval flag", func() { + session := podmanTest.Podman([]string{"container", "run", "-d", ALPINE, "sleep", "1"}) session.Wait(20) + Expect(session.ExitCode()).To(Equal(0)) + session = podmanTest.Podman([]string{"container", "wait", "-i", "5000", session.OutputToString()}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + + It("podman container wait on latest container with --interval flag", func() { + session := podmanTest.Podman([]string{"container", "run", "-d", ALPINE, "sleep", "1"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + session = podmanTest.Podman([]string{"container", "wait", "--interval", "1s", session.OutputToString()}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + + It("podman container wait on container with bogus --interval", func() { + session := podmanTest.Podman([]string{"container", "run", "-d", ALPINE, "sleep", "1"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + session = podmanTest.Podman([]string{"container", "wait", "--interval", "100days", session.OutputToString()}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(125)) }) It("podman wait on three containers", func() { diff --git a/vendor/modules.txt b/vendor/modules.txt index 2f8a79aa8..9840261a9 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -701,7 +701,7 @@ gopkg.in/yaml.v3 # k8s.io/api v0.0.0-20190620084959-7cf5895f2711 k8s.io/api/apps/v1 k8s.io/api/core/v1 -# k8s.io/apimachinery v0.19.2 +# k8s.io/apimachinery v0.19.3 k8s.io/apimachinery/pkg/api/errors k8s.io/apimachinery/pkg/api/resource k8s.io/apimachinery/pkg/apis/meta/v1 |