package main import ( "bytes" "context" "crypto/tls" "encoding/json" "fmt" "log" "net/http" "os" "strings" "time" "golang.org/x/crypto/ssh" ) type Cluster struct { ID string `json:"id"` Name string `json:"name"` } type ClustersResponse struct { Data []Cluster `json:"data"` } type RegistrationTokenResponse struct { Command string `json:"command"` } type NodeCommandResponse struct { NodeCommand string `json:"nodeCommand"` } func createRegistrationToken(serverURL, bearerToken, clusterID string) (string, error) { url := fmt.Sprintf("%s/v3/clusterregistrationtokens", serverURL) payload := strings.NewReader(fmt.Sprintf(`{ "clusterId": "%s" }`, clusterID)) req, err := http.NewRequest("POST", url, payload) if err != nil { return "", fmt.Errorf("failed to create registration token request: %v", err) } req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", bearerToken)) req.Header.Set("Content-Type", "application/json") client := createHTTPClient() resp, err := client.Do(req) if err != nil { return "", fmt.Errorf("failed to create registration token: %v", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusCreated { return "", fmt.Errorf("failed to create registration token: unexpected status code %d", resp.StatusCode) } var tokenResp RegistrationTokenResponse err = json.NewDecoder(resp.Body).Decode(&tokenResp) if err != nil { return "", fmt.Errorf("failed to decode registration token response: %v", err) } return tokenResp.Command, nil } func getNodeCommand(serverURL, bearerToken, clusterID string) (string, error) { url := fmt.Sprintf("%s/v3/clusterregistrationtokens", serverURL) payload := map[string]interface{}{ "clusterId": clusterID, } payloadBytes, err := json.Marshal(payload) if err != nil { return "", fmt.Errorf("failed to marshal payload: %v", err) } req, err := http.NewRequest("POST", url, bytes.NewReader(payloadBytes)) if err != nil { return "", fmt.Errorf("failed to create node command request: %v", err) } req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", bearerToken)) req.Header.Set("Content-Type", "application/json") client := createHTTPClient() resp, err := client.Do(req) if err != nil { return "", fmt.Errorf("failed to get node command: %v", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusCreated { return "", fmt.Errorf("failed to get node command: unexpected status code %d", resp.StatusCode) } var nodeCommandResponse NodeCommandResponse err = json.NewDecoder(resp.Body).Decode(&nodeCommandResponse) if err != nil { return "", fmt.Errorf("failed to decode node command response: %v", err) } return nodeCommandResponse.NodeCommand, nil } func getClusterID(serverURL, bearerToken, clusterName string) (string, error) { url := fmt.Sprintf("%s/v3/clusters", serverURL) req, err := http.NewRequest("GET", url, nil) if err != nil { return "", fmt.Errorf("failed to create cluster list request: %v", err) } req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", bearerToken)) client := createHTTPClient() resp, err := client.Do(req) if err != nil { return "", fmt.Errorf("failed to get cluster list: %v", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return "", fmt.Errorf("failed to get cluster list: unexpected status code %d", resp.StatusCode) } // Parse the API response var clustersResponse ClustersResponse err = json.NewDecoder(resp.Body).Decode(&clustersResponse) if err != nil { return "", fmt.Errorf("failed to decode cluster list response: %v", err) } // Print cluster names for _, cluster := range clustersResponse.Data { if cluster.Name == clusterName { fmt.Printf("Cluster ID: %s, Name: %s\n", cluster.ID, cluster.Name) return cluster.ID, nil } } return "", fmt.Errorf("cluster '%s' not found", clusterName) } // Create an HTTP client with insecure TLS configuration func createHTTPClient() *http.Client { transport := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, } return &http.Client{Transport: transport} } func sshExec(nodeIP, sshUsername, sshPassword, remoteSSHCommand string, sshPort int) (string, error) { // SSH 连接配置 config := &ssh.ClientConfig{ User: sshUsername, Auth: []ssh.AuthMethod{ ssh.Password(sshPassword), }, HostKeyCallback: ssh.InsecureIgnoreHostKey(), } // 连接到远程服务器 client, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", nodeIP, sshPort), config) if err != nil { return "", fmt.Errorf("failed to connect to node %s: %v", nodeIP, err) } defer client.Close() // 创建会话 session, err := client.NewSession() if err != nil { return "", fmt.Errorf("failed to create SSH session: %v", err) } defer session.Close() // 创建一个具有超时的上下文 ctx, cancel := context.WithTimeout(context.Background(), 300*time.Second) defer cancel() // 通过会话执行远程命令 outputChan := make(chan string) errorChan := make(chan error) go func() { // 创建一个连接标准输入的管道 stdinPipe, err := session.StdinPipe() if err != nil { errorChan <- fmt.Errorf("failed to create stdin pipe: %v", err) return } // 启动会话 if err := session.Start(fmt.Sprintf("sudo -SE %s", remoteSSHCommand)); err != nil { errorChan <- fmt.Errorf("failed to start command: %v err:%v", remoteSSHCommand, err) return } // 将密码写入标准输入管道 _, err = fmt.Fprintf(stdinPipe, "%s\n", sshPassword) if err != nil { errorChan <- fmt.Errorf("failed to write password to stdin: %v", err) return } // 等待会话结束 if err := session.Wait(); err != nil { errorChan <- fmt.Errorf("command execution failed: %v err:%v", remoteSSHCommand, err) return } outputChan <- "" }() // 等待结果或超时 select { case <-ctx.Done(): // 关闭会话以终止远程命令 session.Close() // 等待会话关闭的 goroutine 结束 <-outputChan return "", fmt.Errorf("SSH command execution timed out") case err := <-errorChan: return "", err case <-outputChan: fmt.Printf("Command: %v executed on the remote server: %s\n", remoteSSHCommand, nodeIP) return "", nil } } // 安装Docker func installDocker(nodeIP, sshUsername, sshPassword string, sshPort int) error { // 检查Docker是否已安装 checkCommand := "which docker" _, err := sshExec(nodeIP, sshUsername, sshPassword, checkCommand, sshPort) if err == nil { fmt.Println("Docker is already installed on the remote server.") return nil } // 安装Docker installCommand := "sudo curl -fsSL https://get.docker.com -o get-docker.sh && sudo sh get-docker.sh" _, err = sshExec(nodeIP, sshUsername, sshPassword, installCommand, sshPort) if err != nil { return fmt.Errorf("failed to install Docker on the remote server: %v", err) } fmt.Println("Docker has been installed on the remote server.") return nil } // 安装kubectl func installKubectl(nodeIP, sshUsername, sshPassword string, sshPort int) error { // 检查kubectl是否已安装 checkCommand := "which kubectl" _, err := sshExec(nodeIP, sshUsername, sshPassword, checkCommand, sshPort) if err == nil { fmt.Println("kubectl is already installed on the remote server.") return nil } // 安装kubectl installCommand := "sudo curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl && sudo chmod +x kubectl && sudo mv kubectl /usr/local/bin/" _, err = sshExec(nodeIP, sshUsername, sshPassword, installCommand, sshPort) if err != nil { return fmt.Errorf("failed to install kubectl on the remote server: %v", err) } fmt.Println("kubectl has been installed on the remote server.") return nil } type Node struct { ClusterName string `json:"clusterName"` Roles []string `json:"roles"` IP string `json:"ip"` SSHUsername string `json:"sshUsername"` SSHPassword string `json:"sshPassword"` SSHPort int `json:"sshPort"` } type RancherConfig struct { RancherURL string `json:"rancherURL"` BearerToken string `json:"bearerToken"` } type ClusterCreateRequest struct { Name string `json:"name"` Driver string `json:"driver"` ControlPlaneProvider string `json:"controlPlaneProvider"` WorkerNodeProvider string `json:"workerNodeProvider"` RancherKubernetesEngineConfig interface{} `json:"rancherKubernetesEngineConfig"` // Add other cluster properties as needed } type ClusterCreateResponse struct { ID string `json:"id"` Name string `json:"name"` // Add other response fields as needed } func createClusterData(clusterName string) []byte { requestBody := ClusterCreateRequest{ Name: clusterName, Driver: "custom", ControlPlaneProvider: "external", WorkerNodeProvider: "external", RancherKubernetesEngineConfig: map[string]interface{}{ "ignoreDockerVersion": true, "kubernetesVersion": "v1.20.15-rancher2-1", // Add other RKE config properties as needed }, // Add other cluster properties as needed } requestBodyBytes, err := json.Marshal(requestBody) if err != nil { log.Fatalf("Failed to marshal request body: %v", err) } return requestBodyBytes } // Deploy Kubernetes roles on a node using SSH func deployk8sRolesOnNode(nodeIP, sshUsername, sshPassword, remoteSSHCommand string, sshPort int, roles []string) error { rancherAgentInstalled, err := isRancherAgentInstalled(nodeIP, sshUsername, sshPassword, sshPort) if err == nil { return nil } if !rancherAgentInstalled { // Add role parameters to nodeCommand for _, role := range roles { switch role { case "etcd": remoteSSHCommand += " --etcd" case "controlplane": remoteSSHCommand += " --controlplane" case "worker": remoteSSHCommand += " --worker" default: log.Fatalf("invalid role specified : %s", role) } } _, err := sshExec(nodeIP, sshUsername, sshPassword, remoteSSHCommand, sshPort) if err != nil { return fmt.Errorf("failed to deploy Kubernetes roles on the remote server: %v", err) } return nil } func isRancherInstalled(ip, username, password string, sshPort int) (bool, error) { // 检查Rancher容器是否已运行 checkRancherCommand := "sudo docker ps --format '{{.Image}}' | grep -q rancher/rancher:v2.5.17" _, err := sshExec(ip, username, password, checkRancherCommand, sshPort) if err != nil { // 如果执行命令出错,则说明Rancher未安装或发生其他错误 return false, fmt.Errorf("failed to check Rancher installation: %v", err) } return true, nil } func isRancherAgentInstalled(ip, username, password string, sshPort int) (bool, error) { // 检查Rancher容器是否已运行 checkRancherCommand := "sudo docker ps --format '{{.Image}}' | grep -q rancher/rancher-agent:v2.5." _, err := sshExec(ip, username, password, checkRancherCommand, sshPort) if err != nil { // 如果执行命令出错,则说明Rancher未安装或发生其他错误 return false, fmt.Errorf("failed to check Rancher installation: %v", err) } return true, nil } func installRancher(ip, username, password string, sshPort int) error { rancherInstalled, err := isRancherInstalled(ip, username, password, sshPort) if err == nil { return nil } if !rancherInstalled { // 安装Rancher命令 rancherCommand := "sudo docker run --privileged -d --restart=unless-stopped -p 8081:80 -p 8443:443 -v /opt/rancher:/var/lib/rancher registry.cn-hangzhou.aliyuncs.com/rancher/rancher:v2.5.17" _, err = sshExec(ip, username, password, rancherCommand, sshPort) if err != nil { return fmt.Errorf("failed to install Rancher: %v", err) } } else { fmt.Println("Rancher is already installed on the remote server.") } return nil } func installDockerAndRancher(ip, username, password string, sshPort int) error { // 安装Docker命令 err := installDocker(ip, username, password, sshPort) if err != nil { return err } // 安装Rancher命令 err = installRancher(ip, username, password, sshPort) if err != nil { return err } return nil } func createCluster(rancherConfig RancherConfig, clusterName string) error { requestBody := createClusterData(clusterName) url := fmt.Sprintf("%s/v3/clusters", rancherConfig.RancherURL) req, err := http.NewRequest("POST", url, bytes.NewBuffer(requestBody)) if err != nil { return fmt.Errorf("Failed to create HTTP request: %v", err) } req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", rancherConfig.BearerToken)) req.Header.Set("Content-Type", "application/json") client := createHTTPClient() resp, err := client.Do(req) if err != nil { return fmt.Errorf("Failed to send HTTP request: %v", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusCreated { return fmt.Errorf("Failed to create cluster, status code: %d", resp.StatusCode) } var responseBody ClusterCreateResponse err = json.NewDecoder(resp.Body).Decode(&responseBody) if err != nil { return fmt.Errorf("Failed to decode response body: %v", err) } fmt.Printf("Cluster created: ID=%s, Name=%s\n", responseBody.ID, responseBody.Name) return nil } func main() { clusterName := "kubernetus" nodes := []Node{ { ClusterName: clusterName, Roles: []string{"etcd", "controlplane", "worker"}, IP: "192.168.20.189", SSHUsername: "basic", SSHPassword: "123", SSHPort: 22, }, { ClusterName: clusterName, Roles: []string{"worker"}, IP: "192.168.20.10", SSHUsername: "basic", SSHPassword: "123", SSHPort: 22, }, { ClusterName: clusterName, Roles: []string{"worker"}, IP: "192.168.20.115", SSHUsername: "basic", SSHPassword: "alien123", SSHPort: 22, }, // Add more nodes here if needed } //install rancher on master node err := installDockerAndRancher(nodes[0].IP, nodes[0].SSHUsername, nodes[0].SSHPassword, nodes[0].SSHPort) if err != nil { log.Fatalf("Failed to install Rancher: %v", err) } // Create the cluster // Rancher configuration /*rancherConfig := RancherConfig{ RancherURL: "https://192.168.20.119:8443", BearerToken: "token-nnrsc:w68zdt8s47fnpjd5xqdl5hhzpz4j2d56kt5nx49nsswcbpdzc28kh5", }*/ rancherConfig := RancherConfig{ RancherURL: "https://192.168.20.189:8443", BearerToken: "token-t4cdf:h7zhmbvbzdvd9mmjw8zmt8rh4m7rl5gtqpqljlhl9tlr2z26j9lf4l", } // Deploy clusterId clusterID, err := getClusterID(rancherConfig.RancherURL, rancherConfig.BearerToken, clusterName) if err != nil { log.Fatal(err) err = createCluster(rancherConfig, clusterName) if err != nil { log.Fatalf("Failed to create cluster: %v", err) } fmt.Printf("Cluster created: %s\n", clusterName) clusterID, err = getClusterID(rancherConfig.RancherURL, rancherConfig.BearerToken, clusterName) if err != nil { log.Fatal(err) } } fmt.Println(clusterID) // Deploy nodeCommand nodeCommand, err := getNodeCommand(rancherConfig.RancherURL, rancherConfig.BearerToken, clusterID) if err != nil { log.Fatal(err) } fmt.Println(nodeCommand) for _, node := range nodes { //Deploy Docker on each node err = installDocker(node.IP, node.SSHUsername, node.SSHPassword, node.SSHPort) if err != nil { log.Fatal(err) } // Deploy Kubectl on each node err = installKubectl(node.IP, node.SSHUsername, node.SSHPassword, node.SSHPort) if err != nil { log.Fatal(err) } // Deploy Kubernetes roles on each node err = deployk8sRolesOnNode(node.IP, node.SSHUsername, node.SSHPassword, nodeCommand, node.SSHPort, node.Roles) if err != nil { log.Fatal(err) } } os.Exit(0) }