package node

import (
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/astaxie/beego"
	"github.com/astaxie/beego/httplib"
	"k8s-console/dto"
	"k8s-console/dto/node"
	"k8s-console/utils"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"regexp"
	"strconv"
	"strings"
	"time"
)

var (
	infoLogger  = utils.InfoLogger
	errorLogger = utils.ErrorLogger
)

// NodeController operations for harborImage
type NodeController struct {
	beego.Controller
}

// URLMapping ...
func (c *NodeController) URLMapping() {
	c.Mapping("getNodeList", c.GetNodeList)
	c.Mapping("getNodeDetails", c.GetNodeDetails)
	c.Mapping("nodeDetailsUnschedulable", c.NodeDetailsUnschedulable)
	c.Mapping("nodeDetailsLabelsOperation", c.NodeDetailsLabelsOperation)
	c.Mapping("getCpuAndRamRateData", c.GetCpuAndRamRateData)
}

// Post ...
// @Title GetNodeList
// @Description get NodeList
// @Param	lang		query 	string	true	"api result language type"
// @Param	body		body 	node.GetNodeListRequestDto	true	""
// @Success 200
// @Failure 300 {object} dto.BaseResponseDto
// @router /getNodeList [post]
func (c *NodeController) GetNodeList() {
	lang := c.GetString("lang")
	infoLogger.Println("语言: ", lang)
	var requestDto node.GetNodeListRequestDto
	var errorResponseDto = dto.BaseResponseDto{300, utils.GetI18nLang(lang, "sysError")}
	var successResponseDto = dto.GetPageListBaseResponseDto{}
	successResponseDto.Status = 200
	if err := json.Unmarshal(c.Ctx.Input.RequestBody, &requestDto); err != nil {
		errorLogger.Println(err.Error())
		errorResponseDto.Message = utils.GetI18nLang(lang, "requestParamIncorrectFormat")
		c.Data["json"] = errorResponseDto
		c.ServeJSON()
		return
	}
	infoLogger.Println("查询集群所有节点请求参数: ", requestDto)

	pageIndex := requestDto.PageIndex
	pageSize := requestDto.PageSize
	if pageIndex < 0 {
		errorResponseDto.Message = utils.GetI18nLang(lang, "pageIndexRange")
		c.Data["json"] = errorResponseDto
		c.ServeJSON()
		return
	}
	if pageSize <= 0 {
		errorResponseDto.Message = utils.GetI18nLang(lang, "pageSizeRange")
		c.Data["json"] = errorResponseDto
		c.ServeJSON()
		return
	}
	//nameSpace := requestDto.NameSpace

	// 获取k8s客户端
	k8sClient := utils.GetK8sClient()
	if k8sClient == nil {
		errorResponseDto.Message = utils.GetI18nLang(lang, "createK8sConFail")
		c.Data["json"] = errorResponseDto
		c.ServeJSON()
		return
	}

	//查询集群下所有节点node信息
	nodeList, err := k8sClient.CoreV1().Nodes().List(metav1.ListOptions{})
	if err != nil {
		errorLogger.Printf("查询集群所有节点node失败: %s", err.Error())
		errorResponseDto.Message = utils.GetI18nLang(lang, "getNodeList_getTheClusterNodesFail")
		c.Data["json"] = errorResponseDto
		c.ServeJSON()
		return
	}

	itemList := nodeList.Items

	totalRecords := len(itemList)
	totalPages := 0

	result := []node.GetNodeListResultDataResponseDto{}
	for _, item := range nodeList.Items {
		creationTimestamp, err := utils.ParseDateToString(lang, item.CreationTimestamp.Time)
		if err != nil {
			errorLogger.Printf("时间格式转换失败: %s", err.Error())
		}
		nodeConditionType := string(item.Status.Conditions[len(item.Status.Conditions)-1].Type)
		nodeConditionStatus := string(item.Status.Conditions[len(item.Status.Conditions)-1].Status)
		nodeState := "NotReady" // 节点状态（下线）
		if nodeConditionType == "Ready" && nodeConditionStatus == "True" {
			nodeState = "Ready" // 节点状态（正常）
		}

		nodeRole := item.Labels["kubernetes.io/role"] // 节点角色

		// Unschedulable为true时，在节点状态后面加上"SchedulingDisabled"，对应页面显示为“禁止调度”
		//if item.Spec.Unschedulable {
		//	nodeState = nodeState + ",SchedulingDisabled" // 节点状态（正常/下线|不可调度）
		//}

		infoLogger.Println(item.Name)                           //节点名称
		infoLogger.Println(creationTimestamp)                   //节点已运行时间
		infoLogger.Println(item.Status.NodeInfo.KubeletVersion) //版本
		infoLogger.Println(nodeRole)                            //节点角色
		infoLogger.Println(nodeState)                           //节点状态
		infoLogger.Println(item.Spec.Unschedulable)             //节点禁止调度

		o := node.GetNodeListResultDataResponseDto{
			NodeName:          item.Name,
			NodeRole:          nodeRole,
			NodeState:         nodeState,
			NodeRuntime:       creationTimestamp,
			NodeVersion:       item.Status.NodeInfo.KubeletVersion,
			NodeUnschedulable: item.Spec.Unschedulable,
		}
		result = append(result, o)
	}

	resultList := []node.GetNodeListResultDataResponseDto{}
	if totalRecords > 0 {
		totalPages = totalRecords/pageSize + 1
		if totalRecords%pageSize == 0 {
			totalPages = totalRecords / pageSize
		}
		if totalRecords <= pageIndex*pageSize {
			errorResponseDto.Message = utils.GetI18nLang(lang, "pageIndexOutRange")
			c.Data["json"] = errorResponseDto
			c.ServeJSON()
			return
		}
		resultList = result[pageIndex*pageSize:]
		if len(result)-pageIndex*pageSize >= pageSize {
			resultList = result[pageIndex*pageSize : (pageIndex+1)*pageSize]
		}

	} else {
		if totalRecords < pageIndex*pageSize {
			errorResponseDto.Message = utils.GetI18nLang(lang, "pageIndexOutRange")
			c.Data["json"] = errorResponseDto
			c.ServeJSON()
			return
		}
	}

	successResponseDto.Result = resultList
	successResponseDto.PageInfo = dto.PageInfoBaseResponseDto{
		TotalRecords: totalRecords,
		TotalPages:   totalPages,
	}
	c.Data["json"] = successResponseDto
	c.ServeJSON()
}

