From 8d94bb910c55d0da24c641bf38c2b0e5d8950f2c Mon Sep 17 00:00:00 2001
From: cheliequan <liequanche@126.com>
Date: 星期三, 24 五月 2023 15:20:11 +0800
Subject: [PATCH] 增加自动安装rancher、docker和kubectl和创建集群相关函数
---
src/cluster/cluster.go | 545 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 545 insertions(+), 0 deletions(-)
diff --git a/src/cluster/cluster.go b/src/cluster/cluster.go
new file mode 100644
index 0000000..68686a0
--- /dev/null
+++ b/src/cluster/cluster.go
@@ -0,0 +1,545 @@
+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 {
+ // 妫�鏌ocker鏄惁宸插畨瑁�
+ 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 {
+ // 妫�鏌ubectl鏄惁宸插畨瑁�
+ 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) {
+ // 妫�鏌ancher瀹瑰櫒鏄惁宸茶繍琛�
+ 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) {
+ // 妫�鏌ancher瀹瑰櫒鏄惁宸茶繍琛�
+ 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)
+}
--
Gitblit v1.8.0