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