// Post ...
// @Title NodeDetailsUnschedulable
// @Description NodeDetails Unschedulable
// @Param	lang		query 	string	true	"api result language type"
// @Param	body		body 	node.NodeDetailsOperationRequestDto	true	""
// @Success 200
// @Failure 300 {object} dto.BaseResponseDto
// @router /nodeDetailsUnschedulable [post]
func (c *NodeController) NodeDetailsUnschedulable() {
	lang := c.GetString("lang")
	infoLogger.Println("语言: ", lang)
	var requestDto node.NodeDetailsOperationRequestDto
	var responseDto = dto.BaseResponseDto{300, utils.GetI18nLang(lang, "sysError")}
	paramMap := make(map[string]interface{})

	if err := json.Unmarshal(c.Ctx.Input.RequestBody, &paramMap); err != nil {
		errorLogger.Println(err.Error())
		responseDto.Message = utils.GetI18nLang(lang, "requestParamIncorrectFormat")
		c.Data["json"] = responseDto
		c.ServeJSON()
		return
	}
	infoLogger.Println("节点详情禁止调度操作接口请求参数: ", paramMap)

	if err := json.Unmarshal(c.Ctx.Input.RequestBody, &requestDto); err != nil {
		errorLogger.Println(err.Error())
		responseDto.Message = utils.GetI18nLang(lang, "requestParamIncorrectFormat")
		c.Data["json"] = responseDto
		c.ServeJSON()
		return
	}

	if _, ok := paramMap["nodeName"]; !ok {
		responseDto.Message = "节点名称参数不能为空"
		c.Data["json"] = responseDto
		c.ServeJSON()
		return
	}

	if _, ok := paramMap["unschedulable"]; !ok {
		responseDto.Message = "不可调度参数不能为空"
		c.Data["json"] = responseDto
		c.ServeJSON()
		return
	}

	// 校验节点名称参数
	if strings.TrimSpace(paramMap["nodeName"].(string)) == "" {
		responseDto.Message = "节点名称参数不能为空"
		c.Data["json"] = responseDto
		c.ServeJSON()
		return
	}

	if paramMap["unschedulable"] == nil {
		responseDto.Message = "不可调度参数值类型有误"
		c.Data["json"] = responseDto
		c.ServeJSON()
		return
	}

	// 获取k8s客户端
	k8sClient := utils.GetK8sClient()
	if k8sClient == nil {
		responseDto.Message = utils.GetI18nLang(lang, "createK8sConFail")
		c.Data["json"] = responseDto
		c.ServeJSON()
		return
	}

	//根据节点名称查询该节点node详情信息
	nodeDetails, err := k8sClient.CoreV1().Nodes().Get(requestDto.NodeName, metav1.GetOptions{})
	if err != nil {
		errorLogger.Printf("根据节点名称%s查询该节点详情信息: %s", requestDto.NodeName, err.Error())
		responseDto.Message = err.Error()
		c.Data["json"] = responseDto
		c.ServeJSON()
		return
	}

	nodeDetails.Spec.Unschedulable = requestDto.Unschedulable

	_, err = k8sClient.CoreV1().Nodes().Update(nodeDetails)
	if err != nil {
		errorLogger.Printf("根据节点名称%s设置该节点禁止调度时异常: %s", requestDto.NodeName, err.Error())
		responseDto.Message = err.Error()
		c.Data["json"] = responseDto
		c.ServeJSON()
		return
	}

	responseDto.Status = 200
	responseDto.Message = utils.GetI18nLang(lang, "success")
	c.Data["json"] = responseDto
	c.ServeJSON()
}

