New file |
| | |
| | | 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) |
| | | } |