소스 검색

group ports to render ranges as ... ranges

Signed-off-by: Nicolas De Loof <[email protected]>
Nicolas De Loof 4 년 전
부모
커밋
97a0efd7c3
4개의 변경된 파일104개의 추가작업 그리고 17개의 파일을 삭제
  1. 72 9
      cmd/compose/ps.go
  2. 30 1
      pkg/api/api.go
  3. 1 6
      pkg/compose/ps.go
  4. 1 1
      pkg/compose/ps_test.go

+ 72 - 9
cmd/compose/ps.go

@@ -147,14 +147,7 @@ SERVICES:
 func writter(containers []api.ContainerSummary) func(w io.Writer) {
 	return func(w io.Writer) {
 		for _, container := range containers {
-			var ports []string
-			for _, p := range container.Publishers {
-				if p.URL == "" {
-					ports = append(ports, fmt.Sprintf("%d/%s", p.TargetPort, p.Protocol))
-				} else {
-					ports = append(ports, fmt.Sprintf("%s->%d/%s", p.URL, p.TargetPort, p.Protocol))
-				}
-			}
+			ports := DisplayablePorts(container)
 			status := container.State
 			if status == "running" && container.Health != "" {
 				status = fmt.Sprintf("%s (%s)", container.State, container.Health)
@@ -162,7 +155,7 @@ func writter(containers []api.ContainerSummary) func(w io.Writer) {
 				status = fmt.Sprintf("%s (%d)", container.State, container.ExitCode)
 			}
 			command := formatter2.Ellipsis(container.Command, 20)
-			_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", container.Name, strconv.Quote(command), container.Service, status, strings.Join(ports, ", "))
+			_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", container.Name, strconv.Quote(command), container.Service, status, ports)
 		}
 	}
 }
@@ -182,3 +175,73 @@ func filterByStatus(containers []api.ContainerSummary, status string) []api.Cont
 	}
 	return filtered
 }
+
+type portRange struct {
+	pStart   int
+	pEnd     int
+	tStart   int
+	tEnd     int
+	IP       string
+	protocol string
+}
+
+func (pr portRange) String() string {
+	var (
+		pub string
+		tgt string
+	)
+
+	if pr.pEnd > pr.pStart {
+		pub = fmt.Sprintf("%s:%d-%d->", pr.IP, pr.pStart, pr.pEnd)
+	} else if pr.pStart > 0 {
+		pub = fmt.Sprintf("%s:%d->", pr.IP, pr.pStart)
+	}
+	if pr.tEnd > pr.tStart {
+		tgt = fmt.Sprintf("%d-%d", pr.tStart, pr.tEnd)
+	} else {
+		tgt = fmt.Sprintf("%d", pr.tStart)
+	}
+	return fmt.Sprintf("%s%s/%s", pub, tgt, pr.protocol)
+}
+
+// DisplayablePorts is copy pasted from https://github.com/docker/cli/pull/581/files
+func DisplayablePorts(c api.ContainerSummary) string {
+	if c.Publishers == nil {
+		return ""
+	}
+
+	sort.Sort(c.Publishers)
+
+	pr := portRange{}
+	ports := []string{}
+	for _, p := range c.Publishers {
+		prIsRange := pr.tEnd != pr.tStart
+		tOverlaps := p.TargetPort <= pr.tEnd
+
+		// Start a new port-range if:
+		// - the protocol is different from the current port-range
+		// - published or target port are not consecutive to the current port-range
+		// - the current port-range is a _range_, and the target port overlaps with the current range's target-ports
+		if p.Protocol != pr.protocol || p.URL != pr.IP || p.PublishedPort-pr.pEnd > 1 || p.TargetPort-pr.tEnd > 1 || prIsRange && tOverlaps {
+			// start a new port-range, and print the previous port-range (if any)
+			if pr.pStart > 0 {
+				ports = append(ports, pr.String())
+			}
+			pr = portRange{
+				pStart:   p.PublishedPort,
+				pEnd:     p.PublishedPort,
+				tStart:   p.TargetPort,
+				tEnd:     p.TargetPort,
+				protocol: p.Protocol,
+				IP:       p.URL,
+			}
+			continue
+		}
+		pr.pEnd = p.PublishedPort
+		pr.tEnd = p.TargetPort
+	}
+	if pr.tStart > 0 {
+		ports = append(ports, pr.String())
+	}
+	return strings.Join(ports, ", ")
+}

+ 30 - 1
pkg/api/api.go

@@ -297,7 +297,36 @@ type ContainerSummary struct {
 	State      string
 	Health     string
 	ExitCode   int
-	Publishers []PortPublisher
+	Publishers PortPublishers
+}
+
+// PortPublishers is a slice of PortPublisher
+type PortPublishers []PortPublisher
+
+// Len implements sort.Interface
+func (p PortPublishers) Len() int {
+	return len(p)
+}
+
+// Less implements sort.Interface
+func (p PortPublishers) Less(i, j int) bool {
+	left := p[i]
+	right := p[j]
+	if left.URL != right.URL {
+		return left.URL < right.URL
+	}
+	if left.TargetPort != right.TargetPort {
+		return left.TargetPort < right.TargetPort
+	}
+	if left.PublishedPort != right.PublishedPort {
+		return left.PublishedPort < right.PublishedPort
+	}
+	return left.Protocol < right.Protocol
+}
+
+// Swap implements sort.Interface
+func (p PortPublishers) Swap(i, j int) {
+	p[i], p[j] = p[j], p[i]
 }
 
 // ContainerProcSummary holds container processes top data

+ 1 - 6
pkg/compose/ps.go

@@ -18,7 +18,6 @@ package compose
 
 import (
 	"context"
-	"fmt"
 	"sort"
 
 	"golang.org/x/sync/errgroup"
@@ -46,12 +45,8 @@ func (s *composeService) Ps(ctx context.Context, projectName string, options api
 				return container.Ports[i].PrivatePort < container.Ports[j].PrivatePort
 			})
 			for _, p := range container.Ports {
-				var url string
-				if p.PublicPort != 0 {
-					url = fmt.Sprintf("%s:%d", p.IP, p.PublicPort)
-				}
 				publishers = append(publishers, api.PortPublisher{
-					URL:           url,
+					URL:           p.IP,
 					TargetPort:    int(p.PrivatePort),
 					PublishedPort: int(p.PublicPort),
 					Protocol:      p.Type,

+ 1 - 1
pkg/compose/ps_test.go

@@ -54,7 +54,7 @@ func TestPs(t *testing.T) {
 
 	expected := []compose.ContainerSummary{
 		{ID: "123", Name: "123", Project: strings.ToLower(testProject), Service: "service1", State: "running", Health: "healthy", Publishers: nil},
-		{ID: "456", Name: "456", Project: strings.ToLower(testProject), Service: "service1", State: "running", Health: "", Publishers: []compose.PortPublisher{{URL: "localhost:80", TargetPort: 90,
+		{ID: "456", Name: "456", Project: strings.ToLower(testProject), Service: "service1", State: "running", Health: "", Publishers: []compose.PortPublisher{{URL: "localhost", TargetPort: 90,
 			PublishedPort: 80}}},
 		{ID: "789", Name: "789", Project: strings.ToLower(testProject), Service: "service2", State: "exited", Health: "", ExitCode: 130, Publishers: nil},
 	}