// Post ...
// @Title NodeDetailsLabelsOperation
// @Description NodeDetails Labels Operation
// @Param	lang		query 	string	true	"api result language type"
// @Param	body		body 	node.NodeDetailsOperationRequestDto	true	""
// @Success 200
// @Failure 300 {object} dto.BaseResponseDto
// @router /nodeDetailsLabelsOperation [post]
func (c *NodeController) NodeDetailsLabelsOperation() {
	lang := c.GetString("lang")
	infoLogger.Println("语言: ", lang)
	var requestDto node.NodeDetailsOperationRequestDto
	var responseDto = dto.BaseResponseDto{300, utils.GetI18nLang(lang, "sysError")}

	if err := json.Unmarshal(c.Ctx.Input.RequestBody, &requestDto); err != nil {
		errorLogger.Println(err.Error())
		responseDto.Message = utils.GetI18nLang(lang, "requestParamIncorrectFormat")
		c.Data["json"] = responseDto
		c.ServeJSON()
		return
	}
	infoLogger.Println("节点详情标签增删改操作接口请求参数: ", requestDto)

	// 校验节点名称参数
	if strings.TrimSpace(requestDto.NodeName) == "" {
		responseDto.Message = "节点名称参数不能为空"
		c.Data["json"] = responseDto
		c.ServeJSON()
		return
	}

	if strings.TrimSpace(requestDto.OperationType) == "" {
		responseDto.Message = "操作类型参数不能为空"
		c.Data["json"] = responseDto
		c.ServeJSON()
		return
	}

	if strings.TrimSpace(requestDto.LabelKey) == "" {
		responseDto.Message = "标签键参数不能为空"
		c.Data["json"] = responseDto
		c.ServeJSON()
		return
	}

	if strings.TrimSpace(requestDto.LabelValue) == "" {
		responseDto.Message = "标签值参数不能为空"
		c.Data["json"] = responseDto
		c.ServeJSON()
		return
	}

	if len([]rune(requestDto.LabelKey)) > 63 || len([]rune(requestDto.LabelValue)) > 63 {
		responseDto.Message = "标签键值字符最大长度不能超过63"
		c.Data["json"] = responseDto
		c.ServeJSON()
		return
	}

	labelKeyMatched, err := regexp.MatchString("^[a-zA-Z0-9]+([-_\\./]*[a-zA-Z0-9]+)*$", requestDto.LabelKey)
	if err != nil {
		errorLogger.Printf("根据节点名称%s验证节点标签键值匹配正则表达式时发生异常，错误信息: %s", requestDto.NodeName, err.Error())
		responseDto.Message = err.Error()
		c.Data["json"] = responseDto
		c.ServeJSON()
		return
	}
	if !labelKeyMatched {
		responseDto.Message = "节点标签键校验失败"
		c.Data["json"] = responseDto
		c.ServeJSON()
		return
	}

	labelValueMatched, err := regexp.MatchString("^[a-zA-Z0-9]+([-_\\./]*[a-zA-Z0-9]+)*$", requestDto.LabelValue)
	if err != nil {
		errorLogger.Printf("根据节点名称%s验证节点标签键值匹配正则表达式时发生异常，错误信息: %s", requestDto.NodeName, err.Error())
		responseDto.Message = err.Error()
		c.Data["json"] = responseDto
		c.ServeJSON()
		return
	}
	if !labelValueMatched {
		responseDto.Message = "节点标签值校验失败"
		c.Data["json"] = responseDto
		c.ServeJSON()
		return
	}

	// 获取k8s客户端
	k8sClient := utils.GetK8sClient()
	if k8sClient == nil {
		responseDto.Message = utils.GetI18nLang(lang, "createK8sConFail")
		c.Data["json"] = responseDto
		c.ServeJSON()
		return
	}

	//根据节点名称查询该节点node详情信息
	nodeDetails, err := k8sClient.CoreV1().Nodes().Get(requestDto.NodeName, metav1.GetOptions{})
	if err != nil {
		errorLogger.Printf("根据节点名称%s查询该节点详情信息: %s", requestDto.NodeName, err.Error())
		responseDto.Message = err.Error()
		c.Data["json"] = responseDto
		c.ServeJSON()
		return
	}

	labelsMap := nodeDetails.Labels

	// 增加标签
	if strings.ToLower(requestDto.OperationType) == "add" {
		// 验证传入的标签键是否存在
		if _, ok := labelsMap[requestDto.LabelKey]; ok {
			responseDto.Message = "标签键已存在"
			c.Data["json"] = responseDto
			c.ServeJSON()
			return
		}
		labelsMap[requestDto.LabelKey] = requestDto.LabelValue
	} else if strings.ToLower(requestDto.OperationType) == "delete" { // 删除标签
		delete(labelsMap, requestDto.LabelKey)
	} else if strings.ToLower(requestDto.OperationType) == "update" { // 修改标签
		if strings.TrimSpace(requestDto.OldLabelKey) == "" || strings.TrimSpace(requestDto.OldLabelValue) == "" {
			responseDto.Message = "操作类型为更新时，修改前的标签键值对参数不能为空"
			c.Data["json"] = responseDto
			c.ServeJSON()
			return
		}
		// 先直接删除修改前的标签键值对
		delete(labelsMap, requestDto.OldLabelKey)
		infoLogger.Println(labelsMap)
		// 然后判断修改之后的标签键值对是否存在，不存在则新增插入
		if _, ok := labelsMap[requestDto.LabelKey]; ok {
			responseDto.Message = "标签键已存在"
			c.Data["json"] = responseDto
			c.ServeJSON()
			return
		}
		labelsMap[requestDto.LabelKey] = requestDto.LabelValue
	}
	nodeDetails.Labels = labelsMap
	_, err = k8sClient.CoreV1().Nodes().Update(nodeDetails)
	if err != nil {
		errorLogger.Printf("根据节点名称%s增删改标签时异常: %s", requestDto.NodeName, err.Error())
		responseDto.Message = err.Error()
		c.Data["json"] = responseDto
		c.ServeJSON()
		return
	}

	responseDto.Status = 200
	responseDto.Message = utils.GetI18nLang(lang, "success")
	c.Data["json"] = responseDto
	c.ServeJSON()
}

