mirror of
https://github.com/kubenetworks/kubevpn.git
synced 2025-10-29 09:53:02 +08:00
300 lines
8.3 KiB
Go
300 lines
8.3 KiB
Go
package util
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"net"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/containernetworking/cni/libcni"
|
|
log "github.com/sirupsen/logrus"
|
|
"github.com/wencaiwulue/kubevpn/pkg/config"
|
|
v12 "k8s.io/api/core/v1"
|
|
"k8s.io/apimachinery/pkg/api/errors"
|
|
"k8s.io/apimachinery/pkg/api/resource"
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/fields"
|
|
"k8s.io/apimachinery/pkg/util/sets"
|
|
"k8s.io/client-go/kubernetes"
|
|
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
|
"k8s.io/client-go/rest"
|
|
"k8s.io/utils/pointer"
|
|
"sigs.k8s.io/yaml"
|
|
)
|
|
|
|
// get cidr by dump cluster info
|
|
func getCIDRByDumpClusterInfo(clientset *kubernetes.Clientset) ([]*net.IPNet, error) {
|
|
p, err := clientset.CoreV1().Pods("kube-system").List(context.Background(), v1.ListOptions{
|
|
FieldSelector: fields.OneTermEqualSelector("status.phase", string(v12.PodRunning)).String(),
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
marshal, err := yaml.Marshal(p)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
svcCIDR := `service-cluster-ip-range`
|
|
podCIDR := `cluster-cidr`
|
|
reader := bufio.NewReader(bytes.NewBufferString(string(marshal)))
|
|
pod := sets.NewString()
|
|
svc := sets.NewString()
|
|
v4P := regexp.MustCompile(v4)
|
|
v6P := regexp.MustCompile(v6)
|
|
for {
|
|
line, _, err := reader.ReadLine()
|
|
if err != nil {
|
|
break
|
|
}
|
|
if strings.Contains(string(line), svcCIDR) {
|
|
ipv4 := v4P.FindAllString(string(line), -1)
|
|
ipv6 := v6P.FindAllString(string(line), -1)
|
|
svc.Insert(ipv4...).Insert(ipv6...)
|
|
}
|
|
if strings.Contains(string(line), podCIDR) {
|
|
ipv4 := v4P.FindAllString(string(line), -1)
|
|
ipv6 := v6P.FindAllString(string(line), -1)
|
|
pod.Insert(ipv4...).Insert(ipv6...)
|
|
}
|
|
}
|
|
if pod.Len() != 1 {
|
|
return nil, fmt.Errorf("pod cidr range is not 1, real: %d", pod.Len())
|
|
}
|
|
if svc.Len() != 1 {
|
|
return nil, fmt.Errorf("service cidr range is not 1, real: %d", pod.Len())
|
|
}
|
|
_, ipnet, err := net.ParseCIDR(pod.List()[0])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
_, ipnet1, err := net.ParseCIDR(svc.List()[0])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return []*net.IPNet{ipnet, ipnet1}, nil
|
|
}
|
|
|
|
func getCIDRFromCNI(clientset *kubernetes.Clientset, restclient *rest.RESTClient, restconfig *rest.Config, namespace string) ([]*net.IPNet, error) {
|
|
pod, err := createCIDRPod(clientset, namespace)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var cmd = `grep -a -R "service-cluster-ip-range\|cluster-ip-range\|cluster-cidr\|cidr" /etc/cni/proc/*/cmdline | grep -a -v grep`
|
|
|
|
var result []*net.IPNet
|
|
content, err := Shell(clientset, restclient, restconfig, pod.Name, pod.Namespace, cmd)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
result = parseCIDRFromString(content)
|
|
|
|
if len(result) != 2 {
|
|
return nil, fmt.Errorf("can not found any cidr")
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func getServiceCIDRByCreateSvc(serviceInterface corev1.ServiceInterface) (*net.IPNet, error) {
|
|
defaultCIDRIndex := "valid IPs is"
|
|
_, err := serviceInterface.Create(context.Background(), &v12.Service{
|
|
ObjectMeta: v1.ObjectMeta{GenerateName: "foo-svc-"},
|
|
Spec: v12.ServiceSpec{Ports: []v12.ServicePort{{Port: 80}}, ClusterIP: "0.0.0.0"},
|
|
}, v1.CreateOptions{})
|
|
if err != nil {
|
|
idx := strings.LastIndex(err.Error(), defaultCIDRIndex)
|
|
if idx != -1 {
|
|
_, cidr, err := net.ParseCIDR(strings.TrimSpace(err.Error()[idx+len(defaultCIDRIndex):]))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return cidr, nil
|
|
}
|
|
return nil, fmt.Errorf("can not found any keyword of service cidr info, err: %s", err.Error())
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
func getPodCIDRFromCNI(clientset *kubernetes.Clientset, restclient *rest.RESTClient, restconfig *rest.Config, namespace string) (*net.IPNet, error) {
|
|
pod, err := createCIDRPod(clientset, namespace)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var cmd = "cat /etc/cni/net.d/*.conflist"
|
|
|
|
content, err := Shell(clientset, restclient, restconfig, pod.Name, pod.Namespace, cmd)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
conf, err := libcni.ConfListFromFile(content)
|
|
if err == nil {
|
|
log.Infoln("get cni config", conf.Name)
|
|
}
|
|
|
|
result := parseCIDRFromString(content)
|
|
|
|
if len(result) == 0 {
|
|
return nil, fmt.Errorf("can not found any cidr")
|
|
}
|
|
if len(result) != 1 {
|
|
return nil, fmt.Errorf("pod cidr range is not 1, real: %d", len(result))
|
|
}
|
|
|
|
return result[0], nil
|
|
}
|
|
|
|
const name = "cni-net-dir-kubevpn"
|
|
|
|
func createCIDRPod(clientset *kubernetes.Clientset, namespace string) (*v12.Pod, error) {
|
|
var procName = "proc-dir-kubevpn"
|
|
pod := &v12.Pod{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Name: name,
|
|
Namespace: namespace,
|
|
},
|
|
Spec: v12.PodSpec{
|
|
Volumes: []v12.Volume{
|
|
{
|
|
Name: name,
|
|
VolumeSource: v12.VolumeSource{
|
|
HostPath: &v12.HostPathVolumeSource{
|
|
Path: config.DefaultNetDir,
|
|
Type: (*v12.HostPathType)(pointer.String(string(v12.HostPathDirectoryOrCreate))),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: procName,
|
|
VolumeSource: v12.VolumeSource{
|
|
HostPath: &v12.HostPathVolumeSource{
|
|
Path: config.Proc,
|
|
Type: (*v12.HostPathType)(pointer.String(string(v12.HostPathDirectoryOrCreate))),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Containers: []v12.Container{
|
|
{
|
|
Name: name,
|
|
Image: config.Image,
|
|
Command: []string{"tail", "-f", "/dev/null"},
|
|
Resources: v12.ResourceRequirements{
|
|
Requests: map[v12.ResourceName]resource.Quantity{
|
|
v12.ResourceCPU: resource.MustParse("16m"),
|
|
v12.ResourceMemory: resource.MustParse("16Mi"),
|
|
},
|
|
Limits: map[v12.ResourceName]resource.Quantity{
|
|
v12.ResourceCPU: resource.MustParse("16m"),
|
|
v12.ResourceMemory: resource.MustParse("16Mi"),
|
|
},
|
|
},
|
|
VolumeMounts: []v12.VolumeMount{
|
|
{
|
|
Name: name,
|
|
ReadOnly: true,
|
|
MountPath: config.DefaultNetDir,
|
|
},
|
|
{
|
|
Name: procName,
|
|
ReadOnly: true,
|
|
MountPath: "/etc/cni" + config.Proc,
|
|
},
|
|
},
|
|
ImagePullPolicy: v12.PullIfNotPresent,
|
|
},
|
|
},
|
|
Affinity: &v12.Affinity{
|
|
NodeAffinity: &v12.NodeAffinity{
|
|
PreferredDuringSchedulingIgnoredDuringExecution: []v12.PreferredSchedulingTerm{
|
|
{
|
|
Weight: 50,
|
|
Preference: v12.NodeSelectorTerm{
|
|
MatchExpressions: []v12.NodeSelectorRequirement{
|
|
{
|
|
Key: "node-role.kubernetes.io/master",
|
|
Operator: v12.NodeSelectorOpExists,
|
|
},
|
|
{
|
|
Key: "node-role.kubernetes.io/control-plane",
|
|
Operator: v12.NodeSelectorOpExists,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Tolerations: []v12.Toleration{
|
|
{
|
|
Key: "node-role.kubernetes.io/master",
|
|
Operator: v12.TolerationOpEqual,
|
|
Effect: v12.TaintEffectNoSchedule,
|
|
}, {
|
|
Key: "node-role.kubernetes.io/control-plane",
|
|
Operator: v12.TolerationOpEqual,
|
|
Effect: v12.TaintEffectNoSchedule,
|
|
},
|
|
},
|
|
TopologySpreadConstraints: []v12.TopologySpreadConstraint{
|
|
{
|
|
MaxSkew: 1,
|
|
TopologyKey: "kubernetes.io/hostname",
|
|
WhenUnsatisfiable: v12.ScheduleAnyway,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
get, err := clientset.CoreV1().Pods(pod.Namespace).Get(context.Background(), pod.Name, v1.GetOptions{})
|
|
if errors.IsNotFound(err) || get.Status.Phase != v12.PodRunning {
|
|
pod, err = clientset.CoreV1().Pods(namespace).Create(context.Background(), pod, v1.CreateOptions{})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = WaitPod(clientset.CoreV1().Pods(namespace), v1.ListOptions{
|
|
FieldSelector: fields.OneTermEqualSelector("metadata.name", pod.Name).String(),
|
|
}, func(pod *v12.Pod) bool {
|
|
return pod.Status.Phase == v12.PodRunning
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return pod, nil
|
|
}
|
|
|
|
func getPodCIDRFromPod(clientset *kubernetes.Clientset, namespace string, svc *net.IPNet) ([]*net.IPNet, error) {
|
|
get, err := clientset.CoreV1().Pods(namespace).Get(context.Background(), name, v1.GetOptions{})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ip := net.ParseIP(get.Status.PodIP)
|
|
return []*net.IPNet{svc, {IP: ip, Mask: svc.Mask}}, nil
|
|
}
|
|
|
|
func parseCIDRFromString(content string) (result []*net.IPNet) {
|
|
ipv4 := regexp.MustCompile(v4).FindAllString(content, -1)
|
|
ipv6 := regexp.MustCompile(v6).FindAllString(content, -1)
|
|
|
|
for _, s := range ipv4 {
|
|
_, ipNet, err := net.ParseCIDR(s)
|
|
if err == nil {
|
|
result = append(result, ipNet)
|
|
}
|
|
}
|
|
|
|
for _, s := range ipv6 {
|
|
_, ipNet, err := net.ParseCIDR(s)
|
|
if err == nil {
|
|
result = append(result, ipNet)
|
|
}
|
|
}
|
|
return result
|
|
}
|