Skip to content

introduce String method on PortMapping #111

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 24, 2025

Conversation

ndeloof
Copy link
Contributor

@ndeloof ndeloof commented Jan 16, 2024

This introduce PortMapping.String() for (some) symmetry with ParsePortSpec

nat/nat.go Outdated
@@ -150,6 +150,10 @@ type PortMapping struct {
Binding PortBinding
}

func (p *PortMapping) String() string {
return fmt.Sprintf("%s:%s:%s", p.Binding.HostIP, p.Binding.HostPort, p.Port)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use net.JoinHostPort() here, to account for IPv6 addresses; https://pkg.go.dev/net#JoinHostPort

e.g.;

Suggested change
return fmt.Sprintf("%s:%s:%s", p.Binding.HostIP, p.Binding.HostPort, p.Port)
return net.JoinHostPort(p.Binding.HostIP, p.Binding.HostPort+":"+p.Port.Port())

But I need to have a closer look, as there's many possible formats (ports mapped here can also have the protocol (e.g. 80/tcp), which may have to be taken into account here.

And of course we also have PortMap (which is a list of mapped ports) 😞

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I guess HostPort doesn't have the scheme, so maybe that part is not an issue

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we know if HostIP defaults to 0.0.0.0 (or :: IPv6)? 🤔

@ndeloof ndeloof force-pushed the PortMapping_String branch from 008e7cc to a314a9d Compare January 22, 2024 11:27
@thaJeztah thaJeztah force-pushed the PortMapping_String branch 2 times, most recently from 7f68fd5 to a2b6795 Compare April 24, 2025 09:45
@thaJeztah
Copy link
Member

I rebased, and pushed some changes;

  • updated the test to use a test-table to test various permutations
  • swapped the fmt.Sprintf to string-concat the values (ISTR some of these may be run in a loop, so probably doesn't hurt to slightly optimize)
diff --git a/nat/nat.go b/nat/nat.go
index 5b53382..6c80825 100644
--- a/nat/nat.go
+++ b/nat/nat.go
@@ -151,7 +151,7 @@ type PortMapping struct {
 }
 
 func (p *PortMapping) String() string {
-	return net.JoinHostPort(p.Binding.HostIP, fmt.Sprintf("%s:%s", p.Binding.HostPort, p.Port))
+	return net.JoinHostPort(p.Binding.HostIP, p.Binding.HostPort+":"+string(p.Port))
 }
 
 func splitParts(rawport string) (string, string, string) {
diff --git a/nat/nat_test.go b/nat/nat_test.go
index 3497643..571bf9e 100644
--- a/nat/nat_test.go
+++ b/nat/nat_test.go
@@ -699,14 +699,50 @@ func TestParseNetworkOptsSctp(t *testing.T) {
 }
 
 func TestStringer(t *testing.T) {
-	spec := "192.168.1.100:8080:6000/udp"
-	mappings, err := ParsePortSpec(spec)
-	if err != nil {
-		t.Fatal(err)
+	tests := []struct {
+		doc      string
+		in       string
+		expected string
+	}{
+		{
+			doc:      "no host mapping",
+			in:       ":8080:6000/tcp",
+			expected: ":8080:6000/tcp",
+		},
+		{
+			doc:      "no proto",
+			in:       "192.168.1.100:8080:6000",
+			expected: "192.168.1.100:8080:6000/tcp",
+		},
+		{
+			doc:      "ipv4 mapping",
+			in:       "192.168.1.100:8080:6000/udp",
+			expected: "192.168.1.100:8080:6000/udp",
+		},
+		{
+			doc:      "ipv6 mapping",
+			in:       "[::1]:8080:6000/udp",
+			expected: "[::1]:8080:6000/udp",
+		},
+		{
+			doc:      "ipv6 legacy mapping",
+			in:       "::1:8080:6000/udp",
+			expected: "[::1]:8080:6000/udp",
+		},
 	}
-	for _, m := range mappings {
-		if m.String() != spec {
-			t.Fail()
-		}
+	for _, tc := range tests {
+		t.Run(tc.doc, func(t *testing.T) {
+			mappings, err := ParsePortSpec(tc.in)
+			if err != nil {
+				t.Fatal(err)
+			}
+			if len(mappings) != 1 {
+				// All tests produce a single mapping
+				t.Fatalf("Expected 1 got %d", len(mappings))
+			}
+			if actual := mappings[0].String(); actual != tc.expected {
+				t.Errorf("Expected %s got %s", tc.expected, actual)
+			}
+		})
 	}
 }
-- 
2.44.0

@@ -697,3 +697,52 @@ func TestParseNetworkOptsSctp(t *testing.T) {
}
}
}

func TestStringer(t *testing.T) {
Copy link
Contributor

@robmry robmry Apr 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe worth adding an in with a missing port, just to check it doesn't come out as a zero or something.

For maximum evil, perhaps ::::80 -> [::]::80/tcp?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK; added some evil permutations 👍

Signed-off-by: Nicolas De Loof <[email protected]>
Signed-off-by: Sebastiaan van Stijn <[email protected]>
@thaJeztah thaJeztah force-pushed the PortMapping_String branch from a2b6795 to d630407 Compare April 24, 2025 10:45
@thaJeztah thaJeztah merged commit 6c377ea into docker:master Apr 24, 2025
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants