diff --git a/pkg/scheduler/algorithm/predicates/utils.go b/pkg/scheduler/algorithm/predicates/utils.go index 2b9c94f9dc..de2826a4df 100644 --- a/pkg/scheduler/algorithm/predicates/utils.go +++ b/pkg/scheduler/algorithm/predicates/utils.go @@ -19,7 +19,7 @@ package predicates import ( "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/labels" - schedutil "k8s.io/kubernetes/pkg/scheduler/util" + schedulercache "k8s.io/kubernetes/pkg/scheduler/cache" ) // FindLabelsInSet gets as many key/value pairs as possible out of a label set. @@ -68,7 +68,7 @@ func CreateSelectorFromLabels(aL map[string]string) labels.Selector { // portsConflict check whether existingPorts and wantPorts conflict with each other // return true if we have a conflict -func portsConflict(existingPorts schedutil.HostPortInfo, wantPorts []*v1.ContainerPort) bool { +func portsConflict(existingPorts schedulercache.HostPortInfo, wantPorts []*v1.ContainerPort) bool { for _, cp := range wantPorts { if existingPorts.CheckConflict(cp.HostIP, string(cp.Protocol), cp.HostPort) { return true diff --git a/pkg/scheduler/cache/BUILD b/pkg/scheduler/cache/BUILD index 84d3e6ca9b..861cfbdbbe 100644 --- a/pkg/scheduler/cache/BUILD +++ b/pkg/scheduler/cache/BUILD @@ -3,6 +3,7 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "go_default_library", srcs = [ + "host_ports.go", "node_info.go", "util.go", ], @@ -11,7 +12,6 @@ go_library( deps = [ "//pkg/apis/core/v1/helper:go_default_library", "//pkg/scheduler/algorithm/priorities/util:go_default_library", - "//pkg/scheduler/util:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", @@ -22,12 +22,12 @@ go_library( go_test( name = "go_default_test", srcs = [ + "host_ports_test.go", "node_info_test.go", "util_test.go", ], embed = [":go_default_library"], deps = [ - "//pkg/scheduler/util:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", diff --git a/pkg/scheduler/cache/host_ports.go b/pkg/scheduler/cache/host_ports.go new file mode 100644 index 0000000000..e96c6be374 --- /dev/null +++ b/pkg/scheduler/cache/host_ports.go @@ -0,0 +1,135 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed 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 cache + +import ( + "k8s.io/api/core/v1" +) + +// DefaultBindAllHostIP defines the default ip address used to bind to all host. +const DefaultBindAllHostIP = "0.0.0.0" + +// ProtocolPort represents a protocol port pair, e.g. tcp:80. +type ProtocolPort struct { + Protocol string + Port int32 +} + +// NewProtocolPort creates a ProtocolPort instance. +func NewProtocolPort(protocol string, port int32) *ProtocolPort { + pp := &ProtocolPort{ + Protocol: protocol, + Port: port, + } + + if len(pp.Protocol) == 0 { + pp.Protocol = string(v1.ProtocolTCP) + } + + return pp +} + +// HostPortInfo stores mapping from ip to a set of ProtocolPort +type HostPortInfo map[string]map[ProtocolPort]struct{} + +// Add adds (ip, protocol, port) to HostPortInfo +func (h HostPortInfo) Add(ip, protocol string, port int32) { + if port <= 0 { + return + } + + h.sanitize(&ip, &protocol) + + pp := NewProtocolPort(protocol, port) + if _, ok := h[ip]; !ok { + h[ip] = map[ProtocolPort]struct{}{ + *pp: {}, + } + return + } + + h[ip][*pp] = struct{}{} +} + +// Remove removes (ip, protocol, port) from HostPortInfo +func (h HostPortInfo) Remove(ip, protocol string, port int32) { + if port <= 0 { + return + } + + h.sanitize(&ip, &protocol) + + pp := NewProtocolPort(protocol, port) + if m, ok := h[ip]; ok { + delete(m, *pp) + if len(h[ip]) == 0 { + delete(h, ip) + } + } +} + +// Len returns the total number of (ip, protocol, port) tuple in HostPortInfo +func (h HostPortInfo) Len() int { + length := 0 + for _, m := range h { + length += len(m) + } + return length +} + +// CheckConflict checks if the input (ip, protocol, port) conflicts with the existing +// ones in HostPortInfo. +func (h HostPortInfo) CheckConflict(ip, protocol string, port int32) bool { + if port <= 0 { + return false + } + + h.sanitize(&ip, &protocol) + + pp := NewProtocolPort(protocol, port) + + // If ip is 0.0.0.0 check all IP's (protocol, port) pair + if ip == DefaultBindAllHostIP { + for _, m := range h { + if _, ok := m[*pp]; ok { + return true + } + } + return false + } + + // If ip isn't 0.0.0.0, only check IP and 0.0.0.0's (protocol, port) pair + for _, key := range []string{DefaultBindAllHostIP, ip} { + if m, ok := h[key]; ok { + if _, ok2 := m[*pp]; ok2 { + return true + } + } + } + + return false +} + +// sanitize the parameters +func (h HostPortInfo) sanitize(ip, protocol *string) { + if len(*ip) == 0 { + *ip = DefaultBindAllHostIP + } + if len(*protocol) == 0 { + *protocol = string(v1.ProtocolTCP) + } +} diff --git a/pkg/scheduler/cache/host_ports_test.go b/pkg/scheduler/cache/host_ports_test.go new file mode 100644 index 0000000000..390d4d1510 --- /dev/null +++ b/pkg/scheduler/cache/host_ports_test.go @@ -0,0 +1,231 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed 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 cache + +import ( + "testing" +) + +type hostPortInfoParam struct { + protocol, ip string + port int32 +} + +func TestHostPortInfo_AddRemove(t *testing.T) { + tests := []struct { + desc string + added []hostPortInfoParam + removed []hostPortInfoParam + length int + }{ + { + desc: "normal add case", + added: []hostPortInfoParam{ + {"TCP", "127.0.0.1", 79}, + {"UDP", "127.0.0.1", 80}, + {"TCP", "127.0.0.1", 81}, + {"TCP", "127.0.0.1", 82}, + // this might not make sense in real case, but the struct doesn't forbid it. + {"TCP", "0.0.0.0", 79}, + {"UDP", "0.0.0.0", 80}, + {"TCP", "0.0.0.0", 81}, + {"TCP", "0.0.0.0", 82}, + {"TCP", "0.0.0.0", 0}, + {"TCP", "0.0.0.0", -1}, + }, + length: 8, + }, + { + desc: "empty ip and protocol add should work", + added: []hostPortInfoParam{ + {"", "127.0.0.1", 79}, + {"UDP", "127.0.0.1", 80}, + {"", "127.0.0.1", 81}, + {"", "127.0.0.1", 82}, + {"", "", 79}, + {"UDP", "", 80}, + {"", "", 81}, + {"", "", 82}, + {"", "", 0}, + {"", "", -1}, + }, + length: 8, + }, + { + desc: "normal remove case", + added: []hostPortInfoParam{ + {"TCP", "127.0.0.1", 79}, + {"UDP", "127.0.0.1", 80}, + {"TCP", "127.0.0.1", 81}, + {"TCP", "127.0.0.1", 82}, + {"TCP", "0.0.0.0", 79}, + {"UDP", "0.0.0.0", 80}, + {"TCP", "0.0.0.0", 81}, + {"TCP", "0.0.0.0", 82}, + }, + removed: []hostPortInfoParam{ + {"TCP", "127.0.0.1", 79}, + {"UDP", "127.0.0.1", 80}, + {"TCP", "127.0.0.1", 81}, + {"TCP", "127.0.0.1", 82}, + {"TCP", "0.0.0.0", 79}, + {"UDP", "0.0.0.0", 80}, + {"TCP", "0.0.0.0", 81}, + {"TCP", "0.0.0.0", 82}, + }, + length: 0, + }, + { + desc: "empty ip and protocol remove should work", + added: []hostPortInfoParam{ + {"TCP", "127.0.0.1", 79}, + {"UDP", "127.0.0.1", 80}, + {"TCP", "127.0.0.1", 81}, + {"TCP", "127.0.0.1", 82}, + {"TCP", "0.0.0.0", 79}, + {"UDP", "0.0.0.0", 80}, + {"TCP", "0.0.0.0", 81}, + {"TCP", "0.0.0.0", 82}, + }, + removed: []hostPortInfoParam{ + {"", "127.0.0.1", 79}, + {"", "127.0.0.1", 81}, + {"", "127.0.0.1", 82}, + {"UDP", "127.0.0.1", 80}, + {"", "", 79}, + {"", "", 81}, + {"", "", 82}, + {"UDP", "", 80}, + }, + length: 0, + }, + } + + for _, test := range tests { + hp := make(HostPortInfo) + for _, param := range test.added { + hp.Add(param.ip, param.protocol, param.port) + } + for _, param := range test.removed { + hp.Remove(param.ip, param.protocol, param.port) + } + if hp.Len() != test.length { + t.Errorf("%v failed: expect length %d; got %d", test.desc, test.length, hp.Len()) + t.Error(hp) + } + } +} + +func TestHostPortInfo_Check(t *testing.T) { + tests := []struct { + desc string + added []hostPortInfoParam + check hostPortInfoParam + expect bool + }{ + { + desc: "empty check should check 0.0.0.0 and TCP", + added: []hostPortInfoParam{ + {"TCP", "127.0.0.1", 80}, + }, + check: hostPortInfoParam{"", "", 81}, + expect: false, + }, + { + desc: "empty check should check 0.0.0.0 and TCP (conflicted)", + added: []hostPortInfoParam{ + {"TCP", "127.0.0.1", 80}, + }, + check: hostPortInfoParam{"", "", 80}, + expect: true, + }, + { + desc: "empty port check should pass", + added: []hostPortInfoParam{ + {"TCP", "127.0.0.1", 80}, + }, + check: hostPortInfoParam{"", "", 0}, + expect: false, + }, + { + desc: "0.0.0.0 should check all registered IPs", + added: []hostPortInfoParam{ + {"TCP", "127.0.0.1", 80}, + }, + check: hostPortInfoParam{"TCP", "0.0.0.0", 80}, + expect: true, + }, + { + desc: "0.0.0.0 with different protocol should be allowed", + added: []hostPortInfoParam{ + {"UDP", "127.0.0.1", 80}, + }, + check: hostPortInfoParam{"TCP", "0.0.0.0", 80}, + expect: false, + }, + { + desc: "0.0.0.0 with different port should be allowed", + added: []hostPortInfoParam{ + {"TCP", "127.0.0.1", 79}, + {"TCP", "127.0.0.1", 81}, + {"TCP", "127.0.0.1", 82}, + }, + check: hostPortInfoParam{"TCP", "0.0.0.0", 80}, + expect: false, + }, + { + desc: "normal ip should check all registered 0.0.0.0", + added: []hostPortInfoParam{ + {"TCP", "0.0.0.0", 80}, + }, + check: hostPortInfoParam{"TCP", "127.0.0.1", 80}, + expect: true, + }, + { + desc: "normal ip with different port/protocol should be allowed (0.0.0.0)", + added: []hostPortInfoParam{ + {"TCP", "0.0.0.0", 79}, + {"UDP", "0.0.0.0", 80}, + {"TCP", "0.0.0.0", 81}, + {"TCP", "0.0.0.0", 82}, + }, + check: hostPortInfoParam{"TCP", "127.0.0.1", 80}, + expect: false, + }, + { + desc: "normal ip with different port/protocol should be allowed", + added: []hostPortInfoParam{ + {"TCP", "127.0.0.1", 79}, + {"UDP", "127.0.0.1", 80}, + {"TCP", "127.0.0.1", 81}, + {"TCP", "127.0.0.1", 82}, + }, + check: hostPortInfoParam{"TCP", "127.0.0.1", 80}, + expect: false, + }, + } + + for _, test := range tests { + hp := make(HostPortInfo) + for _, param := range test.added { + hp.Add(param.ip, param.protocol, param.port) + } + if hp.CheckConflict(test.check.ip, test.check.protocol, test.check.port) != test.expect { + t.Errorf("%v failed, expected %t; got %t", test.desc, test.expect, !test.expect) + } + } +} diff --git a/pkg/scheduler/cache/node_info.go b/pkg/scheduler/cache/node_info.go index 8bc5923c61..1af4f647b1 100644 --- a/pkg/scheduler/cache/node_info.go +++ b/pkg/scheduler/cache/node_info.go @@ -28,7 +28,6 @@ import ( "k8s.io/apimachinery/pkg/api/resource" v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper" priorityutil "k8s.io/kubernetes/pkg/scheduler/algorithm/priorities/util" - "k8s.io/kubernetes/pkg/scheduler/util" ) var ( @@ -51,7 +50,7 @@ type NodeInfo struct { pods []*v1.Pod podsWithAffinity []*v1.Pod - usedPorts util.HostPortInfo + usedPorts HostPortInfo // Total requested resource of all pods on this node. // It includes assumed pods which scheduler sends binding to apiserver but @@ -269,7 +268,7 @@ func NewNodeInfo(pods ...*v1.Pod) *NodeInfo { allocatableResource: &Resource{}, TransientInfo: NewTransientSchedulerInfo(), generation: nextGeneration(), - usedPorts: make(util.HostPortInfo), + usedPorts: make(HostPortInfo), imageStates: make(map[string]*ImageStateSummary), } for _, pod := range pods { @@ -300,7 +299,7 @@ func (n *NodeInfo) SetPods(pods []*v1.Pod) { } // UsedPorts returns used ports on this node. -func (n *NodeInfo) UsedPorts() util.HostPortInfo { +func (n *NodeInfo) UsedPorts() HostPortInfo { if n == nil { return nil } @@ -308,7 +307,7 @@ func (n *NodeInfo) UsedPorts() util.HostPortInfo { } // SetUsedPorts sets the used ports on this node. -func (n *NodeInfo) SetUsedPorts(newUsedPorts util.HostPortInfo) { +func (n *NodeInfo) SetUsedPorts(newUsedPorts HostPortInfo) { n.usedPorts = newUsedPorts } @@ -443,7 +442,7 @@ func (n *NodeInfo) Clone() *NodeInfo { memoryPressureCondition: n.memoryPressureCondition, diskPressureCondition: n.diskPressureCondition, pidPressureCondition: n.pidPressureCondition, - usedPorts: make(util.HostPortInfo), + usedPorts: make(HostPortInfo), imageStates: n.imageStates, generation: n.generation, } @@ -451,10 +450,10 @@ func (n *NodeInfo) Clone() *NodeInfo { clone.pods = append([]*v1.Pod(nil), n.pods...) } if len(n.usedPorts) > 0 { - // util.HostPortInfo is a map-in-map struct + // HostPortInfo is a map-in-map struct // make sure it's deep copied for ip, portMap := range n.usedPorts { - clone.usedPorts[ip] = make(map[util.ProtocolPort]struct{}) + clone.usedPorts[ip] = make(map[ProtocolPort]struct{}) for protocolPort, v := range portMap { clone.usedPorts[ip][protocolPort] = v } diff --git a/pkg/scheduler/cache/node_info_test.go b/pkg/scheduler/cache/node_info_test.go index e45537e3a2..38c498a3d7 100644 --- a/pkg/scheduler/cache/node_info_test.go +++ b/pkg/scheduler/cache/node_info_test.go @@ -26,7 +26,6 @@ import ( "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" - "k8s.io/kubernetes/pkg/scheduler/util" ) func TestNewResource(t *testing.T) { @@ -303,8 +302,8 @@ func TestNewNodeInfo(t *testing.T) { TransientInfo: NewTransientSchedulerInfo(), allocatableResource: &Resource{}, generation: 2, - usedPorts: util.HostPortInfo{ - "127.0.0.1": map[util.ProtocolPort]struct{}{ + usedPorts: HostPortInfo{ + "127.0.0.1": map[ProtocolPort]struct{}{ {Protocol: "TCP", Port: 80}: {}, {Protocol: "TCP", Port: 8080}: {}, }, @@ -392,8 +391,8 @@ func TestNodeInfoClone(t *testing.T) { TransientInfo: NewTransientSchedulerInfo(), allocatableResource: &Resource{}, generation: 2, - usedPorts: util.HostPortInfo{ - "127.0.0.1": map[util.ProtocolPort]struct{}{ + usedPorts: HostPortInfo{ + "127.0.0.1": map[ProtocolPort]struct{}{ {Protocol: "TCP", Port: 80}: {}, {Protocol: "TCP", Port: 8080}: {}, }, @@ -462,8 +461,8 @@ func TestNodeInfoClone(t *testing.T) { TransientInfo: NewTransientSchedulerInfo(), allocatableResource: &Resource{}, generation: 2, - usedPorts: util.HostPortInfo{ - "127.0.0.1": map[util.ProtocolPort]struct{}{ + usedPorts: HostPortInfo{ + "127.0.0.1": map[ProtocolPort]struct{}{ {Protocol: "TCP", Port: 80}: {}, {Protocol: "TCP", Port: 8080}: {}, }, @@ -621,8 +620,8 @@ func TestNodeInfoAddPod(t *testing.T) { TransientInfo: NewTransientSchedulerInfo(), allocatableResource: &Resource{}, generation: 2, - usedPorts: util.HostPortInfo{ - "127.0.0.1": map[util.ProtocolPort]struct{}{ + usedPorts: HostPortInfo{ + "127.0.0.1": map[ProtocolPort]struct{}{ {Protocol: "TCP", Port: 80}: {}, {Protocol: "TCP", Port: 8080}: {}, }, @@ -740,8 +739,8 @@ func TestNodeInfoRemovePod(t *testing.T) { TransientInfo: NewTransientSchedulerInfo(), allocatableResource: &Resource{}, generation: 2, - usedPorts: util.HostPortInfo{ - "127.0.0.1": map[util.ProtocolPort]struct{}{ + usedPorts: HostPortInfo{ + "127.0.0.1": map[ProtocolPort]struct{}{ {Protocol: "TCP", Port: 80}: {}, {Protocol: "TCP", Port: 8080}: {}, }, @@ -857,8 +856,8 @@ func TestNodeInfoRemovePod(t *testing.T) { TransientInfo: NewTransientSchedulerInfo(), allocatableResource: &Resource{}, generation: 3, - usedPorts: util.HostPortInfo{ - "127.0.0.1": map[util.ProtocolPort]struct{}{ + usedPorts: HostPortInfo{ + "127.0.0.1": map[ProtocolPort]struct{}{ {Protocol: "TCP", Port: 8080}: {}, }, }, diff --git a/pkg/scheduler/internal/cache/BUILD b/pkg/scheduler/internal/cache/BUILD index a573dfabb9..f689fa6162 100644 --- a/pkg/scheduler/internal/cache/BUILD +++ b/pkg/scheduler/internal/cache/BUILD @@ -34,7 +34,6 @@ go_test( "//pkg/kubelet/apis:go_default_library", "//pkg/scheduler/algorithm/priorities/util:go_default_library", "//pkg/scheduler/cache:go_default_library", - "//pkg/scheduler/util:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", diff --git a/pkg/scheduler/internal/cache/cache_test.go b/pkg/scheduler/internal/cache/cache_test.go index 858a3ec710..8186784968 100644 --- a/pkg/scheduler/internal/cache/cache_test.go +++ b/pkg/scheduler/internal/cache/cache_test.go @@ -32,8 +32,6 @@ import ( "k8s.io/kubernetes/pkg/features" priorityutil "k8s.io/kubernetes/pkg/scheduler/algorithm/priorities/util" schedulercache "k8s.io/kubernetes/pkg/scheduler/cache" - "k8s.io/kubernetes/pkg/scheduler/util" - schedutil "k8s.io/kubernetes/pkg/scheduler/util" ) func deepEqualWithoutGeneration(t *testing.T, testcase int, actual, expected *schedulercache.NodeInfo) { @@ -67,8 +65,8 @@ func (b *hostPortInfoBuilder) add(protocol, ip string, port int32) *hostPortInfo return b } -func (b *hostPortInfoBuilder) build() schedutil.HostPortInfo { - res := make(schedutil.HostPortInfo) +func (b *hostPortInfoBuilder) build() schedulercache.HostPortInfo { + res := make(schedulercache.HostPortInfo) for _, param := range b.inputs { res.Add(param.ip, param.protocol, param.port) } @@ -78,7 +76,7 @@ func (b *hostPortInfoBuilder) build() schedutil.HostPortInfo { func newNodeInfo(requestedResource *schedulercache.Resource, nonzeroRequest *schedulercache.Resource, pods []*v1.Pod, - usedPorts util.HostPortInfo, + usedPorts schedulercache.HostPortInfo, imageStates map[string]*schedulercache.ImageStateSummary, ) *schedulercache.NodeInfo { nodeInfo := schedulercache.NewNodeInfo(pods...) @@ -747,7 +745,7 @@ func TestEphemeralStorageResource(t *testing.T) { Memory: priorityutil.DefaultMemoryRequest, }, []*v1.Pod{podE}, - schedutil.HostPortInfo{}, + schedulercache.HostPortInfo{}, make(map[string]*schedulercache.ImageStateSummary), ), }, diff --git a/pkg/scheduler/util/utils.go b/pkg/scheduler/util/utils.go index 731ad7a014..d6fe01b376 100644 --- a/pkg/scheduler/util/utils.go +++ b/pkg/scheduler/util/utils.go @@ -25,120 +25,6 @@ import ( "k8s.io/kubernetes/pkg/features" ) -// DefaultBindAllHostIP defines the default ip address used to bind to all host. -const DefaultBindAllHostIP = "0.0.0.0" - -// ProtocolPort represents a protocol port pair, e.g. tcp:80. -type ProtocolPort struct { - Protocol string - Port int32 -} - -// NewProtocolPort creates a ProtocolPort instance. -func NewProtocolPort(protocol string, port int32) *ProtocolPort { - pp := &ProtocolPort{ - Protocol: protocol, - Port: port, - } - - if len(pp.Protocol) == 0 { - pp.Protocol = string(v1.ProtocolTCP) - } - - return pp -} - -// HostPortInfo stores mapping from ip to a set of ProtocolPort -type HostPortInfo map[string]map[ProtocolPort]struct{} - -// Add adds (ip, protocol, port) to HostPortInfo -func (h HostPortInfo) Add(ip, protocol string, port int32) { - if port <= 0 { - return - } - - h.sanitize(&ip, &protocol) - - pp := NewProtocolPort(protocol, port) - if _, ok := h[ip]; !ok { - h[ip] = map[ProtocolPort]struct{}{ - *pp: {}, - } - return - } - - h[ip][*pp] = struct{}{} -} - -// Remove removes (ip, protocol, port) from HostPortInfo -func (h HostPortInfo) Remove(ip, protocol string, port int32) { - if port <= 0 { - return - } - - h.sanitize(&ip, &protocol) - - pp := NewProtocolPort(protocol, port) - if m, ok := h[ip]; ok { - delete(m, *pp) - if len(h[ip]) == 0 { - delete(h, ip) - } - } -} - -// Len returns the total number of (ip, protocol, port) tuple in HostPortInfo -func (h HostPortInfo) Len() int { - length := 0 - for _, m := range h { - length += len(m) - } - return length -} - -// CheckConflict checks if the input (ip, protocol, port) conflicts with the existing -// ones in HostPortInfo. -func (h HostPortInfo) CheckConflict(ip, protocol string, port int32) bool { - if port <= 0 { - return false - } - - h.sanitize(&ip, &protocol) - - pp := NewProtocolPort(protocol, port) - - // If ip is 0.0.0.0 check all IP's (protocol, port) pair - if ip == DefaultBindAllHostIP { - for _, m := range h { - if _, ok := m[*pp]; ok { - return true - } - } - return false - } - - // If ip isn't 0.0.0.0, only check IP and 0.0.0.0's (protocol, port) pair - for _, key := range []string{DefaultBindAllHostIP, ip} { - if m, ok := h[key]; ok { - if _, ok2 := m[*pp]; ok2 { - return true - } - } - } - - return false -} - -// sanitize the parameters -func (h HostPortInfo) sanitize(ip, protocol *string) { - if len(*ip) == 0 { - *ip = DefaultBindAllHostIP - } - if len(*protocol) == 0 { - *protocol = string(v1.ProtocolTCP) - } -} - // GetContainerPorts returns the used host ports of Pods: if 'port' was used, a 'port:true' pair // will be in the result; but it does not resolve port conflict. func GetContainerPorts(pods ...*v1.Pod) []*v1.ContainerPort { diff --git a/pkg/scheduler/util/utils_test.go b/pkg/scheduler/util/utils_test.go index 62378ae99e..206e5424c8 100644 --- a/pkg/scheduler/util/utils_test.go +++ b/pkg/scheduler/util/utils_test.go @@ -96,216 +96,6 @@ func TestSortableList(t *testing.T) { } } -type hostPortInfoParam struct { - protocol, ip string - port int32 -} - -func TestHostPortInfo_AddRemove(t *testing.T) { - tests := []struct { - desc string - added []hostPortInfoParam - removed []hostPortInfoParam - length int - }{ - { - desc: "normal add case", - added: []hostPortInfoParam{ - {"TCP", "127.0.0.1", 79}, - {"UDP", "127.0.0.1", 80}, - {"TCP", "127.0.0.1", 81}, - {"TCP", "127.0.0.1", 82}, - // this might not make sense in real case, but the struct doesn't forbid it. - {"TCP", "0.0.0.0", 79}, - {"UDP", "0.0.0.0", 80}, - {"TCP", "0.0.0.0", 81}, - {"TCP", "0.0.0.0", 82}, - {"TCP", "0.0.0.0", 0}, - {"TCP", "0.0.0.0", -1}, - }, - length: 8, - }, - { - desc: "empty ip and protocol add should work", - added: []hostPortInfoParam{ - {"", "127.0.0.1", 79}, - {"UDP", "127.0.0.1", 80}, - {"", "127.0.0.1", 81}, - {"", "127.0.0.1", 82}, - {"", "", 79}, - {"UDP", "", 80}, - {"", "", 81}, - {"", "", 82}, - {"", "", 0}, - {"", "", -1}, - }, - length: 8, - }, - { - desc: "normal remove case", - added: []hostPortInfoParam{ - {"TCP", "127.0.0.1", 79}, - {"UDP", "127.0.0.1", 80}, - {"TCP", "127.0.0.1", 81}, - {"TCP", "127.0.0.1", 82}, - {"TCP", "0.0.0.0", 79}, - {"UDP", "0.0.0.0", 80}, - {"TCP", "0.0.0.0", 81}, - {"TCP", "0.0.0.0", 82}, - }, - removed: []hostPortInfoParam{ - {"TCP", "127.0.0.1", 79}, - {"UDP", "127.0.0.1", 80}, - {"TCP", "127.0.0.1", 81}, - {"TCP", "127.0.0.1", 82}, - {"TCP", "0.0.0.0", 79}, - {"UDP", "0.0.0.0", 80}, - {"TCP", "0.0.0.0", 81}, - {"TCP", "0.0.0.0", 82}, - }, - length: 0, - }, - { - desc: "empty ip and protocol remove should work", - added: []hostPortInfoParam{ - {"TCP", "127.0.0.1", 79}, - {"UDP", "127.0.0.1", 80}, - {"TCP", "127.0.0.1", 81}, - {"TCP", "127.0.0.1", 82}, - {"TCP", "0.0.0.0", 79}, - {"UDP", "0.0.0.0", 80}, - {"TCP", "0.0.0.0", 81}, - {"TCP", "0.0.0.0", 82}, - }, - removed: []hostPortInfoParam{ - {"", "127.0.0.1", 79}, - {"", "127.0.0.1", 81}, - {"", "127.0.0.1", 82}, - {"UDP", "127.0.0.1", 80}, - {"", "", 79}, - {"", "", 81}, - {"", "", 82}, - {"UDP", "", 80}, - }, - length: 0, - }, - } - - for _, test := range tests { - hp := make(HostPortInfo) - for _, param := range test.added { - hp.Add(param.ip, param.protocol, param.port) - } - for _, param := range test.removed { - hp.Remove(param.ip, param.protocol, param.port) - } - if hp.Len() != test.length { - t.Errorf("%v failed: expect length %d; got %d", test.desc, test.length, hp.Len()) - t.Error(hp) - } - } -} - -func TestHostPortInfo_Check(t *testing.T) { - tests := []struct { - desc string - added []hostPortInfoParam - check hostPortInfoParam - expect bool - }{ - { - desc: "empty check should check 0.0.0.0 and TCP", - added: []hostPortInfoParam{ - {"TCP", "127.0.0.1", 80}, - }, - check: hostPortInfoParam{"", "", 81}, - expect: false, - }, - { - desc: "empty check should check 0.0.0.0 and TCP (conflicted)", - added: []hostPortInfoParam{ - {"TCP", "127.0.0.1", 80}, - }, - check: hostPortInfoParam{"", "", 80}, - expect: true, - }, - { - desc: "empty port check should pass", - added: []hostPortInfoParam{ - {"TCP", "127.0.0.1", 80}, - }, - check: hostPortInfoParam{"", "", 0}, - expect: false, - }, - { - desc: "0.0.0.0 should check all registered IPs", - added: []hostPortInfoParam{ - {"TCP", "127.0.0.1", 80}, - }, - check: hostPortInfoParam{"TCP", "0.0.0.0", 80}, - expect: true, - }, - { - desc: "0.0.0.0 with different protocol should be allowed", - added: []hostPortInfoParam{ - {"UDP", "127.0.0.1", 80}, - }, - check: hostPortInfoParam{"TCP", "0.0.0.0", 80}, - expect: false, - }, - { - desc: "0.0.0.0 with different port should be allowed", - added: []hostPortInfoParam{ - {"TCP", "127.0.0.1", 79}, - {"TCP", "127.0.0.1", 81}, - {"TCP", "127.0.0.1", 82}, - }, - check: hostPortInfoParam{"TCP", "0.0.0.0", 80}, - expect: false, - }, - { - desc: "normal ip should check all registered 0.0.0.0", - added: []hostPortInfoParam{ - {"TCP", "0.0.0.0", 80}, - }, - check: hostPortInfoParam{"TCP", "127.0.0.1", 80}, - expect: true, - }, - { - desc: "normal ip with different port/protocol should be allowed (0.0.0.0)", - added: []hostPortInfoParam{ - {"TCP", "0.0.0.0", 79}, - {"UDP", "0.0.0.0", 80}, - {"TCP", "0.0.0.0", 81}, - {"TCP", "0.0.0.0", 82}, - }, - check: hostPortInfoParam{"TCP", "127.0.0.1", 80}, - expect: false, - }, - { - desc: "normal ip with different port/protocol should be allowed", - added: []hostPortInfoParam{ - {"TCP", "127.0.0.1", 79}, - {"UDP", "127.0.0.1", 80}, - {"TCP", "127.0.0.1", 81}, - {"TCP", "127.0.0.1", 82}, - }, - check: hostPortInfoParam{"TCP", "127.0.0.1", 80}, - expect: false, - }, - } - - for _, test := range tests { - hp := make(HostPortInfo) - for _, param := range test.added { - hp.Add(param.ip, param.protocol, param.port) - } - if hp.CheckConflict(test.check.ip, test.check.protocol, test.check.port) != test.expect { - t.Errorf("%v failed, expected %t; got %t", test.desc, test.expect, !test.expect) - } - } -} - func TestGetContainerPorts(t *testing.T) { tests := []struct { pod1 *v1.Pod