Code change:
t.Errorf("unexpected portallocator state: %d free", free) } } <ins> func TestCollectServiceNodePorts(t *testing.T) { tests := []struct { name        string serviceSpec corev1.ServiceSpec expected    []int }{ { name: "no duplicated nodePorts", serviceSpec: corev1.ServiceSpec{ Ports: []corev1.ServicePort{ {NodePort: 111, Protocol: corev1.ProtocolTCP}, {NodePort: 112, Protocol: corev1.ProtocolUDP}, {NodePort: 113, Protocol: corev1.ProtocolUDP}, }, }, expected: []int{111, 112, 113}, }, { name: "duplicated nodePort with TCP protocol", serviceSpec: corev1.ServiceSpec{ Ports: []corev1.ServicePort{ {NodePort: 111, Protocol: corev1.ProtocolTCP}, {NodePort: 111, Protocol: corev1.ProtocolTCP}, {NodePort: 112, Protocol: corev1.ProtocolUDP}, }, }, expected: []int{111, 111, 112}, }, { name: "duplicated nodePort with UDP protocol", serviceSpec: corev1.ServiceSpec{ Ports: []corev1.ServicePort{ {NodePort: 111, Protocol: corev1.ProtocolUDP}, {NodePort: 111, Protocol: corev1.ProtocolUDP}, {NodePort: 112, Protocol: corev1.ProtocolTCP}, }, }, expected: []int{111, 111, 112}, }, { name: "duplicated nodePort with different protocol", serviceSpec: corev1.ServiceSpec{ Ports: []corev1.ServicePort{ {NodePort: 111, Protocol: corev1.ProtocolTCP}, {NodePort: 112, Protocol: corev1.ProtocolTCP}, {NodePort: 111, Protocol: corev1.ProtocolUDP}, }, }, expected: []int{111, 112}, }, { name: "no duplicated port(with health check port)", serviceSpec: corev1.ServiceSpec{ Ports: []corev1.ServicePort{ {NodePort: 111, Protocol: corev1.ProtocolTCP}, {NodePort: 112, Protocol: corev1.ProtocolUDP}, }, HealthCheckNodePort: 113, }, expected: []int{111, 112, 113}, }, { name: "nodePort has different protocol with duplicated health check port", serviceSpec: corev1.ServiceSpec{ Ports: []corev1.ServicePort{ {NodePort: 111, Protocol: corev1.ProtocolUDP}, {NodePort: 112, Protocol: corev1.ProtocolTCP}, }, HealthCheckNodePort: 111, }, expected: []int{111, 112}, }, { name: "nodePort has same protocol as duplicated health check port", serviceSpec: corev1.ServiceSpec{ Ports: []corev1.ServicePort{ {NodePort: 111, Protocol: corev1.ProtocolUDP}, {NodePort: 112, Protocol: corev1.ProtocolTCP}, }, HealthCheckNodePort: 112, }, expected: []int{111, 112, 112}, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ports := collectServiceNodePorts(&corev1.Service{ ObjectMeta: metav1.ObjectMeta{Namespace: "one", Name: "one"}, Spec:       tc.serviceSpec, }) sort.Ints(ports) if !reflect.DeepEqual(tc.expected, ports) { t.Fatalf("Invalid resultnexpected: %vngot: %v", tc.expected, ports) } }) } } </ins>

Related discussion:
text: /assign
Ahh, I thought you meant that you had accidentally been allocated the same nodePorts and I was very confused when you said "all of them". I have reproduced this issue, and I can see it by inspection. The obvious fix would be to collect ports split by protocol, but I think that's not right since we don't store it split. An even simpler fix would be for to build a . Then for each protocol, for each port, build that contains all the port numbers, but remove any duplicates ONLY IF they are duplicated across protocols. Duplicates in the same protocol should be retained (to report an error later). I don't have time to tackle this tonight (and write a test), but it seems like a good bug.
perhaps I can fix this problem /assign
things may be a little more complicated. Right now the is not aware of the protocol of the port so I guess simply rewriting function to return is not enough, the port allocator will still return on same node port even with different protocol. perhaps we need to refactor the to make it protocol-aware.
I don't think we need to do make the allocator be protocol aware. IIRC we allow you to use the same nodeport on different protocols ONLY within a single service. This means (I think) that we can do hyper-local deduplication as we collect the nodeports for each service. If we find the same nodeport number with different protocols, just remove the duplicates because it is allowed. I.e. a service that looks like: return - deduped. if ANY OTHER service tried to use it would be an error at the "rebuilt" allocator Likewise if a single Service claims to use the same nodeport number more than once on the same protocol, it should be an error: return - NOT deduped.
obviously, we want a great unit test here :P
ah got it! thanks for your detailed explanation!
Hi! I have filed a PR to resolve this issue, could you take a look?