// Post ...
// @Title GetNodeDetails
// @Description get NodeDetails
// @Param	lang		query 	string	true	"api result language type"
// @Param	body		body 	node.GetNodeDetailsRequestDto	true	""
// @Success 200
// @Failure 300 {object} dto.BaseResponseDto
// @router /getNodeDetails [post]
func (c *NodeController) GetNodeDetails() {
	lang := c.GetString("lang")
	infoLogger.Println("语言: ", lang)
	var requestDto node.GetNodeDetailsRequestDto
	var errorResponseDto = dto.BaseResponseDto{300, utils.GetI18nLang(lang, "sysError")}
	var successResponseDto = node.GetNodeDetailsResponseDto{}
	successResponseDto.Status = 200
	if err := json.Unmarshal(c.Ctx.Input.RequestBody, &requestDto); err != nil {
		errorLogger.Println(err.Error())
		errorResponseDto.Message = utils.GetI18nLang(lang, "requestParamIncorrectFormat")
		c.Data["json"] = errorResponseDto
		c.ServeJSON()
		return
	}
	infoLogger.Println("查询集群节点详情请求参数: ", requestDto)

	// 获取k8s客户端
	k8sClient := utils.GetK8sClient()
	if k8sClient == nil {
		errorResponseDto.Message = utils.GetI18nLang(lang, "createK8sConFail")
		c.Data["json"] = errorResponseDto
		c.ServeJSON()
		return
	}

	//根据节点名称查询该节点node详情信息
	nodeDetails, err := k8sClient.CoreV1().Nodes().Get(requestDto.NodeName, metav1.GetOptions{})
	if err != nil {
		errorLogger.Printf("根据节点名称%s查询该节点详情信息: %s", requestDto.NodeName, err.Error())
		errorResponseDto.Message = err.Error()
		c.Data["json"] = errorResponseDto
		c.ServeJSON()
		return
	}

	// 污点格式化
	taint := "无"
	if len(nodeDetails.Spec.Taints) > 0 {
		taint = "有"
	}

	// 地址格式化
	nodeAddress := ""
	for index, address := range nodeDetails.Status.Addresses {
		if index == len(nodeDetails.Status.Addresses)-1 {
			nodeAddress += string(address.Type) + ":" + address.Address
		} else {
			nodeAddress += string(address.Type) + ":" + address.Address + " "
		}
	}

	// 节点详情
	details := node.Details{
		NodeName:      nodeDetails.Name,
		CreateTime:    nodeDetails.CreationTimestamp.Format("2006-01-02 15:04:05"),
		Annotations:   nodeDetails.Annotations,
		Taints:        taint,
		Unschedulable: nodeDetails.Spec.Unschedulable,
		Addresses:     nodeAddress,
		Labels:        nodeDetails.Labels,
	}

	// 系统信息
	sysInfo := node.SystemInformation{
		MachineID:               nodeDetails.Status.NodeInfo.MachineID,
		SystemUUID:              nodeDetails.Status.NodeInfo.SystemUUID,
		BootID:                  nodeDetails.Status.NodeInfo.BootID,
		KernelVersion:           nodeDetails.Status.NodeInfo.KernelVersion,
		OsImage:                 nodeDetails.Status.NodeInfo.OSImage,
		OperatingSystem:         nodeDetails.Status.NodeInfo.OperatingSystem,
		Architecture:            nodeDetails.Status.NodeInfo.Architecture,
		ContainerRuntimeVersion: nodeDetails.Status.NodeInfo.ContainerRuntimeVersion,
		KubeletVersion:          nodeDetails.Status.NodeInfo.KubeletVersion,
	}

	// 节点最近情况
	conditions := []node.Condition{}
	for _, condition := range nodeDetails.Status.Conditions {
		lastHeartbeatTime, err := utils.ParseDateToString(lang, condition.LastHeartbeatTime.Time)
		if err != nil {
			errorLogger.Printf("最近心跳时间格式转换失败: %s", err.Error())
		}
		lastTransitionTime, err := utils.ParseDateToString(lang, condition.LastTransitionTime.Time)
		if err != nil {
			errorLogger.Printf("最近更改时间格式转换失败: %s", err.Error())
		}

		c := node.Condition{
			Type:               string(condition.Type),
			Status:             string(condition.Status),
			LastHeartbeatTime:  lastHeartbeatTime,
			LastTransitionTime: lastTransitionTime,
			Reason:             condition.Reason,
			Message:            condition.Message,
		}
		conditions = append(conditions, c)
	}

	successResponseDto.NodeDetails = details
	successResponseDto.SysInfo = sysInfo
	successResponseDto.Recently = conditions
	c.Data["json"] = successResponseDto
	c.ServeJSON()
}

