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