cheliequan
2023-05-22 62aacec7bb21854e26cf619e49a9a0e533e5c10f
创建、删除和测试函数在一个文件中
1个文件已添加
523 ■■■■■ 已修改文件
src/main/main.go 523 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/main.go
New file
@@ -0,0 +1,523 @@
package main
import (
    "context"
    "flag"
    "fmt"
    "log"
    "math/rand"
    "os"
    "path/filepath"
    "time"
    appsv1 "k8s.io/api/apps/v1"
    apiv1 "k8s.io/api/core/v1"
    "k8s.io/apimachinery/pkg/api/errors"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/apimachinery/pkg/util/intstr"
    "k8s.io/client-go/kubernetes"
    "k8s.io/client-go/tools/clientcmd"
)
var (
    replicas      int32 = 3
    port          int32 = 9081
    usedNodePorts       = make(map[int32]bool)
    namespaces          = []string{"guangsheng", "geruimi", "tongsheng"}
)
func main() {
    createCmd := flag.NewFlagSet("create", flag.ExitOnError)
    deleteCmd := flag.NewFlagSet("delete", flag.ExitOnError)
    testCmd := flag.NewFlagSet("test", flag.ExitOnError)
    createNamespace := createCmd.String("ns", "", "Namespace name")
    createDeployment := createCmd.String("deployment", "", "Deployment name")
    createService := createCmd.String("service", "", "Service name")
    deleteNamespace := deleteCmd.String("ns", "", "Namespace name")
    deleteDeployment := deleteCmd.String("deployment", "", "Deployment name")
    deleteService := deleteCmd.String("service", "", "Service name")
    if len(os.Args) < 2 {
        fmt.Println("create/delete/test command is required")
        os.Exit(1)
    }
    switch os.Args[1] {
    case "create":
        createCmd.Parse(os.Args[2:])
    case "delete":
        deleteCmd.Parse(os.Args[2:])
    case "test":
        testCmd.Parse(os.Args[2:])
    default:
        flag.PrintDefaults()
        os.Exit(1)
    }
    // 配置 Kubernetes 集群的 kubeconfig 路径
    kubeconfig := flag.String("kubeconfig", filepath.Join(homeDir(), ".kube", "config"), "kubeconfig file")
    flag.Parse()
    // 创建 Kubernetes 客户端
    config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
    if err != nil {
        panic(err.Error())
    }
    clientset, err := kubernetes.NewForConfig(config)
    if err != nil {
        panic(err.Error())
    }
    // 执行 create 命令
    if createCmd.Parsed() {
        if *createNamespace == "" || *createDeployment == "" || *createService == "" {
            fmt.Println("Namespace, Deployment, and Service names are required")
            createCmd.PrintDefaults()
            os.Exit(1)
        }
        err := CreateDeploymentAndService(clientset, *createNamespace, *createDeployment, *createService)
        if err != nil {
            panic(err)
        }
        nodeport, err := GetServiceNodePort(clientset, *createNamespace, *createService)
        if err != nil {
            panic(err)
        }
        log.Printf("Service NodePort: %d\n", nodeport)
    }
    // 执行 delete 命令
    if deleteCmd.Parsed() {
        if *deleteNamespace == "" || *deleteDeployment == "" || *deleteService == "" {
            fmt.Println("Namespace, Deployment, and Service names are required")
            deleteCmd.PrintDefaults()
            os.Exit(1)
        }
        err := DeleteResources(clientset, *deleteNamespace, *deleteDeployment, *deleteService)
        if err != nil {
            panic(err)
        }
        fmt.Println("Resources deleted.")
    }
    // 执行创建和删除测试
    if testCmd.Parsed() {
        fmt.Println("\033[97;40mRunning create and delete tests...\033[0m")
        // 创建测试
        fmt.Println("\033[97;40m--- Create Test ---\033[0m")
        for _, ns := range namespaces {
            deploymentName := ns
            serviceName := ns
            err := CreateDeploymentAndService(clientset, ns, deploymentName, serviceName)
            if err != nil {
                log.Printf("\033[97;41mFailed to create resources in namespace %s: %v\033[0m\n", ns, err)
            } else {
                nodeport, err := GetServiceNodePort(clientset, ns, ns)
                if err != nil {
                    panic(err)
                }
                log.Printf("Service NodePort: %d\n", nodeport)
                log.Printf("\033[97;42mSuccessfully created resources in namespace %s\033[0m\n", ns)
            }
        }
        // 延迟 1000 秒后删除创建的资源
        time.Sleep(1000 * time.Second)
        // 删除测试
        fmt.Println("\033[97;40m--- Delete Test ---\033[0m")
        // 删除多个 Namespace 下的相同名称的 Deployment 和 Service
        for _, ns := range namespaces {
            err = DeleteResources(clientset, ns, ns, ns)
            if err != nil {
                panic(err)
            }
        }
        fmt.Println("Resources deleted.")
    }
}
// CheckAndDeleteResources 检测并删除指定的 Namespace、Deployment 和 Service
func CheckAndDeleteResources(clientset *kubernetes.Clientset, namespace, deploymentName, serviceName string) error {
    fmt.Println("\033[1;37;40mChecking and deleting resources in Namespace:", namespace, "\033[0m")
    // 检查并删除 Deployment
    err := deleteDeploymentIfExists(clientset, namespace, deploymentName)
    if err != nil {
        return err
    }
    // 检查并删除 Service
    err = deleteServiceIfExists(clientset, namespace, serviceName)
    if err != nil {
        return err
    }
    return nil
}
// deleteDeploymentIfExists 检查并删除指定的 Deployment(如果存在)
func deleteDeploymentIfExists(clientset *kubernetes.Clientset, namespace, deploymentName string) error {
    fmt.Println("\033[1;37;40mChecking and deleting Deployment:", deploymentName, "\033[0m")
    err := clientset.AppsV1().Deployments(namespace).Delete(context.TODO(), deploymentName, metav1.DeleteOptions{})
    if err != nil {
        if errors.IsNotFound(err) {
            fmt.Printf("Deployment %s not found in Namespace %s\n", deploymentName, namespace)
        } else {
            return fmt.Errorf("failed to delete Deployment: %v", err)
        }
    } else {
        fmt.Printf("Deployment %s deleted from Namespace %s\n", deploymentName, namespace)
    }
    return nil
}
// deleteServiceIfExists 检查并删除指定的 Service(如果存在)
func deleteServiceIfExists(clientset *kubernetes.Clientset, namespace, serviceName string) error {
    fmt.Println("\033[1;37;40mChecking and deleting Service:", serviceName, "\033[0m")
    err := clientset.CoreV1().Services(namespace).Delete(context.TODO(), serviceName, metav1.DeleteOptions{})
    if err != nil {
        if errors.IsNotFound(err) {
            fmt.Printf("Service %s not found in Namespace %s\n", serviceName, namespace)
        } else {
            return fmt.Errorf("failed to delete Service: %v", err)
        }
    } else {
        fmt.Printf("Service %s deleted from Namespace %s\n", serviceName, namespace)
    }
    return nil
}
func CreateDeploymentAndService(clientset *kubernetes.Clientset, namespace, deploymentName, serviceName string) error {
    fmt.Println("\033[1;37;40mCreating resources in Namespace:", namespace, "\033[0m")
    // 检测并删除已存在的 Deployment 和 Service
    err := CheckAndDeleteResources(clientset, namespace, deploymentName, serviceName)
    if err != nil {
        return err
    }
    // 创建 Namespace
    err = createNamespace(clientset, namespace)
    if err != nil {
        return err
    }
    // 创建 Deployment
    err = createDeployment(clientset, namespace, deploymentName)
    if err != nil {
        return err
    }
    // 创建 Service
    err = createService(clientset, namespace, serviceName)
    if err != nil {
        return err
    }
    return nil
}
// createNamespace 创建指定的 Namespace
func createNamespace(clientset *kubernetes.Clientset, namespace string) error {
    log.Printf("Creating Namespace: %s\n", namespace)
    ns := &apiv1.Namespace{
        ObjectMeta: metav1.ObjectMeta{
            Name: namespace,
        },
    }
    _, err := clientset.CoreV1().Namespaces().Create(context.TODO(), ns, metav1.CreateOptions{})
    if err != nil {
        if !errors.IsAlreadyExists(err) {
            return fmt.Errorf("failed to create Namespace: %w", err)
        }
        log.Printf("Namespace %s already exists\n", namespace)
    } else {
        log.Printf("Namespace %s created\n", namespace)
    }
    return nil
}
func createDeployment(clientset *kubernetes.Clientset, namespace, deploymentName string) error {
    fmt.Println("\033[1;37;40mCreating Deployment:", deploymentName, "\033[0m")
    deployment := &appsv1.Deployment{
        ObjectMeta: metav1.ObjectMeta{
            Name: deploymentName,
        },
        Spec: appsv1.DeploymentSpec{
            Replicas: &replicas,
            Selector: &metav1.LabelSelector{
                MatchLabels: map[string]string{
                    "cid": namespace,
                },
            },
            Template: apiv1.PodTemplateSpec{
                ObjectMeta: metav1.ObjectMeta{
                    Labels: map[string]string{
                        "cid": namespace,
                    },
                },
                Spec: apiv1.PodSpec{
                    TopologySpreadConstraints: []apiv1.TopologySpreadConstraint{
                        {
                            MaxSkew:           1,
                            TopologyKey:       "kubernetes.io/hostname",
                            WhenUnsatisfiable: apiv1.DoNotSchedule,
                            LabelSelector: &metav1.LabelSelector{
                                MatchLabels: map[string]string{
                                    "cid": namespace,
                                },
                            },
                        },
                    },
                    Containers: []apiv1.Container{
                        {
                            Name:  namespace,
                            Image: "192.168.20.119/apsserver/apsserver:v0.2",
                            Env: []apiv1.EnvVar{
                                {
                                    Name:  "NODE_ID",
                                    Value: namespace,
                                },
                            },
                        },
                    },
                },
            },
        },
    }
    _, err := clientset.AppsV1().Deployments(namespace).Create(context.TODO(), deployment, metav1.CreateOptions{})
    if err != nil {
        if !errors.IsAlreadyExists(err) {
            return fmt.Errorf("failed to create Deployment: %v", err)
        }
        fmt.Printf("Deployment %s already exists in Namespace %s\n", deploymentName, namespace)
    } else {
        fmt.Printf("Deployment %s created in Namespace %s\n", deploymentName, namespace)
    }
    return nil
}
// createService 创建指定的 Service
func createService(clientset *kubernetes.Clientset, namespace, serviceName string) error {
    fmt.Println("\033[1;37;40mCreating Service:", serviceName, "\033[0m")
    nodePort, err := getRandomNodePort(clientset)
    if err != nil {
        return err
    }
    service := &apiv1.Service{
        ObjectMeta: metav1.ObjectMeta{
            Name: serviceName,
        },
        Spec: apiv1.ServiceSpec{
            Selector: map[string]string{
                "cid": namespace,
            },
            Type: apiv1.ServiceTypeNodePort,
            Ports: []apiv1.ServicePort{
                {
                    Name:       "http",
                    Protocol:   apiv1.ProtocolTCP,
                    Port:       port,
                    TargetPort: intstr.FromInt(int(port)),
                    NodePort:   nodePort,
                },
            },
        },
    }
    _, err = clientset.CoreV1().Services(namespace).Create(context.TODO(), service, metav1.CreateOptions{})
    if err != nil {
        if !errors.IsAlreadyExists(err) {
            return fmt.Errorf("failed to create Service: %v", err)
        }
        fmt.Printf("Service %s already exists in Namespace %s\n", serviceName, namespace)
    } else {
        fmt.Printf("Service %s created in Namespace %s\n", serviceName, namespace)
    }
    return nil
}
// getRandomNodePort 获取一个未使用的随机 NodePort
func getRandomNodePort(clientset *kubernetes.Clientset) (int32, error) {
    // 获取一个随机的 NodePort
    nodePort := int32(0)
    for {
        // 生成一个随机的 NodePort
        nodePort = generateRandomNodePort()
        // 检查该 NodePort 是否已被使用
        used, err := isNodePortUsed(clientset, nodePort)
        if err != nil {
            return 0, err
        }
        // 如果未被使用,则标记为已使用,并退出循环
        if !used {
            usedNodePorts[nodePort] = true
            break
        }
    }
    return nodePort, nil
}
// generateRandomNodePort 生成一个随机的 NodePort
func generateRandomNodePort() int32 {
    // 在范围 30000-32767 中生成随机数作为 NodePort
    return int32(rand.Intn(32767-30000+1) + 30000)
}
// isNodePortUsed 检查指定的 NodePort 是否已被使用
func isNodePortUsed(clientset *kubernetes.Clientset, nodePort int32) (bool, error) {
    // 获取所有 Service
    services, err := clientset.CoreV1().Services("").List(context.TODO(), metav1.ListOptions{})
    if err != nil {
        return false, err
    }
    // 检查每个 Service 的 NodePort 是否与指定的 NodePort 相同
    for _, svc := range services.Items {
        for _, port := range svc.Spec.Ports {
            if port.NodePort == nodePort {
                return true, nil
            }
        }
    }
    return false, nil
}
// DeleteResources 删除指定的 Namespace、Deployment 和 Service
func DeleteResources(clientset *kubernetes.Clientset, namespace, deploymentName, serviceName string) error {
    fmt.Println("\033[1;37;40mDeleting resources in Namespace:", namespace, "\033[0m")
    // 删除 Deployment
    err := deleteDeployment(clientset, namespace, deploymentName)
    if err != nil {
        return err
    }
    // 删除 Service
    err = deleteService(clientset, namespace, serviceName)
    if err != nil {
        return err
    }
    // 删除 Namespace
    err = deleteNamespace(clientset, namespace)
    if err != nil {
        return err
    }
    return nil
}
// deleteDeployment 删除指定的 Deployment
func deleteDeployment(clientset *kubernetes.Clientset, namespace, deploymentName string) error {
    fmt.Println("\033[1;37;40mDeleting Deployment:", deploymentName, "\033[0m")
    err := clientset.AppsV1().Deployments(namespace).Delete(context.TODO(), deploymentName, metav1.DeleteOptions{})
    if err != nil {
        if errors.IsNotFound(err) {
            fmt.Printf("Deployment %s not found in Namespace %s\n", deploymentName, namespace)
        } else {
            return fmt.Errorf("failed to delete Deployment: %v", err)
        }
    } else {
        fmt.Printf("Deployment %s deleted from Namespace %s\n", deploymentName, namespace)
    }
    return nil
}
// deleteService 删除指定的 Service
func deleteService(clientset *kubernetes.Clientset, namespace, serviceName string) error {
    fmt.Println("\033[1;37;40mDeleting Service:", serviceName, "\033[0m")
    err := clientset.CoreV1().Services(namespace).Delete(context.TODO(), serviceName, metav1.DeleteOptions{})
    if err != nil {
        if errors.IsNotFound(err) {
            fmt.Printf("Service %s not found in Namespace %s\n", serviceName, namespace)
        } else {
            return fmt.Errorf("failed to delete Service: %v", err)
        }
    } else {
        fmt.Printf("Service %s deleted from Namespace %s\n", serviceName, namespace)
    }
    return nil
}
// deleteNamespace 删除指定的 Namespace
func deleteNamespace(clientset *kubernetes.Clientset, namespace string) error {
    fmt.Println("\033[1;37;40mDeleting Namespace:", namespace, "\033[0m")
    err := clientset.CoreV1().Namespaces().Delete(context.TODO(), namespace, metav1.DeleteOptions{})
    if err != nil {
        if errors.IsNotFound(err) {
            fmt.Printf("Namespace %s not found\n", namespace)
        } else {
            return fmt.Errorf("failed to delete Namespace: %v", err)
        }
    } else {
        fmt.Printf("Namespace %s deleted\n", namespace)
    }
    return nil
}
// homeDir 获取当前用户的家目录路径
func homeDir() string {
    if h := os.Getenv("HOME"); h != "" {
        return h
    }
    return os.Getenv("USERPROFILE") // Windows 环境下获取用户目录
}
// GetServiceNodePort 获取指定 Service 的 NodePort
func GetServiceNodePort(clientset *kubernetes.Clientset, namespace, serviceName string) (int32, error) {
    svc, err := clientset.CoreV1().Services(namespace).Get(context.TODO(), serviceName, metav1.GetOptions{})
    if err != nil {
        return 0, fmt.Errorf("failed to get Service: %v", err)
    }
    // 检查 Service 类型是否为 NodePort
    if svc.Spec.Type != apiv1.ServiceTypeNodePort {
        return 0, fmt.Errorf("Service %s is not of type NodePort", serviceName)
    }
    // 获取第一个端口的 NodePort
    if len(svc.Spec.Ports) > 0 {
        return svc.Spec.Ports[0].NodePort, nil
    }
    return 0, fmt.Errorf("no ports defined for Service %s", serviceName)
}