// Post ...
// @Title GetCpuAndRamRateData
// @Description get cpu and ram rate data by all nodes
// @Param	lang		query 	string	true	"api result language type"
// @Param	body		body 	node.GetNodeDetailsRequestDto	true	""
// @Success 200
// @Failure 300 {object} dto.BaseResponseDto
// @router /getCpuAndRamRateData [post]
func (c *NodeController) GetCpuAndRamRateData() {
	lang := c.GetString("lang")
	infoLogger.Println("语言: ", lang)
	var requestDto node.GetNodeDetailsRequestDto
	var errorResponseDto = dto.BaseResponseDto{300, utils.GetI18nLang(lang, "sysError")}
	var successResponseDto = node.GetCpuAndRamRateDataResponseDto{}
	successResponseDto.Status = 200
	if err := json.Unmarshal(c.Ctx.Input.RequestBody, &requestDto); err != nil {
		errorLogger.Println(err.Error())
		errorResponseDto.Message = utils.GetI18nLang(lang, "requestParamIncorrectFormat")
		c.Data["json"] = errorResponseDto
		c.ServeJSON()
		return
	}
	infoLogger.Println("节点CPU、内存使用率数据查询接口请求参数: ", requestDto)

	// Prometheus安装的指定命名空间
	nameSpace := "monitor"
	// Prometheus的ingress名称
	prometheusIngName := "prometheus-server"
	// 获取k8s客户端
	k8sClient := utils.GetK8sClient()
	if k8sClient == nil {
		errorResponseDto.Message = utils.GetI18nLang(lang, "createK8sConFail")
		c.Data["json"] = errorResponseDto
		c.ServeJSON()
		return
	}

	//查询集群所有命名空间信息
	nameSpaceList, err := k8sClient.CoreV1().Namespaces().List(metav1.ListOptions{})
	if err != nil {
		errorLogger.Printf("查询集群所有命名空间失败: %s", err.Error())
		errorResponseDto.Message = utils.GetI18nLang(lang, "getClusterAllNamespacesFail")
		c.Data["json"] = errorResponseDto
		c.ServeJSON()
		return
	}
	allNameSpaceList := []string{}
	for _, item := range nameSpaceList.Items {
		infoLogger.Println(item.Name)
		allNameSpaceList = append(allNameSpaceList, item.Name)
	}

	if len(allNameSpaceList) > 0 && !utils.IsContains(allNameSpaceList, nameSpace) {
		errorLogger.Println("集群不存在monitor命名空间")
		errorResponseDto.Message = utils.GetI18nLang(lang, "theNameSpaceNotExistInCluster")
		c.Data["json"] = errorResponseDto
		c.ServeJSON()
		return
	}
	// 通过查询prometheus-server的Ingress获取泛域名
	prometheusIngress, err := k8sClient.ExtensionsV1beta1().Ingresses(nameSpace).Get(prometheusIngName, metav1.GetOptions{})
	if err != nil {
		errorLogger.Printf("查询集群%s空间Ingress名称为%s的信息失败: %s", nameSpace, prometheusIngName, err.Error())
		errorResponseDto.Message = err.Error()
		c.Data["json"] = errorResponseDto
		c.ServeJSON()
		return
	}
	// prometheus域名
	prometheusDomain := ""
	rules := prometheusIngress.Spec.Rules
	if len(rules) > 0 {
		prometheusDomain = rules[0].Host
	}
	infoLogger.Println("Prometheus域名: " + prometheusDomain)

	// Prometheus HTTP API 查询指定时间范围的数值接口url
	prometheusApiUrl := "/api/v1/query_range"
	prometheusServerUrl := "http://" + prometheusDomain + prometheusApiUrl
	infoLogger.Println("Prometheus查询指定时间范围的数值接口url: " + prometheusServerUrl)

	t := time.Now()
	//获取当前时间戳
	timeNowUnix := t.Unix()
	infoLogger.Printf("当前时间戳: %v\n", timeNowUnix)
	//获得2小时前的时间戳
	twoHourAgoUnix := t.Add(-time.Hour * 2).Unix()
	infoLogger.Printf("2小时前的时间戳:%v\n", twoHourAgoUnix)

	// 返给前端的CPU使用率数据数组
	cpuArray := []node.CpuRam{}
	// 返给前端的内存使用率数据数组
	ramArray := []node.CpuRam{}

	// 查询具体节点的CPU、内存使用率数据
	if strings.TrimSpace(requestDto.NodeName) != "" {
		//根据节点名称查询该节点node详情信息
		_, err := k8sClient.CoreV1().Nodes().Get(requestDto.NodeName, metav1.GetOptions{})
		if err != nil {
			errorLogger.Printf("根据节点名称%s查询该节点详情信息失败: %s", requestDto.NodeName, err.Error())
			errorResponseDto.Message = err.Error()
			c.Data["json"] = errorResponseDto
			c.ServeJSON()
			return
		}
		// 节点的CPU使用率查询表达式
		queryNodeCpuExpression := "avg(rate(container_cpu_usage_seconds_total{id=\"/\",kubernetes_io_hostname=~\"^" + requestDto.NodeName + "$\"}[2h]))"
		// 节点的内存使用率查询表达式
		queryNodeRamExpression := "sum(container_memory_working_set_bytes{id=\"/\",kubernetes_io_hostname=~\"^" + requestDto.NodeName + "$\"})/sum(machine_memory_bytes{kubernetes_io_hostname=~\"^" + requestDto.NodeName + "$\"})*100"
		// 转化后的节点的CPU使用率数据
		nodeCpuArray, err := dataConversion(prometheusServerUrl, queryNodeCpuExpression, strconv.FormatInt(twoHourAgoUnix, 10), strconv.FormatInt(timeNowUnix, 10), "1200")
		if err != nil {
			errorResponseDto.Message = err.Error()
			c.Data["json"] = errorResponseDto
			c.ServeJSON()
			return
		}
		cpuArray = nodeCpuArray

		// 转化后的节点的内存使用率数据
		nodeRamArray, err := dataConversion(prometheusServerUrl, queryNodeRamExpression, strconv.FormatInt(twoHourAgoUnix, 10), strconv.FormatInt(timeNowUnix, 10), "1200")
		if err != nil {
			errorResponseDto.Message = err.Error()
			c.Data["json"] = errorResponseDto
			c.ServeJSON()
			return
		}
		ramArray = nodeRamArray
	} else { // 查询所有节点的CPU、内存使用率数据
		// 所有节点的CPU使用率查询表达式
		queryAllNodeCpuExpression := "avg(rate(container_cpu_usage_seconds_total{id=\"/\",kubernetes_io_hostname=~\"^.*$\"}[2h]))"
		// 所有节点的内存使用率查询表达式
		queryAllNodeRamExpression := "sum(container_memory_working_set_bytes{id=\"/\",kubernetes_io_hostname=~\"^.*$\"})/sum(machine_memory_bytes{kubernetes_io_hostname=~\"^.*$\"})*100"
		// 转化后的所有节点的CPU使用率数据
		allNodeCpuArray, err := dataConversion(prometheusServerUrl, queryAllNodeCpuExpression, strconv.FormatInt(twoHourAgoUnix, 10), strconv.FormatInt(timeNowUnix, 10), "1200")
		if err != nil {
			errorResponseDto.Message = err.Error()
			c.Data["json"] = errorResponseDto
			c.ServeJSON()
			return
		}
		cpuArray = allNodeCpuArray

		// 转化后的所有节点的内存使用率数据
		allNodeRamArray, err := dataConversion(prometheusServerUrl, queryAllNodeRamExpression, strconv.FormatInt(twoHourAgoUnix, 10), strconv.FormatInt(timeNowUnix, 10), "1200")
		if err != nil {
			errorResponseDto.Message = err.Error()
			c.Data["json"] = errorResponseDto
			c.ServeJSON()
			return
		}
		ramArray = allNodeRamArray
	}

	successResponseDto.Cpu = cpuArray
	successResponseDto.Ram = ramArray
	c.Data["json"] = successResponseDto
	c.ServeJSON()
}

