feat: support new plugin named AlibabaCloud-AutoNLBs (#246)

Signed-off-by: ChrisLiu <chrisliu1995@163.com>
This commit is contained in:
ChrisLiu 2025-06-26 17:27:03 +08:00 committed by GitHub
parent 0ff70733c6
commit 7dfe07097b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 819 additions and 0 deletions

View File

@ -0,0 +1,538 @@
/*
Copyright 2025 The Kruise 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 alibabacloud
import (
"context"
"fmt"
gamekruiseiov1alpha1 "github.com/openkruise/kruise-game/apis/v1alpha1"
"github.com/openkruise/kruise-game/cloudprovider"
cperrors "github.com/openkruise/kruise-game/cloudprovider/errors"
"github.com/openkruise/kruise-game/cloudprovider/utils"
"github.com/openkruise/kruise-game/pkg/util"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
log "k8s.io/klog/v2"
"k8s.io/utils/ptr"
"sigs.k8s.io/controller-runtime/pkg/client"
"strconv"
"strings"
"sync"
)
const (
AutoNLBsNetwork = "AlibabaCloud-AutoNLBs"
AliasAutoNLBs = "Auto-NLBs-Network"
ReserveNlbNumConfigName = "ReserveNlbNum"
EipTypesConfigName = "EipTypes"
ZoneMapsConfigName = "ZoneMaps"
MinPortConfigName = "MinPort"
MaxPortConfigName = "MaxPort"
BlockPortsConfigName = "BlockPorts"
NLBZoneMapsServiceAnnotationKey = "service.beta.kubernetes.io/alibaba-cloud-loadbalancer-zone-maps"
NLBAddressTypeAnnotationKey = "service.beta.kubernetes.io/alibaba-cloud-loadbalancer-address-type"
IntranetEIPType = "intranet"
DefaultEIPType = "default"
)
type AutoNLBsPlugin struct {
gssMaxPodIndex map[string]int
mutex sync.RWMutex
}
type autoNLBsConfig struct {
minPort int32
maxPort int32
blockPorts []int32
zoneMaps string
reserveNlbNum int
targetPorts []int
protocols []corev1.Protocol
eipTypes []string
externalTrafficPolicy corev1.ServiceExternalTrafficPolicyType
*nlbHealthConfig
}
func (a *AutoNLBsPlugin) Name() string {
return AutoNLBsNetwork
}
func (a *AutoNLBsPlugin) Alias() string {
return AliasAutoNLBs
}
func (a *AutoNLBsPlugin) Init(c client.Client, options cloudprovider.CloudProviderOptions, ctx context.Context) error {
gssList := &gamekruiseiov1alpha1.GameServerSetList{}
err := c.List(ctx, gssList, &client.ListOptions{})
if err != nil {
log.Errorf("cannot list gameserverset in cluster because %s", err.Error())
return err
}
for _, gss := range gssList.Items {
if gss.Spec.Network != nil && gss.Spec.Network.NetworkType == AutoNLBsNetwork {
a.gssMaxPodIndex[gss.GetNamespace()+"/"+gss.GetName()] = int(*gss.Spec.Replicas)
nc, err := parseAutoNLBsConfig(gss.Spec.Network.NetworkConf)
if err != nil {
log.Errorf("pasrse config wronge because %s", err.Error())
return err
}
err = a.ensureServices(ctx, c, gss.GetNamespace(), gss.GetName(), nc)
if err != nil {
log.Errorf("ensure services error because %s", err.Error())
return err
}
}
}
return nil
}
func (a *AutoNLBsPlugin) OnPodAdded(c client.Client, pod *corev1.Pod, ctx context.Context) (*corev1.Pod, cperrors.PluginError) {
networkManager := utils.NewNetworkManager(pod, c)
networkConfig := networkManager.GetNetworkConfig()
conf, err := parseAutoNLBsConfig(networkConfig)
if err != nil {
return pod, cperrors.NewPluginError(cperrors.ParameterError, err.Error())
}
a.ensureMaxPodIndex(pod)
gssName := pod.GetLabels()[gamekruiseiov1alpha1.GameServerOwnerGssKey]
if err := a.ensureServices(ctx, c, pod.GetNamespace(), gssName, conf); err != nil {
return pod, cperrors.NewPluginError(cperrors.ApiCallError, err.Error())
}
containerPorts := make([]corev1.ContainerPort, 0)
podIndex := util.GetIndexFromGsName(pod.GetName())
for i, port := range conf.targetPorts {
if conf.protocols[i] == ProtocolTCPUDP {
containerPortTCP := corev1.ContainerPort{
ContainerPort: int32(port),
Protocol: corev1.ProtocolTCP,
Name: "tcp-" + strconv.Itoa(podIndex) + "-" + strconv.Itoa(port),
}
containerPortUDP := corev1.ContainerPort{
ContainerPort: int32(port),
Protocol: corev1.ProtocolUDP,
Name: "udp-" + strconv.Itoa(podIndex) + "-" + strconv.Itoa(port),
}
containerPorts = append(containerPorts, containerPortTCP, containerPortUDP)
} else {
containerPort := corev1.ContainerPort{
ContainerPort: int32(port),
Protocol: conf.protocols[i],
Name: strings.ToLower(string(conf.protocols[i])) + "-" + strconv.Itoa(podIndex) + "-" + strconv.Itoa(port),
}
containerPorts = append(containerPorts, containerPort)
}
}
pod.Spec.Containers[0].Ports = containerPorts
lenRange := int(conf.maxPort) - int(conf.minPort) - len(conf.blockPorts) + 1
svcIndex := podIndex / (lenRange / len(conf.targetPorts))
for _, eipType := range conf.eipTypes {
svcName := gssName + "-" + eipType + "-" + strconv.Itoa(svcIndex)
pod.Spec.ReadinessGates = append(pod.Spec.ReadinessGates, corev1.PodReadinessGate{
ConditionType: corev1.PodConditionType(PrefixReadyReadinessGate + svcName),
})
}
return pod, nil
}
func (a *AutoNLBsPlugin) OnPodUpdated(c client.Client, pod *corev1.Pod, ctx context.Context) (*corev1.Pod, cperrors.PluginError) {
networkManager := utils.NewNetworkManager(pod, c)
networkStatus, _ := networkManager.GetNetworkStatus()
networkConfig := networkManager.GetNetworkConfig()
conf, err := parseAutoNLBsConfig(networkConfig)
if err != nil {
return pod, cperrors.NewPluginError(cperrors.ParameterError, err.Error())
}
if networkStatus == nil {
pod, err := networkManager.UpdateNetworkStatus(gamekruiseiov1alpha1.NetworkStatus{
CurrentNetworkState: gamekruiseiov1alpha1.NetworkNotReady,
}, pod)
return pod, cperrors.ToPluginError(err, cperrors.InternalError)
}
_, readyCondition := util.GetPodConditionFromList(pod.Status.Conditions, corev1.PodReady)
if readyCondition == nil || readyCondition.Status == corev1.ConditionFalse {
networkStatus.CurrentNetworkState = gamekruiseiov1alpha1.NetworkNotReady
pod, err = networkManager.UpdateNetworkStatus(*networkStatus, pod)
return pod, cperrors.ToPluginError(err, cperrors.InternalError)
}
var internalPorts []gamekruiseiov1alpha1.NetworkPort
var externalPorts []gamekruiseiov1alpha1.NetworkPort
endPoints := ""
podIndex := util.GetIndexFromGsName(pod.GetName())
lenRange := int(conf.maxPort) - int(conf.minPort) - len(conf.blockPorts) + 1
svcIndex := podIndex / (lenRange / len(conf.targetPorts))
for i, eipType := range conf.eipTypes {
svcName := pod.GetLabels()[gamekruiseiov1alpha1.GameServerOwnerGssKey] + "-" + eipType + "-" + strconv.Itoa(svcIndex)
svc := &corev1.Service{}
err := c.Get(ctx, types.NamespacedName{
Name: svcName,
Namespace: pod.GetNamespace(),
}, svc)
if err != nil {
return pod, cperrors.NewPluginError(cperrors.ApiCallError, err.Error())
}
endPoints = endPoints + svc.Status.LoadBalancer.Ingress[0].Hostname + "/" + eipType
if i == len(conf.eipTypes)-1 {
for i, port := range conf.targetPorts {
if conf.protocols[i] == ProtocolTCPUDP {
portNameTCP := "tcp-" + strconv.Itoa(podIndex) + strconv.Itoa(port)
portNameUDP := "udp-" + strconv.Itoa(podIndex) + strconv.Itoa(port)
iPort := intstr.FromInt(port)
internalPorts = append(internalPorts, gamekruiseiov1alpha1.NetworkPort{
Name: portNameTCP,
Protocol: corev1.ProtocolTCP,
Port: &iPort,
}, gamekruiseiov1alpha1.NetworkPort{
Name: portNameUDP,
Protocol: corev1.ProtocolUDP,
Port: &iPort,
})
for _, svcPort := range svc.Spec.Ports {
if svcPort.Name == portNameTCP || svcPort.Name == portNameUDP {
ePort := intstr.FromInt32(svcPort.Port)
externalPorts = append(externalPorts, gamekruiseiov1alpha1.NetworkPort{
Name: portNameTCP,
Protocol: corev1.ProtocolTCP,
Port: &ePort,
}, gamekruiseiov1alpha1.NetworkPort{
Name: portNameUDP,
Protocol: corev1.ProtocolUDP,
Port: &ePort,
})
break
}
}
} else {
portName := strings.ToLower(string(conf.protocols[i])) + "-" + strconv.Itoa(podIndex) + "-" + strconv.Itoa(port)
iPort := intstr.FromInt(port)
internalPorts = append(internalPorts, gamekruiseiov1alpha1.NetworkPort{
Name: portName,
Protocol: conf.protocols[i],
Port: &iPort,
})
for _, svcPort := range svc.Spec.Ports {
if svcPort.Name == portName {
ePort := intstr.FromInt32(svcPort.Port)
externalPorts = append(externalPorts, gamekruiseiov1alpha1.NetworkPort{
Name: portName,
Protocol: conf.protocols[i],
Port: &ePort,
})
break
}
}
}
}
} else {
endPoints = endPoints + ","
}
}
networkStatus = &gamekruiseiov1alpha1.NetworkStatus{
InternalAddresses: []gamekruiseiov1alpha1.NetworkAddress{
{
IP: pod.Status.PodIP,
Ports: internalPorts,
},
},
ExternalAddresses: []gamekruiseiov1alpha1.NetworkAddress{
{
EndPoint: endPoints,
Ports: externalPorts,
},
},
CurrentNetworkState: gamekruiseiov1alpha1.NetworkReady,
}
pod, err = networkManager.UpdateNetworkStatus(*networkStatus, pod)
return pod, cperrors.ToPluginError(err, cperrors.InternalError)
}
func (a *AutoNLBsPlugin) OnPodDeleted(client client.Client, pod *corev1.Pod, ctx context.Context) cperrors.PluginError {
return nil
}
func init() {
autoNLBsPlugin := AutoNLBsPlugin{
mutex: sync.RWMutex{},
gssMaxPodIndex: make(map[string]int),
}
alibabaCloudProvider.registerPlugin(&autoNLBsPlugin)
}
func (a *AutoNLBsPlugin) ensureMaxPodIndex(pod *corev1.Pod) {
a.mutex.Lock()
defer a.mutex.Unlock()
podIndex := util.GetIndexFromGsName(pod.GetName())
gssNsName := pod.GetNamespace() + "/" + pod.GetLabels()[gamekruiseiov1alpha1.GameServerOwnerGssKey]
if podIndex > a.gssMaxPodIndex[gssNsName] {
a.gssMaxPodIndex[gssNsName] = podIndex
}
}
func (a *AutoNLBsPlugin) checkSvcNumToCreate(namespace, gssName string, config *autoNLBsConfig) int {
a.mutex.RLock()
defer a.mutex.RUnlock()
lenRange := int(config.maxPort) - int(config.minPort) - len(config.blockPorts) + 1
expectSvcNum := a.gssMaxPodIndex[namespace+"/"+gssName]/(lenRange/len(config.targetPorts)) + config.reserveNlbNum + 1
return expectSvcNum
}
func (a *AutoNLBsPlugin) ensureServices(ctx context.Context, client client.Client, namespace, gssName string, config *autoNLBsConfig) error {
expectSvcNum := a.checkSvcNumToCreate(namespace, gssName, config)
for _, eipType := range config.eipTypes {
for j := 0; j < expectSvcNum; j++ {
// get svc
svcName := gssName + "-" + eipType + "-" + strconv.Itoa(j)
svc := &corev1.Service{}
err := client.Get(ctx, types.NamespacedName{
Name: svcName,
Namespace: namespace,
}, svc)
if err != nil {
if errors.IsNotFound(err) {
// create svc
toAddSvc := a.consSvc(namespace, gssName, eipType, j, config)
if err := setSvcOwner(client, ctx, toAddSvc, namespace, gssName); err != nil {
return err
} else {
if err := client.Create(ctx, toAddSvc); err != nil {
return err
}
}
} else {
return err
}
}
}
}
return nil
}
func (a *AutoNLBsPlugin) consSvcPorts(svcIndex int, config *autoNLBsConfig) []corev1.ServicePort {
lenRange := int(config.maxPort) - int(config.minPort) - len(config.blockPorts) + 1
ports := make([]corev1.ServicePort, 0)
toAllocatedPort := config.minPort
portNumPerPod := lenRange / len(config.targetPorts)
for podIndex := svcIndex * portNumPerPod; podIndex < (svcIndex+1)*portNumPerPod; podIndex++ {
for i, protocol := range config.protocols {
if protocol == ProtocolTCPUDP {
svcPortTCP := corev1.ServicePort{
Name: "tcp-" + strconv.Itoa(podIndex) + "-" + strconv.Itoa(config.targetPorts[i]),
TargetPort: intstr.FromString("tcp-" + strconv.Itoa(podIndex) + "-" + strconv.Itoa(config.targetPorts[i])),
Port: toAllocatedPort,
Protocol: corev1.ProtocolTCP,
}
svcPortUDP := corev1.ServicePort{
Name: "udp-" + strconv.Itoa(podIndex) + "-" + strconv.Itoa(config.targetPorts[i]),
TargetPort: intstr.FromString("udp-" + strconv.Itoa(podIndex) + "-" + strconv.Itoa(config.targetPorts[i])),
Port: toAllocatedPort,
Protocol: corev1.ProtocolUDP,
}
ports = append(ports, svcPortTCP, svcPortUDP)
} else {
svcPort := corev1.ServicePort{
Name: strings.ToLower(string(protocol)) + "-" + strconv.Itoa(podIndex) + "-" + strconv.Itoa(config.targetPorts[i]),
TargetPort: intstr.FromString(strings.ToLower(string(protocol)) + "-" + strconv.Itoa(podIndex) + "-" + strconv.Itoa(config.targetPorts[i])),
Port: toAllocatedPort,
Protocol: protocol,
}
ports = append(ports, svcPort)
}
toAllocatedPort++
for util.IsNumInListInt32(toAllocatedPort, config.blockPorts) {
toAllocatedPort++
}
}
}
return ports
}
func (a *AutoNLBsPlugin) consSvc(namespace, gssName, eipType string, svcIndex int, conf *autoNLBsConfig) *corev1.Service {
loadBalancerClass := "alibabacloud.com/nlb"
svcAnnotations := map[string]string{
//SlbConfigHashKey: util.GetHash(conf),
NLBZoneMapsServiceAnnotationKey: conf.zoneMaps,
LBHealthCheckFlagAnnotationKey: conf.lBHealthCheckFlag,
}
if conf.lBHealthCheckFlag == "on" {
svcAnnotations[LBHealthCheckTypeAnnotationKey] = conf.lBHealthCheckType
svcAnnotations[LBHealthCheckConnectPortAnnotationKey] = conf.lBHealthCheckConnectPort
svcAnnotations[LBHealthCheckConnectTimeoutAnnotationKey] = conf.lBHealthCheckConnectTimeout
svcAnnotations[LBHealthCheckIntervalAnnotationKey] = conf.lBHealthCheckInterval
svcAnnotations[LBHealthyThresholdAnnotationKey] = conf.lBHealthyThreshold
svcAnnotations[LBUnhealthyThresholdAnnotationKey] = conf.lBUnhealthyThreshold
if conf.lBHealthCheckType == "http" {
svcAnnotations[LBHealthCheckDomainAnnotationKey] = conf.lBHealthCheckDomain
svcAnnotations[LBHealthCheckUriAnnotationKey] = conf.lBHealthCheckUri
svcAnnotations[LBHealthCheckMethodAnnotationKey] = conf.lBHealthCheckMethod
}
}
if eipType == IntranetEIPType {
svcAnnotations[NLBAddressTypeAnnotationKey] = eipType
}
return &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: gssName + "-" + eipType + "-" + strconv.Itoa(svcIndex),
Namespace: namespace,
Annotations: svcAnnotations,
},
Spec: corev1.ServiceSpec{
Ports: a.consSvcPorts(svcIndex, conf),
Type: corev1.ServiceTypeLoadBalancer,
Selector: map[string]string{
gamekruiseiov1alpha1.GameServerOwnerGssKey: gssName,
},
LoadBalancerClass: &loadBalancerClass,
AllocateLoadBalancerNodePorts: ptr.To[bool](false),
ExternalTrafficPolicy: conf.externalTrafficPolicy,
},
}
}
func setSvcOwner(c client.Client, ctx context.Context, svc *corev1.Service, namespace, gssName string) error {
gss := &gamekruiseiov1alpha1.GameServerSet{}
err := c.Get(ctx, types.NamespacedName{
Namespace: namespace,
Name: gssName,
}, gss)
if err != nil {
return err
}
ownerRef := []metav1.OwnerReference{
{
APIVersion: gss.APIVersion,
Kind: gss.Kind,
Name: gss.GetName(),
UID: gss.GetUID(),
Controller: ptr.To[bool](true),
BlockOwnerDeletion: ptr.To[bool](true),
},
}
svc.OwnerReferences = ownerRef
return nil
}
func parseAutoNLBsConfig(conf []gamekruiseiov1alpha1.NetworkConfParams) (*autoNLBsConfig, error) {
reserveNlbNum := 1
eipTypes := []string{"default"}
ports := make([]int, 0)
protocols := make([]corev1.Protocol, 0)
externalTrafficPolicy := corev1.ServiceExternalTrafficPolicyTypeLocal
zoneMaps := ""
blockPorts := make([]int32, 0)
minPort := int32(1000)
maxPort := int32(1499)
for _, c := range conf {
switch c.Name {
case PortProtocolsConfigName:
for _, pp := range strings.Split(c.Value, ",") {
ppSlice := strings.Split(pp, "/")
port, err := strconv.Atoi(ppSlice[0])
if err != nil {
return nil, fmt.Errorf("invalid PortProtocols %s", c.Value)
}
ports = append(ports, port)
if len(ppSlice) != 2 {
protocols = append(protocols, corev1.ProtocolTCP)
} else {
protocols = append(protocols, corev1.Protocol(ppSlice[1]))
}
}
case ExternalTrafficPolicyTypeConfigName:
if strings.EqualFold(c.Value, string(corev1.ServiceExternalTrafficPolicyTypeCluster)) {
externalTrafficPolicy = corev1.ServiceExternalTrafficPolicyTypeCluster
}
case ReserveNlbNumConfigName:
reserveNlbNum, _ = strconv.Atoi(c.Value)
case EipTypesConfigName:
eipTypes = strings.Split(c.Value, ",")
case ZoneMapsConfigName:
zoneMaps = c.Value
case BlockPortsConfigName:
blockPorts = util.StringToInt32Slice(c.Value, ",")
case MinPortConfigName:
val, err := strconv.ParseInt(c.Value, 10, 32)
if err != nil {
return nil, fmt.Errorf("invalid MinPort %s", c.Value)
} else {
minPort = int32(val)
}
case MaxPortConfigName:
val, err := strconv.ParseInt(c.Value, 10, 32)
if err != nil {
return nil, fmt.Errorf("invalid MaxPort %s", c.Value)
} else {
maxPort = int32(val)
}
}
}
if minPort > maxPort {
return nil, fmt.Errorf("invalid MinPort %d and MaxPort %d", minPort, maxPort)
}
if zoneMaps == "" {
return nil, fmt.Errorf("invalid ZoneMaps, which can not be empty")
}
// check ports & protocols
if len(ports) == 0 || len(protocols) == 0 {
return nil, fmt.Errorf("invalid PortProtocols, which can not be empty")
}
nlbHealthConfig, err := parseNlbHealthConfig(conf)
if err != nil {
return nil, err
}
return &autoNLBsConfig{
blockPorts: blockPorts,
minPort: minPort,
maxPort: maxPort,
nlbHealthConfig: nlbHealthConfig,
reserveNlbNum: reserveNlbNum,
eipTypes: eipTypes,
protocols: protocols,
targetPorts: ports,
zoneMaps: zoneMaps,
externalTrafficPolicy: externalTrafficPolicy,
}, nil
}

View File

@ -0,0 +1,272 @@
/*
Copyright 2025 The Kruise 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 alibabacloud
import (
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"reflect"
"sync"
"testing"
)
func TestIsNeedToCreateService(t *testing.T) {
tests := []struct {
ns string
gssName string
config *autoNLBsConfig
a *AutoNLBsPlugin
expectSvcNum int
}{
// case 0
{
ns: "default",
gssName: "pod",
config: &autoNLBsConfig{
protocols: []corev1.Protocol{
corev1.ProtocolTCP,
corev1.ProtocolUDP,
},
reserveNlbNum: 2,
targetPorts: []int{
6666,
8888,
},
maxPort: 2500,
minPort: 1000,
blockPorts: []int32{},
},
a: &AutoNLBsPlugin{
gssMaxPodIndex: map[string]int{
"default/pod": 1499,
},
mutex: sync.RWMutex{},
},
expectSvcNum: 4,
},
// case 1
{
ns: "default",
gssName: "pod",
config: &autoNLBsConfig{
protocols: []corev1.Protocol{
corev1.ProtocolTCP,
corev1.ProtocolTCP,
corev1.ProtocolUDP,
},
reserveNlbNum: 2,
targetPorts: []int{
6666,
7777,
8888,
},
maxPort: 1005,
minPort: 1000,
blockPorts: []int32{},
},
a: &AutoNLBsPlugin{
gssMaxPodIndex: map[string]int{
"default/pod": 1,
},
mutex: sync.RWMutex{},
},
expectSvcNum: 3,
},
}
for i, test := range tests {
a := test.a
expectSvcNum := a.checkSvcNumToCreate(test.ns, test.gssName, test.config)
if expectSvcNum != test.expectSvcNum {
t.Errorf("case %d: expect toAddSvcNum: %d, but got toAddSvcNum: %d", i, test.expectSvcNum, expectSvcNum)
}
}
}
func TestConsSvcPorts(t *testing.T) {
tests := []struct {
a *AutoNLBsPlugin
svcIndex int
config *autoNLBsConfig
expectSvcPorts []corev1.ServicePort
}{
// case 0
{
a: &AutoNLBsPlugin{
mutex: sync.RWMutex{},
},
svcIndex: 0,
config: &autoNLBsConfig{
protocols: []corev1.Protocol{
corev1.ProtocolTCP,
corev1.ProtocolUDP,
},
targetPorts: []int{
6666,
8888,
},
maxPort: 1003,
minPort: 1000,
blockPorts: []int32{},
},
expectSvcPorts: []corev1.ServicePort{
{
Name: "tcp-0-6666",
TargetPort: intstr.FromString("tcp-0-6666"),
Port: 1000,
Protocol: corev1.ProtocolTCP,
},
{
Name: "udp-0-8888",
TargetPort: intstr.FromString("udp-0-8888"),
Port: 1001,
Protocol: corev1.ProtocolUDP,
},
{
Name: "tcp-1-6666",
TargetPort: intstr.FromString("tcp-1-6666"),
Port: 1002,
Protocol: corev1.ProtocolTCP,
},
{
Name: "udp-1-8888",
TargetPort: intstr.FromString("udp-1-8888"),
Port: 1003,
Protocol: corev1.ProtocolUDP,
},
},
},
// case 1
{
a: &AutoNLBsPlugin{
mutex: sync.RWMutex{},
},
svcIndex: 1,
config: &autoNLBsConfig{
protocols: []corev1.Protocol{
corev1.ProtocolTCP,
corev1.ProtocolTCP,
corev1.ProtocolUDP,
},
targetPorts: []int{
6666,
7777,
8888,
},
maxPort: 1004,
minPort: 1000,
blockPorts: []int32{},
},
expectSvcPorts: []corev1.ServicePort{
{
Name: "tcp-1-6666",
TargetPort: intstr.FromString("tcp-1-6666"),
Port: 1000,
Protocol: corev1.ProtocolTCP,
},
{
Name: "tcp-1-7777",
TargetPort: intstr.FromString("tcp-1-7777"),
Port: 1001,
Protocol: corev1.ProtocolTCP,
},
{
Name: "udp-1-8888",
TargetPort: intstr.FromString("udp-1-8888"),
Port: 1002,
Protocol: corev1.ProtocolUDP,
},
},
},
// case 2
{
a: &AutoNLBsPlugin{
mutex: sync.RWMutex{},
},
svcIndex: 3,
config: &autoNLBsConfig{
protocols: []corev1.Protocol{
ProtocolTCPUDP,
},
targetPorts: []int{
6666,
},
maxPort: 1004,
minPort: 1000,
blockPorts: []int32{1002},
},
expectSvcPorts: []corev1.ServicePort{
{
Name: "tcp-12-6666",
TargetPort: intstr.FromString("tcp-12-6666"),
Port: 1000,
Protocol: corev1.ProtocolTCP,
},
{
Name: "udp-12-6666",
TargetPort: intstr.FromString("udp-12-6666"),
Port: 1000,
Protocol: corev1.ProtocolUDP,
},
{
Name: "tcp-13-6666",
TargetPort: intstr.FromString("tcp-13-6666"),
Port: 1001,
Protocol: corev1.ProtocolTCP,
},
{
Name: "udp-13-6666",
TargetPort: intstr.FromString("udp-13-6666"),
Port: 1001,
Protocol: corev1.ProtocolUDP,
},
{
Name: "tcp-14-6666",
TargetPort: intstr.FromString("tcp-14-6666"),
Port: 1003,
Protocol: corev1.ProtocolTCP,
},
{
Name: "udp-14-6666",
TargetPort: intstr.FromString("udp-14-6666"),
Port: 1003,
Protocol: corev1.ProtocolUDP,
},
{
Name: "tcp-15-6666",
TargetPort: intstr.FromString("tcp-15-6666"),
Port: 1004,
Protocol: corev1.ProtocolTCP,
},
{
Name: "udp-15-6666",
TargetPort: intstr.FromString("udp-15-6666"),
Port: 1004,
Protocol: corev1.ProtocolUDP,
},
},
},
}
for i, test := range tests {
svcPorts := test.a.consSvcPorts(test.svcIndex, test.config)
if !reflect.DeepEqual(svcPorts, test.expectSvcPorts) {
t.Errorf("case %d: expect svcPorts: %v, but got svcPorts: %v", i, test.expectSvcPorts, svcPorts)
}
}
}

View File

@ -34,6 +34,15 @@ func IsNumInList(num int, list []int) bool {
return false
}
func IsNumInListInt32(num int32, list []int32) bool {
for _, n := range list {
if num == n {
return true
}
}
return false
}
func IsStringInList(str string, list []string) bool {
for _, s := range list {
if s == str {