package libpod import ( "fmt" "net" "reflect" "testing" "github.com/stretchr/testify/assert" "github.com/containers/common/libnetwork/types" "github.com/containers/podman/v4/libpod/define" ) func Test_ocicniPortsToNetTypesPorts(t *testing.T) { tests := []struct { name string arg []types.OCICNIPortMapping want []types.PortMapping }{ { name: "no ports", arg: nil, want: nil, }, { name: "empty ports", arg: []types.OCICNIPortMapping{}, want: nil, }, { name: "single port", arg: []types.OCICNIPortMapping{ { HostPort: 8080, ContainerPort: 80, Protocol: "tcp", }, }, want: []types.PortMapping{ { HostPort: 8080, ContainerPort: 80, Protocol: "tcp", Range: 1, }, }, }, { name: "two separate ports", arg: []types.OCICNIPortMapping{ { HostPort: 8080, ContainerPort: 80, Protocol: "tcp", }, { HostPort: 9000, ContainerPort: 90, Protocol: "tcp", }, }, want: []types.PortMapping{ { HostPort: 8080, ContainerPort: 80, Protocol: "tcp", Range: 1, }, { HostPort: 9000, ContainerPort: 90, Protocol: "tcp", Range: 1, }, }, }, { name: "two ports joined", arg: []types.OCICNIPortMapping{ { HostPort: 8080, ContainerPort: 80, Protocol: "tcp", }, { HostPort: 8081, ContainerPort: 81, Protocol: "tcp", }, }, want: []types.PortMapping{ { HostPort: 8080, ContainerPort: 80, Protocol: "tcp", Range: 2, }, }, }, { name: "three ports with different container port are not joined", arg: []types.OCICNIPortMapping{ { HostPort: 8080, ContainerPort: 80, Protocol: "tcp", }, { HostPort: 8081, ContainerPort: 79, Protocol: "tcp", }, { HostPort: 8082, ContainerPort: 82, Protocol: "tcp", }, }, want: []types.PortMapping{ { HostPort: 8080, ContainerPort: 80, Protocol: "tcp", Range: 1, }, { HostPort: 8081, ContainerPort: 79, Protocol: "tcp", Range: 1, }, { HostPort: 8082, ContainerPort: 82, Protocol: "tcp", Range: 1, }, }, }, { name: "three ports joined (not sorted)", arg: []types.OCICNIPortMapping{ { HostPort: 8081, ContainerPort: 81, Protocol: "tcp", }, { HostPort: 8080, ContainerPort: 80, Protocol: "tcp", }, { HostPort: 8082, ContainerPort: 82, Protocol: "tcp", }, }, want: []types.PortMapping{ { HostPort: 8080, ContainerPort: 80, Protocol: "tcp", Range: 3, }, }, }, { name: "different protocols ports are not joined", arg: []types.OCICNIPortMapping{ { HostPort: 8080, ContainerPort: 80, Protocol: "tcp", }, { HostPort: 8081, ContainerPort: 81, Protocol: "udp", }, }, want: []types.PortMapping{ { HostPort: 8080, ContainerPort: 80, Protocol: "tcp", Range: 1, }, { HostPort: 8081, ContainerPort: 81, Protocol: "udp", Range: 1, }, }, }, { name: "different host ip ports are not joined", arg: []types.OCICNIPortMapping{ { HostPort: 8080, ContainerPort: 80, Protocol: "tcp", HostIP: "192.168.1.1", }, { HostPort: 8081, ContainerPort: 81, Protocol: "tcp", HostIP: "192.168.1.2", }, }, want: []types.PortMapping{ { HostPort: 8080, ContainerPort: 80, Protocol: "tcp", Range: 1, HostIP: "192.168.1.1", }, { HostPort: 8081, ContainerPort: 81, Protocol: "tcp", Range: 1, HostIP: "192.168.1.2", }, }, }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { result := ocicniPortsToNetTypesPorts(tt.arg) assert.Equal(t, tt.want, result, "ports do not match") }) } } func Test_resultToBasicNetworkConfig(t *testing.T) { testCases := []struct { description string expectError bool inputResult types.StatusBlock expectedNetworkConfig define.InspectBasicNetworkConfig }{ { description: "single secondary IPv4 address is shown as define.Address", inputResult: types.StatusBlock{ Interfaces: map[string]types.NetInterface{ "eth1": { Subnets: []types.NetAddress{ { Gateway: net.ParseIP("172.26.0.1"), IPNet: types.IPNet{ IPNet: net.IPNet{ IP: net.ParseIP("172.26.0.2"), Mask: net.CIDRMask(20, 32), }, }, }, { IPNet: types.IPNet{ IPNet: net.IPNet{ IP: net.ParseIP("172.26.0.3"), Mask: net.CIDRMask(10, 32), }, }, }, }, }, }, }, expectedNetworkConfig: define.InspectBasicNetworkConfig{ IPAddress: "172.26.0.2", IPPrefixLen: 20, Gateway: "172.26.0.1", SecondaryIPAddresses: []define.Address{ { Addr: "172.26.0.3", PrefixLength: 10, }, }, }, }, { description: "multiple secondary IPv4 addresses are shown as define.Address", inputResult: types.StatusBlock{ Interfaces: map[string]types.NetInterface{ "eth1": { Subnets: []types.NetAddress{ { Gateway: net.ParseIP("172.26.0.1"), IPNet: types.IPNet{ IPNet: net.IPNet{ IP: net.ParseIP("172.26.0.2"), Mask: net.CIDRMask(20, 32), }, }, }, { IPNet: types.IPNet{ IPNet: net.IPNet{ IP: net.ParseIP("172.26.0.3"), Mask: net.CIDRMask(10, 32), }, }, }, { IPNet: types.IPNet{ IPNet: net.IPNet{ IP: net.ParseIP("172.26.0.4"), Mask: net.CIDRMask(24, 32), }, }, }, }, }, }, }, expectedNetworkConfig: define.InspectBasicNetworkConfig{ IPAddress: "172.26.0.2", IPPrefixLen: 20, Gateway: "172.26.0.1", SecondaryIPAddresses: []define.Address{ { Addr: "172.26.0.3", PrefixLength: 10, }, { Addr: "172.26.0.4", PrefixLength: 24, }, }, }, }, { description: "single secondary IPv6 address is shown as define.Address", inputResult: types.StatusBlock{ Interfaces: map[string]types.NetInterface{ "eth1": { Subnets: []types.NetAddress{ { Gateway: net.ParseIP("ff02::fb"), IPNet: types.IPNet{ IPNet: net.IPNet{ IP: net.ParseIP("ff02::fc"), Mask: net.CIDRMask(20, 128), }, }, }, { IPNet: types.IPNet{ IPNet: net.IPNet{ IP: net.ParseIP("ff02::fd"), Mask: net.CIDRMask(10, 128), }, }, }, }, }, }, }, expectedNetworkConfig: define.InspectBasicNetworkConfig{ GlobalIPv6Address: "ff02::fc", GlobalIPv6PrefixLen: 20, IPv6Gateway: "ff02::fb", SecondaryIPv6Addresses: []define.Address{ { Addr: "ff02::fd", PrefixLength: 10, }, }, }, }, { description: "multiple secondary IPv6 addresses are shown as define.Address", inputResult: types.StatusBlock{ Interfaces: map[string]types.NetInterface{ "eth1": { Subnets: []types.NetAddress{ { Gateway: net.ParseIP("ff02::fb"), IPNet: types.IPNet{ IPNet: net.IPNet{ IP: net.ParseIP("ff02::fc"), Mask: net.CIDRMask(20, 128), }, }, }, { IPNet: types.IPNet{ IPNet: net.IPNet{ IP: net.ParseIP("ff02::fd"), Mask: net.CIDRMask(10, 128), }, }, }, { IPNet: types.IPNet{ IPNet: net.IPNet{ IP: net.ParseIP("ff02::fe"), Mask: net.CIDRMask(24, 128), }, }, }, }, }, }, }, expectedNetworkConfig: define.InspectBasicNetworkConfig{ GlobalIPv6Address: "ff02::fc", GlobalIPv6PrefixLen: 20, IPv6Gateway: "ff02::fb", SecondaryIPv6Addresses: []define.Address{ { Addr: "ff02::fd", PrefixLength: 10, }, { Addr: "ff02::fe", PrefixLength: 24, }, }, }, }, } for _, tcl := range testCases { tc := tcl t.Run(tc.description, func(t *testing.T) { t.Parallel() actualNetworkConfig, err := resultToBasicNetworkConfig(tc.inputResult) if tc.expectError && err == nil { t.Fatalf("Expected error didn't happen") } if !tc.expectError && err != nil { t.Fatalf("Unexpected error happened: %v", err) } if !reflect.DeepEqual(tc.expectedNetworkConfig, actualNetworkConfig) { t.Fatalf( "Expected networkConfig %+v didn't match actual value %+v", tc.expectedNetworkConfig, actualNetworkConfig) } }) } } func benchmarkOCICNIPortsToNetTypesPorts(b *testing.B, ports []types.OCICNIPortMapping) { for n := 0; n < b.N; n++ { ocicniPortsToNetTypesPorts(ports) } } func Benchmark_ocicniPortsToNetTypesPortsNoPorts(b *testing.B) { benchmarkOCICNIPortsToNetTypesPorts(b, nil) } func Benchmark_ocicniPortsToNetTypesPorts1(b *testing.B) { benchmarkOCICNIPortsToNetTypesPorts(b, []types.OCICNIPortMapping{ { HostPort: 8080, ContainerPort: 80, Protocol: "tcp", }, }) } func Benchmark_ocicniPortsToNetTypesPorts10(b *testing.B) { ports := make([]types.OCICNIPortMapping, 0, 10) for i := int32(8080); i < 8090; i++ { ports = append(ports, types.OCICNIPortMapping{ HostPort: i, ContainerPort: i, Protocol: "tcp", }) } b.ResetTimer() benchmarkOCICNIPortsToNetTypesPorts(b, ports) } func Benchmark_ocicniPortsToNetTypesPorts100(b *testing.B) { ports := make([]types.OCICNIPortMapping, 0, 100) for i := int32(8080); i < 8180; i++ { ports = append(ports, types.OCICNIPortMapping{ HostPort: i, ContainerPort: i, Protocol: "tcp", }) } b.ResetTimer() benchmarkOCICNIPortsToNetTypesPorts(b, ports) } func Benchmark_ocicniPortsToNetTypesPorts1k(b *testing.B) { ports := make([]types.OCICNIPortMapping, 0, 1000) for i := int32(8080); i < 9080; i++ { ports = append(ports, types.OCICNIPortMapping{ HostPort: i, ContainerPort: i, Protocol: "tcp", }) } b.ResetTimer() benchmarkOCICNIPortsToNetTypesPorts(b, ports) } func Benchmark_ocicniPortsToNetTypesPorts10k(b *testing.B) { ports := make([]types.OCICNIPortMapping, 0, 30000) for i := int32(8080); i < 18080; i++ { ports = append(ports, types.OCICNIPortMapping{ HostPort: i, ContainerPort: i, Protocol: "tcp", }) } b.ResetTimer() benchmarkOCICNIPortsToNetTypesPorts(b, ports) } func Benchmark_ocicniPortsToNetTypesPorts1m(b *testing.B) { ports := make([]types.OCICNIPortMapping, 0, 1000000) for j := 0; j < 20; j++ { for i := int32(1); i <= 50000; i++ { ports = append(ports, types.OCICNIPortMapping{ HostPort: i, ContainerPort: i, Protocol: "tcp", HostIP: fmt.Sprintf("192.168.1.%d", j), }) } } b.ResetTimer() benchmarkOCICNIPortsToNetTypesPorts(b, ports) }