// 将Prometheus HTTP API获取到的数据按照指定格式转化
func dataConversion(api string, query string, start string, end string, step string) ([]node.CpuRam, error) {
	array := []node.CpuRam{}
	infoLogger.Println("Prometheus查询指定时间范围的数值接口url: " + api)
	infoLogger.Println("查询表达式query: " + query)
	infoLogger.Println("开始时间start: " + start)
	infoLogger.Println("结束时间end: " + end)
	infoLogger.Println("间距step: " + step)
	reqPost := httplib.Post(api)
	reqPost.Param("query", query)
	reqPost.Param("start", start)
	reqPost.Param("end", end)
	reqPost.Param("step", step)
	reqPost.Header("Accept", "application/json")
	reqPost.Header("Content-Type", "application/json")
	reqPost.SetTimeout(5*time.Second, 5*time.Second)
	reqPost.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
	result, err := reqPost.String()
	if err != nil {
		errorLogger.Printf("通过Prometheus指定时间范围的数值查询接口获取数据时失败，异常信息为: %s", err.Error())
		return array, errors.New(err.Error())
	}
	var rateResult node.RateResult
	if err := json.Unmarshal([]byte(result), &rateResult); err != nil {
		errorLogger.Printf("通过Prometheus指定时间范围的数值查询接口获取数据转换为RateResult结构体时失败，异常信息为: %s", err.Error())
		return array, errors.New(err.Error())
	}
	infoLogger.Printf("成功转换为RateResult结构体: %s", rateResult)
	if rateResult.Status != "success" || len(rateResult.Data.Result) <= 0 {
		errorLogger.Println("指定时间范围的数值查询接口调用成功，但是没有获取到有效数据！")
		return array, errors.New("指定时间范围的数值查询接口调用成功，但是没有获取到有效数据！")
	}
	//定义一个二维数组，取出时间戳、数值
	arr := rateResult.Data.Result[0].Values

	//for-range 遍历
	for _, v := range arr {
		// 只取前两小时的数据
		//if i > 6 {
		//	break
		//}
		var timeVal = "" // 时间点
		var rateVal = "" // 比率数值
		for j, v2 := range v {
			if j == 0 {
				// 将时间戳转换成float64类型
				timeUnix := strconv.FormatFloat(v2.(float64), 'f', -1, 64)
				// 时间戳 to 时间
				unix, err := strconv.ParseInt(timeUnix, 10, 64)
				if err != nil {
					errorLogger.Printf("将string类型的时间戳转换为int64类型的时间失败，异常信息为: %s", err.Error())
					return array, errors.New(err.Error())
				}
				tm := time.Unix(unix, 0)
				// 格式化成时:分
				timeMoment := tm.Format("15:04")
				timeVal = timeMoment
			}
			if j == 1 {
				// 先将string类型的数值转成float64
				rate, err := strconv.ParseFloat(v2.(string), 64)
				if err != nil {
					errorLogger.Printf("将string类型的比率数值转换为float64类型时失败，异常信息为: %s", err.Error())
					return array, errors.New(err.Error())
				}
				// 然后格式化保留两位小数
				s := fmt.Sprintf("%0.2f", rate)
				rateVal = s
			}
		}
		cpu := node.CpuRam{Time: timeVal, Rate: rateVal}
		array = append(array, cpu)
	}

	return array, nil
}
