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