From 5e46d4f67bd4d8283f6bedb78fcc3a33a7ec993c Mon Sep 17 00:00:00 2001 From: baoyinghai_yewu Date: Fri, 2 Aug 2024 16:16:12 +0800 Subject: [PATCH] feat: Support automatic configuration of the IP family for API server's SVC Signed-off-by: baoyinghai_yewu --- pkg/kubenest/controlplane/service.go | 4 + .../apiserver/mainfests_service.go | 4 + .../apiserver/mainfests_service_test.go | 91 +++++++++++++++++++ pkg/utils/utils.go | 15 +++ 4 files changed, 114 insertions(+) create mode 100644 pkg/kubenest/manifest/controlplane/apiserver/mainfests_service_test.go diff --git a/pkg/kubenest/controlplane/service.go b/pkg/kubenest/controlplane/service.go index 44740d570..154555035 100644 --- a/pkg/kubenest/controlplane/service.go +++ b/pkg/kubenest/controlplane/service.go @@ -16,6 +16,7 @@ import ( "github.com/kosmos.io/kosmos/pkg/kubenest/manifest/controlplane/apiserver" "github.com/kosmos.io/kosmos/pkg/kubenest/manifest/controlplane/etcd" "github.com/kosmos.io/kosmos/pkg/kubenest/util" + "github.com/kosmos.io/kosmos/pkg/utils" ) func EnsureVirtualClusterService(client clientset.Interface, name, namespace string, portMap map[string]int32) error { @@ -47,14 +48,17 @@ func DeleteVirtualClusterService(client clientset.Interface, name, namespace str } func createServerService(client clientset.Interface, name, namespace string, portMap map[string]int32) error { + ipFamilies := utils.IPFamilyGenerator(constants.ApiServerServiceSubnet) apiserverServiceBytes, err := util.ParseTemplate(apiserver.ApiserverService, struct { ServiceName, Namespace, ServiceType string ServicePort int32 + IPFamilies []corev1.IPFamily }{ ServiceName: fmt.Sprintf("%s-%s", name, "apiserver"), Namespace: namespace, ServiceType: constants.ApiServerServiceType, ServicePort: portMap[constants.ApiServerPortKey], + IPFamilies: ipFamilies, }) if err != nil { return fmt.Errorf("error when parsing virtualClusterApiserver serive template: %w", err) diff --git a/pkg/kubenest/manifest/controlplane/apiserver/mainfests_service.go b/pkg/kubenest/manifest/controlplane/apiserver/mainfests_service.go index 7c0dd7966..d97138cbf 100644 --- a/pkg/kubenest/manifest/controlplane/apiserver/mainfests_service.go +++ b/pkg/kubenest/manifest/controlplane/apiserver/mainfests_service.go @@ -11,6 +11,10 @@ metadata: name: {{ .ServiceName }} namespace: {{ .Namespace }} spec: + ipFamilies: + {{- range .IPFamilies }} + - {{ . }} + {{- end }} ports: - name: client port: {{ .ServicePort }} diff --git a/pkg/kubenest/manifest/controlplane/apiserver/mainfests_service_test.go b/pkg/kubenest/manifest/controlplane/apiserver/mainfests_service_test.go new file mode 100644 index 000000000..1290184fd --- /dev/null +++ b/pkg/kubenest/manifest/controlplane/apiserver/mainfests_service_test.go @@ -0,0 +1,91 @@ +package apiserver + +import ( + "fmt" + "testing" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/yaml" + + "github.com/kosmos.io/kosmos/pkg/kubenest/constants" + "github.com/kosmos.io/kosmos/pkg/kubenest/util" + "github.com/kosmos.io/kosmos/pkg/utils" +) + +func ParseServerTemplate(apiServerServiceSubnet string) (*corev1.Service, error) { + ipFamilies := utils.IPFamilyGenerator(apiServerServiceSubnet) + apiserverServiceBytes, err := util.ParseTemplate(ApiserverService, struct { + ServiceName, Namespace, ServiceType string + ServicePort int32 + IPFamilies []corev1.IPFamily + }{ + ServiceName: fmt.Sprintf("%s-%s", "test", "apiserver"), + Namespace: "test-namespace", + ServiceType: constants.ApiServerServiceType, + ServicePort: 40010, + IPFamilies: ipFamilies, + }) + + if err != nil { + return nil, fmt.Errorf("error when parsing virtualClusterApiserver serive template: %s", err) + } + + apiserverService := &corev1.Service{} + if err := yaml.Unmarshal([]byte(apiserverServiceBytes), apiserverService); err != nil { + return nil, fmt.Errorf("error when decoding virtual cluster apiserver service: %s", err) + } + return apiserverService, nil +} + +func CompareIPFamilies(a []corev1.IPFamily, b []corev1.IPFamily) bool { + if len(a) != len(b) { + return false + } + for i, v := range a { + if v != b[i] { + return false + } + } + return true +} + +func TestSyncIPPool(t *testing.T) { + tests := []struct { + name string + input string + want []corev1.IPFamily + }{ + { + name: "ipv4 only", + input: "10.237.6.0/18", + want: []corev1.IPFamily{corev1.IPv4Protocol}, + }, + { + name: "ipv6 only", + input: "2409:8c2f:3800:0011::0a18:0000/114", + want: []corev1.IPFamily{corev1.IPv6Protocol}, + }, + { + name: "ipv4 first", + input: "10.237.6.0/18,2409:8c2f:3800:0011::0a18:0000/114", + want: []corev1.IPFamily{corev1.IPv4Protocol, corev1.IPv6Protocol}, + }, + { + name: "ipv6 first", + input: "2409:8c2f:3800:0011::0a18:0000/114,10.237.6.0/18", + want: []corev1.IPFamily{corev1.IPv6Protocol, corev1.IPv4Protocol}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + svc, err := ParseServerTemplate(tt.input) + if err != nil { + t.Fatalf("happen error: %s", err) + } + + if !CompareIPFamilies(svc.Spec.IPFamilies, tt.want) { + t.Errorf("ParseServerTemplate()=%v, want %v", svc.Spec.IPFamilies, tt.want) + } + }) + } +} diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 544f153fd..08db3f2c1 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -4,6 +4,8 @@ import ( "fmt" "os" "strings" + + corev1 "k8s.io/api/core/v1" ) func ContainsString(arr []string, s string) bool { @@ -42,3 +44,16 @@ func GenerateAddrStr(addr string, port string) string { } return fmt.Sprintf("%s:%s", addr, port) } + +func IPFamilyGenerator(apiServerServiceSubnet string) []corev1.IPFamily { + ipNetStrArray := strings.Split(apiServerServiceSubnet, ",") + ipFamilies := []corev1.IPFamily{} + for _, ipstr := range ipNetStrArray { + if IsIPv6(ipstr) { + ipFamilies = append(ipFamilies, corev1.IPv6Protocol) + } else { + ipFamilies = append(ipFamilies, corev1.IPv4Protocol) + } + } + return ipFamilies +}