blob: c6d50319ed6e288c8f47402767bd86f1a8d46495 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net
import (
"fmt"
"net"
)
import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
import (
"github.com/apache/dubbo-kubernetes/pkg/test"
)
var _ = Describe("ReserveTCPAddr()", func() {
It("should successfully reserve a free TCP address (ip + port)", func() {
// given
loopback := "127.0.0.1"
// setup
freePort, err := test.FindFreePort(loopback)
Expect(err).ToNot(HaveOccurred())
// and
address := fmt.Sprintf("%s:%d", loopback, freePort)
// when
actualPort, err := ReserveTCPAddr(address)
// then
Expect(err).ToNot(HaveOccurred())
// and
Expect(actualPort).To(Equal(freePort))
})
It("should fail to reserve a TCP address already in use (ip + port)", func() {
// given
loopback := "127.0.0.1"
// setup
freePort, err := test.FindFreePort(loopback)
Expect(err).ToNot(HaveOccurred())
// and
address := fmt.Sprintf("%s:%d", loopback, freePort)
By("simulating another Envoy instance that already uses this port")
// when
l, err := net.Listen("tcp", address)
// then
Expect(err).ToNot(HaveOccurred())
// and
defer l.Close()
// when
actualPort, err := ReserveTCPAddr(address)
// then
Expect(err.Error()).To(ContainSubstring(`bind: address already in use`))
// and
Expect(actualPort).To(Equal(uint32(0)))
})
})
var _ = Describe("PickTCPPort()", func() {
It("should be able to pick the 1st port in the range", func() {
// given
loopback := "127.0.0.1"
// setup
freePort, err := test.FindFreePort(loopback)
Expect(err).ToNot(HaveOccurred())
// when
actualPort, err := PickTCPPort(loopback, freePort, freePort)
// then
Expect(err).ToNot(HaveOccurred())
// and
Expect(actualPort).To(Equal(freePort))
})
Describe("should be able to pick the Nth port in the range", func() {
// given
loopback := "127.0.0.1"
findFreePortRange := func(n uint32) (uint32, uint32) {
var lowestPort uint32
var highestPort uint32
Expect(n).To(BeNumerically(">", 0))
attempts:
for a := 0; a < 65535; a++ {
// first port in a range
freePort, err := test.FindFreePort(loopback)
Expect(err).ToNot(HaveOccurred())
// next n-1 ports in that range
for i := uint32(1); i < n; i++ {
address := fmt.Sprintf("%s:%d", loopback, freePort+i)
if _, err := ReserveTCPAddr(address); err != nil {
continue attempts
}
}
return freePort, freePort + n - 1
}
Fail(fmt.Sprintf(`unable to find "%d" free ports in a row`, n))
return lowestPort, highestPort
}
type testCase struct {
n uint32
}
testSet := func(n uint32) []TableEntry {
cases := make([]TableEntry, 0, n)
for i := uint32(2); i <= n; i++ {
cases = append(cases, Entry(fmt.Sprintf("%d", i), testCase{n: i}))
}
return cases
}
DescribeTable("should be able to pick the Nth port in the range",
func(given testCase) {
By("finding N consecutive free ports in a row")
lowestPort, highestPort := findFreePortRange(given.n)
By("simulating another Envoy instances using first N-1 ports")
for i := uint32(0); i < given.n-1; i++ {
// given
address := fmt.Sprintf("%s:%d", loopback, lowestPort+i)
// when
l, err := net.Listen("tcp", address)
// then
Expect(err).ToNot(HaveOccurred())
// and
defer l.Close()
}
// when
actualPort, err := PickTCPPort(loopback, lowestPort, highestPort)
// then
Expect(err).ToNot(HaveOccurred())
// and
Expect(actualPort).To(Equal(highestPort))
},
testSet(10),
)
})
It("should fail to pick a free port when all ports in the range are in use", func() {
// given
loopback := "127.0.0.1"
// setup
freePort, err := test.FindFreePort(loopback)
Expect(err).ToNot(HaveOccurred())
// and
address := fmt.Sprintf("%s:%d", loopback, freePort)
By("simulating another Envoy instance that already uses this port")
// when
l, err := net.Listen("tcp", address)
// then
Expect(err).ToNot(HaveOccurred())
// and
defer l.Close()
// when
actualPort, err := PickTCPPort(loopback, freePort, freePort)
// then
Expect(err.Error()).To(ContainSubstring("unable to find port in range"))
// and
Expect(actualPort).To(Equal(uint32(0)))
})
It("should be able to pick a random port", func() {
// given
loopback := "127.0.0.1"
// when
actualPort, err := PickTCPPort(loopback, 0, 0)
// then
Expect(err).ToNot(HaveOccurred())
// and
Expect(actualPort).ToNot(Equal(uint32(0)))
})
It("should re-order port range bounds if necessary", func() {
// given
loopback := "127.0.0.1"
// when
actualPort, err := PickTCPPort(loopback, 1, 0)
// then
Expect(err).ToNot(HaveOccurred())
// and
Expect(actualPort).ToNot(Equal(uint32(0)))
})
}, Ordered)