Skip to content

Commit 53bfa68

Browse files
committed
[service] fix: use ipv6-aware host and port concatenation function
1 parent 9c3481b commit 53bfa68

File tree

4 files changed

+96
-14
lines changed

4 files changed

+96
-14
lines changed

internal/testutil/testutil.go

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package testutil // import "go.opentelemetry.io/collector/internal/testutil"
55

66
import (
7+
"fmt"
78
"net"
89
"os/exec"
910
"runtime"
@@ -31,15 +32,44 @@ func GetAvailableLocalAddress(t testing.TB) string {
3132
// which do not show up under the "netstat -ano" but can only be found by
3233
// "netsh interface ipv4 show excludedportrange protocol=tcp". We'll use []exclusions to hold those ranges and
3334
// retry if the port returned by GetAvailableLocalAddress falls in one of those them.
35+
network := "tcp4"
3436
var exclusions []portpair
3537
portFound := false
3638
if runtime.GOOS == "windows" {
37-
exclusions = getExclusionsList(t)
39+
exclusions = getExclusionsList(network, t)
3840
}
3941

4042
var endpoint string
4143
for !portFound {
42-
endpoint = findAvailableAddress(t)
44+
endpoint = findAvailableAddress(network, t)
45+
_, port, err := net.SplitHostPort(endpoint)
46+
require.NoError(t, err)
47+
portFound = true
48+
if runtime.GOOS == "windows" {
49+
for _, pair := range exclusions {
50+
if port >= pair.first && port <= pair.last {
51+
portFound = false
52+
break
53+
}
54+
}
55+
}
56+
}
57+
58+
return endpoint
59+
}
60+
61+
// GetAvailableLocalIPv6Address is IPv6 version of GetAvailableLocalAddress.
62+
func GetAvailableLocalIPv6Address(t testing.TB) string {
63+
network := "tcp6"
64+
var exclusions []portpair
65+
portFound := false
66+
if runtime.GOOS == "windows" {
67+
exclusions = getExclusionsList(network, t)
68+
}
69+
70+
var endpoint string
71+
for !portFound {
72+
endpoint = findAvailableAddress(network, t)
4373
_, port, err := net.SplitHostPort(endpoint)
4474
require.NoError(t, err)
4575
portFound = true
@@ -72,8 +102,17 @@ func GetAvailableLocalAddressPrometheus(t testing.TB) *config.Prometheus {
72102
}
73103
}
74104

75-
func findAvailableAddress(t testing.TB) string {
76-
ln, err := net.Listen("tcp", "localhost:0")
105+
func findAvailableAddress(network string, t testing.TB) string {
106+
var host string
107+
switch network {
108+
case "tcp", "tcp4":
109+
host = "localhost"
110+
case "tcp6":
111+
host = "[::1]"
112+
}
113+
require.NotZero(t, host, "network must be either of tcp, tcp4 or tcp6")
114+
115+
ln, err := net.Listen("tcp", fmt.Sprintf("%s:0", host))
77116
require.NoError(t, err, "Failed to get a free local port")
78117
// There is a possible race if something else takes this same port before
79118
// the test uses it, however, that is unlikely in practice.
@@ -84,8 +123,16 @@ func findAvailableAddress(t testing.TB) string {
84123
}
85124

86125
// Get excluded ports on Windows from the command: netsh interface ipv4 show excludedportrange protocol=tcp
87-
func getExclusionsList(t testing.TB) []portpair {
88-
cmdTCP := exec.Command("netsh", "interface", "ipv4", "show", "excludedportrange", "protocol=tcp")
126+
func getExclusionsList(network string, t testing.TB) []portpair {
127+
var cmdTCP *exec.Cmd
128+
switch network {
129+
case "tcp", "tcp4":
130+
cmdTCP = exec.Command("netsh", "interface", "ipv4", "show", "excludedportrange", "protocol=tcp")
131+
case "tcp6":
132+
cmdTCP = exec.Command("netsh", "interface", "ipv6", "show", "excludedportrange", "protocol=tcp")
133+
}
134+
require.NotZero(t, cmdTCP, "network must be either of tcp, tcp4 or tcp6")
135+
89136
outputTCP, errTCP := cmdTCP.CombinedOutput()
90137
require.NoError(t, errTCP)
91138
exclusions := createExclusionsList(string(outputTCP), t)

internal/testutil/testutil_test.go

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,32 @@ func TestGetAvailableLocalAddress(t *testing.T) {
2929
require.Nil(t, ln1)
3030
}
3131

32+
func TestGetAvailableLocalIpv6Address(t *testing.T) {
33+
endpoint := GetAvailableLocalIPv6Address(t)
34+
35+
// Endpoint should be free.
36+
ln0, err := net.Listen("tcp", endpoint)
37+
require.NoError(t, err)
38+
require.NotNil(t, ln0)
39+
t.Cleanup(func() {
40+
assert.NoError(t, ln0.Close())
41+
})
42+
43+
// Ensure that the endpoint wasn't something like ":0" by checking that a
44+
// second listener will fail.
45+
ln1, err := net.Listen("tcp", endpoint)
46+
require.Error(t, err)
47+
require.Nil(t, ln1)
48+
}
49+
3250
func TestCreateExclusionsList(t *testing.T) {
3351
// Test two examples of typical output from "netsh interface ipv4 show excludedportrange protocol=tcp"
3452
emptyExclusionsText := `
3553
3654
Protocol tcp Port Exclusion Ranges
3755
38-
Start Port End Port
39-
---------- --------
56+
Start Port End Port
57+
---------- --------
4058
4159
* - Administered port exclusions.`
4260

service/internal/proctelemetry/config.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"encoding/json"
99
"errors"
1010
"fmt"
11+
"net"
1112
"net/http"
1213
"net/url"
1314
"os"
@@ -175,7 +176,7 @@ func initPrometheusExporter(prometheusConfig *config.Prometheus, asyncErrorChann
175176
return nil, nil, fmt.Errorf("error creating otel prometheus exporter: %w", err)
176177
}
177178

178-
return exporter, InitPrometheusServer(promRegistry, fmt.Sprintf("%s:%d", *prometheusConfig.Host, *prometheusConfig.Port), asyncErrorChannel), nil
179+
return exporter, InitPrometheusServer(promRegistry, net.JoinHostPort(*prometheusConfig.Host, fmt.Sprintf("%d", *prometheusConfig.Port)), asyncErrorChannel), nil
179180
}
180181

181182
func initPullExporter(exporter config.MetricExporter, asyncErrorChannel chan error) (sdkmetric.Reader, *http.Server, error) {

service/service_test.go

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"bufio"
88
"context"
99
"errors"
10+
"fmt"
1011
"net/http"
1112
"strings"
1213
"sync"
@@ -253,13 +254,16 @@ func TestServiceTelemetryCleanupOnError(t *testing.T) {
253254

254255
func TestServiceTelemetry(t *testing.T) {
255256
for _, tc := range ownMetricsTestCases() {
256-
t.Run(tc.name, func(t *testing.T) {
257-
testCollectorStartHelper(t, tc)
257+
t.Run(fmt.Sprintf("ipv4_%s", tc.name), func(t *testing.T) {
258+
testCollectorStartHelper(t, tc, "tcp4")
259+
})
260+
t.Run(fmt.Sprintf("ipv6_%s", tc.name), func(t *testing.T) {
261+
testCollectorStartHelper(t, tc, "tcp6")
258262
})
259263
}
260264
}
261265

262-
func testCollectorStartHelper(t *testing.T, tc ownMetricsTestCase) {
266+
func testCollectorStartHelper(t *testing.T, tc ownMetricsTestCase, network string) {
263267
var once sync.Once
264268
loggingHookCalled := false
265269
hook := func(zapcore.Entry) error {
@@ -269,8 +273,20 @@ func testCollectorStartHelper(t *testing.T, tc ownMetricsTestCase) {
269273
return nil
270274
}
271275

272-
metricsAddr := testutil.GetAvailableLocalAddress(t)
273-
zpagesAddr := testutil.GetAvailableLocalAddress(t)
276+
var (
277+
metricsAddr string
278+
zpagesAddr string
279+
)
280+
switch network {
281+
case "tcp", "tcp4":
282+
metricsAddr = testutil.GetAvailableLocalIPv6Address(t)
283+
zpagesAddr = testutil.GetAvailableLocalIPv6Address(t)
284+
case "tcp6":
285+
metricsAddr = testutil.GetAvailableLocalIPv6Address(t)
286+
zpagesAddr = testutil.GetAvailableLocalIPv6Address(t)
287+
}
288+
require.NotZero(t, metricsAddr, "network must be either of tcp, tcp4 or tcp6")
289+
require.NotZero(t, zpagesAddr, "network must be either of tcp, tcp4 or tcp6")
274290

275291
set := newNopSettings()
276292
set.BuildInfo = component.BuildInfo{Version: "test version", Command: otelCommand}

0 commit comments

Comments
 (0)