test
This commit is contained in:
parent
4fdef4d47a
commit
480c1b553f
698
app/cluster.go
698
app/cluster.go
|
@ -1,698 +0,0 @@
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"jcc-schedule/model"
|
|
||||||
|
|
||||||
//"github.com/go-sql-driver/mysql"
|
|
||||||
"github.com/golang/glog"
|
|
||||||
"github.com/karmada-io/karmada/pkg/karmadactl"
|
|
||||||
"github.com/karmada-io/karmada/pkg/karmadactl/options"
|
|
||||||
"github.com/karmada-io/karmada/pkg/util"
|
|
||||||
"gopkg.in/yaml.v3"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
v1 "k8s.io/client-go/tools/clientcmd/api/v1"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Cluster struct {
|
|
||||||
ClusterName string `json:"cluster_name"`
|
|
||||||
DomainId int32 `json:"domain_id"`
|
|
||||||
DomainName string `json:"domain_name"`
|
|
||||||
Version string `json:"version"`
|
|
||||||
Mode string `json:"mode"`
|
|
||||||
Labels map[string]string `json:"labels"`
|
|
||||||
State string `json:"state"`
|
|
||||||
CreateTime time.Time `json:"create_time"`
|
|
||||||
Description *string `json:"description"`
|
|
||||||
NodeNum int32 `json:"node_num"`
|
|
||||||
Edge bool `json:"edge"`
|
|
||||||
Monitoring bool `json:"monitoring"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type JoinRequest struct {
|
|
||||||
// Name of the member cluster
|
|
||||||
MemberName string `json:"member_name"`
|
|
||||||
// ClusterProvider of the member cluster
|
|
||||||
MemberProvider string `json:"member_provider"`
|
|
||||||
|
|
||||||
Labels map[string]string `json:"labels,omitempty" protobuf:"bytes,11,rep,name=labels"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ClusterParam struct {
|
|
||||||
ClusterName string `json:"clusterName"`
|
|
||||||
Version string `json:"version"`
|
|
||||||
UserName string `json:"userName"`
|
|
||||||
MasterNodes []string `json:"masterNodes"`
|
|
||||||
MemberNodes []string `json:"memberNodes"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClusterAnsible 集群结构
|
|
||||||
type ClusterAnsible struct {
|
|
||||||
ClusterName string `json:"cluster_name"`
|
|
||||||
Nodes []NodeAnsible `json:"nodes"`
|
|
||||||
Etcds []string `json:"etcds"`
|
|
||||||
Controls []string `json:"controls"`
|
|
||||||
Workers []string `json:"workers"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// NodeAnsible 节点信息
|
|
||||||
type NodeAnsible struct {
|
|
||||||
NodeName string `json:"node_name"`
|
|
||||||
ExtAddr string `json:"ext_addr"`
|
|
||||||
IntAddr string `json:"int_addr"`
|
|
||||||
User string `json:"user"`
|
|
||||||
Password string `json:"password"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ProxyCluster struct {
|
|
||||||
MemberName string `json:"member_name"`
|
|
||||||
MemberClusterIp string `json:"memberClusterIp"`
|
|
||||||
DomainId string `json:"domain_id"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary 查询集群列表
|
|
||||||
// @Description 查询集群列表
|
|
||||||
// @Tags cluster
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param pageNum query int true "页码"
|
|
||||||
// @Param pageSize query int true "每页数量"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 500
|
|
||||||
// @Router /api/v1/cluster/list [get]
|
|
||||||
func ListCluster(c *gin.Context) {
|
|
||||||
clusterName := c.Query("cluster_name")
|
|
||||||
clusterList := make([]Cluster, 0)
|
|
||||||
clusters, err := KarmadaClient.ClusterV1alpha1().Clusters().List(context.TODO(), metav1.ListOptions{})
|
|
||||||
if err != nil {
|
|
||||||
glog.Info("failed to retrieve cluster(%s). error: %v", clusters, err)
|
|
||||||
Response(c, http.StatusBadRequest, "failed to retrieve cluster", err)
|
|
||||||
}
|
|
||||||
//遍历集群
|
|
||||||
for i := 0; i < len(clusters.Items); i++ {
|
|
||||||
cluster := Cluster{
|
|
||||||
ClusterName: clusters.Items[i].Name,
|
|
||||||
State: string(clusters.Items[i].Status.Conditions[0].Status),
|
|
||||||
CreateTime: clusters.Items[i].CreationTimestamp.Time,
|
|
||||||
Labels: clusters.Items[i].Labels,
|
|
||||||
Mode: string(clusters.Items[i].Spec.SyncMode),
|
|
||||||
Version: clusters.Items[i].Status.KubernetesVersion,
|
|
||||||
NodeNum: clusters.Items[i].Status.NodeSummary.ReadyNum,
|
|
||||||
}
|
|
||||||
rows, err := DB.Query(`SELECT domain_id,domain_name,description FROM joint_domain.domain_cluster dc WHERE cluster_name = ? `, clusters.Items[i].Name)
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "query failed", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for rows.Next() {
|
|
||||||
err := rows.Scan(&cluster.DomainId, &cluster.DomainName, &cluster.Description)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rows.Close()
|
|
||||||
// 查询是否有边缘节点
|
|
||||||
edge := GetRedis(c, clusters.Items[i].Name)
|
|
||||||
if strings.EqualFold(edge, "edge") {
|
|
||||||
cluster.Edge = true
|
|
||||||
}
|
|
||||||
//查询是否有监控
|
|
||||||
monitoring := GetMonitoringClusters()
|
|
||||||
for _, clusterName := range monitoring {
|
|
||||||
if strings.EqualFold(clusterName, clusters.Items[i].Name) {
|
|
||||||
cluster.Monitoring = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(clusterName) != 0 && !strings.Contains(cluster.ClusterName, clusterName) {
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
clusterList = append(clusterList, cluster)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
var clusterHistory Cluster
|
|
||||||
rows, _ := DB.Query("SELECT t.cluster_name as ClusterName, t.domain_id as DomainId ,t.domain_name as DomainName, t.node as NodeNum , t.version as Version FROM cluster_resource_history t")
|
|
||||||
for rows.Next() {
|
|
||||||
err := rows.Scan(&clusterHistory.ClusterName, &clusterHistory.DomainId, &clusterHistory.DomainName, &clusterHistory.NodeNum, &clusterHistory.Version)
|
|
||||||
if err != nil {
|
|
||||||
glog.Errorf("query failed!,error %v", err)
|
|
||||||
Response(c, http.StatusBadRequest, "query failed!", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
clusterList = append(clusterList, clusterHistory)
|
|
||||||
}
|
|
||||||
|
|
||||||
pageNum := c.Query("pageNum")
|
|
||||||
pageSize := c.Query("pageSize")
|
|
||||||
// 分页
|
|
||||||
page := &Page[Cluster]{}
|
|
||||||
page.List = clusterList
|
|
||||||
data := Paginator(page, int64(len(clusterList)), pageNum, pageSize)
|
|
||||||
Response(c, http.StatusOK, "success", data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary 查询有标签的集群列表
|
|
||||||
// @Description 查询有标签的集群列表
|
|
||||||
// @Tags cluster
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param cluster_name query string true "集群名称"
|
|
||||||
// @Param pageNum query int true "页码"
|
|
||||||
// @Param pageSize query int true "每页数量"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 500
|
|
||||||
// @Router /api/v1/cluster/listWithLabel [get]
|
|
||||||
func ListClusterWithLabel(c *gin.Context) {
|
|
||||||
|
|
||||||
clusterName := c.Query("cluster_name")
|
|
||||||
|
|
||||||
clusterList := make([]Cluster, 0)
|
|
||||||
clusters, err := KarmadaClient.ClusterV1alpha1().Clusters().List(context.TODO(), metav1.ListOptions{})
|
|
||||||
if err != nil {
|
|
||||||
glog.Info("failed to retrieve cluster(%s). error: %v", clusters, err)
|
|
||||||
Response(c, http.StatusBadRequest, "failed to retrieve cluster", err)
|
|
||||||
}
|
|
||||||
for i := 0; i < len(clusters.Items); i++ {
|
|
||||||
|
|
||||||
cluster := Cluster{
|
|
||||||
ClusterName: clusters.Items[i].Name,
|
|
||||||
State: string(clusters.Items[i].Status.Conditions[0].Status),
|
|
||||||
CreateTime: clusters.Items[i].CreationTimestamp.Time,
|
|
||||||
Labels: clusters.Items[i].Labels,
|
|
||||||
Mode: string(clusters.Items[i].Spec.SyncMode),
|
|
||||||
Version: clusters.Items[i].Status.KubernetesVersion,
|
|
||||||
}
|
|
||||||
rows, err := DB.Query(`SELECT domain_id,domain_name FROM domain_cluster dc WHERE cluster_name = ? `, clusters.Items[i].Name)
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "query failed", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for rows.Next() {
|
|
||||||
rows.Scan(&cluster.DomainId, &cluster.DomainName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(cluster.Labels) != 0 {
|
|
||||||
if len(clusterName) > 0 {
|
|
||||||
if strings.Contains(cluster.ClusterName, clusterName) {
|
|
||||||
clusterList = append(clusterList, cluster)
|
|
||||||
} else {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
clusterList = append(clusterList, cluster)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
rows.Close()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
total := len(clusterList)
|
|
||||||
page := &Page[Cluster]{}
|
|
||||||
page.List = clusterList
|
|
||||||
pageNum := c.Query("pageNum")
|
|
||||||
pageSize := c.Query("pageSize")
|
|
||||||
data := Paginator(page, int64(total), pageNum, pageSize)
|
|
||||||
Response(c, http.StatusOK, "success", data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary 刪除集群
|
|
||||||
// @Description 刪除集群
|
|
||||||
// @Tags cluster
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param cluster_name query string true "集群名称"
|
|
||||||
// @Param domain_id query string true "域id"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 500
|
|
||||||
// @Router /api/v1/cluster/unJoin [delete]
|
|
||||||
func UnJoin(c *gin.Context) {
|
|
||||||
clusterName := c.Query("cluster_name")
|
|
||||||
domainId := c.Query("domain_id")
|
|
||||||
if clusterName == "" || domainId == "" {
|
|
||||||
Response(c, http.StatusBadRequest, "invalid request params.", "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
opts := karmadactl.CommandUnjoinOption{
|
|
||||||
DryRun: false,
|
|
||||||
ClusterNamespace: options.DefaultKarmadaClusterNamespace,
|
|
||||||
ClusterName: clusterName,
|
|
||||||
Wait: options.DefaultKarmadactlCommandDuration,
|
|
||||||
}
|
|
||||||
|
|
||||||
go kdUnCluster(opts)
|
|
||||||
Response(c, http.StatusOK, "success", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除指定集群
|
|
||||||
func kdUnCluster(opts karmadactl.CommandUnjoinOption) {
|
|
||||||
glog.Info("调度中心删除集群,集群名称:", opts.ClusterName)
|
|
||||||
err := karmadactl.UnJoinCluster(ControlPlaneRestConfig, nil, opts)
|
|
||||||
if err != nil {
|
|
||||||
glog.Errorf("failed to delete cluster. error: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary 直接连模式加入联邦
|
|
||||||
// @Description 直连模式加入联邦
|
|
||||||
// @Tags cluster
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param member_name formData string true "集群名称"
|
|
||||||
// @Param file formData file true "配置文件"
|
|
||||||
// @Param domain_id formData string true "资源域"
|
|
||||||
// @Param description formData string false "描述"
|
|
||||||
// @Param labels formData string true "标签"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 500
|
|
||||||
// @Router /api/v1/cluster/join [post]
|
|
||||||
func Join(c *gin.Context) {
|
|
||||||
var labels map[string]string
|
|
||||||
//member集群名称
|
|
||||||
memberName := c.PostForm("member_name")
|
|
||||||
if memberName == "" {
|
|
||||||
Response(c, http.StatusInternalServerError, fmt.Sprintf("failed to join cluster. memberName is null"), "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
file, _ := c.FormFile("file")
|
|
||||||
dir, _ := os.Getwd()
|
|
||||||
memberClusterConfigName := dir + "/karmadaConfig/" + memberName + ".config"
|
|
||||||
// 上传文件至指定目录
|
|
||||||
if err := c.SaveUploadedFile(file, memberClusterConfigName); err != nil {
|
|
||||||
glog.Errorf("File uplabels = {map[string]string} load failed err %v", err)
|
|
||||||
Response(c, http.StatusInternalServerError, fmt.Sprintf("File upload failed err: %s", err), "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
//资源域名称
|
|
||||||
domainId := c.PostForm("domain_id")
|
|
||||||
if domainId == "" {
|
|
||||||
Response(c, http.StatusInternalServerError, fmt.Sprintf("failed to join cluster. domain is null"), "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
//member集群标签
|
|
||||||
labels, _ = jsonToMap(c.PostForm("labels"))
|
|
||||||
defaultLabel := map[string]string{
|
|
||||||
"domain": domainId,
|
|
||||||
}
|
|
||||||
labels = mergeMap(labels, defaultLabel)
|
|
||||||
//查询域是否存在
|
|
||||||
rows, _ := DB.Query("select domain_name from domain where domain_id = ?", domainId)
|
|
||||||
defer rows.Close()
|
|
||||||
var memberProvider string
|
|
||||||
for rows.Next() {
|
|
||||||
err := rows.Scan(&memberProvider)
|
|
||||||
if err != nil {
|
|
||||||
glog.Errorf("query failed!,error %v", err)
|
|
||||||
Response(c, http.StatusBadRequest, "query failed!", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if memberProvider == "" {
|
|
||||||
Response(c, http.StatusInternalServerError, fmt.Sprintf("failed to join cluster. Domain does not exist"), "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
opts := karmadactl.CommandJoinOption{
|
|
||||||
DryRun: false,
|
|
||||||
ClusterNamespace: "karmada-cluster",
|
|
||||||
ClusterName: memberName,
|
|
||||||
ClusterKubeConfig: memberClusterConfigName,
|
|
||||||
ClusterProvider: memberProvider,
|
|
||||||
}
|
|
||||||
memberClusterRestConfig, err := KarmadaConfig.GetRestConfig(opts.KarmadaContext, memberClusterConfigName)
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, fmt.Sprintf("failed to get member cluster rest config. context: %s, kube-config: %s, error: %v",
|
|
||||||
opts.KarmadaContext, opts.KubeConfig, err), "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = karmadactl.JoinCluster(ControlPlaneRestConfig, memberClusterRestConfig, opts)
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, fmt.Sprintf("failed to join cluster. context: %s, kube-config: %s, error: %v",
|
|
||||||
opts.KarmadaContext, opts.KubeConfig, err), "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
//将域和集群绑定
|
|
||||||
description := c.PostForm("description")
|
|
||||||
_, err = DB.Exec(`insert into domain_cluster(domain_id, domain_name, cluster_name, description) values(?,?,?,?)`, domainId, memberProvider, memberName, description)
|
|
||||||
|
|
||||||
//新加入集群数据同步到PCM的计算中心下
|
|
||||||
//longitude := c.PostForm("longitude")
|
|
||||||
//latitude := c.PostForm("latitude")
|
|
||||||
clusterNameCh := c.PostForm("member_name_ch")
|
|
||||||
_, err = DBPCM.Exec(`insert into compute_cluster (name, type, center_id, center_name, jcce_domain_id, jcce_domain_name, longitude, latitude,description) values(?,?,?,?,?,?,?,?,?)`, clusterNameCh, "cloud", 66, "国家计算中心长沙超算中心", domainId, memberProvider, 112.355042, 28.570066, description)
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "sync info to PCM failed", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if labels != nil {
|
|
||||||
// 给集群打上标签
|
|
||||||
cluster, _, err := util.GetClusterWithKarmadaClient(KarmadaClient, memberName)
|
|
||||||
if cluster == nil {
|
|
||||||
Response(c, http.StatusBadRequest, "Get cluster failed", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
cluster.Labels = labels
|
|
||||||
_, err = KarmadaClient.ClusterV1alpha1().Clusters().Update(context.TODO(), cluster, metav1.UpdateOptions{})
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "update failed", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Response(c, http.StatusOK, "success", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary 代理模式加入集群
|
|
||||||
// @Description 代理模式加入集群
|
|
||||||
// @Tags cluster
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param param body ProxyCluster true "json"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 500
|
|
||||||
// @Router /api/v1/cluster/proxyJoinCluster [post]
|
|
||||||
func ProxyJoinCluster(c *gin.Context) {
|
|
||||||
var proxyParam ProxyCluster
|
|
||||||
if err := c.BindJSON(&proxyParam); err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "invalid request params.", "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
//资源域名称
|
|
||||||
domainId := proxyParam.DomainId
|
|
||||||
if domainId == "" {
|
|
||||||
Response(c, http.StatusInternalServerError, fmt.Sprintf("failed to join cluster. domain is null"), "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
//查询域是否存在
|
|
||||||
rows, _ := DB.Query("select domain_name from domain where domain_id = ?", domainId)
|
|
||||||
defer rows.Close()
|
|
||||||
var memberProvider string
|
|
||||||
for rows.Next() {
|
|
||||||
err := rows.Scan(&memberProvider)
|
|
||||||
if err != nil {
|
|
||||||
glog.Errorf("query failed!,error %v", err)
|
|
||||||
Response(c, http.StatusBadRequest, "query failed!", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if memberProvider == "" {
|
|
||||||
Response(c, http.StatusInternalServerError, fmt.Sprintf("failed to join cluster. Domain does not exist"), "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, err := DB.Exec(`insert into domain_cluster( domain_id, domain_name, cluster_name, description) values(?,?,?,?)`, domainId, memberProvider, proxyParam.MemberName, proxyParam.Description)
|
|
||||||
|
|
||||||
data, err := os.ReadFile("karmadaConfig/karmada-host")
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("File reading error", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_karmadaConfig := &v1.Config{}
|
|
||||||
err = yaml.Unmarshal(data, &_karmadaConfig)
|
|
||||||
rawUrl := _karmadaConfig.Clusters[0].Cluster.Server
|
|
||||||
newUrl, _ := url.Parse(rawUrl)
|
|
||||||
hostIp := newUrl.Hostname()
|
|
||||||
config := GetAgentTemplate()
|
|
||||||
config = strings.Replace(config, "{member_cluster_name}", proxyParam.MemberName, -1)
|
|
||||||
config = strings.Replace(config, "{member_cluster_ip}", proxyParam.MemberClusterIp, -1)
|
|
||||||
config = strings.Replace(config, "{host_cluster_ip}", hostIp, -1)
|
|
||||||
config = strings.Replace(config, "{karmada-apiserver.config}", base64.StdEncoding.EncodeToString(data), -1)
|
|
||||||
c.String(http.StatusOK, fmt.Sprintln(config))
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary 查询集群是否存在
|
|
||||||
// @Description 查询集群是否存在
|
|
||||||
// @Tags cluster
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param clusterName path string true "集群名称"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 500
|
|
||||||
// @Router /api/v1/cluster/{clusterName} [get]
|
|
||||||
func ClusterExist(c *gin.Context) {
|
|
||||||
//查询集群名称在karmada集群中是否存在
|
|
||||||
clusters, err := KarmadaClient.ClusterV1alpha1().Clusters().List(context.TODO(), metav1.ListOptions{})
|
|
||||||
if err != nil {
|
|
||||||
glog.Info("failed to retrieve cluster(%s). error: %v", clusters, err)
|
|
||||||
Response(c, http.StatusBadRequest, "failed to retrieve cluster", err)
|
|
||||||
}
|
|
||||||
clusterName := c.Param("clusterName")
|
|
||||||
//遍历集群
|
|
||||||
for i := 0; i < len(clusters.Items); i++ {
|
|
||||||
if clusters.Items[i].Name == clusterName {
|
|
||||||
Response(c, http.StatusOK, "true", "集群名已存在")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Response(c, http.StatusOK, "false", "集群名不存在")
|
|
||||||
}
|
|
||||||
|
|
||||||
// TagCluster 集群打标签
|
|
||||||
// @Summary 集群打标签
|
|
||||||
// @Description 集群打标签
|
|
||||||
// @Tags cluster
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param param body Cluster true "json"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 500
|
|
||||||
// @Router /api/v1/cluster/tag [post]
|
|
||||||
func TagCluster(c *gin.Context) {
|
|
||||||
var clusterTag Cluster
|
|
||||||
if err := c.BindJSON(&clusterTag); err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "invalid request params.", "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
cluster, _, err := util.GetClusterWithKarmadaClient(KarmadaClient, clusterTag.ClusterName)
|
|
||||||
if cluster == nil {
|
|
||||||
Response(c, http.StatusBadRequest, "Get cluster failed", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
cluster.Labels = clusterTag.Labels
|
|
||||||
_, err = KarmadaClient.ClusterV1alpha1().Clusters().Update(context.TODO(), cluster, metav1.UpdateOptions{})
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "update failed", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
Response(c, http.StatusOK, "success", cluster)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnTagCluster 集群删除标签
|
|
||||||
// @Summary 集群删除标签
|
|
||||||
// @Description 集群删除标签
|
|
||||||
// @Tags cluster
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param param body Cluster true "json"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 500
|
|
||||||
// @Router /api/v1/cluster/unTag [post]
|
|
||||||
func UnTagCluster(c *gin.Context) {
|
|
||||||
var clusterTag Cluster
|
|
||||||
if err := c.BindJSON(&clusterTag); err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "invalid request params.", "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
cluster, _, err := util.GetClusterWithKarmadaClient(KarmadaClient, clusterTag.ClusterName)
|
|
||||||
if cluster == nil {
|
|
||||||
Response(c, http.StatusBadRequest, "get cluster failed", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
deleteMaps(cluster.Labels, clusterTag.Labels)
|
|
||||||
_, err = KarmadaClient.ClusterV1alpha1().Clusters().Update(context.TODO(), cluster, metav1.UpdateOptions{})
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, fmt.Sprintf("failed to update cluster. error: %v", err), "")
|
|
||||||
}
|
|
||||||
|
|
||||||
Response(c, http.StatusOK, "success", cluster)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除map中的某个key
|
|
||||||
func deleteMaps(labels map[string]string, keys map[string]string) {
|
|
||||||
for key := range keys {
|
|
||||||
delete(labels, key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListByDomain 根据项目名称、工作负载、域id查询集群 listByDomain
|
|
||||||
// @Summary 根据项目名称、工作负载、域id查询集群
|
|
||||||
// @Description 根据项目名称、工作负载、域id查询集群
|
|
||||||
// @Tags cluster
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param namespace path string true "项目名称"
|
|
||||||
// @Param deployment_name path string true "工作负载"
|
|
||||||
// @Param domain_id path string true "域id"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 500
|
|
||||||
// @Router /api/v1/cluster/listByDomain [post]
|
|
||||||
func ListByDomain(c *gin.Context) {
|
|
||||||
namespaceName := c.Query("namespace")
|
|
||||||
deploymentName := c.Query("deployment_name")
|
|
||||||
domainId := c.Query("domain_id")
|
|
||||||
|
|
||||||
propagationPolicy, err := KarmadaClient.PolicyV1alpha1().PropagationPolicies(namespaceName).Get(context.TODO(), "deployment."+namespaceName+"."+deploymentName, metav1.GetOptions{})
|
|
||||||
if err != nil {
|
|
||||||
}
|
|
||||||
var clusterList []string
|
|
||||||
for _, clusterName := range propagationPolicy.Spec.Placement.ClusterAffinity.ClusterNames {
|
|
||||||
clusterList = append(clusterList, clusterName)
|
|
||||||
}
|
|
||||||
var result []string
|
|
||||||
Gorm.Debug().Raw("SELECT distinct cluster_name from domain_cluster where cluster_name in (?) and domain_id = ?", clusterList, domainId).Scan(&result)
|
|
||||||
Response(c, http.StatusOK, "success", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary 集群注册
|
|
||||||
// @Description 集群注册
|
|
||||||
// @Tags cluster
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param param body ClusterParam true "json"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 400
|
|
||||||
// @Router /api/v1/cluster/createCluster [post]
|
|
||||||
func CreateCluster(c *gin.Context) {
|
|
||||||
copyContext := c.Copy()
|
|
||||||
var param ClusterParam
|
|
||||||
if err := c.BindJSON(¶m); err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "invalid request params.", "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
//TODO 查询用户id并绑定
|
|
||||||
//查询集群名称是否存在
|
|
||||||
rows, _ := DB.Query("select cluster_name from cluster where cluster_name = ?", param.ClusterName)
|
|
||||||
defer rows.Close()
|
|
||||||
var _clusterName string
|
|
||||||
for rows.Next() {
|
|
||||||
var clusterName string
|
|
||||||
err := rows.Scan(&clusterName)
|
|
||||||
if err != nil {
|
|
||||||
glog.Errorf("query failed!,error %v", err)
|
|
||||||
Response(c, http.StatusBadRequest, "query failed!", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_clusterName = clusterName
|
|
||||||
}
|
|
||||||
if _clusterName != "" {
|
|
||||||
Response(c, http.StatusInternalServerError, fmt.Sprintf("failed to create cluster. Cluster name already exists"), "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
nodes := make([]NodeAnsible, 0)
|
|
||||||
//master节点
|
|
||||||
_controls := make([]string, 0)
|
|
||||||
for i, ip := range param.MasterNodes {
|
|
||||||
nodeName := param.ClusterName + "-control-" + strconv.Itoa(i+1)
|
|
||||||
node := NodeAnsible{
|
|
||||||
NodeName: nodeName,
|
|
||||||
ExtAddr: ip,
|
|
||||||
IntAddr: ip,
|
|
||||||
User: "root",
|
|
||||||
Password: "Nudt@123",
|
|
||||||
}
|
|
||||||
_controls = append(_controls, nodeName)
|
|
||||||
nodes = append(nodes, node)
|
|
||||||
}
|
|
||||||
//member节点
|
|
||||||
_workers := make([]string, 0)
|
|
||||||
for i, ip := range param.MemberNodes {
|
|
||||||
nodeName := param.ClusterName + "-worker-" + strconv.Itoa(i+1)
|
|
||||||
node := NodeAnsible{
|
|
||||||
NodeName: nodeName,
|
|
||||||
ExtAddr: ip,
|
|
||||||
IntAddr: ip,
|
|
||||||
User: "root",
|
|
||||||
Password: "Nudt@123",
|
|
||||||
}
|
|
||||||
_workers = append(_workers, nodeName)
|
|
||||||
nodes = append(nodes, node)
|
|
||||||
}
|
|
||||||
|
|
||||||
var res = ClusterAnsible{
|
|
||||||
ClusterName: param.ClusterName,
|
|
||||||
Nodes: nodes,
|
|
||||||
Etcds: _controls,
|
|
||||||
Controls: _controls,
|
|
||||||
Workers: _workers,
|
|
||||||
}
|
|
||||||
buf, _ := json.Marshal(res)
|
|
||||||
cli := Cli{
|
|
||||||
addr: "10.101.15.75:22",
|
|
||||||
user: "zyen",
|
|
||||||
pwd: "Nudt@123",
|
|
||||||
}
|
|
||||||
|
|
||||||
clusterIp := param.MasterNodes[0]
|
|
||||||
//集群入库
|
|
||||||
_, err := DB.Exec(`insert into cluster(cluster_name, version,user_name,create_time,cluster_ip) values(?,?,?,?,?)`, param.ClusterName, param.Version, param.UserName, time.Now(), clusterIp)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, fmt.Sprintf("failed to create cluster. error: %v", err), "")
|
|
||||||
}
|
|
||||||
ansibleParam := "'" + string(buf) + "'"
|
|
||||||
go func() {
|
|
||||||
// 建立连接对象
|
|
||||||
cl, _ := cli.Connect()
|
|
||||||
// 退出时关闭连接
|
|
||||||
defer cl.client.Close()
|
|
||||||
glog.Info("异步执行:" + copyContext.Request.URL.Path)
|
|
||||||
_, err = cl.Run("/home/zyen/ansible-env/bin/ansible-playbook ~/playground/ansible/playbooks/gen_private_files.yaml -e " + ansibleParam)
|
|
||||||
_, err = cl.Run("/home/zyen/ansible-env/bin/ansible-playbook ~/playground/ansible/playbooks/k8s_deploy.yaml -e " + ansibleParam)
|
|
||||||
if err != nil {
|
|
||||||
glog.Errorf("创建集群失败, %v", err)
|
|
||||||
Response(c, http.StatusBadRequest, "创建集群失败!", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
Response(c, http.StatusOK, "success", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary 查询自建集群列表
|
|
||||||
// @Description 查询自建集群列表
|
|
||||||
// @Tags cluster
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param pageNum path string true "页码"
|
|
||||||
// @Param pageSize path string true "每页数量"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 400
|
|
||||||
// @Router /api/v1/cluster/listFree [get]
|
|
||||||
func ListFreeCluster(c *gin.Context) {
|
|
||||||
pageNumParam := c.Query("pageNum")
|
|
||||||
pageSizeParam := c.Query("pageSize")
|
|
||||||
sqlStr := `select count(*) from cluster limit ?,?`
|
|
||||||
var total int64
|
|
||||||
pageNum, pageSize := PageParamInit(pageNumParam, pageSizeParam)
|
|
||||||
err := DB.Get(&total, sqlStr, (pageNum-1)*pageSize, pageSize)
|
|
||||||
|
|
||||||
clusters := make([]*model.Cluster, 0, total)
|
|
||||||
sqlStr = `select
|
|
||||||
id,cluster_name,version,user_name,create_time,cluster_ip
|
|
||||||
from cluster
|
|
||||||
ORDER BY create_time
|
|
||||||
DESC
|
|
||||||
limit ?,?`
|
|
||||||
err = DB.Select(&clusters, sqlStr, (pageNum-1)*pageSize, pageSize)
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "服务繁忙!", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
page := &Page[*model.Cluster]{}
|
|
||||||
page.List = clusters
|
|
||||||
data := Paginate(page, pageNum, pageSize, total)
|
|
||||||
Response(c, http.StatusOK, "success", data)
|
|
||||||
}
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
package app
|
||||||
|
|
||||||
|
import "k8s.io/client-go/kubernetes"
|
||||||
|
|
||||||
|
type ClusterClient struct {
|
||||||
|
KubeClient *kubernetes.Clientset
|
||||||
|
ClusterName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClusterClientSet(clusterName string) {
|
||||||
|
|
||||||
|
}
|
|
@ -1,92 +0,0 @@
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"github.com/golang/glog"
|
|
||||||
"github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1"
|
|
||||||
karmadainformers "github.com/karmada-io/karmada/pkg/generated/informers/externalversions"
|
|
||||||
"github.com/karmada-io/karmada/pkg/util"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
|
||||||
"k8s.io/client-go/tools/cache"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Watch() {
|
|
||||||
// 初始化 informer factory
|
|
||||||
informerFactory := karmadainformers.NewSharedInformerFactory(KarmadaClient, time.Second*30)
|
|
||||||
// 对 cluster 监听
|
|
||||||
clusterInformer := informerFactory.Cluster().V1alpha1().Clusters()
|
|
||||||
// 创建 Informer(相当于注册到工厂中去,这样下面启动的时候就会去 List & Watch 对应的资源)
|
|
||||||
informer := clusterInformer.Informer()
|
|
||||||
// 创建 Lister
|
|
||||||
clusterLister := clusterInformer.Lister()
|
|
||||||
// 注册事件处理程序
|
|
||||||
informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
|
|
||||||
AddFunc: AddCluster,
|
|
||||||
UpdateFunc: updateCluster,
|
|
||||||
DeleteFunc: deleteCluster,
|
|
||||||
})
|
|
||||||
|
|
||||||
stopper := make(chan struct{})
|
|
||||||
defer close(stopper)
|
|
||||||
|
|
||||||
// 启动 informer,List & Watch
|
|
||||||
informerFactory.Start(stopper)
|
|
||||||
// 等待所有启动的 Informer 的缓存被同步
|
|
||||||
informerFactory.WaitForCacheSync(stopper)
|
|
||||||
|
|
||||||
// 从本地缓存中获取所有 cluster 列表
|
|
||||||
clusters, err := clusterLister.List(labels.Everything())
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
for idx, cluster := range clusters {
|
|
||||||
fmt.Printf("%d -> %s\n", idx+1, cluster.Name)
|
|
||||||
}
|
|
||||||
<-stopper
|
|
||||||
}
|
|
||||||
|
|
||||||
// OnAdd handles object add event and push the object to queue.
|
|
||||||
func AddCluster(obj interface{}) {
|
|
||||||
cluster := obj.(*v1alpha1.Cluster)
|
|
||||||
cluster, _, err := util.GetClusterWithKarmadaClient(KarmadaClient, cluster.Name)
|
|
||||||
rows, _ := DB.Query("select domain_id from domain_cluster where cluster_name = ?", cluster.Name)
|
|
||||||
defer rows.Close()
|
|
||||||
var domainId string
|
|
||||||
for rows.Next() {
|
|
||||||
err := rows.Scan(&domainId)
|
|
||||||
if err != nil {
|
|
||||||
glog.Errorf("query failed!,error %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if domainId == "" {
|
|
||||||
glog.Errorf(" 【%v】 - Cluster and domain are not bound, please check the data", cluster.Name)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_labels := make(map[string]string)
|
|
||||||
//新增默认域的默认标签
|
|
||||||
_labels["domain"] = domainId
|
|
||||||
cluster.Labels = mergeMap(cluster.Labels, _labels)
|
|
||||||
_, err = KarmadaClient.ClusterV1alpha1().Clusters().Update(context.TODO(), cluster, metav1.UpdateOptions{})
|
|
||||||
if err != nil {
|
|
||||||
glog.Errorf("Cluster default domain tag update failed,error %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// OnUpdate handles object update event and push the object to queue.
|
|
||||||
func updateCluster(oldObj, newObj interface{}) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// OnDelete handles object delete event and push the object to queue.
|
|
||||||
func deleteCluster(obj interface{}) {
|
|
||||||
cluster := obj.(*v1alpha1.Cluster)
|
|
||||||
_, err := DB.Exec(`DELETE FROM domain_cluster where cluster_name = ?`, cluster.Name)
|
|
||||||
if err != nil {
|
|
||||||
glog.Errorf("Cluster deletion failure,error %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -14,11 +14,6 @@ func (e *sliceError) Error() string {
|
||||||
return e.msg
|
return e.msg
|
||||||
}
|
}
|
||||||
|
|
||||||
func Errorf(format string, args ...interface{}) error {
|
|
||||||
msg := fmt.Sprintf(format, args...)
|
|
||||||
return &sliceError{msg}
|
|
||||||
}
|
|
||||||
|
|
||||||
func mergeMap(maps ...map[string]string) map[string]string {
|
func mergeMap(maps ...map[string]string) map[string]string {
|
||||||
result := make(map[string]string)
|
result := make(map[string]string)
|
||||||
for _, m := range maps {
|
for _, m := range maps {
|
||||||
|
@ -70,23 +65,6 @@ func jsonToMap(jsonStr string) (map[string]string, error) {
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func RemoveRepeatedDomain(arr []Domain) (newArr []Domain) {
|
|
||||||
newArr = make([]Domain, 0)
|
|
||||||
for i := 0; i < len(arr); i++ {
|
|
||||||
repeat := false
|
|
||||||
for j := i + 1; j < len(arr); j++ {
|
|
||||||
if arr[i].DomainId == arr[j].DomainId {
|
|
||||||
repeat = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !repeat {
|
|
||||||
newArr = append(newArr, arr[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeDuplicateArr(arr []string) []string {
|
func removeDuplicateArr(arr []string) []string {
|
||||||
set := make(map[string]struct{}, len(arr))
|
set := make(map[string]struct{}, len(arr))
|
||||||
j := 0
|
j := 0
|
||||||
|
|
|
@ -1,75 +0,0 @@
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
v1 "k8s.io/api/core/v1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ConfigMap struct {
|
|
||||||
ConfigMap v1.ConfigMap `json:"configMap"`
|
|
||||||
ClusterName []string `json:"clusterName"`
|
|
||||||
TemplateId string `json:"templateId"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type SecretRes struct {
|
|
||||||
Kind string `json:"kind"`
|
|
||||||
ApiVersion string `json:"apiVersion"`
|
|
||||||
Metadata string `json:"metadata"`
|
|
||||||
ResourceVersion string `json:"resourceVersion"`
|
|
||||||
Items []v1.Secret `json:"items"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ConfigMaoObject struct {
|
|
||||||
Kind string `json:"kind"`
|
|
||||||
ApiVersion string `json:"apiVersion"`
|
|
||||||
Metadata string `json:"metadata"`
|
|
||||||
ResourceVersion string `json:"resourceVersion"`
|
|
||||||
Items []v1.ConfigMap `json:"items"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateConfigMap 创建configMap
|
|
||||||
func CreateConfigMap(c *gin.Context) {
|
|
||||||
var cmRequest ConfigMap
|
|
||||||
if err := c.BindJSON(&cmRequest); err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "invalid request params.", "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
cmRequest.ConfigMap.Labels["jcce"] = "true"
|
|
||||||
ClientSet.CoreV1().ConfigMaps(cmRequest.ConfigMap.Namespace).Create(context.TODO(), &cmRequest.ConfigMap, metav1.CreateOptions{})
|
|
||||||
// 创建调度策略实例
|
|
||||||
CreatePropagationPolicies(PropagationPolicy{
|
|
||||||
ClusterName: cmRequest.ClusterName,
|
|
||||||
TemplateId: cmRequest.TemplateId,
|
|
||||||
ResourceName: cmRequest.ConfigMap.Name,
|
|
||||||
Name: "cm" + "." + cmRequest.ConfigMap.Namespace + "." + cmRequest.ConfigMap.Name,
|
|
||||||
Namespace: cmRequest.ConfigMap.Namespace,
|
|
||||||
Kind: "ConfigMap",
|
|
||||||
})
|
|
||||||
Response(c, http.StatusOK, "success", nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListSecret 查询命名空间下的secret
|
|
||||||
func ListSecret(c *gin.Context) {
|
|
||||||
clusterName := c.Query("clusterName")
|
|
||||||
namespace := c.Query("namespace")
|
|
||||||
result := SearchObject(secretConst, clusterName, namespace, "")
|
|
||||||
raw, _ := result.Raw()
|
|
||||||
var secretRes SecretRes
|
|
||||||
json.Unmarshal(raw, &secretRes)
|
|
||||||
Response(c, http.StatusOK, "success", secretRes.Items)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListConfigMap 查询命名空间下的configMap
|
|
||||||
func ListConfigMap(c *gin.Context) {
|
|
||||||
clusterName := c.Query("clusterName")
|
|
||||||
namespace := c.Query("namespace")
|
|
||||||
result := SearchObject(configMapConst, clusterName, namespace, "")
|
|
||||||
raw, _ := result.Raw()
|
|
||||||
var configMaoObject ConfigMaoObject
|
|
||||||
json.Unmarshal(raw, &configMaoObject)
|
|
||||||
Response(c, http.StatusOK, "success", configMaoObject.Items)
|
|
||||||
}
|
|
89
app/crd.go
89
app/crd.go
|
@ -1,89 +0,0 @@
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
type CRDRes struct {
|
|
||||||
Kind string `json:"kind"`
|
|
||||||
ApiVersion string `json:"apiVersion"`
|
|
||||||
Metadata string `json:"metadata"`
|
|
||||||
ResourceVersion string `json:"resourceVersion"`
|
|
||||||
Items []v1.CustomResourceDefinition `json:"items"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CRDParam struct {
|
|
||||||
ClusterName []string `json:"clusterName"`
|
|
||||||
Domain string `json:"domain"`
|
|
||||||
TemplateId string `json:"templateId"`
|
|
||||||
CustomResourceDefinition v1.CustomResourceDefinition `json:"CustomResourceDefinition"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func ListCRD(c *gin.Context) {
|
|
||||||
clusterName := c.Query("clusterName")
|
|
||||||
Response(c, http.StatusOK, "success", queryCRD(clusterName))
|
|
||||||
}
|
|
||||||
|
|
||||||
func queryCRD(clusterName string) []v1.CustomResourceDefinition {
|
|
||||||
result := SearchObject(crdConst, clusterName, "", "")
|
|
||||||
raw, _ := result.Raw()
|
|
||||||
var cRDRes CRDRes
|
|
||||||
json.Unmarshal(raw, &cRDRes)
|
|
||||||
return cRDRes.Items
|
|
||||||
}
|
|
||||||
|
|
||||||
const crUrl = "/apis/cluster.karmada.io/v1alpha1/clusters/%s/proxy/apis/%s/namespaces/%s/%s"
|
|
||||||
|
|
||||||
func CreateCRD(c *gin.Context) {
|
|
||||||
var param CRDParam
|
|
||||||
if err := c.BindJSON(¶m); err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "invalid request params.", "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
crdList, err := CrDClient.ApiextensionsV1().CustomResourceDefinitions().Create(context.TODO(), ¶m.CustomResourceDefinition, metav1.CreateOptions{})
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 创建调度实例
|
|
||||||
CreatePropagationPolicies(PropagationPolicy{
|
|
||||||
ClusterName: param.ClusterName,
|
|
||||||
TemplateId: param.TemplateId,
|
|
||||||
ResourceName: param.CustomResourceDefinition.Name,
|
|
||||||
Name: "crd" + param.CustomResourceDefinition.Namespace + param.CustomResourceDefinition.Name,
|
|
||||||
Namespace: param.CustomResourceDefinition.Namespace,
|
|
||||||
Kind: "CustomResourceDefinition",
|
|
||||||
})
|
|
||||||
Response(c, http.StatusOK, "success", crdList)
|
|
||||||
}
|
|
||||||
|
|
||||||
func DetailCRD(c *gin.Context) {
|
|
||||||
|
|
||||||
clusterName := c.Query("clusterName")
|
|
||||||
namespace := c.Query("namespace")
|
|
||||||
kind := c.Query("kind")
|
|
||||||
customResourceDefinitions := queryCRD(clusterName)
|
|
||||||
for _, crd := range customResourceDefinitions {
|
|
||||||
if strings.EqualFold(crd.Spec.Names.Plural, kind) || strings.EqualFold(crd.Spec.Names.Singular, kind) {
|
|
||||||
url := fmt.Sprintf(crUrl, clusterName, crd.Spec.Group+"/"+crd.Spec.Versions[0].Name, namespace, crd.Spec.Names.Plural)
|
|
||||||
result := KarmadaClient.SearchV1alpha1().RESTClient().Get().AbsPath(url).Do(context.TODO())
|
|
||||||
|
|
||||||
raw, _ := result.Raw()
|
|
||||||
|
|
||||||
Response(c, http.StatusOK, "success", raw)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//if err != nil {
|
|
||||||
// return
|
|
||||||
//}
|
|
||||||
//Response(c, http.StatusOK, "success", crdList)
|
|
||||||
}
|
|
96
app/cron.go
96
app/cron.go
|
@ -1,96 +0,0 @@
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/tls"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"github.com/robfig/cron/v3"
|
|
||||||
"io/ioutil"
|
|
||||||
"jcc-schedule/tool"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Monitor struct {
|
|
||||||
CpuAvgUsage float64 `json:"cpuAvgUsage"`
|
|
||||||
MemAvgUsage float64 `json:"memAvgUsage"`
|
|
||||||
CpuTotalUsage float64 `json:"cpuTotalUsage"`
|
|
||||||
MemTotalUsage float64 `json:"memTotalUsage"`
|
|
||||||
Date string `json:"date"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func SyncMonitor(c *cron.Cron) {
|
|
||||||
//创建定时任务
|
|
||||||
EntryID, err := c.AddFunc("0 0 12 * * ?", func() {
|
|
||||||
mrs := []MetricResult{}
|
|
||||||
monitor := Monitor{Date: time.Now().Format("2006-01-02 15:04:05")}
|
|
||||||
mrs = getOverallMetrics(metric_range_1d, steps_7d)
|
|
||||||
for _, mr := range mrs {
|
|
||||||
|
|
||||||
for _, res := range mr.MetricData.Result {
|
|
||||||
for _, value := range res {
|
|
||||||
bytes, err := json.Marshal(value)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
split := strings.Split(string(bytes), ",")
|
|
||||||
|
|
||||||
if mr.MetricName == "mem_avg_usage" {
|
|
||||||
memAvg, err := strconv.ParseFloat(split[1][:13], 64)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
monitor.MemAvgUsage = tool.FloatConv(memAvg / 1073741824)
|
|
||||||
}
|
|
||||||
if mr.MetricName == "cpu_avg_usage" {
|
|
||||||
cpuAvg, err := strconv.ParseFloat(split[1][:5], 64)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
monitor.CpuAvgUsage = cpuAvg
|
|
||||||
}
|
|
||||||
if mr.MetricName == "mem_total_usage" {
|
|
||||||
memTotal, err := strconv.ParseFloat(split[1][:13], 64)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
monitor.MemTotalUsage = tool.FloatConv(memTotal / 1073741824)
|
|
||||||
}
|
|
||||||
if mr.MetricName == "cpu_total_usage" {
|
|
||||||
cpuTotal, err := strconv.ParseFloat(split[1][:5], 64)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
monitor.CpuTotalUsage = cpuTotal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
jsonStr, _ := json.Marshal(monitor)
|
|
||||||
req, err := http.NewRequest("POST", "https://10.101.15.6/apis/jcc-ledger/contract/syncMonitor", bytes.NewBuffer(jsonStr))
|
|
||||||
req.Header.Add("content-type", "application/json;charset=UTF-8")
|
|
||||||
|
|
||||||
req.Header.Add("ledgerName", strconv.FormatInt(time.Now().Unix(), 10))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer req.Body.Close()
|
|
||||||
|
|
||||||
client := &http.Client{Transport: &http.Transport{
|
|
||||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
|
||||||
}}
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
result, _ := ioutil.ReadAll(resp.Body)
|
|
||||||
println(result)
|
|
||||||
})
|
|
||||||
|
|
||||||
fmt.Println(time.Now(), EntryID, err)
|
|
||||||
}
|
|
1133
app/deployment.go
1133
app/deployment.go
File diff suppressed because it is too large
Load Diff
307
app/domain.go
307
app/domain.go
|
@ -1,307 +0,0 @@
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/karmada-io/karmada/pkg/util"
|
|
||||||
v2 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Domain struct {
|
|
||||||
DomainId int32 `json:"domain_id"`
|
|
||||||
DomainName string `json:"domain_name"`
|
|
||||||
LabelType string `json:"label_type"`
|
|
||||||
Location [2]float64 `json:"location"`
|
|
||||||
Labels []map[string]string `json:"labels"`
|
|
||||||
Clusters []string `json:"clusters"`
|
|
||||||
Deployments []string `json:"deployments"`
|
|
||||||
CPURate float64 `json:"cpu_rate"`
|
|
||||||
MemoryRate float64 `json:"memory_rate"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
LabelId []int32 `json:"labelId"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type DomainUsedRate struct {
|
|
||||||
Rate float64 `json:"rate"`
|
|
||||||
DomainName string `json:"domainName"`
|
|
||||||
ClusterCount float64 `json:"clusterCount"`
|
|
||||||
IsUsing int32 `json:"IsUsing"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateDomain 创建域
|
|
||||||
// @Summary 创建域
|
|
||||||
// @Description 创建域
|
|
||||||
// @Tags domain
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param param body Domain true "json"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 400
|
|
||||||
// @Router /api/v1/domain/create [post]
|
|
||||||
func CreateDomain(c *gin.Context) {
|
|
||||||
//获取填写的参数
|
|
||||||
domain := Domain{}
|
|
||||||
c.BindJSON(&domain)
|
|
||||||
sqlCreateDomain := "insert into domain(domain_name,longitude,latitude) values (?,?,?)"
|
|
||||||
_, err := DB.Exec(sqlCreateDomain, domain.DomainName, domain.Location[0], domain.Location[1])
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "query failed!", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
domainIdMax, _ := DB.Query("select max(domain_id) from domain")
|
|
||||||
defer domainIdMax.Close()
|
|
||||||
var domainId int32
|
|
||||||
for domainIdMax.Next() {
|
|
||||||
var id int32
|
|
||||||
err := domainIdMax.Scan(&id)
|
|
||||||
domainId = id
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "query failed!", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var keys []string
|
|
||||||
var values []string
|
|
||||||
for _, label := range domain.Labels {
|
|
||||||
for k, v := range label {
|
|
||||||
keys = append(keys, k)
|
|
||||||
values = append(values, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < len(domain.Clusters); i++ {
|
|
||||||
sqlStr2 := "insert into domain_cluster(domain_id,domain_name,cluster_name,description) values (?,?,?,?)"
|
|
||||||
_, err := DB.Exec(sqlStr2, domainId, domain.DomainName, domain.Clusters[i], domain.Description)
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "query failed!", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < len(domain.Labels[0]); i++ {
|
|
||||||
sqlDomainLabel := "insert into domain_label(domain_id,domain_name,label_id,label_type,label_name) values (?,?,?,?,?)"
|
|
||||||
_, err := DB.Exec(sqlDomainLabel, domainId, domain.DomainName, domain.LabelId[i], keys[i], values[i])
|
|
||||||
//b, err := DB.Exec(sqlStr3, domainId, domain.DomainName, 4, keys[i], values[i])
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "query failed!", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Response(c, http.StatusOK, "success", len(domain.Labels[0]))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListDomain 查询Domain列表
|
|
||||||
// @Summary 查询Domain列表
|
|
||||||
// @Description 查询Domain列表
|
|
||||||
// @Tags domain
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param domain_name query string false "domain名称"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 500
|
|
||||||
// @Router /api/v1/domain/list [get]
|
|
||||||
func ListDomain(c *gin.Context) {
|
|
||||||
|
|
||||||
domainName := c.Query("domain_name")
|
|
||||||
|
|
||||||
//获取域列表
|
|
||||||
rows, _ := DB.Query("select domain_id,domain_name,longitude,latitude from domain where domain_name like ?", "%"+domainName+"%")
|
|
||||||
|
|
||||||
defer rows.Close()
|
|
||||||
domainList := make([]Domain, 0)
|
|
||||||
for rows.Next() {
|
|
||||||
var dm Domain
|
|
||||||
var longitude float64
|
|
||||||
var latitude float64
|
|
||||||
err := rows.Scan(&dm.DomainId, &dm.DomainName, &longitude, &latitude)
|
|
||||||
|
|
||||||
var location = [...]float64{longitude, latitude}
|
|
||||||
dm.Location = location
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "query failed!", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//获取单个域的标签
|
|
||||||
sqlDomainLabel := "select d.domain_name,l.label_type,l.label_name from domain d,domain_label l where d.domain_id = l.domain_id and d.domain_id = ?"
|
|
||||||
rowsLabel, err := DB.Query(sqlDomainLabel, dm.DomainId)
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "query failed!", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
domainLabelList := make(map[string]string)
|
|
||||||
for rowsLabel.Next() {
|
|
||||||
var domainName string
|
|
||||||
var labelKey string
|
|
||||||
var labelValue string
|
|
||||||
err := rowsLabel.Scan(&domainName, &labelKey, &labelValue)
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "query failed!", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
domainLabelList[labelKey] = labelValue
|
|
||||||
}
|
|
||||||
dm.Labels = append(dm.Labels, domainLabelList)
|
|
||||||
|
|
||||||
//获取单个域下的所有集群
|
|
||||||
sqlDomainCluster := "select d.domain_name,c.cluster_name from domain d,domain_cluster c where d.domain_id = c.domain_id and d.domain_id = ?"
|
|
||||||
rowsCluster, err := DB.Query(sqlDomainCluster, dm.DomainId)
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "query failed!", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var clusters []string
|
|
||||||
for rowsCluster.Next() {
|
|
||||||
var domainName string
|
|
||||||
var clusterName string
|
|
||||||
err := rowsCluster.Scan(&domainName, &clusterName)
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "query failed!", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
clusters = append(clusters, clusterName)
|
|
||||||
}
|
|
||||||
dm.Clusters = clusters
|
|
||||||
|
|
||||||
domainList = append(domainList, dm)
|
|
||||||
}
|
|
||||||
|
|
||||||
Response(c, http.StatusOK, "success", domainList)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//DescribeDomain 获取域的详细信息
|
|
||||||
/*
|
|
||||||
入参: domainID namespace名称
|
|
||||||
出参: deployment数量 资源使用量
|
|
||||||
首先通过domain_cluster表查出该域所有的集群列表,遍历集群列表
|
|
||||||
在单个集群使用namespace查询所有deployment,将deploy名称放入上层domain数组
|
|
||||||
*/
|
|
||||||
// @Summary 获取域的详细信息
|
|
||||||
// @Description 获取域的详细信息
|
|
||||||
// @Tags domain
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param domain_id query string true "domainId"
|
|
||||||
// @Param namespace query string true "命名空间"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 500
|
|
||||||
// @Router /api/v1/domain/describe [get]
|
|
||||||
func DescribeDomain(c *gin.Context) {
|
|
||||||
|
|
||||||
var domain Domain
|
|
||||||
domainId := c.Query("domain_id")
|
|
||||||
namespace := c.Query("namespace")
|
|
||||||
rows, _ := DB.Query("select d.domain_name,dc.cluster_name,d.longitude,d.latitude from domain_cluster dc,domain d where dc.domain_id = d.domain_id and dc.domain_id = ?", domainId)
|
|
||||||
var domainName string
|
|
||||||
var longitude float64
|
|
||||||
var latitude float64
|
|
||||||
var totalAllocatableCPU int64
|
|
||||||
var totalAllocatableMemory int64
|
|
||||||
var totalAllocatedCPU int64
|
|
||||||
var totalAllocatedMemory int64
|
|
||||||
for rows.Next() {
|
|
||||||
var clusterName string
|
|
||||||
err := rows.Scan(&domainName, &clusterName, &longitude, &latitude)
|
|
||||||
if err != nil {
|
|
||||||
println(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
deploys := GetDeployFromOS(namespace, "", clusterName)
|
|
||||||
hits, _ := deploys.Get("hits").Get("hits").Array()
|
|
||||||
|
|
||||||
for i := 0; i < len(hits); i++ {
|
|
||||||
deployName, _ := deploys.Get("hits").Get("hits").GetIndex(i).Get("_source").Get("metadata").Get("name").String()
|
|
||||||
domain.Deployments = append(domain.Deployments, deployName)
|
|
||||||
}
|
|
||||||
domain.Clusters = append(domain.Clusters, clusterName)
|
|
||||||
cluster, _, _ := util.GetClusterWithKarmadaClient(KarmadaClient, clusterName)
|
|
||||||
allocatableCPU := cluster.Status.ResourceSummary.Allocatable.Cpu().MilliValue()
|
|
||||||
allocatableMemory := cluster.Status.ResourceSummary.Allocatable.Memory().MilliValue()
|
|
||||||
allocatedCPU := cluster.Status.ResourceSummary.Allocated.Cpu().MilliValue()
|
|
||||||
allocatedMemory := cluster.Status.ResourceSummary.Allocated.Memory().MilliValue()
|
|
||||||
|
|
||||||
totalAllocatableCPU += allocatableCPU
|
|
||||||
totalAllocatableMemory += allocatableMemory
|
|
||||||
totalAllocatedCPU += allocatedCPU
|
|
||||||
totalAllocatedMemory += allocatedMemory
|
|
||||||
|
|
||||||
}
|
|
||||||
ddId, _ := strconv.ParseInt(domainId, 10, 32)
|
|
||||||
domain.DomainId = int32(ddId)
|
|
||||||
domain.DomainName = domainName
|
|
||||||
domain.Deployments = removeDuplicateArr(domain.Deployments)
|
|
||||||
domain.Location[0] = longitude
|
|
||||||
domain.Location[1] = latitude
|
|
||||||
domain.CPURate = float64(totalAllocatedCPU) / float64(totalAllocatableCPU)
|
|
||||||
domain.MemoryRate = float64(totalAllocatedMemory) / float64(totalAllocatableMemory)
|
|
||||||
|
|
||||||
Response(c, http.StatusOK, "success", domain)
|
|
||||||
}
|
|
||||||
func ListClusters(c *gin.Context) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListByDeployment 根据项目名称和工作负载查询域列表
|
|
||||||
// @Summary 根据项目名称和工作负载查询域列表
|
|
||||||
// @Description 根据项目名称和工作负载查询域列表
|
|
||||||
// @Tags domain
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param deployment_name query string true "工作负载"
|
|
||||||
// @Param namespace query string true "命名空间"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 500
|
|
||||||
// @Router /api/v1/domain/listByDeployment [get]
|
|
||||||
func ListByDeployment(c *gin.Context) {
|
|
||||||
namespaceName := c.Query("namespace")
|
|
||||||
deploymentName := c.Query("deployment_name")
|
|
||||||
|
|
||||||
propagationPolicy, err := KarmadaClient.PolicyV1alpha1().PropagationPolicies(namespaceName).Get(context.TODO(), "deployment."+namespaceName+"."+deploymentName, v2.GetOptions{})
|
|
||||||
if err != nil {
|
|
||||||
|
|
||||||
}
|
|
||||||
var clusterList []string
|
|
||||||
for _, clusterName := range propagationPolicy.Spec.Placement.ClusterAffinity.ClusterNames {
|
|
||||||
clusterList = append(clusterList, clusterName)
|
|
||||||
}
|
|
||||||
var domainInfos []DomainInfo
|
|
||||||
Gorm.Debug().Raw("SELECT distinct domain_id,domain_name from domain_cluster where cluster_name in (?)", clusterList).Scan(&domainInfos)
|
|
||||||
|
|
||||||
Response(c, http.StatusOK, "success", domainInfos)
|
|
||||||
}
|
|
||||||
|
|
||||||
type DomainInfo struct {
|
|
||||||
DomainId int `json:"domainId"`
|
|
||||||
DomainName string `json:"domainName"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func QueryDomainUsedRate(c *gin.Context) {
|
|
||||||
var domainUsedRates []DomainUsedRate
|
|
||||||
rows, err := DB.Query("SELECT d.domain_name,COUNT(dc.id),d.is_using IsUsing from domain d inner join domain_cluster dc on dc.domain_id = d.domain_id group by d.domain_id")
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "update namespace failed", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var domainCount float64
|
|
||||||
for rows.Next() {
|
|
||||||
var domainUsedRate DomainUsedRate
|
|
||||||
err := rows.Scan(&domainUsedRate.DomainName, &domainUsedRate.ClusterCount, &domainUsedRate.IsUsing)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
domainUsedRates = append(domainUsedRates, domainUsedRate)
|
|
||||||
domainCount = domainCount + domainUsedRate.ClusterCount
|
|
||||||
}
|
|
||||||
rows.Close()
|
|
||||||
for index, _ := range domainUsedRates {
|
|
||||||
float, _ := strconv.ParseFloat(fmt.Sprintf("%.4f", domainUsedRates[index].ClusterCount/domainCount), 64)
|
|
||||||
domainUsedRates[index].Rate = float
|
|
||||||
}
|
|
||||||
Response(c, http.StatusOK, "success", domainUsedRates)
|
|
||||||
}
|
|
142
app/label.go
142
app/label.go
|
@ -1,142 +0,0 @@
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Label struct {
|
|
||||||
LabelId int32 `json:"label_id"`
|
|
||||||
LabelTypeId int32 `json:"label_type_id"`
|
|
||||||
LabelType *string `json:"label_type"`
|
|
||||||
LabelName string `json:"label_name"`
|
|
||||||
CreateTime *string `json:"create_time"`
|
|
||||||
UpdateTime *string `json:"update_time"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary 创建域标
|
|
||||||
// @Description 创建域标
|
|
||||||
// @Tags label
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param param body Label true "json"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 400
|
|
||||||
// @Router /api/v1/label/create [post]
|
|
||||||
func CreateLabel(c *gin.Context) {
|
|
||||||
var j Label
|
|
||||||
if err := c.BindJSON(&j); err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "invalid request params.", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, err := DB.Exec(`INSERT label(label_type_id,label_name,create_time,update_time) VALUES (?,?,?,?)`, j.LabelTypeId, j.LabelName, time.Now(), time.Now())
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "insert failed", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
Response(c, http.StatusOK, "success", nil)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary 删除域标
|
|
||||||
// @Description 删除域标
|
|
||||||
// @Tags label
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param param body Label true "json"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 400
|
|
||||||
// @Router /api/v1/label/delete [delete]
|
|
||||||
func DeleteLabel(c *gin.Context) {
|
|
||||||
var j Label
|
|
||||||
if err := c.BindJSON(&j); err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "invalid request params.", "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, err := DB.Exec(`DELETE FROM label WHERE label_id = ?`, j.LabelId)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "insert failed", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
Response(c, http.StatusOK, "success", nil)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary 查询域标列表
|
|
||||||
// @Description 查询域标列表
|
|
||||||
// @Tags label
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param label_name query string false "标签名"
|
|
||||||
// @Param pageNum query string true "页码 "
|
|
||||||
// @Param pageSize query string true "每页数量"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 500
|
|
||||||
// @Router /api/v1/label/list [post]
|
|
||||||
func ListLabel(c *gin.Context) {
|
|
||||||
|
|
||||||
labelName := c.Query("label_name")
|
|
||||||
|
|
||||||
LabelList := make([]Label, 0)
|
|
||||||
rows, err := DB.Query(`SELECT l.label_id,l.label_type_id ,lt.label_type ,l.label_name, l.create_time,l.update_time FROM label l,label_type lt WHERE l.label_type_id = lt.label_type_id and l.label_name like ?`, "%"+labelName+"%")
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "query failed", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for rows.Next() {
|
|
||||||
var lt Label
|
|
||||||
var createTimeString string
|
|
||||||
var updateTimeString string
|
|
||||||
err := rows.Scan(<.LabelId, <.LabelTypeId, <.LabelType, <.LabelName, &createTimeString, &updateTimeString)
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "query failed!", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
lt.CreateTime = &createTimeString
|
|
||||||
lt.UpdateTime = &updateTimeString
|
|
||||||
|
|
||||||
LabelList = append(LabelList, lt)
|
|
||||||
}
|
|
||||||
|
|
||||||
total := len(LabelList)
|
|
||||||
page := &Page[Label]{}
|
|
||||||
page.List = LabelList
|
|
||||||
pageNum := c.Query("pageNum")
|
|
||||||
pageSize := c.Query("pageSize")
|
|
||||||
data := Paginator(page, int64(total), pageNum, pageSize)
|
|
||||||
Response(c, http.StatusOK, "success", data)
|
|
||||||
//Response(c, http.StatusOK, "success", LabelList)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary 更新域标
|
|
||||||
// @Description 更新域标
|
|
||||||
// @Tags label
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param param body Label true "json"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 400
|
|
||||||
// @Router /api/v1/label/update [post]
|
|
||||||
func UpdateLabel(c *gin.Context) {
|
|
||||||
var j Label
|
|
||||||
if err := c.BindJSON(&j); err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "invalid request params.", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := DB.Exec(`UPDATE label SET label_type_id = ?,label_name = ?,update_time = ? WHERE label_id = ?`, j.LabelTypeId, j.LabelName, time.Now(), j.LabelId)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "update failed", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
Response(c, http.StatusOK, "success", nil)
|
|
||||||
|
|
||||||
}
|
|
132
app/labelType.go
132
app/labelType.go
|
@ -1,132 +0,0 @@
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
type LabelType struct {
|
|
||||||
LabelTypeId int32 `json:"label_type_id"`
|
|
||||||
LabelType string `json:"label_type"`
|
|
||||||
MultiCheck *bool `json:"multi_check"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateLabelType 创建域标类型
|
|
||||||
// @Summary 创建域标类型
|
|
||||||
// @Description 创建域标类型
|
|
||||||
// @Tags labelType
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param param body LabelType true "json"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 400
|
|
||||||
// @Router /api/v1/labelType/create [post]
|
|
||||||
func CreateLabelType(c *gin.Context) {
|
|
||||||
var j LabelType
|
|
||||||
if err := c.BindJSON(&j); err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "invalid request params.", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, err := DB.Exec(`INSERT label_type(label_type,multi_check) VALUES (?,?)`, j.LabelType, j.MultiCheck)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "insert failed", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
Response(c, http.StatusOK, "success", nil)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary 删除域标类型
|
|
||||||
// @Description 创建域标类型
|
|
||||||
// @Tags labelType
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param param body LabelType true "json"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 400
|
|
||||||
// @Router /api/v1/labelType/delete [delete]
|
|
||||||
func DeleteLabelType(c *gin.Context) {
|
|
||||||
var j LabelType
|
|
||||||
if err := c.BindJSON(&j); err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "invalid request params.", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, err := DB.Exec(`DELETE FROM label_type WHERE label_type_id = ?`, j.LabelTypeId)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "insert failed", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
Response(c, http.StatusOK, "success", nil)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListLabelType 查询域标类型
|
|
||||||
// @Summary 查询域标类型
|
|
||||||
// @Description 查询域标类型
|
|
||||||
// @Tags labelType
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param label_type query string false "标签类型"
|
|
||||||
// @Param pageNum query string true "页码 "
|
|
||||||
// @Param pageSize query string true "每页数量"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 500
|
|
||||||
// @Router /api/v1/labelType/list [get]
|
|
||||||
func ListLabelType(c *gin.Context) {
|
|
||||||
|
|
||||||
labelType := c.Query("label_type")
|
|
||||||
|
|
||||||
LabelTypeList := make([]LabelType, 0)
|
|
||||||
rows, err := DB.Query(`SELECT label_type_id,label_type,multi_check FROM label_type WHERE label_type like ?`, "%"+labelType+"%")
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "query failed", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
for rows.Next() {
|
|
||||||
var lt LabelType
|
|
||||||
err := rows.Scan(<.LabelTypeId, <.LabelType, <.MultiCheck)
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "query failed!", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
LabelTypeList = append(LabelTypeList, lt)
|
|
||||||
}
|
|
||||||
|
|
||||||
total := len(LabelTypeList)
|
|
||||||
page := &Page[LabelType]{}
|
|
||||||
page.List = LabelTypeList
|
|
||||||
pageNum := c.Query("pageNum")
|
|
||||||
pageSize := c.Query("pageSize")
|
|
||||||
data := Paginator(page, int64(total), pageNum, pageSize)
|
|
||||||
Response(c, http.StatusOK, "success", data)
|
|
||||||
//Response(c, http.StatusOK, "success", LabelTypeList)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary 更新域标类型
|
|
||||||
// @Description 更新域标类型
|
|
||||||
// @Tags labelType
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param param body LabelType true "json"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 400
|
|
||||||
// @Router /api/v1/labelType/update [post]
|
|
||||||
func UpdateLabelType(c *gin.Context) {
|
|
||||||
var j LabelType
|
|
||||||
if err := c.BindJSON(&j); err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "invalid request params.", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, err := DB.Exec(`UPDATE label_type SET label_type = ?,multi_check = ? WHERE label_type_id = ?`, j.LabelType, j.MultiCheck, j.LabelTypeId)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "update failed", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
Response(c, http.StatusOK, "success", nil)
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,170 +0,0 @@
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"io/ioutil"
|
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
|
||||||
"k8s.io/client-go/kubernetes/scheme"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
baseUrl = "http://10.101.15.8:8428/prometheus/api/v1/query_range?"
|
|
||||||
PodName_Replacer = "podName"
|
|
||||||
ClusterName_Replacer = "clusterName"
|
|
||||||
Node_Replacer = "nodeName"
|
|
||||||
Namespcae_Replacer = "nameSpace"
|
|
||||||
Schedule_Replacer = "Schedule"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ListMetricResult struct {
|
|
||||||
MetricResult []MetricResult `json:"metricResult,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type MetricResult struct {
|
|
||||||
MetricName string `json:"metric_name, omitempty"`
|
|
||||||
MetricData MetricData `json:"data, omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type MetricData struct {
|
|
||||||
Result []map[string]interface{} `json:"result"`
|
|
||||||
ResultType string `json:"resultType"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ResultData struct {
|
|
||||||
Status string `json:"status"`
|
|
||||||
Data MetricData `json:"data"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type MetricUrl struct {
|
|
||||||
searchType string
|
|
||||||
searchName string
|
|
||||||
urls string
|
|
||||||
}
|
|
||||||
|
|
||||||
func AddTypeMetaToObject(obj runtime.Object) error {
|
|
||||||
versionKinds, _, err := scheme.Scheme.ObjectKinds(obj)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("apiVersion or kind not found; %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, vk := range versionKinds {
|
|
||||||
if len(vk.Kind) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if len(vk.Version) == 0 || vk.Version == runtime.APIVersionInternal {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
obj.GetObjectKind().SetGroupVersionKind(vk)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getMetricRange(metricRange string) (start int64, end int64) {
|
|
||||||
end = time.Now().Unix()
|
|
||||||
range_milli, _ := time.ParseDuration(metricRange)
|
|
||||||
start = end - int64(range_milli.Seconds())
|
|
||||||
return start, end
|
|
||||||
}
|
|
||||||
|
|
||||||
func replacePromql(promql string, replace_map map[string]string, replace_type string) (str string) {
|
|
||||||
newPromql := promql
|
|
||||||
switch replace_type {
|
|
||||||
case PodName_Replacer:
|
|
||||||
newPromql = strings.Replace(newPromql, ClusterName_Replacer, replace_map[ClusterName_Replacer], -1)
|
|
||||||
newPromql = strings.Replace(newPromql, PodName_Replacer, replace_map[PodName_Replacer], -1)
|
|
||||||
case ClusterName_Replacer:
|
|
||||||
newPromql = strings.Replace(newPromql, ClusterName_Replacer, replace_map[ClusterName_Replacer], -1)
|
|
||||||
case Node_Replacer:
|
|
||||||
newPromql = strings.Replace(newPromql, ClusterName_Replacer, replace_map[ClusterName_Replacer], -1)
|
|
||||||
newPromql = strings.Replace(newPromql, Node_Replacer, replace_map[Node_Replacer], -1)
|
|
||||||
case Namespcae_Replacer:
|
|
||||||
newPromql = strings.Replace(newPromql, ClusterName_Replacer, replace_map[ClusterName_Replacer], -1)
|
|
||||||
newPromql = strings.Replace(newPromql, Namespcae_Replacer, replace_map[Namespcae_Replacer], -1)
|
|
||||||
case Schedule_Replacer:
|
|
||||||
newPromql = strings.Replace(newPromql, ClusterName_Replacer, replace_map[ClusterName_Replacer], -1)
|
|
||||||
newPromql = strings.Replace(newPromql, Schedule_Replacer, replace_map[Schedule_Replacer], -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
return newPromql
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetMetricUrl(promql string, replace_map map[string]string, replace_type string, metric_range string, steps int64) (metricUrl string) {
|
|
||||||
var newPromql string
|
|
||||||
if replace_map != nil {
|
|
||||||
newPromql = replacePromql(promql, replace_map, replace_type)
|
|
||||||
} else {
|
|
||||||
newPromql = promql
|
|
||||||
}
|
|
||||||
start, end := getMetricRange(metric_range)
|
|
||||||
params := url.Values{}
|
|
||||||
params.Add("query", newPromql)
|
|
||||||
params.Add("start", strconv.FormatInt(start, 10))
|
|
||||||
params.Add("end", strconv.FormatInt(end, 10))
|
|
||||||
params.Add("step", strconv.FormatInt(steps, 10))
|
|
||||||
|
|
||||||
config := GetNacosConfig()
|
|
||||||
return config.Vmui.BaseUrl + params.Encode()
|
|
||||||
}
|
|
||||||
|
|
||||||
func HttpGetMetrics(metricName string, metricUrls []MetricUrl, wg *sync.WaitGroup, done chan<- MetricResult) {
|
|
||||||
client := &http.Client{Timeout: 60 * time.Second}
|
|
||||||
md := MetricData{
|
|
||||||
ResultType: "matrix",
|
|
||||||
}
|
|
||||||
for _, metricUrl := range metricUrls {
|
|
||||||
resp, err := client.Get(metricUrl.urls)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln(err)
|
|
||||||
}
|
|
||||||
body, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln(err)
|
|
||||||
}
|
|
||||||
var rd ResultData
|
|
||||||
err = json.Unmarshal(body, &rd)
|
|
||||||
if err != nil {
|
|
||||||
log.Panicln(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(rd.Data.Result) == 0 {
|
|
||||||
m := map[string]string{
|
|
||||||
strings.ToLower(metricUrl.searchType): metricUrl.searchName,
|
|
||||||
}
|
|
||||||
v := []interface{}{
|
|
||||||
0,
|
|
||||||
"0",
|
|
||||||
}
|
|
||||||
r := map[string]interface{}{
|
|
||||||
"metric": m,
|
|
||||||
"values": v,
|
|
||||||
}
|
|
||||||
md.Result = append(md.Result, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, r := range rd.Data.Result {
|
|
||||||
format, ok := r["metric"].(map[string]interface{})
|
|
||||||
if ok && len(format) == 0 {
|
|
||||||
r["metric"] = map[string]interface{}{
|
|
||||||
strings.ToLower(metricUrl.searchType): metricUrl.searchName,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
md.Result = append(md.Result, r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mr := MetricResult{
|
|
||||||
metricName, md,
|
|
||||||
}
|
|
||||||
done <- mr
|
|
||||||
wg.Done()
|
|
||||||
}
|
|
124
app/nacos.go
124
app/nacos.go
|
@ -1,124 +0,0 @@
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/nacos-group/nacos-sdk-go/clients"
|
|
||||||
"github.com/nacos-group/nacos-sdk-go/clients/config_client"
|
|
||||||
"github.com/nacos-group/nacos-sdk-go/common/constant"
|
|
||||||
"github.com/nacos-group/nacos-sdk-go/vo"
|
|
||||||
"gopkg.in/yaml.v3"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Config struct {
|
|
||||||
App struct {
|
|
||||||
Name string `yaml:"name"`
|
|
||||||
Version string `yaml:"version"`
|
|
||||||
}
|
|
||||||
OpenSearch struct {
|
|
||||||
Url string `yaml:"url"`
|
|
||||||
UserName string `yaml:"username"`
|
|
||||||
PassWord string `yaml:"password"`
|
|
||||||
}
|
|
||||||
Mysql struct {
|
|
||||||
Url string `yaml:"url"`
|
|
||||||
UrlPCM string `yaml:"url-pcm"`
|
|
||||||
MaxOpenConn int `yaml:"max-open-conn"`
|
|
||||||
MaxIdleConn int `yaml:"max-idle-conn"`
|
|
||||||
}
|
|
||||||
Redis struct {
|
|
||||||
Host string `yaml:"host"`
|
|
||||||
Password string `yaml:"password"`
|
|
||||||
DB int `yaml:"db"`
|
|
||||||
}
|
|
||||||
Karmada struct {
|
|
||||||
ConfigPath string `yaml:"config-path"`
|
|
||||||
MemberConfigPath string `yaml:"member-config-path"`
|
|
||||||
}
|
|
||||||
Vmui struct {
|
|
||||||
BaseUrl string `yaml:"base-url"`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetNacosConfig() Config {
|
|
||||||
configClient := GetClient()
|
|
||||||
// 获取配置
|
|
||||||
dataId := "dispatch_center_test"
|
|
||||||
group := "DEFAULT_GROUP"
|
|
||||||
content, err := configClient.GetConfig(vo.ConfigParam{
|
|
||||||
DataId: dataId,
|
|
||||||
Group: group})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("获取%s配置失败: %s", dataId, err.Error())
|
|
||||||
}
|
|
||||||
var config = Config{}
|
|
||||||
err = yaml.Unmarshal([]byte(content), &config)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("解析%s配置失败: %s", dataId, err.Error())
|
|
||||||
}
|
|
||||||
CreateKarmadaHostConfig()
|
|
||||||
return config
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetClient() config_client.IConfigClient {
|
|
||||||
//开发环境需要指定nacos地址
|
|
||||||
serverConfig := []constant.ServerConfig{
|
|
||||||
{
|
|
||||||
IpAddr: "nacos.jcce.dev",
|
|
||||||
Port: 8848,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
// 创建clientConfig
|
|
||||||
clientConfig := constant.ClientConfig{
|
|
||||||
NamespaceId: "test", // 如果需要支持多namespace,我们可以场景多个client,它们有不同的NamespaceId。当namespace是public时,此处填空字符串。
|
|
||||||
TimeoutMs: 5000,
|
|
||||||
NotLoadCacheAtStart: true,
|
|
||||||
LogLevel: "debug",
|
|
||||||
}
|
|
||||||
// 创建动态配置客户端
|
|
||||||
configClient, err := clients.CreateConfigClient(map[string]interface{}{
|
|
||||||
"serverConfigs": serverConfig,
|
|
||||||
"clientConfig": clientConfig,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("初始化nacos失败: %s", err.Error())
|
|
||||||
}
|
|
||||||
return configClient
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateKarmadaHostConfig 从nacos读取配置创建karmada-host集群配置文件
|
|
||||||
func CreateKarmadaHostConfig() {
|
|
||||||
configName := "karmada-host"
|
|
||||||
client := GetClient()
|
|
||||||
content, err := client.GetConfig(vo.ConfigParam{
|
|
||||||
DataId: "karmada-host",
|
|
||||||
Group: "DEFAULT_GROUP",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("获取%s配置失败: %s", "golang", err.Error())
|
|
||||||
}
|
|
||||||
dir, _ := os.Getwd()
|
|
||||||
err = os.MkdirAll(dir+"/karmadaConfig", 0766)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("nacos 配置目录创建失败,error:%s", err.Error())
|
|
||||||
}
|
|
||||||
clusterConfig, err := os.Create("karmadaConfig/" + configName)
|
|
||||||
defer clusterConfig.Close()
|
|
||||||
clusterConfig.Write([]byte(content))
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetAgentTemplate() string {
|
|
||||||
configClient := GetClient()
|
|
||||||
// 获取配置
|
|
||||||
dataId := "agent-template"
|
|
||||||
group := "DEFAULT_GROUP"
|
|
||||||
content, err := configClient.GetConfig(vo.ConfigParam{
|
|
||||||
DataId: dataId,
|
|
||||||
Group: group})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("获取%s配置失败: %s", dataId, err.Error())
|
|
||||||
}
|
|
||||||
return content
|
|
||||||
}
|
|
706
app/namespace.go
706
app/namespace.go
|
@ -1,706 +0,0 @@
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/jmoiron/sqlx"
|
|
||||||
"github.com/karmada-io/karmada/pkg/util"
|
|
||||||
"io"
|
|
||||||
v1 "k8s.io/api/batch/v1"
|
|
||||||
coreV1 "k8s.io/api/core/v1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"net/http"
|
|
||||||
"sigs.k8s.io/yaml"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
namespace_memory_usage_wo_cache = "namespace:container_memory_usage_bytes_wo_cache:sum{cluster_name='clusterName',namespace='nameSpace', prometheus_replica='prometheus-k8s-0'}"
|
|
||||||
namespace_cpu_usage = "sum by (namespace) (namespace:workload_cpu_usage:sum{cluster_name='clusterName',namespace='nameSpace', prometheus_replica='prometheus-k8s-0'})"
|
|
||||||
namespace_pod_count = "count(sum by (pod) (kube_pod_container_info{cluster_name='clusterName', namespace='nameSpace', prometheus_replica='prometheus-k8s-0'}))"
|
|
||||||
metric_range_12h = "12h"
|
|
||||||
steps_namespace = 1080
|
|
||||||
)
|
|
||||||
|
|
||||||
type Namespace struct {
|
|
||||||
NsName string `json:"ns_name"`
|
|
||||||
State string `json:"state"`
|
|
||||||
Age string `json:"age"`
|
|
||||||
CrossDomain bool `json:"cross_domain"`
|
|
||||||
Domains []Domain `json:"domains"`
|
|
||||||
Clusters []string `json:"clusters"`
|
|
||||||
Deployments []string `json:"deployments"`
|
|
||||||
RequirePodNum int32 `json:"require_pod_num"`
|
|
||||||
AvailablePodNum int32 `json:"available_pod_num"`
|
|
||||||
Alias string `json:"alias"`
|
|
||||||
Describe string `json:"describe"`
|
|
||||||
TemplateId string `json:"templateId"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type NamespaceParam struct {
|
|
||||||
TemplateId string `json:"templateId"`
|
|
||||||
Namespace coreV1.Namespace `json:"namespace"`
|
|
||||||
ClusterName []string `json:"clusterName"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type DomainResult struct {
|
|
||||||
DomainName string `json:"domain_name"`
|
|
||||||
Location [2]float64 `json:"location"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type NamespaceObject struct {
|
|
||||||
Kind string `json:"kind"`
|
|
||||||
ApiVersion string `json:"apiVersion"`
|
|
||||||
Metadata string `json:"metadata"`
|
|
||||||
ResourceVersion string `json:"resourceVersion"`
|
|
||||||
Items []coreV1.Namespace `json:"items"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type JobObject struct {
|
|
||||||
Kind string `json:"kind"`
|
|
||||||
ApiVersion string `json:"apiVersion"`
|
|
||||||
Metadata string `json:"metadata"`
|
|
||||||
ResourceVersion string `json:"resourceVersion"`
|
|
||||||
Items []v1.Job `json:"items"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type NamespacePodCount struct {
|
|
||||||
Namespace string `json:"namespace"`
|
|
||||||
Count int32 `json:"count"`
|
|
||||||
DomainType bool `json:"domainType"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type NsResources struct {
|
|
||||||
PodCount int `json:"podCount"`
|
|
||||||
DeploymentCount int `json:"deploymentCount"`
|
|
||||||
StatefulSetCount int `json:"statefulSetCount"`
|
|
||||||
DaemonSetCount int `json:"daemonSetCount"`
|
|
||||||
JobCount int `json:"jobCount"`
|
|
||||||
PvcCount int `json:"pvcCount"`
|
|
||||||
ServiceCount int `json:"serviceCount"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type NsDomain struct {
|
|
||||||
DomainName string `json:"domainName"`
|
|
||||||
Clusters []NsCluster `json:"clusters"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type NsCluster struct {
|
|
||||||
ClusterName string `json:"clusterName"`
|
|
||||||
NameSpaces []NsNameSpace `json:"nameSpaces"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type NsNameSpace struct {
|
|
||||||
NameSpace string `json:"nameSpace"`
|
|
||||||
NameSpaceType string `json:"nameSpaceType"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type NsClusterType struct {
|
|
||||||
ClusterName string `db:"cluster_name" json:"clusterName"`
|
|
||||||
ClusterType string `db:"cluster_type" json:"ClusterType"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetNamespaceResources(c *gin.Context) {
|
|
||||||
clusterName := c.Query("clusterName")
|
|
||||||
namespace := c.Query("namespace")
|
|
||||||
// 查询pod
|
|
||||||
podResult := SearchObject(podListConst, clusterName, namespace, "")
|
|
||||||
var podObject PodObject
|
|
||||||
podRaw, _ := podResult.Raw()
|
|
||||||
json.Unmarshal(podRaw, &podObject)
|
|
||||||
// 查询deployment
|
|
||||||
deploymentResult := SearchObject(deploymentListConst, clusterName, namespace, "")
|
|
||||||
var deploymentObject DeploymentObject
|
|
||||||
deploymentRaw, _ := deploymentResult.Raw()
|
|
||||||
json.Unmarshal(deploymentRaw, &deploymentObject)
|
|
||||||
// 查询statefulSet
|
|
||||||
statefulSetResult := SearchObject(statefulSetListConst, clusterName, namespace, "")
|
|
||||||
var statefulSetObject StatefulSetObject
|
|
||||||
statefulSetRaw, _ := statefulSetResult.Raw()
|
|
||||||
json.Unmarshal(statefulSetRaw, &statefulSetObject)
|
|
||||||
// 查询daemonSet
|
|
||||||
daemonSetResult := SearchObject(daemonSetListConst, clusterName, namespace, "")
|
|
||||||
var daemonSetObject DaemonSetObject
|
|
||||||
daemonSetRaw, _ := daemonSetResult.Raw()
|
|
||||||
json.Unmarshal(daemonSetRaw, &daemonSetObject)
|
|
||||||
// 查询job
|
|
||||||
jobResult := SearchObject(jobListConst, clusterName, namespace, "")
|
|
||||||
var jobObject JobObject
|
|
||||||
jobRaw, _ := jobResult.Raw()
|
|
||||||
json.Unmarshal(jobRaw, &jobObject)
|
|
||||||
// 查询pvc
|
|
||||||
pvcResult := SearchObject(pvcListConst, clusterName, namespace, "")
|
|
||||||
var pvcObject PVCObject
|
|
||||||
pvcRaw, _ := pvcResult.Raw()
|
|
||||||
json.Unmarshal(pvcRaw, &pvcObject)
|
|
||||||
// 查询service
|
|
||||||
serviceResult := SearchObject(serviceConst, clusterName, namespace, "")
|
|
||||||
var serviceObject ServiceObject
|
|
||||||
serviceRaw, _ := serviceResult.Raw()
|
|
||||||
json.Unmarshal(serviceRaw, &serviceObject)
|
|
||||||
Response(c, http.StatusOK, "success", NsResources{
|
|
||||||
PodCount: len(podObject.Items),
|
|
||||||
DeploymentCount: len(deploymentObject.Items),
|
|
||||||
StatefulSetCount: len(statefulSetObject.Items),
|
|
||||||
DaemonSetCount: len(daemonSetObject.Items),
|
|
||||||
JobCount: len(jobObject.Items),
|
|
||||||
PvcCount: len(pvcObject.Items),
|
|
||||||
ServiceCount: len(serviceObject.Items),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateNamespace 创建命名空间(项目)
|
|
||||||
// @Summary 创建命名空间(项目)
|
|
||||||
// @Description 创建命名空间(项目)
|
|
||||||
// @Tags namespace
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param param body Namespace true "json"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 400
|
|
||||||
// @Router /api/v1/namespace/create [post]
|
|
||||||
func CreateNamespace(c *gin.Context) {
|
|
||||||
var param NamespaceParam
|
|
||||||
if err := c.BindJSON(¶m); err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "invalid request params.", "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 判断是karmada分发还是指定集群创建
|
|
||||||
if param.Namespace.Labels != nil && param.Namespace.Labels["jcce"] == "true" {
|
|
||||||
_, createErr := ClientSet.CoreV1().Namespaces().Create(context.TODO(), ¶m.Namespace, metav1.CreateOptions{})
|
|
||||||
if createErr != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "failed", createErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 创建调度策略实例
|
|
||||||
policyErr := CreatePropagationPolicies(PropagationPolicy{
|
|
||||||
ClusterName: param.ClusterName,
|
|
||||||
TemplateId: param.TemplateId,
|
|
||||||
ResourceName: param.Namespace.Name,
|
|
||||||
Name: "namespace" + "." + param.Namespace.Name,
|
|
||||||
Kind: "Namespace",
|
|
||||||
})
|
|
||||||
if policyErr != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "failed", policyErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
result := PostObject(namespaceConst, param.ClusterName[0], "", "", param.Namespace)
|
|
||||||
if result.Error() != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "failed", result.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Response(c, http.StatusOK, "success", "")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListNamespaceFromCluster 根据指定集群查询命名空间
|
|
||||||
func ListNamespaceFromCluster(c *gin.Context) {
|
|
||||||
clusterName := c.Query("clusterName")
|
|
||||||
name := c.Query("name")
|
|
||||||
pageNum := c.Query("pageNum")
|
|
||||||
pageSize := c.Query("pageSize")
|
|
||||||
searchObject := SearchObject(namespaceConst, clusterName, "", "")
|
|
||||||
raw, _ := searchObject.Raw()
|
|
||||||
var namespaceObject NamespaceObject
|
|
||||||
json.Unmarshal(raw, &namespaceObject)
|
|
||||||
var result []coreV1.Namespace
|
|
||||||
// 模糊查询
|
|
||||||
if len(name) != 0 {
|
|
||||||
for _, namespace := range namespaceObject.Items {
|
|
||||||
if strings.Contains(namespace.Name, name) {
|
|
||||||
result = append(result, namespace)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
result = namespaceObject.Items
|
|
||||||
}
|
|
||||||
if result == nil {
|
|
||||||
Response(c, http.StatusOK, "success", nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 分页
|
|
||||||
page := &Page[coreV1.Namespace]{}
|
|
||||||
page.List = result
|
|
||||||
// 排序
|
|
||||||
sort.SliceStable(result, func(i, j int) bool {
|
|
||||||
return result[i].CreationTimestamp.Time.After(result[j].CreationTimestamp.Time)
|
|
||||||
})
|
|
||||||
data := Paginator(page, int64(len(result)), pageNum, pageSize)
|
|
||||||
Response(c, http.StatusOK, "success", data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListName 获取所有项目名
|
|
||||||
func ListName(c *gin.Context) {
|
|
||||||
clusterName := c.Query("clusterName")
|
|
||||||
result := SearchObject(namespaceConst, clusterName, "", "")
|
|
||||||
raw, _ := result.Raw()
|
|
||||||
var namespaceObject NamespaceObject
|
|
||||||
json.Unmarshal(raw, &namespaceObject)
|
|
||||||
var namespaceList []string
|
|
||||||
for _, namespace := range namespaceObject.Items {
|
|
||||||
namespaceList = append(namespaceList, namespace.Name)
|
|
||||||
}
|
|
||||||
Response(c, http.StatusOK, "success", namespaceList)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ListPodsCount(c *gin.Context) {
|
|
||||||
var result []NamespacePodCount
|
|
||||||
namespaceMap := make(map[string]int32)
|
|
||||||
namespaceDomainMap := make(map[string]bool)
|
|
||||||
// 查询所有工作负载
|
|
||||||
deploymentList, err := ClientSet.AppsV1().Deployments("").List(context.TODO(), metav1.ListOptions{})
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "failed", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
policyList, err := KarmadaClient.PolicyV1alpha1().PropagationPolicies("").List(context.TODO(), metav1.ListOptions{})
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "failed", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, deployment := range deploymentList.Items {
|
|
||||||
// 根据命名空间计算pod数量
|
|
||||||
if _, ok := namespaceMap[deployment.Namespace]; ok {
|
|
||||||
namespaceMap[deployment.Namespace] = namespaceMap[deployment.Namespace] + deployment.Status.AvailableReplicas
|
|
||||||
} else {
|
|
||||||
namespaceMap[deployment.Namespace] = deployment.Status.AvailableReplicas
|
|
||||||
}
|
|
||||||
// 根据调度策略实例查询分发到多少个域
|
|
||||||
for _, policy := range policyList.Items {
|
|
||||||
if strings.EqualFold(policy.Name, "deployment"+"."+deployment.Namespace+"."+deployment.Name) {
|
|
||||||
// 拼接查询的集群列表
|
|
||||||
var domainCount int
|
|
||||||
query, args, _ := sqlx.In("SELECT COUNT( *) as domainCount from domain_cluster dc WHERE cluster_name in (?)", policy.Spec.Placement.ClusterAffinity.ClusterNames)
|
|
||||||
DB.QueryRow(query, args...).Scan(&domainCount)
|
|
||||||
if domainCount > 1 {
|
|
||||||
namespaceDomainMap[deployment.Namespace] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for k, v := range namespaceMap {
|
|
||||||
result = append(result, NamespacePodCount{
|
|
||||||
Namespace: k,
|
|
||||||
Count: v,
|
|
||||||
DomainType: namespaceDomainMap[k]})
|
|
||||||
}
|
|
||||||
Response(c, http.StatusOK, "success", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsExistNamespace(c *gin.Context) {
|
|
||||||
clusterName := c.Query("clusterName")
|
|
||||||
name := c.Query("name")
|
|
||||||
result := SearchObject(detailNamespaceConst, clusterName, "", name)
|
|
||||||
if result.Error() != nil {
|
|
||||||
Response(c, http.StatusOK, "success", false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
raw, _ := result.Raw()
|
|
||||||
var namespace coreV1.Namespace
|
|
||||||
json.Unmarshal(raw, &namespace)
|
|
||||||
if len(namespace.Name) == 0 {
|
|
||||||
Response(c, http.StatusOK, "success", false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
Response(c, http.StatusOK, "success", true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListNamespace 查询Namespace列表
|
|
||||||
// @Summary 查询Namespace列表
|
|
||||||
// @Description 查询Namespace列表
|
|
||||||
// @Tags namespace
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param pageNum query string true "页码 "
|
|
||||||
// @Param pageSize query string true "每页数量"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 400
|
|
||||||
// @Router /api/v1/namespace/list [get]
|
|
||||||
func ListNamespace(c *gin.Context) {
|
|
||||||
var nameList []string
|
|
||||||
list, err := ClientSet.CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{})
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "failed", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, namespace := range list.Items {
|
|
||||||
if namespace.Labels["jcce"] == "true" {
|
|
||||||
nameList = append(nameList, namespace.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Response(c, http.StatusOK, "success", nameList)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListNamespaceByDomain 根据域查询集群下Namespace列表
|
|
||||||
// @Summary 根据域查询集群下Namespace列表
|
|
||||||
// @Description 根据域查询集群下Namespace列表
|
|
||||||
// @Tags namespace
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 400
|
|
||||||
// @Router /api/v1/namespace/nameSpaceByRegion [get]
|
|
||||||
func ListNamespaceByDomain(c *gin.Context) {
|
|
||||||
domainNameList := make([]string, 0)
|
|
||||||
|
|
||||||
domainList := make([]NsDomain, 0)
|
|
||||||
clusterNameList := make([]NsClusterType, 0)
|
|
||||||
var namespaceObject NamespaceObject
|
|
||||||
|
|
||||||
//查询所有域
|
|
||||||
err := DB.Select(&domainNameList, `select domain_name from domain_cluster group by domain_name`)
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "failed", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
//查询域下面的集群
|
|
||||||
for _, s := range domainNameList {
|
|
||||||
cerr := DB.Select(&clusterNameList, `select cluster_name,cluster_type from domain_cluster where domain_name=?`, s)
|
|
||||||
if cerr != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "failed", cerr.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
clusterList := make([]NsCluster, 0)
|
|
||||||
//查询集群下的namespace
|
|
||||||
for _, c := range clusterNameList {
|
|
||||||
cluster := NsCluster{}
|
|
||||||
cluster.ClusterName = c.ClusterName
|
|
||||||
|
|
||||||
result := SearchObject(namespaceConst, c.ClusterName, "", "")
|
|
||||||
raw, _ := result.Raw()
|
|
||||||
json.Unmarshal(raw, &namespaceObject)
|
|
||||||
namespaceList := make([]NsNameSpace, 0)
|
|
||||||
for _, ns := range namespaceObject.Items {
|
|
||||||
namespace := NsNameSpace{}
|
|
||||||
namespace.NameSpace = ns.Name
|
|
||||||
namespace.NameSpaceType = c.ClusterType
|
|
||||||
namespaceList = append(namespaceList, namespace)
|
|
||||||
cluster.NameSpaces = namespaceList
|
|
||||||
}
|
|
||||||
|
|
||||||
clusterList = append(clusterList, cluster)
|
|
||||||
}
|
|
||||||
domain := NsDomain{}
|
|
||||||
domain.DomainName = s
|
|
||||||
domain.Clusters = clusterList
|
|
||||||
domainList = append(domainList, domain)
|
|
||||||
}
|
|
||||||
|
|
||||||
Response(c, http.StatusOK, "success", domainList)
|
|
||||||
}
|
|
||||||
|
|
||||||
func DetailNamespace(c *gin.Context) {
|
|
||||||
clusterName := c.Query("clusterName")
|
|
||||||
name := c.Query("name")
|
|
||||||
result := SearchObject(detailNamespaceConst, clusterName, "", name)
|
|
||||||
raw, _ := result.Raw()
|
|
||||||
var namespace coreV1.Namespace
|
|
||||||
json.Unmarshal(raw, &namespace)
|
|
||||||
Response(c, http.StatusOK, "success", namespace)
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary 查询Namespace详情
|
|
||||||
// @Description 查询Namespace详情
|
|
||||||
// @Tags namespace
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param namespace query string true "命名空间"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 400
|
|
||||||
// @Router /api/v1/namespace/describe [get]
|
|
||||||
func DescribeNamespace(c *gin.Context) {
|
|
||||||
|
|
||||||
namespace := c.Query("namespace")
|
|
||||||
|
|
||||||
optsGet := metav1.GetOptions{
|
|
||||||
TypeMeta: metav1.TypeMeta{},
|
|
||||||
ResourceVersion: "",
|
|
||||||
}
|
|
||||||
|
|
||||||
nsDescribe, _ := ClientSet.CoreV1().Namespaces().Get(context.TODO(), namespace, optsGet)
|
|
||||||
|
|
||||||
//集群工作负载分配到的所有集群和域的集合,这里的数字可以从控制平面来取
|
|
||||||
deploymentList, err := ClientSet.AppsV1().Deployments(namespace).List(context.TODO(), metav1.ListOptions{})
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var totalRequired int32
|
|
||||||
var totalAvailable int32
|
|
||||||
for _, deploy := range deploymentList.Items {
|
|
||||||
replicaRequired := *deploy.Spec.Replicas
|
|
||||||
replicaAvailable := deploy.Status.AvailableReplicas
|
|
||||||
totalRequired += replicaRequired
|
|
||||||
totalAvailable += replicaAvailable
|
|
||||||
}
|
|
||||||
|
|
||||||
//通过OpenSearch查询该ns下的分布在不同集群的所有deployment
|
|
||||||
deploys := GetDeployFromOS(namespace, "", "")
|
|
||||||
hits, _ := deploys.Get("hits").Get("hits").Array()
|
|
||||||
|
|
||||||
//集群详情中包含集群下所有的工作负载列表,所有工作负载所在的集群列表,以及所有集群所在的域列表
|
|
||||||
var deploySet []string
|
|
||||||
var clusterSet []string
|
|
||||||
var domainSet []Domain
|
|
||||||
|
|
||||||
for i := 0; i < len(hits); i++ {
|
|
||||||
|
|
||||||
clusterName, _ := deploys.Get("hits").Get("hits").GetIndex(i).Get("_source").Get("metadata").Get("annotations").Get("resource.karmada.io/cached-from-cluster").String()
|
|
||||||
deployName, _ := deploys.Get("hits").Get("hits").GetIndex(i).Get("_source").Get("metadata").Get("name").String()
|
|
||||||
//通过cluster name从数据库获取 其域名
|
|
||||||
rows, _ := DB.Query("select dc.domain_id,dc.domain_name,d.longitude,d.latitude from domain_cluster dc,domain d where dc.domain_id = d.domain_id and dc.cluster_name = ?", clusterName)
|
|
||||||
|
|
||||||
clusterSet = append(clusterSet, clusterName)
|
|
||||||
|
|
||||||
var domainId int32
|
|
||||||
var domainName string
|
|
||||||
var longitude float64
|
|
||||||
var latitude float64
|
|
||||||
|
|
||||||
for rows.Next() {
|
|
||||||
err := rows.Scan(&domainId, &domainName, &longitude, &latitude)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
deploySet = append(deploySet, deployName+":"+clusterName)
|
|
||||||
|
|
||||||
var totalAllocatableCPU int64
|
|
||||||
var totalAllocatableMemory int64
|
|
||||||
var totalAllocatedCPU int64
|
|
||||||
var totalAllocatedMemory int64
|
|
||||||
var domain Domain
|
|
||||||
deploys := GetDeployFromOS(namespace, "", clusterName)
|
|
||||||
hits, _ := deploys.Get("hits").Get("hits").Array()
|
|
||||||
|
|
||||||
for i := 0; i < len(hits); i++ {
|
|
||||||
deployName, _ := deploys.Get("hits").Get("hits").GetIndex(i).Get("_source").Get("metadata").Get("name").String()
|
|
||||||
domain.Deployments = append(domain.Deployments, deployName)
|
|
||||||
}
|
|
||||||
domain.Clusters = append(domain.Clusters, clusterName)
|
|
||||||
cluster, _, _ := util.GetClusterWithKarmadaClient(KarmadaClient, clusterName)
|
|
||||||
if cluster == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
allocatableCPU := cluster.Status.ResourceSummary.Allocatable.Cpu().MilliValue()
|
|
||||||
allocatableMemory := cluster.Status.ResourceSummary.Allocatable.Memory().MilliValue()
|
|
||||||
allocatedCPU := cluster.Status.ResourceSummary.Allocated.Cpu().MilliValue()
|
|
||||||
allocatedMemory := cluster.Status.ResourceSummary.Allocated.Memory().MilliValue()
|
|
||||||
|
|
||||||
totalAllocatableCPU += allocatableCPU
|
|
||||||
totalAllocatableMemory += allocatableMemory
|
|
||||||
totalAllocatedCPU += allocatedCPU
|
|
||||||
totalAllocatedMemory += allocatedMemory
|
|
||||||
domain.DomainId = domainId
|
|
||||||
domain.DomainName = domainName
|
|
||||||
domain.Deployments = removeDuplicateArr(domain.Deployments)
|
|
||||||
domain.Location[0] = longitude
|
|
||||||
domain.Location[1] = latitude
|
|
||||||
domain.CPURate = float64(totalAllocatedCPU) / float64(totalAllocatableCPU)
|
|
||||||
domain.MemoryRate = float64(totalAllocatedMemory) / float64(totalAllocatableMemory)
|
|
||||||
|
|
||||||
domainSet = append(domainSet, domain)
|
|
||||||
}
|
|
||||||
|
|
||||||
//域和集群 结果去重
|
|
||||||
clusterSet = removeDuplicateArr(clusterSet)
|
|
||||||
domainSet = RemoveRepeatedDomain(domainSet)
|
|
||||||
|
|
||||||
nsResult := Namespace{
|
|
||||||
NsName: nsDescribe.Name,
|
|
||||||
State: string(nsDescribe.Status.Phase),
|
|
||||||
Age: strconv.FormatFloat(time.Now().Sub(nsDescribe.CreationTimestamp.Time).Hours()/24, 'f', 0, 64) + "d",
|
|
||||||
RequirePodNum: totalRequired,
|
|
||||||
AvailablePodNum: totalAvailable,
|
|
||||||
Deployments: deploySet,
|
|
||||||
Domains: domainSet,
|
|
||||||
Clusters: clusterSet,
|
|
||||||
}
|
|
||||||
|
|
||||||
Response(c, http.StatusOK, "success", nsResult)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateNamespace 更新命名空间(项目)
|
|
||||||
// @Summary 更新命名空间(项目)
|
|
||||||
// @Description 更新命名空间(项目)
|
|
||||||
// @Tags namespace
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param param body Namespace true "json"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 400
|
|
||||||
// @Router /api/v1/namespace/update [post]
|
|
||||||
func UpdateNamespace(c *gin.Context) {
|
|
||||||
clusterName := c.Query("clusterName")
|
|
||||||
var namespace coreV1.Namespace
|
|
||||||
if err := c.BindJSON(&namespace); err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "invalid request params.", "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(namespace.Labels["jcce"]) != 0 {
|
|
||||||
_, err := ClientSet.CoreV1().Namespaces().Update(context.TODO(), &namespace, metav1.UpdateOptions{})
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "update namespace failed", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
result := UpdateObject(detailNamespaceConst, clusterName, "", namespace.Name, namespace)
|
|
||||||
if result.Error() != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "update namespace failed", result.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Response(c, http.StatusOK, "success", "")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteNamespace 删除命名空间(项目)
|
|
||||||
func DeleteNamespace(c *gin.Context) {
|
|
||||||
clusterName := c.Param("clusterName")
|
|
||||||
name := c.Param("name")
|
|
||||||
label := c.Param("label")
|
|
||||||
if strings.EqualFold(label, "jcce") {
|
|
||||||
err := ClientSet.CoreV1().Namespaces().Delete(context.TODO(), name, metav1.DeleteOptions{})
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "delete namespace failed", "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 删除数据库记录policy实例
|
|
||||||
_, err = DB.Exec(`delete from joint_domain.namespace where namespace_name = ? `, name)
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "delete db failed", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 删除调度策略实例
|
|
||||||
DeletePropagationPolicies(name, "namespace"+"."+name)
|
|
||||||
} else {
|
|
||||||
result := DeleteObject(detailNamespaceConst, clusterName, "", name)
|
|
||||||
if result.Error() != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "delete namespace failed", result.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Response(c, http.StatusOK, "success", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary namespace监控
|
|
||||||
// @Description namespace监控
|
|
||||||
// @Tags namespace
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param clusterName query string true "集群名"
|
|
||||||
// @Param namespace query string true "命名空间名称"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 400
|
|
||||||
// @Router /api/v1/namespace/getMetrics [get]
|
|
||||||
func GetNamespaceMetrics(c *gin.Context) {
|
|
||||||
clusterName, _ := c.GetQuery("clusterName")
|
|
||||||
namespace, _ := c.GetQuery("namespace")
|
|
||||||
|
|
||||||
replaceMap := make(map[string]string)
|
|
||||||
replaceMap[ClusterName] = clusterName
|
|
||||||
replaceMap["nameSpace"] = namespace
|
|
||||||
|
|
||||||
metricMap := map[string][]MetricUrl{
|
|
||||||
"namespace_memory_usage_wo_cache": {{"nameSpace", namespace, GetMetricUrl(namespace_memory_usage_wo_cache, replaceMap, "nameSpace", metric_range_12h, steps_namespace)}},
|
|
||||||
"namespace_cpu_usage": {{"nameSpace", namespace, GetMetricUrl(namespace_cpu_usage, replaceMap, "nameSpace", metric_range_12h, steps_namespace)}},
|
|
||||||
}
|
|
||||||
|
|
||||||
ch := make(chan MetricResult, len(metricMap))
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
|
|
||||||
for k, v := range metricMap {
|
|
||||||
wg.Add(1)
|
|
||||||
go HttpGetMetrics(k, v, &wg, ch)
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
close(ch)
|
|
||||||
|
|
||||||
mr := []MetricResult{}
|
|
||||||
for v := range ch {
|
|
||||||
mr = append(mr, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
Response(c, http.StatusOK, "success", mr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary 批量获取namespace监控
|
|
||||||
// @Description 批量获取namespace监控
|
|
||||||
// @Tags namespace
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param clusterName query string true "集群名"
|
|
||||||
// @Param namespaces query string true "逗号分隔多个命名空间名称"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 400
|
|
||||||
// @Router /api/v1/namespace/getBatchMetrics [get]
|
|
||||||
func GetBatchNamespaceMetrics(c *gin.Context) {
|
|
||||||
clusterName := c.Query("clusterName")
|
|
||||||
namespaces := c.Query("namespaces")
|
|
||||||
|
|
||||||
namespace := strings.Split(namespaces, ",")
|
|
||||||
|
|
||||||
metricUrls := make([][]MetricUrl, 3)
|
|
||||||
for _, name := range namespace {
|
|
||||||
imap := map[string]string{
|
|
||||||
ClusterName: clusterName,
|
|
||||||
"nameSpace": name,
|
|
||||||
}
|
|
||||||
metricUrls[0] = append(metricUrls[0], MetricUrl{"nameSpace", name, GetMetricUrl(namespace_memory_usage_wo_cache, imap, "nameSpace", metric_range_1s, steps_1s)})
|
|
||||||
metricUrls[1] = append(metricUrls[1], MetricUrl{"nameSpace", name, GetMetricUrl(namespace_cpu_usage, imap, "nameSpace", metric_range_1s, steps_1s)})
|
|
||||||
metricUrls[2] = append(metricUrls[2], MetricUrl{"nameSpace", name, GetMetricUrl(namespace_pod_count, imap, "nameSpace", metric_range_1s, steps_1s)})
|
|
||||||
}
|
|
||||||
|
|
||||||
metricMap := map[string][]MetricUrl{
|
|
||||||
"namespace_memory_usage_wo_cache": metricUrls[0],
|
|
||||||
"namespace_cpu_usage": metricUrls[1],
|
|
||||||
"namespace_pod_count": metricUrls[2],
|
|
||||||
}
|
|
||||||
|
|
||||||
ch := make(chan MetricResult, len(metricMap))
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
|
|
||||||
for k, v := range metricMap {
|
|
||||||
wg.Add(1)
|
|
||||||
go HttpGetMetrics(k, v, &wg, ch)
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
close(ch)
|
|
||||||
|
|
||||||
mr := []MetricResult{}
|
|
||||||
for v := range ch {
|
|
||||||
mr = append(mr, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
Response(c, http.StatusOK, "success", mr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func CreateNamespaceByYaml(c *gin.Context) {
|
|
||||||
file, err := c.FormFile("test")
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
open, err := file.Open()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
all, err := io.ReadAll(open)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var namespace coreV1.Namespace
|
|
||||||
yaml.Unmarshal(all, &namespace)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
result := PostObject(namespaceConst, "host", "", "", namespace)
|
|
||||||
Response(c, http.StatusOK, "success", result)
|
|
||||||
}
|
|
490
app/node.go
490
app/node.go
|
@ -1,490 +0,0 @@
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/robfig/cron/v3"
|
|
||||||
v1 "k8s.io/api/core/v1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
"k8s.io/metrics/pkg/apis/metrics"
|
|
||||||
"math"
|
|
||||||
"net/http"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type NodeObject struct {
|
|
||||||
Kind string `json:"kind"`
|
|
||||||
ApiVersion string `json:"apiVersion"`
|
|
||||||
Metadata string `json:"metadata"`
|
|
||||||
ResourceVersion string `json:"resourceVersion"`
|
|
||||||
Items []v1.Node `json:"items"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type MetricsRes struct {
|
|
||||||
Kind string `json:"kind"`
|
|
||||||
ApiVersion string `json:"apiVersion"`
|
|
||||||
Metadata string `json:"metadata"`
|
|
||||||
ResourceVersion string `json:"resourceVersion"`
|
|
||||||
Items []metrics.NodeMetrics `json:"items"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type NodeExtendRes struct {
|
|
||||||
Node v1.Node `json:"node"`
|
|
||||||
PodStatus v1.PodPhase `json:"podStatus"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type NodeCountResult struct {
|
|
||||||
MasterCount int `json:"masterCount"`
|
|
||||||
AllCount int `json:"allCount"`
|
|
||||||
WorkCount int `json:"workCount"`
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
node_cpu_utilisation_1h = "node:node_cpu_utilisation:avg1m{cluster_name='clusterName',node='nodeName', prometheus_replica='prometheus-k8s-0'}"
|
|
||||||
node_pod_utilisation_1h = "node:pod_utilization:ratio{cluster_name='clusterName',node='nodeName', prometheus_replica='prometheus-k8s-0'}"
|
|
||||||
node_memory_utilisation_1h = "node:node_memory_utilisation:{cluster_name='clusterName',node='nodeName', prometheus_replica='prometheus-k8s-0'}"
|
|
||||||
node_disk_size_utilisation_1h = "node:disk_space_utilization:ratio{cluster_name='clusterName',node='nodeName', prometheus_replica='prometheus-k8s-0'}"
|
|
||||||
|
|
||||||
node_load15_8h = "node:load15:ratio{cluster_name='clusterName',node='nodeName', prometheus_replica='prometheus-k8s-0'}"
|
|
||||||
node_disk_write_iops_8h = "node:data_volume_iops_writes:sum{cluster_name='clusterName',node='nodeName', prometheus_replica='prometheus-k8s-0'}"
|
|
||||||
node_cpu_utilisation_8h = "node:node_cpu_utilisation:avg1m{cluster_name='clusterName',node='nodeName', prometheus_replica='prometheus-k8s-0'}"
|
|
||||||
node_net_bytes_transmitted_8h = "node:node_net_bytes_transmitted:sum_irate{cluster_name='clusterName',node='nodeName', prometheus_replica='prometheus-k8s-0'}"
|
|
||||||
node_disk_inode_total_8h = "node:node_inodes_total:{cluster_name='clusterName',node='nodeName', prometheus_replica='prometheus-k8s-0'}"
|
|
||||||
node_disk_read_throughput_8h = "node:data_volume_throughput_bytes_read:sum{cluster_name='clusterName',node='nodeName', prometheus_replica='prometheus-k8s-0'}"
|
|
||||||
node_net_bytes_received_8h = "node:node_net_bytes_received:sum_irate{cluster_name='clusterName',node='nodeName', prometheus_replica='prometheus-k8s-0'}"
|
|
||||||
node_memory_utilisation_8h = "node:node_memory_utilisation:{cluster_name='clusterName',node='nodeName', prometheus_replica='prometheus-k8s-0'}"
|
|
||||||
node_disk_write_throughput_8h = "node:data_volume_throughput_bytes_written:sum{cluster_name='clusterName',node='nodeName', prometheus_replica='prometheus-k8s-0'}"
|
|
||||||
node_load5_8h = "node:load5:ratio{cluster_name='clusterName',node='nodeName', prometheus_replica='prometheus-k8s-0'}"
|
|
||||||
node_disk_inode_usage_8h = "node:node_inodes_total:{cluster_name='clusterName',node='nodeName', prometheus_replica='prometheus-k8s-0'} * node:disk_inode_utilization:ratio{cluster_name='clusterName',node='nodeName', prometheus_replica='prometheus-k8s-0'}"
|
|
||||||
node_disk_read_iops_8h = "node:data_volume_iops_reads:sum{cluster_name='clusterName',node='nodeName', prometheus_replica='prometheus-k8s-0'}"
|
|
||||||
node_disk_size_utilisation_8h = "node:disk_space_utilization:ratio{cluster_name='clusterName',node='nodeName', prometheus_replica='prometheus-k8s-0'}"
|
|
||||||
node_disk_inode_utilisation_8h = "node:disk_inode_utilization:ratio{cluster_name='clusterName',node='nodeName', prometheus_replica='prometheus-k8s-0'}"
|
|
||||||
node_load1_8h = "node:load1:ratio{cluster_name='clusterName',node='nodeName', prometheus_replica='prometheus-k8s-0'}"
|
|
||||||
|
|
||||||
node_cpu_limits = "sum by (node) (kube_pod_container_resource_limits_cpu_cores{cluster_name='clusterName',node='nodeName', prometheus_replica='prometheus-k8s-0'})"
|
|
||||||
node_cpu_requests = "sum by (node) (kube_pod_container_resource_requests_cpu_cores{cluster_name='clusterName',node='nodeName', prometheus_replica='prometheus-k8s-0'})"
|
|
||||||
node_memory_limits = "sum by (node) (kube_pod_container_resource_limits_memory_bytes{cluster_name='clusterName',node='nodeName', prometheus_replica='prometheus-k8s-0'})"
|
|
||||||
node_memory_requests = "sum by (node) (kube_pod_container_resource_requests_memory_bytes{cluster_name='clusterName',node='nodeName', prometheus_replica='prometheus-k8s-0'})"
|
|
||||||
|
|
||||||
NodeName = "nodeName"
|
|
||||||
metric_range_1h = "1h"
|
|
||||||
steps_1h = 180
|
|
||||||
metric_range_8h = "8.333h"
|
|
||||||
steps_8h = 600
|
|
||||||
max = 10
|
|
||||||
)
|
|
||||||
|
|
||||||
// NodeCount 查询节点数量
|
|
||||||
func NodeCount(c *gin.Context) {
|
|
||||||
clusterName := c.Query("clusterName")
|
|
||||||
result := SearchObject(nodeConst, clusterName, "", "")
|
|
||||||
raw, _ := result.Raw()
|
|
||||||
var nodeRes NodeObject
|
|
||||||
json.Unmarshal(raw, &nodeRes)
|
|
||||||
var masterCount int
|
|
||||||
var edgerCount int
|
|
||||||
for i := range nodeRes.Items {
|
|
||||||
if _, ok := nodeRes.Items[i].Labels["node-role.kubernetes.io/master"]; ok {
|
|
||||||
masterCount++
|
|
||||||
}
|
|
||||||
if _, ok := nodeRes.Items[i].Labels["node-role.kubernetes.io/edge"]; ok {
|
|
||||||
edgerCount++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Response(c, http.StatusOK, "success", NodeCountResult{
|
|
||||||
AllCount: len(nodeRes.Items) - edgerCount,
|
|
||||||
MasterCount: masterCount,
|
|
||||||
WorkCount: len(nodeRes.Items) - masterCount - edgerCount,
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateNode 更新节点信息
|
|
||||||
func UpdateNode(c *gin.Context) {
|
|
||||||
clusterName := c.Query("clusterName")
|
|
||||||
var node v1.Node
|
|
||||||
if err := c.BindJSON(&node); err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "invalid request params.", "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(node.Labels["jcce"]) != 0 {
|
|
||||||
_, err := ClientSet.CoreV1().Nodes().Update(context.TODO(), &node, metav1.UpdateOptions{})
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "update node error.", "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
result := UpdateObject(detailNodeConst, clusterName, "", node.Name, node)
|
|
||||||
if result.Error() != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "update persistentVolumeClaim failed", result.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Response(c, http.StatusOK, "success", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListNode 查询节点列表
|
|
||||||
// @Summary 查询节点列表
|
|
||||||
// @Description 查询节点列表
|
|
||||||
// @Tags node
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param clusterName path string true "集群名"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 500
|
|
||||||
// @Router /api/v1/node/list/{clusterName} [get]
|
|
||||||
func ListNode(c *gin.Context) {
|
|
||||||
clusterName := c.Query("clusterName")
|
|
||||||
pageNum := c.Query("pageNum")
|
|
||||||
pageSize := c.Query("pageSize")
|
|
||||||
queryType := c.Query("queryType")
|
|
||||||
status := c.Query("status")
|
|
||||||
nodeResult := SearchObject(nodeConst, clusterName, "", "")
|
|
||||||
raw, _ := nodeResult.Raw()
|
|
||||||
var nodeRes NodeObject
|
|
||||||
json.Unmarshal(raw, &nodeRes)
|
|
||||||
var result []v1.Node
|
|
||||||
if strings.EqualFold(queryType, "edge") {
|
|
||||||
for i := range nodeRes.Items {
|
|
||||||
if _, ok := nodeRes.Items[i].Labels["node-role.kubernetes.io/edge"]; ok {
|
|
||||||
result = append(result, nodeRes.Items[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for i := range nodeRes.Items {
|
|
||||||
if _, ok := nodeRes.Items[i].Labels["node-role.kubernetes.io/edge"]; !ok {
|
|
||||||
result = append(result, nodeRes.Items[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
count := 0
|
|
||||||
for i := range result {
|
|
||||||
if (strings.EqualFold(status, "unschedulable") && result[i-count].Spec.Unschedulable != true) ||
|
|
||||||
(strings.EqualFold(status, "running") && result[i-count].Spec.Unschedulable == true) ||
|
|
||||||
strings.EqualFold(status, "warning") {
|
|
||||||
result = append(result[:i-count], result[i-count+1:]...)
|
|
||||||
count++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 分页
|
|
||||||
page := &Page[v1.Node]{}
|
|
||||||
page.List = result
|
|
||||||
// 排序
|
|
||||||
sort.SliceStable(page.List, func(i, j int) bool {
|
|
||||||
return page.List[i].CreationTimestamp.Time.After(page.List[j].CreationTimestamp.Time)
|
|
||||||
})
|
|
||||||
data := Paginator(page, int64(len(result)), pageNum, pageSize)
|
|
||||||
Response(c, http.StatusOK, "success", data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DetailNode 查询节点详情
|
|
||||||
func DetailNode(c *gin.Context) {
|
|
||||||
clusterName := c.Query("clusterName")
|
|
||||||
name := c.Query("name")
|
|
||||||
// 查询节点信息
|
|
||||||
nodeRes := SearchObject(detailNodeConst, clusterName, "", name)
|
|
||||||
nodeRaw, _ := nodeRes.Raw()
|
|
||||||
var node v1.Node
|
|
||||||
json.Unmarshal(nodeRaw, &node)
|
|
||||||
|
|
||||||
cpu_capacity, _ := node.Status.Capacity.Cpu().AsInt64()
|
|
||||||
cpu_capacity_float := float64(cpu_capacity)
|
|
||||||
ephemeral_storage, _ := node.Status.Capacity.StorageEphemeral().AsInt64()
|
|
||||||
ephemeral_storage_float := float64(ephemeral_storage)
|
|
||||||
|
|
||||||
replaceMap := make(map[string]string)
|
|
||||||
replaceMap[ClusterName] = clusterName
|
|
||||||
replaceMap[NodeName] = name
|
|
||||||
|
|
||||||
metricMap := map[string][]MetricUrl{
|
|
||||||
"node_cpu_limits": {{NodeName, name, GetMetricUrl(node_cpu_limits, replaceMap, NodeName, metric_range_1s, steps_1s)}},
|
|
||||||
"node_cpu_requests": {{NodeName, name, GetMetricUrl(node_cpu_requests, replaceMap, NodeName, metric_range_1s, steps_1s)}},
|
|
||||||
"node_memory_limits": {{NodeName, name, GetMetricUrl(node_memory_limits, replaceMap, NodeName, metric_range_1s, steps_1s)}},
|
|
||||||
"node_memory_requests": {{NodeName, name, GetMetricUrl(node_memory_requests, replaceMap, NodeName, metric_range_1s, steps_1s)}},
|
|
||||||
}
|
|
||||||
|
|
||||||
ch := make(chan MetricResult, len(metricMap))
|
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
|
|
||||||
for k, v := range metricMap {
|
|
||||||
wg.Add(1)
|
|
||||||
go HttpGetMetrics(k, v, &wg, ch)
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
close(ch)
|
|
||||||
|
|
||||||
aMap := map[string]string{}
|
|
||||||
for v := range ch {
|
|
||||||
if len(v.MetricData.Result) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
format := v.MetricData.Result[0]["values"]
|
|
||||||
valStr := fmt.Sprintf("%v", format)
|
|
||||||
vals := strings.Split(valStr, " ")
|
|
||||||
val := strings.Trim(vals[1], "]]")
|
|
||||||
|
|
||||||
switch v.MetricName {
|
|
||||||
case "node_cpu_limits":
|
|
||||||
aMap["node_cpu_limits"] = val
|
|
||||||
ival, _ := strconv.ParseFloat(val, 64)
|
|
||||||
fraction := ival / cpu_capacity_float
|
|
||||||
aMap["node_cpu_limits_fraction"] = fmt.Sprintf("%.0f%%", math.Round(fraction*100))
|
|
||||||
case "node_cpu_requests":
|
|
||||||
aMap["node_cpu_requests"] = val
|
|
||||||
ival, _ := strconv.ParseFloat(val, 64)
|
|
||||||
fraction := ival / cpu_capacity_float
|
|
||||||
aMap["node_cpu_requests_fraction"] = fmt.Sprintf("%.0f%%", math.Round(fraction*100))
|
|
||||||
case "node_memory_limits":
|
|
||||||
aMap["node_memory_limits"] = val
|
|
||||||
ival, _ := strconv.ParseFloat(val, 64)
|
|
||||||
fraction := ival / ephemeral_storage_float
|
|
||||||
aMap["node_memory_limits_fraction"] = fmt.Sprintf("%.0f%%", math.Round(fraction*100))
|
|
||||||
case "node_memory_requests":
|
|
||||||
aMap["node_memory_requests"] = val
|
|
||||||
ival, _ := strconv.ParseFloat(val, 64)
|
|
||||||
fraction := ival / ephemeral_storage_float
|
|
||||||
aMap["node_memory_requests_fraction"] = fmt.Sprintf("%.0f%%", math.Round(fraction*100))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeMetricLength := 8
|
|
||||||
if len(aMap) != nodeMetricLength {
|
|
||||||
Response(c, http.StatusOK, "query node list success", node)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
formatted_str := fmt.Sprintf(`[{"op":"replace","path":"/metadata/annotations/node.kubesphere.io~1cpu-limits", "value": "%s"},
|
|
||||||
{"op":"replace","path":"/metadata/annotations/node.kubesphere.io~1cpu-limits-fraction", "value": "%s"},
|
|
||||||
{"op":"replace","path":"/metadata/annotations/node.kubesphere.io~1cpu_requests", "value": "%s"},
|
|
||||||
{"op":"replace","path":"/metadata/annotations/node.kubesphere.io~1cpu_requests-fraction", "value": "%s"},
|
|
||||||
{"op":"replace","path":"/metadata/annotations/node.kubesphere.io~1memory_limits", "value": "%s"},
|
|
||||||
{"op":"replace","path":"/metadata/annotations/node.kubesphere.io~1memory_limits-fraction", "value": "%s"},
|
|
||||||
{"op":"replace","path":"/metadata/annotations/node.kubesphere.io~1memory_requests", "value": "%s"},
|
|
||||||
{"op":"replace","path":"/metadata/annotations/node.kubesphere.io~1memory_requests-fraction", "value": "%s"}]`,
|
|
||||||
aMap["node_cpu_limits"], aMap["node_cpu_limits_fraction"], aMap["node_cpu_requests"],
|
|
||||||
aMap["node_cpu_requests_fraction"], aMap["node_memory_limits"], aMap["node_memory_limits_fraction"],
|
|
||||||
aMap["node_memory_requests"], aMap["node_memory_requests_fraction"])
|
|
||||||
aliasJson := []byte(formatted_str)
|
|
||||||
|
|
||||||
PatchObject(detailNodeConst, clusterName, "", node.Name, aliasJson, types.JSONPatchType)
|
|
||||||
|
|
||||||
Response(c, http.StatusOK, "query node list success", node)
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary 根据集群名称 命名空间 pod名称查询节点信息
|
|
||||||
// @Description 根据集群名称 命名空间 pod名称查询节点信息
|
|
||||||
// @Tags node
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param clusterName path string true "集群名"
|
|
||||||
// @Param namespace path string true "命名空间名"
|
|
||||||
// @Param podName path string true "Pod名"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 500
|
|
||||||
// @Router /api/v1/node/detail/{clusterName}/{namespace}/{podName} [get]
|
|
||||||
func queryNodeInfo(c *gin.Context) {
|
|
||||||
clusterName := c.Query("clusterName")
|
|
||||||
namespace := c.Query("namespace")
|
|
||||||
podName := c.Query("podName")
|
|
||||||
var result NodeExtendRes
|
|
||||||
// 查询节点信息
|
|
||||||
nodeRes := SearchObject(nodeConst, clusterName, "", "")
|
|
||||||
nodeRaw, _ := nodeRes.Raw()
|
|
||||||
var nodeList NodeObject
|
|
||||||
json.Unmarshal(nodeRaw, &nodeList)
|
|
||||||
// 查询pod信息
|
|
||||||
podResult := SearchObject(podDetailConst, clusterName, namespace, podName)
|
|
||||||
podRaw, _ := podResult.Raw()
|
|
||||||
|
|
||||||
var pod v1.Pod
|
|
||||||
json.Unmarshal(podRaw, &pod)
|
|
||||||
for _, node := range nodeList.Items {
|
|
||||||
if strings.EqualFold(node.Name, pod.Spec.NodeName) {
|
|
||||||
result.Node = node
|
|
||||||
result.PodStatus = pod.Status.Phase
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Response(c, http.StatusOK, "success", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListEdgeNode 查询边缘节点列表
|
|
||||||
// @Summary 查询边缘节点列表
|
|
||||||
// @Description 查询边缘节点列表
|
|
||||||
// @Tags node
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param clusterName path string true "集群名"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 500
|
|
||||||
// @Router /api/v1/node/edge/{clusterName} [get]
|
|
||||||
func ListEdgeNode(c *gin.Context) {
|
|
||||||
clusterName := c.Query("clusterName")
|
|
||||||
result := SearchObject(nodeConst, clusterName, "", "")
|
|
||||||
raw, _ := result.Raw()
|
|
||||||
var nodeRes NodeObject
|
|
||||||
json.Unmarshal(raw, &nodeRes)
|
|
||||||
var res []v1.Node
|
|
||||||
for _, node := range nodeRes.Items {
|
|
||||||
if _, ok := node.Labels["node-role.kubernetes.io/edge"]; ok {
|
|
||||||
res = append(res, node)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Response(c, http.StatusOK, "success", res)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListNodeMetrics 查询边缘节点指标
|
|
||||||
// @Summary 查询边缘节点指标
|
|
||||||
// @Description 查询边缘节点指标
|
|
||||||
// @Tags node
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param clusterName path string true "集群名"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 500
|
|
||||||
// @Router /api/v1/node/metrics/{clusterName} [get]
|
|
||||||
func ListNodeMetrics(c *gin.Context) {
|
|
||||||
clusterName := c.Query("clusterName")
|
|
||||||
result := SearchObject(nodeMetrics, clusterName, "", "")
|
|
||||||
raw, _ := result.Raw()
|
|
||||||
var metricsRes MetricsRes
|
|
||||||
json.Unmarshal(raw, &metricsRes)
|
|
||||||
Response(c, http.StatusOK, "success", metricsRes.Items)
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary 获取集群节点1h监控
|
|
||||||
// @Description 获取集群节点1h监控
|
|
||||||
// @Tags node
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param clusterName query string true "集群名称"
|
|
||||||
// @Param nodeName query string true "节点名称"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 400
|
|
||||||
// @Router /api/v1/node/getNodeMetrics1h [get]
|
|
||||||
func GetNodeMetrics1h(c *gin.Context) {
|
|
||||||
clusterName := c.Query("clusterName")
|
|
||||||
nodeName := c.Query("nodeName")
|
|
||||||
|
|
||||||
replaceMap := make(map[string]string)
|
|
||||||
replaceMap[ClusterName] = clusterName
|
|
||||||
replaceMap[NodeName] = nodeName
|
|
||||||
|
|
||||||
metricMap := map[string][]MetricUrl{
|
|
||||||
"node_cpu_utilisation": {{NodeName, nodeName, GetMetricUrl(node_cpu_utilisation_1h, replaceMap, NodeName, metric_range_1h, steps_1h)}},
|
|
||||||
"node_pod_utilisation": {{NodeName, nodeName, GetMetricUrl(node_pod_utilisation_1h, replaceMap, NodeName, metric_range_1h, steps_1h)}},
|
|
||||||
"node_memory_utilisation": {{NodeName, nodeName, GetMetricUrl(node_memory_utilisation_1h, replaceMap, NodeName, metric_range_1h, steps_1h)}},
|
|
||||||
"node_disk_size_utilisation": {{NodeName, nodeName, GetMetricUrl(node_disk_size_utilisation_1h, replaceMap, NodeName, metric_range_1h, steps_1h)}},
|
|
||||||
}
|
|
||||||
|
|
||||||
ch := make(chan MetricResult, len(metricMap))
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
|
|
||||||
for k, v := range metricMap {
|
|
||||||
wg.Add(1)
|
|
||||||
go HttpGetMetrics(k, v, &wg, ch)
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
close(ch)
|
|
||||||
|
|
||||||
mr := []MetricResult{}
|
|
||||||
for v := range ch {
|
|
||||||
mr = append(mr, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
Response(c, http.StatusOK, "success", mr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary 获取集群节点8h监控
|
|
||||||
// @Description 获取集群节点8h监控
|
|
||||||
// @Tags node
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param clusterName query string true "集群名称"
|
|
||||||
// @Param nodeName query string true "节点名称"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 400
|
|
||||||
// @Router /api/v1/node/getNodeMetrics8h [get]
|
|
||||||
func GetNodeMetrics8h(c *gin.Context) {
|
|
||||||
clusterName := c.Query("clusterName")
|
|
||||||
nodeName := c.Query("nodeName")
|
|
||||||
|
|
||||||
replaceMap := make(map[string]string)
|
|
||||||
replaceMap[ClusterName] = clusterName
|
|
||||||
replaceMap[NodeName] = nodeName
|
|
||||||
|
|
||||||
metricMap := map[string][]MetricUrl{
|
|
||||||
"node_load15": {{NodeName, nodeName, GetMetricUrl(node_load15_8h, replaceMap, NodeName, metric_range_8h, steps_8h)}},
|
|
||||||
"node_disk_write_iops": {{NodeName, nodeName, GetMetricUrl(node_disk_write_iops_8h, replaceMap, NodeName, metric_range_8h, steps_8h)}},
|
|
||||||
"node_cpu_utilisation": {{NodeName, nodeName, GetMetricUrl(node_cpu_utilisation_8h, replaceMap, NodeName, metric_range_8h, steps_8h)}},
|
|
||||||
"node_net_bytes_transmitted": {{NodeName, nodeName, GetMetricUrl(node_net_bytes_transmitted_8h, replaceMap, NodeName, metric_range_8h, steps_8h)}},
|
|
||||||
"node_disk_inode_total": {{NodeName, nodeName, GetMetricUrl(node_disk_inode_total_8h, replaceMap, NodeName, metric_range_8h, steps_8h)}},
|
|
||||||
"node_disk_read_throughput": {{NodeName, nodeName, GetMetricUrl(node_disk_read_throughput_8h, replaceMap, NodeName, metric_range_8h, steps_8h)}},
|
|
||||||
"node_net_bytes_received": {{NodeName, nodeName, GetMetricUrl(node_net_bytes_received_8h, replaceMap, NodeName, metric_range_8h, steps_8h)}},
|
|
||||||
"node_memory_utilisation": {{NodeName, nodeName, GetMetricUrl(node_memory_utilisation_8h, replaceMap, NodeName, metric_range_8h, steps_8h)}},
|
|
||||||
"node_disk_write_throughput": {{NodeName, nodeName, GetMetricUrl(node_disk_write_throughput_8h, replaceMap, NodeName, metric_range_8h, steps_8h)}},
|
|
||||||
"node_load5": {{NodeName, nodeName, GetMetricUrl(node_load5_8h, replaceMap, NodeName, metric_range_8h, steps_8h)}},
|
|
||||||
"node_disk_inode_usage": {{NodeName, nodeName, GetMetricUrl(node_disk_inode_usage_8h, replaceMap, NodeName, metric_range_8h, steps_8h)}},
|
|
||||||
"node_disk_read_iops": {{NodeName, nodeName, GetMetricUrl(node_disk_read_iops_8h, replaceMap, NodeName, metric_range_8h, steps_8h)}},
|
|
||||||
"node_disk_size_utilisation": {{NodeName, nodeName, GetMetricUrl(node_disk_size_utilisation_8h, replaceMap, NodeName, metric_range_8h, steps_8h)}},
|
|
||||||
"node_disk_inode_utilisation": {{NodeName, nodeName, GetMetricUrl(node_disk_inode_utilisation_8h, replaceMap, NodeName, metric_range_8h, steps_8h)}},
|
|
||||||
"node_load1": {{NodeName, nodeName, GetMetricUrl(node_load1_8h, replaceMap, NodeName, metric_range_8h, steps_8h)}},
|
|
||||||
}
|
|
||||||
|
|
||||||
ch := make(chan MetricResult, len(metricMap))
|
|
||||||
limit := make(chan bool, max)
|
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
|
|
||||||
for k, v := range metricMap {
|
|
||||||
limit <- true
|
|
||||||
wg.Add(1)
|
|
||||||
go HttpGetMetrics(k, v, &wg, ch)
|
|
||||||
<-limit
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
close(ch)
|
|
||||||
|
|
||||||
mr := []MetricResult{}
|
|
||||||
for v := range ch {
|
|
||||||
mr = append(mr, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
Response(c, http.StatusOK, "success", mr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func QueryNodeEdgeInfo(c *cron.Cron) {
|
|
||||||
//创建定时任务
|
|
||||||
EntryID, err := c.AddFunc("*/1 * * * *", func() {
|
|
||||||
// 查询已有的集群列表
|
|
||||||
var clusterNameList []string
|
|
||||||
rows, _ := DB.Query(`SELECT cluster_name FROM domain_cluster`)
|
|
||||||
for rows.Next() {
|
|
||||||
var clusterName string
|
|
||||||
err := rows.Scan(&clusterName)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
clusterNameList = append(clusterNameList, clusterName)
|
|
||||||
}
|
|
||||||
for _, clusterName := range clusterNameList {
|
|
||||||
nodeRes := SearchObject(detailNodeConst, clusterName, "", "")
|
|
||||||
nodeRaw, _ := nodeRes.Raw()
|
|
||||||
var nodeObject NodeObject
|
|
||||||
json.Unmarshal(nodeRaw, &nodeObject)
|
|
||||||
for _, node := range nodeObject.Items {
|
|
||||||
if _, ok := node.Labels["node-role.kubernetes.io/edge"]; ok {
|
|
||||||
SetRedis(context.TODO(), clusterName, "edge", 120)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
fmt.Println(time.Now(), EntryID, err)
|
|
||||||
}
|
|
|
@ -1,312 +0,0 @@
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"github.com/bitly/go-simplejson"
|
|
||||||
"github.com/opensearch-project/opensearch-go/v2/opensearchapi"
|
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
"k8s.io/client-go/rest"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
clusterProxy = "/apis/cluster.karmada.io/v1alpha1/clusters/%s/proxy"
|
|
||||||
|
|
||||||
nodeConst = "node"
|
|
||||||
detailHPAConst = "hpa"
|
|
||||||
deploymentConst = "deployment"
|
|
||||||
deploymentListConst = "deploymentList"
|
|
||||||
namespaceConst = "namespace"
|
|
||||||
detailNamespaceConst = "detailNamespace"
|
|
||||||
podDetailConst = "podDetail"
|
|
||||||
podListConst = "podList"
|
|
||||||
detailPvcConst = "detailPvc"
|
|
||||||
pvcListConst = "pvcList"
|
|
||||||
serviceConst = "service"
|
|
||||||
detailServiceConst = "detailService"
|
|
||||||
allServiceConst = "allService"
|
|
||||||
scConst = "sc"
|
|
||||||
pvcConst = "pvc"
|
|
||||||
pvConst = "pv"
|
|
||||||
podMetrics = "podMetrics"
|
|
||||||
nodeMetrics = "nodeMetrics"
|
|
||||||
crdConst = "crd"
|
|
||||||
secretConst = "secret"
|
|
||||||
configMapConst = "configMap"
|
|
||||||
statefulSetConst = "statefulSet"
|
|
||||||
daemonSetConst = "daemonSet"
|
|
||||||
allPodConst = "allPod"
|
|
||||||
detailPodConst = "detailPod"
|
|
||||||
detailNodeConst = "detailNode"
|
|
||||||
statefulSetListConst = "statefulSetList"
|
|
||||||
daemonSetListConst = "daemonSetList"
|
|
||||||
jobListConst = "jobList"
|
|
||||||
allDeploymentConst = "allDeployment"
|
|
||||||
allDaemonSetConst = "allDaemonSet"
|
|
||||||
allStatefulSetConst = "allStatefulSet"
|
|
||||||
controllerVersionConst = "controllerVersion"
|
|
||||||
hpaListConst = "hpaList"
|
|
||||||
replicaSetConst = "replicaSet"
|
|
||||||
|
|
||||||
hpaListUrl = "/apis/autoscaling/v1/namespaces/%s/horizontalpodautoscalers"
|
|
||||||
detailHpaUrl = "/apis/autoscaling/v1/namespaces/%s/horizontalpodautoscalers/%s"
|
|
||||||
deploymentListUrl = "/apis/apps/v1/namespaces/%s/deployments"
|
|
||||||
daemonSetUrl = "/apis/apps/v1/namespaces/%s/daemonsets/%s"
|
|
||||||
statefulSetUrl = "/apis/apps/v1/namespaces/%s/statefulsets/%s"
|
|
||||||
statefulSetListUrl = "/apis/apps/v1/namespaces/%s/statefulsets"
|
|
||||||
daemonSetListUrl = "/apis/apps/v1/namespaces/%s/daemonsets"
|
|
||||||
replicaSetUrl = "/apis/apps/v1/namespaces/%s/replicasets"
|
|
||||||
secretUrl = "/api/v1/namespaces/%s/secrets"
|
|
||||||
configMapUrl = "/api/v1/namespaces/%s/configmaps"
|
|
||||||
scUrl = "/apis/storage.k8s.io/v1/storageclasses"
|
|
||||||
pvUrl = "/api/v1/persistentvolumes"
|
|
||||||
pvcUrl = "/api/v1/persistentvolumeclaims"
|
|
||||||
detailPvcUrl = "/api/v1/namespaces/%s/persistentvolumeclaims/%s"
|
|
||||||
nodeUrl = "/api/v1/nodes"
|
|
||||||
detailNodeUrl = "/api/v1/nodes/%s"
|
|
||||||
namespaceUrl = "/api/v1/namespaces"
|
|
||||||
deploymentUrl = "/apis/apps/v1/namespaces/%s/deployments/%s"
|
|
||||||
allDeploymentUrl = "/apis/apps/v1/deployments"
|
|
||||||
allStatefulSetUrl = "/apis/apps/v1/statefulsets"
|
|
||||||
allDaemonSetUrl = "/apis/apps/v1/daemonsets"
|
|
||||||
podListUrl = "/api/v1/namespaces/%s/pods"
|
|
||||||
allPodUrl = "/api/v1/pods"
|
|
||||||
detailPodUrl = "/api/v1/namespaces/%s/pods/%s"
|
|
||||||
podDetailUrl = "/api/v1/namespaces/%s/pods/%s"
|
|
||||||
serviceUrl = "/api/v1/namespaces/%s/services"
|
|
||||||
allServiceUrl = "/api/v1/services"
|
|
||||||
detailServiceUrl = "/api/v1/namespaces/%s/services/%s"
|
|
||||||
podMetricsUrl = "/apis/metrics.k8s.io/v1beta1/namespaces/%s/pods/%s"
|
|
||||||
nodeMetricsUrl = "/apis/metrics.k8s.io/v1beta1/nodes"
|
|
||||||
crdUrl = "/apis/apiextensions.k8s.io/v1/customresourcedefinitions"
|
|
||||||
detailNamespaceUrl = "/api/v1/namespaces/%s"
|
|
||||||
jobListUrl = "/apis/batch/v1/namespaces/%s/jobs"
|
|
||||||
pvcListUrl = "/api/v1/namespaces/%s/persistentvolumeclaims"
|
|
||||||
controllerVersionUrl = "/apis/apps/v1/namespaces/%s/controllerrevisions"
|
|
||||||
)
|
|
||||||
|
|
||||||
func JointUrl(objectType string, clusterName string, namespace string, objectName string) string {
|
|
||||||
var url bytes.Buffer
|
|
||||||
url.WriteString(fmt.Sprintf(clusterProxy, clusterName))
|
|
||||||
switch objectType {
|
|
||||||
case "podMetrics":
|
|
||||||
url.WriteString(fmt.Sprintf(podMetricsUrl, namespace, objectName))
|
|
||||||
case "nodeMetrics":
|
|
||||||
url.WriteString(nodeMetricsUrl)
|
|
||||||
case "node":
|
|
||||||
url.WriteString(nodeUrl)
|
|
||||||
case "deployment":
|
|
||||||
url.WriteString(fmt.Sprintf(deploymentUrl, namespace, objectName))
|
|
||||||
case "allStatefulSet":
|
|
||||||
url.WriteString(allStatefulSetUrl)
|
|
||||||
case "allDaemonSet":
|
|
||||||
url.WriteString(allDaemonSetUrl)
|
|
||||||
case "allDeployment":
|
|
||||||
url.WriteString(allDeploymentUrl)
|
|
||||||
case "podList":
|
|
||||||
url.WriteString(fmt.Sprintf(podListUrl, namespace))
|
|
||||||
case "podDetail":
|
|
||||||
url.WriteString(fmt.Sprintf(podDetailUrl, namespace, objectName))
|
|
||||||
case "pv":
|
|
||||||
url.WriteString(pvUrl)
|
|
||||||
case "pvc":
|
|
||||||
url.WriteString(pvcUrl)
|
|
||||||
case "crd":
|
|
||||||
url.WriteString(crdUrl)
|
|
||||||
case "namespace":
|
|
||||||
url.WriteString(namespaceUrl)
|
|
||||||
case "sc":
|
|
||||||
url.WriteString(scUrl)
|
|
||||||
case "service":
|
|
||||||
url.WriteString(fmt.Sprintf(serviceUrl, namespace))
|
|
||||||
case "allService":
|
|
||||||
url.WriteString(allServiceUrl)
|
|
||||||
case "detailService":
|
|
||||||
url.WriteString(fmt.Sprintf(detailServiceUrl, namespace, objectName))
|
|
||||||
case "secret":
|
|
||||||
url.WriteString(fmt.Sprintf(secretUrl, namespace))
|
|
||||||
case "replicaSet":
|
|
||||||
url.WriteString(fmt.Sprintf(replicaSetUrl, namespace))
|
|
||||||
case "statefulSet":
|
|
||||||
url.WriteString(fmt.Sprintf(statefulSetUrl, namespace, objectName))
|
|
||||||
case "daemonSet":
|
|
||||||
url.WriteString(fmt.Sprintf(daemonSetUrl, namespace, objectName))
|
|
||||||
case "detailPvc":
|
|
||||||
url.WriteString(fmt.Sprintf(detailPvcUrl, namespace, objectName))
|
|
||||||
case "configMap":
|
|
||||||
url.WriteString(fmt.Sprintf(configMapUrl, namespace))
|
|
||||||
case "allPod":
|
|
||||||
url.WriteString(fmt.Sprintf(allPodUrl))
|
|
||||||
case "detailPod":
|
|
||||||
url.WriteString(fmt.Sprintf(detailPodUrl, namespace, objectName))
|
|
||||||
case "detailNode":
|
|
||||||
url.WriteString(fmt.Sprintf(detailNodeUrl, objectName))
|
|
||||||
case "detailNamespace":
|
|
||||||
url.WriteString(fmt.Sprintf(detailNamespaceUrl, objectName))
|
|
||||||
case "deploymentList":
|
|
||||||
url.WriteString(fmt.Sprintf(deploymentListUrl, namespace))
|
|
||||||
case "statefulSetList":
|
|
||||||
url.WriteString(fmt.Sprintf(statefulSetListUrl, namespace))
|
|
||||||
case "daemonSetList":
|
|
||||||
url.WriteString(fmt.Sprintf(daemonSetListUrl, namespace))
|
|
||||||
case "jobList":
|
|
||||||
url.WriteString(fmt.Sprintf(jobListUrl, namespace))
|
|
||||||
case "pvcList":
|
|
||||||
url.WriteString(fmt.Sprintf(pvcListUrl, namespace))
|
|
||||||
case "controllerVersion":
|
|
||||||
url.WriteString(fmt.Sprintf(controllerVersionUrl, namespace))
|
|
||||||
case "hpa":
|
|
||||||
url.WriteString(fmt.Sprintf(detailHpaUrl, namespace, objectName))
|
|
||||||
case "hpaList":
|
|
||||||
url.WriteString(fmt.Sprintf(hpaListUrl, namespace))
|
|
||||||
}
|
|
||||||
|
|
||||||
return url.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func SearchObject(objectType string, clusterName string, namespace string, objectName string) rest.Result {
|
|
||||||
url := JointUrl(objectType, clusterName, namespace, objectName)
|
|
||||||
return KarmadaClient.SearchV1alpha1().RESTClient().Get().AbsPath(url).Do(context.TODO())
|
|
||||||
}
|
|
||||||
|
|
||||||
func UpdateObject(objectType string, clusterName string, namespace string, objectName string, object interface{}) rest.Result {
|
|
||||||
url := JointUrl(objectType, clusterName, namespace, objectName)
|
|
||||||
objectParam, _ := json.Marshal(object)
|
|
||||||
return KarmadaClient.SearchV1alpha1().RESTClient().Put().Body(objectParam).AbsPath(url).Do(context.TODO())
|
|
||||||
}
|
|
||||||
|
|
||||||
func PostObject(objectType string, clusterName string, namespace string, objectName string, object interface{}) rest.Result {
|
|
||||||
url := JointUrl(objectType, clusterName, namespace, objectName)
|
|
||||||
objectParam, _ := json.Marshal(object)
|
|
||||||
return KarmadaClient.SearchV1alpha1().RESTClient().Post().Body(objectParam).AbsPath(url).Do(context.TODO())
|
|
||||||
}
|
|
||||||
|
|
||||||
func DeleteObject(objectType string, clusterName string, namespace string, objectName string) rest.Result {
|
|
||||||
url := JointUrl(objectType, clusterName, namespace, objectName)
|
|
||||||
return KarmadaClient.SearchV1alpha1().RESTClient().Delete().AbsPath(url).Do(context.TODO())
|
|
||||||
}
|
|
||||||
|
|
||||||
func PatchObject(objectType string, clusterName string, namespace string, objectName string, object interface{}, pt types.PatchType) rest.Result {
|
|
||||||
url := JointUrl(objectType, clusterName, namespace, objectName)
|
|
||||||
return KarmadaClient.SearchV1alpha1().RESTClient().Patch(pt).Body(object).AbsPath(url).Do(context.TODO())
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetDeployFromOS(namespaceName string, deploymentName string, clusterName string) *simplejson.Json {
|
|
||||||
|
|
||||||
var content *strings.Reader
|
|
||||||
if clusterName == "" && namespaceName != "" && deploymentName != "" {
|
|
||||||
content = strings.NewReader(`{
|
|
||||||
"query":{
|
|
||||||
"bool":{
|
|
||||||
"must":[
|
|
||||||
{
|
|
||||||
"match_phrase":{
|
|
||||||
"metadata.name": "` + deploymentName + `"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"match_phrase":{
|
|
||||||
"metadata.namespace": "` + namespaceName + `"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"_source": ["metadata.annotations.resource.karmada.io/cached-from-cluster",
|
|
||||||
"metadata.name",
|
|
||||||
"metadata.namespace",
|
|
||||||
"spec",
|
|
||||||
"status"]
|
|
||||||
}`)
|
|
||||||
} else if deploymentName == "" && clusterName == "" && namespaceName != "" {
|
|
||||||
content = strings.NewReader(`{
|
|
||||||
"query":{
|
|
||||||
"match_phrase":{
|
|
||||||
"metadata.namespace": "` + namespaceName + `"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"_source": ["metadata.annotations.resource.karmada.io/cached-from-cluster",
|
|
||||||
"metadata.name",
|
|
||||||
"metadata.namespace",
|
|
||||||
"spec",
|
|
||||||
"status"]
|
|
||||||
}`)
|
|
||||||
} else if deploymentName == "" && clusterName != "" && namespaceName != "" {
|
|
||||||
content = strings.NewReader(`{
|
|
||||||
"query":{
|
|
||||||
"bool":{
|
|
||||||
"must":[
|
|
||||||
{
|
|
||||||
"match_phrase":{
|
|
||||||
"metadata.annotations.resource.karmada.io/cached-from-cluster": "` + clusterName + `"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"match_phrase":{
|
|
||||||
"metadata.namespace": "` + namespaceName + `"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"_source": ["metadata.annotations.resource.karmada.io/cached-from-cluster",
|
|
||||||
"metadata.name",
|
|
||||||
"metadata.namespace",
|
|
||||||
"spec",
|
|
||||||
"status"]
|
|
||||||
}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
//从OpenSearch根据ns名称和deploy名称查询相关信息
|
|
||||||
searchReq := opensearchapi.SearchRequest{
|
|
||||||
Index: []string{"kubernetes-deployment"},
|
|
||||||
Body: content,
|
|
||||||
}
|
|
||||||
searchResp, _ := searchReq.Do(context.Background(), OpenSearchClient)
|
|
||||||
bufDeploy := new(bytes.Buffer)
|
|
||||||
bufDeploy.ReadFrom(searchResp.Body)
|
|
||||||
|
|
||||||
deployJson, _ := simplejson.NewJson([]byte(bufDeploy.String()))
|
|
||||||
|
|
||||||
return deployJson
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetPodFromOS(namespaceName string, deploymentName string) *simplejson.Json {
|
|
||||||
|
|
||||||
var content *strings.Reader
|
|
||||||
|
|
||||||
content = strings.NewReader(`{
|
|
||||||
"query":{
|
|
||||||
"bool":{
|
|
||||||
"must":[
|
|
||||||
{
|
|
||||||
"match_phrase":{
|
|
||||||
"metadata.namespace": "` + namespaceName + `"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"match_phrase":{
|
|
||||||
"metadata.name": "` + deploymentName + `"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
//从OpenSearch根据ns名称和deploy名称查询相关信息
|
|
||||||
searchReq := opensearchapi.SearchRequest{
|
|
||||||
Index: []string{"kubernetes-podListConst"},
|
|
||||||
Body: content,
|
|
||||||
}
|
|
||||||
searchResp, _ := searchReq.Do(context.Background(), OpenSearchClient)
|
|
||||||
bufPod := new(bytes.Buffer)
|
|
||||||
bufPod.ReadFrom(searchResp.Body)
|
|
||||||
|
|
||||||
podJson, _ := simplejson.NewJson([]byte(bufPod.String()))
|
|
||||||
|
|
||||||
return podJson
|
|
||||||
}
|
|
|
@ -1,138 +0,0 @@
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
policyv1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1"
|
|
||||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
|
||||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
type OverridePolicy struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Namespace string `json:"namespace"`
|
|
||||||
ResourceName string `json:"resource_name"`
|
|
||||||
Kind string `json:"kind"`
|
|
||||||
Labels map[string]string `json:"labels"`
|
|
||||||
ReplicaDivisionPreference string `json:"replica_division_preference"`
|
|
||||||
ReplicaSchedulingType string `json:"replica_scheduling_type"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary 创建override策略列表
|
|
||||||
// @Description 创建override策略列表
|
|
||||||
// @Tags overridePolicy
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param param body OverridePolicy true "json"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 500
|
|
||||||
// @Router /api/v1/OverridePolicy/create [post]
|
|
||||||
func CreateOverridePolicies(c *gin.Context) {
|
|
||||||
|
|
||||||
var policyRequest OverridePolicy
|
|
||||||
if err := c.BindJSON(&policyRequest); err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "invalid request params.", "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
policy := &policyv1alpha1.OverridePolicy{
|
|
||||||
|
|
||||||
ObjectMeta: v1.ObjectMeta{
|
|
||||||
Name: policyRequest.Name,
|
|
||||||
Namespace: policyRequest.Namespace,
|
|
||||||
},
|
|
||||||
Spec: policyv1alpha1.OverrideSpec{
|
|
||||||
ResourceSelectors: []policyv1alpha1.ResourceSelector{
|
|
||||||
{
|
|
||||||
APIVersion: "apps/v1",
|
|
||||||
Kind: policyRequest.Kind,
|
|
||||||
Name: policyRequest.ResourceName,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
OverrideRules: []policyv1alpha1.RuleWithCluster{
|
|
||||||
{
|
|
||||||
TargetCluster: nil,
|
|
||||||
Overriders: policyv1alpha1.Overriders{
|
|
||||||
Plaintext: []policyv1alpha1.PlaintextOverrider{
|
|
||||||
{
|
|
||||||
Path: "/metadata/namespace",
|
|
||||||
Operator: "replace",
|
|
||||||
Value: apiextensionsv1.JSON{Raw: []byte("default")},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
overrideList, err := KarmadaClient.PolicyV1alpha1().OverridePolicies(policyRequest.Namespace).Create(context.TODO(), policy, v1.CreateOptions{})
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "create policy failed", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
Response(c, http.StatusOK, "success", overrideList)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListOverridePolicies 查询重写策略列表
|
|
||||||
// @Summary 查询重写策略列表
|
|
||||||
// @Description 查询重写策略列表
|
|
||||||
// @Tags overridePolicy
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param param body OverridePolicy true "json"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 500
|
|
||||||
// @Router /api/v1/overridePolicy/list [get]
|
|
||||||
func ListOverridePolicies(c *gin.Context) {
|
|
||||||
|
|
||||||
var overridePolicyRequest OverridePolicy
|
|
||||||
if err := c.BindJSON(&overridePolicyRequest); err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "invalid request params.", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
overridePolicyList := make([]OverridePolicy, 0)
|
|
||||||
|
|
||||||
propagationList, _ := KarmadaClient.PolicyV1alpha1().PropagationPolicies(overridePolicyRequest.Namespace).List(context.TODO(), v1.ListOptions{})
|
|
||||||
|
|
||||||
for i := 0; i < len(propagationList.Items); i++ {
|
|
||||||
name := propagationList.Items[i].ObjectMeta.Name
|
|
||||||
namespace := propagationList.Items[i].ObjectMeta.Namespace
|
|
||||||
labels := &propagationList.Items[i].Spec.Placement.ClusterAffinity.LabelSelector.MatchLabels
|
|
||||||
resourceName := propagationList.Items[i].Spec.ResourceSelectors[0].Name
|
|
||||||
kind := propagationList.Items[i].Spec.ResourceSelectors[0].Kind
|
|
||||||
rdp := string(propagationList.Items[i].Spec.Placement.ReplicaScheduling.ReplicaDivisionPreference)
|
|
||||||
rst := string(propagationList.Items[i].Spec.Placement.ReplicaScheduling.ReplicaSchedulingType)
|
|
||||||
|
|
||||||
pp := OverridePolicy{
|
|
||||||
Name: name,
|
|
||||||
Namespace: namespace,
|
|
||||||
Labels: *labels,
|
|
||||||
ResourceName: resourceName,
|
|
||||||
Kind: kind,
|
|
||||||
ReplicaDivisionPreference: rdp,
|
|
||||||
ReplicaSchedulingType: rst,
|
|
||||||
}
|
|
||||||
overridePolicyList = append(overridePolicyList, pp)
|
|
||||||
}
|
|
||||||
|
|
||||||
Response(c, http.StatusOK, "success", overridePolicyList)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateOverridePolicies 查询重写策略列表
|
|
||||||
func UpdateOverridePolicies(c *gin.Context) {
|
|
||||||
//todo
|
|
||||||
Response(c, http.StatusOK, "success", nil)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteOverridePolicies 查询重写策略列表
|
|
||||||
func DeleteOverridePolicies(c *gin.Context) {
|
|
||||||
//todo
|
|
||||||
Response(c, http.StatusOK, "success", nil)
|
|
||||||
|
|
||||||
}
|
|
666
app/overview.go
666
app/overview.go
|
@ -1,666 +0,0 @@
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/golang/glog"
|
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
"golang.org/x/exp/slices"
|
|
||||||
"jcc-schedule/model"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"math"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
monitoring_clusters = "group by (cluster_name) (node:load15:ratio)"
|
|
||||||
apiserver_request_rate = "apiserver:apiserver_request_total:sum_irate{cluster_name='clusterName', prometheus_replica='prometheus-k8s-0'}"
|
|
||||||
apiserver_request_latencies = "apiserver:apiserver_request_duration:avg{cluster_name='clusterName', prometheus_replica='prometheus-k8s-0'}"
|
|
||||||
|
|
||||||
Schedule = "Schedule"
|
|
||||||
scheduler_schedule_attempts = "scheduler:scheduler_schedule_attempts:sum{cluster_name='clusterName', result='Schedule', prometheus_replica='prometheus-k8s-0'}"
|
|
||||||
|
|
||||||
cluster_pod_running_count = "cluster:pod_running:count{cluster_name='clusterName',prometheus_replica='prometheus-k8s-0'}"
|
|
||||||
cluster_memory_total = "sum by (cluster) (node:node_memory_bytes_total:sum{cluster_name='clusterName',prometheus_replica='prometheus-k8s-0'})"
|
|
||||||
cluster_cpu_total = "sum by (cluster) (node:node_num_cpu:sum{cluster_name='clusterName',prometheus_replica='prometheus-k8s-0'})"
|
|
||||||
cluster_disk_size_capacity = "sum by (cluster) (node:disk_space_available:{cluster_name='clusterName',prometheus_replica='prometheus-k8s-0'} / (1 - node:disk_space_utilization:ratio{cluster_name='clusterName',prometheus_replica='prometheus-k8s-0'}))"
|
|
||||||
cluster_cpu_usage = "sum by (cluster) (node:node_num_cpu:sum{cluster_name='clusterName',prometheus_replica='prometheus-k8s-0'} * node:node_cpu_utilisation:avg1m{cluster_name='clusterName',prometheus_replica='prometheus-k8s-0'})"
|
|
||||||
cluster_pod_quota = "sum by (cluster) (node:pod_capacity:sum{cluster_name='clusterName',prometheus_replica='prometheus-k8s-0'})"
|
|
||||||
cluster_memory_usage_wo_cache = "sum by (cluster) (node:node_memory_bytes_total:sum{cluster_name='clusterName',prometheus_replica='prometheus-k8s-0'} - node:node_memory_bytes_available:sum{cluster_name='clusterName',prometheus_replica='prometheus-k8s-0'})"
|
|
||||||
cluster_disk_size_usage = "(sum by (cluster) (node:disk_space_available:{cluster_name='clusterName',prometheus_replica='prometheus-k8s-0'}) / (1 - cluster:disk_utilization:ratio{cluster_name='clusterName',prometheus_replica='prometheus-k8s-0'})) * cluster:disk_utilization:ratio{cluster_name='clusterName',prometheus_replica='prometheus-k8s-0'}"
|
|
||||||
|
|
||||||
node_cpu_total = "node:node_num_cpu:sum{cluster_name='clusterName',prometheus_replica='prometheus-k8s-0'}"
|
|
||||||
node_cpu_usage = "node:node_num_cpu:sum{cluster_name='clusterName',prometheus_replica='prometheus-k8s-0'} * node:node_cpu_utilisation:avg1m{cluster_name='clusterName',prometheus_replica='prometheus-k8s-0'}"
|
|
||||||
node_cpu_utilisation = "node:node_cpu_utilisation:avg1m{cluster_name='clusterName',prometheus_replica='prometheus-k8s-0'}"
|
|
||||||
node_load1 = "node:load1:ratio{cluster_name='clusterName', prometheus_replica='prometheus-k8s-0'}"
|
|
||||||
node_load15 = "node:load15:ratio{cluster_name='clusterName', prometheus_replica='prometheus-k8s-0'}"
|
|
||||||
node_memory_total = "node:node_memory_bytes_total:sum{cluster_name='clusterName',prometheus_replica='prometheus-k8s-0'}"
|
|
||||||
node_memory_usage_wo_cache = "node:node_memory_bytes_total:sum{cluster_name='clusterName',prometheus_replica='prometheus-k8s-0'} - node:node_memory_bytes_available:sum{cluster_name='clusterName',prometheus_replica='prometheus-k8s-0'}"
|
|
||||||
node_memory_utilisation = "node:node_memory_utilisation:{cluster_name='clusterName',prometheus_replica='prometheus-k8s-0'}"
|
|
||||||
node_pod_quota = "node:pod_capacity:sum{cluster_name='clusterName',prometheus_replica='prometheus-k8s-0'}"
|
|
||||||
node_pod_running_count = "node:pod_running:count{cluster_name='clusterName', prometheus_replica='prometheus-k8s-0'}"
|
|
||||||
|
|
||||||
nvidia_smi_utilization_gpu_ratio = "nvidia_smi_utilization_gpu_ratio{cluster_name='clusterName'}"
|
|
||||||
nvidia_smi_utilization_memory_ratio = "nvidia_smi_utilization_memory_ratio{cluster_name='clusterName'}"
|
|
||||||
|
|
||||||
ClusterName = "clusterName"
|
|
||||||
metric_range_1s = "1s"
|
|
||||||
steps_1s = 100
|
|
||||||
buffer = 10
|
|
||||||
cpu_avg_usage = "avg_over_time(sum by (cluster) (node:node_num_cpu:sum{cluster_name='clusterName',prometheus_replica='prometheus-k8s-0'} * node:node_cpu_utilisation:avg1m{cluster_name='clusterName',prometheus_replica='prometheus-k8s-0'}))"
|
|
||||||
mem_avg_usage = "avg_over_time(sum by (cluster) (node:node_memory_bytes_total:sum{cluster_name='clusterName',prometheus_replica='prometheus-k8s-0'} - node:node_memory_bytes_available:sum{cluster_name='clusterName',prometheus_replica='prometheus-k8s-0'}))"
|
|
||||||
cpu_total_usage = "round(:node_cpu_utilisation:avg1m{cluster_name='clusterName',prometheus_replica='prometheus-k8s-0'} * sum(node:node_num_cpu:sum{cluster_name='clusterName',prometheus_replica='prometheus-k8s-0'}), 0.001) "
|
|
||||||
mem_total_usage = "sum(node:node_memory_bytes_total:sum{cluster_name='clusterName',prometheus_replica='prometheus-k8s-0'}) - sum(node:node_memory_bytes_available:sum{cluster_name='clusterName',prometheus_replica='prometheus-k8s-0'})"
|
|
||||||
metric_range_temp = "72h"
|
|
||||||
steps_temp = 7000
|
|
||||||
metric_range_7d = "168h"
|
|
||||||
steps_7d = 90000
|
|
||||||
metric_range_1d = "24h"
|
|
||||||
metric_avg = "avg"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Overview struct {
|
|
||||||
Domain int `json:"domain"`
|
|
||||||
Cluster int `json:"cluster"`
|
|
||||||
Pod int32 `json:"pod"`
|
|
||||||
TotalFlops float64 `json:"totalFlops"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Computility struct {
|
|
||||||
Resources []model.Resources `json:"resources"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary 获取所有kubesphere定义的监控集群
|
|
||||||
// @Description 获取所有kubesphere定义的监控集群
|
|
||||||
// @Tags overview
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 400
|
|
||||||
// @Router /api/v1/overview/getKubesphereClusters [get]
|
|
||||||
func GetMonitoringClusters() []string {
|
|
||||||
|
|
||||||
var clusterNameList []string
|
|
||||||
rows, _ := DB.Query(`SELECT cluster_name FROM domain_cluster`)
|
|
||||||
for rows.Next() {
|
|
||||||
var clusterName string
|
|
||||||
err := rows.Scan(&clusterName)
|
|
||||||
if err != nil {
|
|
||||||
glog.Errorf("query failed!,error %v", err)
|
|
||||||
return clusterNameList
|
|
||||||
}
|
|
||||||
clusterNameList = append(clusterNameList, clusterName)
|
|
||||||
}
|
|
||||||
|
|
||||||
//查询有数据的集群
|
|
||||||
metricMap := map[string][]MetricUrl{
|
|
||||||
"monitoring_clusters": {{"", "", GetMetricUrl(monitoring_clusters, nil, "", metric_range_1s, steps_1s)}},
|
|
||||||
}
|
|
||||||
|
|
||||||
ch := make(chan MetricResult, len(metricMap))
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
|
|
||||||
for k, v := range metricMap {
|
|
||||||
wg.Add(1)
|
|
||||||
go HttpGetMetrics(k, v, &wg, ch)
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
close(ch)
|
|
||||||
|
|
||||||
mr := []MetricResult{}
|
|
||||||
for v := range ch {
|
|
||||||
mr = append(mr, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
var clusters []string
|
|
||||||
|
|
||||||
rd := mr[0]
|
|
||||||
|
|
||||||
for _, r := range rd.MetricData.Result {
|
|
||||||
format, ok := r["metric"].(map[string]interface{})
|
|
||||||
if ok && len(format) != 0 {
|
|
||||||
clusters = append(clusters, format["cluster_name"].(string))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var clusters_filtered []string
|
|
||||||
for _, e := range clusterNameList {
|
|
||||||
if slices.Contains(clusters, e) {
|
|
||||||
clusters_filtered = append(clusters_filtered, e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return clusters_filtered
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary 获取容器集群调度器监控
|
|
||||||
// @Description 获取容器集群调度器监控
|
|
||||||
// @Tags overview
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param clusterName query string true "集群名称"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 400
|
|
||||||
// @Router /api/v1/overview/getScheduleMetrics [get]
|
|
||||||
func GetScheduleMetrics(c *gin.Context) {
|
|
||||||
clusterName := c.Query(ClusterName)
|
|
||||||
|
|
||||||
scheduleSlice := []string{"error", "scheduled", "unschedulable"}
|
|
||||||
var metricUrls []MetricUrl
|
|
||||||
for _, s := range scheduleSlice {
|
|
||||||
imap := map[string]string{
|
|
||||||
ClusterName: clusterName,
|
|
||||||
Schedule: s,
|
|
||||||
}
|
|
||||||
metricUrls = append(metricUrls, MetricUrl{Schedule, s, GetMetricUrl(scheduler_schedule_attempts, imap, Schedule, metric_range_1s, steps_1s)})
|
|
||||||
}
|
|
||||||
|
|
||||||
metricMap := map[string][]MetricUrl{
|
|
||||||
"scheduler_schedule_attempts": metricUrls,
|
|
||||||
}
|
|
||||||
|
|
||||||
ch := make(chan MetricResult, len(metricMap))
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
|
|
||||||
for k, v := range metricMap {
|
|
||||||
wg.Add(1)
|
|
||||||
go HttpGetMetrics(k, v, &wg, ch)
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
close(ch)
|
|
||||||
|
|
||||||
mr := []MetricResult{}
|
|
||||||
for v := range ch {
|
|
||||||
mr = append(mr, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
Response(c, http.StatusOK, "success", mr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary 获取容器集群资源监控
|
|
||||||
// @Description 获取容器集群资源监控
|
|
||||||
// @Tags overview
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param clusterName query string true "集群名称"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 400
|
|
||||||
// @Router /api/v1/overview/getClusterMetrics [get]
|
|
||||||
func GetClusterMetrics(c *gin.Context) {
|
|
||||||
clusterName := c.Query(ClusterName)
|
|
||||||
|
|
||||||
replaceMap := make(map[string]string)
|
|
||||||
replaceMap[ClusterName] = clusterName
|
|
||||||
|
|
||||||
metricMap := map[string][]MetricUrl{
|
|
||||||
"cluster_pod_running_count": {{ClusterName, clusterName, GetMetricUrl(cluster_pod_running_count, replaceMap, ClusterName, metric_range_1s, steps_1s)}},
|
|
||||||
"cluster_memory_total": {{ClusterName, clusterName, GetMetricUrl(cluster_memory_total, replaceMap, ClusterName, metric_range_1s, steps_1s)}},
|
|
||||||
"cluster_cpu_total": {{ClusterName, clusterName, GetMetricUrl(cluster_cpu_total, replaceMap, ClusterName, metric_range_1s, steps_1s)}},
|
|
||||||
"cluster_disk_size_capacity": {{ClusterName, clusterName, GetMetricUrl(cluster_disk_size_capacity, replaceMap, ClusterName, metric_range_1s, steps_1s)}},
|
|
||||||
"cluster_cpu_usage": {{ClusterName, clusterName, GetMetricUrl(cluster_cpu_usage, replaceMap, ClusterName, metric_range_1s, steps_1s)}},
|
|
||||||
"cluster_pod_quota": {{ClusterName, clusterName, GetMetricUrl(cluster_pod_quota, replaceMap, ClusterName, metric_range_1s, steps_1s)}},
|
|
||||||
"cluster_memory_usage_wo_cache": {{ClusterName, clusterName, GetMetricUrl(cluster_memory_usage_wo_cache, replaceMap, ClusterName, metric_range_1s, steps_1s)}},
|
|
||||||
"cluster_disk_size_usage": {{ClusterName, clusterName, GetMetricUrl(cluster_disk_size_usage, replaceMap, ClusterName, metric_range_1s, steps_1s)}},
|
|
||||||
}
|
|
||||||
|
|
||||||
mr := []MetricResult{}
|
|
||||||
ch := make(chan MetricResult, len(metricMap))
|
|
||||||
limit := make(chan bool, buffer)
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
|
|
||||||
for k, v := range metricMap {
|
|
||||||
limit <- true
|
|
||||||
wg.Add(1)
|
|
||||||
go HttpGetMetrics(k, v, &wg, ch)
|
|
||||||
<-limit
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
close(ch)
|
|
||||||
|
|
||||||
for v := range ch {
|
|
||||||
mr = append(mr, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
Response(c, http.StatusOK, "success", mr)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary 获取容器节点资源监控
|
|
||||||
// @Description 获取容器节点资源监控
|
|
||||||
// @Tags overview
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param clusterName query string true "集群名称"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 400
|
|
||||||
// @Router /api/v1/overview/getNodeMetrics [get]
|
|
||||||
func GetNodeMetrics(c *gin.Context) {
|
|
||||||
clusterName := c.Query(ClusterName)
|
|
||||||
|
|
||||||
//client, err := clientSet.GetClientSet(clusterName)
|
|
||||||
//if err != nil {
|
|
||||||
// log.Fatalln(err)
|
|
||||||
//}
|
|
||||||
//nodeList, err := client.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
|
|
||||||
//if err != nil {
|
|
||||||
// log.Fatalln(err)
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//nodes := []string{}
|
|
||||||
//for i := range nodeList.Items {
|
|
||||||
// nodes = append(nodes, nodeList.Items[i].Name)
|
|
||||||
//}
|
|
||||||
replaceMap := make(map[string]string)
|
|
||||||
replaceMap[ClusterName] = clusterName
|
|
||||||
|
|
||||||
mr := []MetricResult{}
|
|
||||||
//node_rs := []map[string]interface{}{}
|
|
||||||
//rd := ResultData {
|
|
||||||
// Status: "success",
|
|
||||||
// Data: MetricData{
|
|
||||||
// Result: node_rs,
|
|
||||||
// ResultType: "",
|
|
||||||
// },
|
|
||||||
//}
|
|
||||||
//nodes_mr := MetricResult{
|
|
||||||
// MetricName: "node",
|
|
||||||
// ResultData: rd,
|
|
||||||
//}
|
|
||||||
//mr = append(mr, nodes_mr)
|
|
||||||
|
|
||||||
metricMap := map[string][]MetricUrl{
|
|
||||||
"node_cpu_total": {{ClusterName, clusterName, GetMetricUrl(node_cpu_total, replaceMap, ClusterName, metric_range_1s, steps_1s)}},
|
|
||||||
"node_cpu_usage": {{ClusterName, clusterName, GetMetricUrl(node_cpu_usage, replaceMap, ClusterName, metric_range_1s, steps_1s)}},
|
|
||||||
"node_cpu_utilisation": {{ClusterName, clusterName, GetMetricUrl(node_cpu_utilisation, replaceMap, ClusterName, metric_range_1s, steps_1s)}},
|
|
||||||
"node_load1": {{ClusterName, clusterName, GetMetricUrl(node_load1, replaceMap, ClusterName, metric_range_1s, steps_1s)}},
|
|
||||||
"node_load15": {{ClusterName, clusterName, GetMetricUrl(node_load15, replaceMap, ClusterName, metric_range_1s, steps_1s)}},
|
|
||||||
"node_memory_total": {{ClusterName, clusterName, GetMetricUrl(node_memory_total, replaceMap, ClusterName, metric_range_1s, steps_1s)}},
|
|
||||||
"node_memory_usage_wo_cache": {{ClusterName, clusterName, GetMetricUrl(node_memory_usage_wo_cache, replaceMap, ClusterName, metric_range_1s, steps_1s)}},
|
|
||||||
"node_memory_utilisation": {{ClusterName, clusterName, GetMetricUrl(node_memory_utilisation, replaceMap, ClusterName, metric_range_1s, steps_1s)}},
|
|
||||||
"node_pod_quota": {{ClusterName, clusterName, GetMetricUrl(node_pod_quota, replaceMap, ClusterName, metric_range_1s, steps_1s)}},
|
|
||||||
"node_pod_running_count": {{ClusterName, clusterName, GetMetricUrl(node_pod_running_count, replaceMap, ClusterName, metric_range_1s, steps_1s)}},
|
|
||||||
"nvidia_smi_utilization_gpu_ratio": {{ClusterName, clusterName, GetMetricUrl(nvidia_smi_utilization_gpu_ratio, replaceMap, ClusterName, metric_range_1s, steps_1s)}},
|
|
||||||
"nvidia_smi_utilization_memory_ratio": {{ClusterName, clusterName, GetMetricUrl(nvidia_smi_utilization_memory_ratio, replaceMap, ClusterName, metric_range_1s, steps_1s)}},
|
|
||||||
}
|
|
||||||
|
|
||||||
ch := make(chan MetricResult, len(metricMap))
|
|
||||||
limit := make(chan bool, buffer)
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
|
|
||||||
for k, v := range metricMap {
|
|
||||||
limit <- true
|
|
||||||
wg.Add(1)
|
|
||||||
go HttpGetMetrics(k, v, &wg, ch)
|
|
||||||
<-limit
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
close(ch)
|
|
||||||
|
|
||||||
for v := range ch {
|
|
||||||
mr = append(mr, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
Response(c, http.StatusOK, "success", mr)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary 获取ApiServer请求数和请求延迟
|
|
||||||
// @Description 获取ApiServer请求数和请求延迟
|
|
||||||
// @Tags overview
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param clusterName query string true "集群名称"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 400
|
|
||||||
// @Router /api/v1/overview/getApiServerMetrics [get]
|
|
||||||
func GetApiServerMetrics(c *gin.Context) {
|
|
||||||
clusterName := c.Query(ClusterName)
|
|
||||||
|
|
||||||
replaceMap := make(map[string]string)
|
|
||||||
replaceMap[ClusterName] = clusterName
|
|
||||||
|
|
||||||
metricMap := map[string][]MetricUrl{
|
|
||||||
"apiserver_request_rate": {{ClusterName, clusterName, GetMetricUrl(apiserver_request_rate, replaceMap, ClusterName, metric_range_1s, steps_1s)}},
|
|
||||||
"apiserver_request_latencies": {{ClusterName, clusterName, GetMetricUrl(apiserver_request_latencies, replaceMap, ClusterName, metric_range_1s, steps_1s)}},
|
|
||||||
}
|
|
||||||
|
|
||||||
ch := make(chan MetricResult, len(metricMap))
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
|
|
||||||
for k, v := range metricMap {
|
|
||||||
wg.Add(1)
|
|
||||||
go HttpGetMetrics(k, v, &wg, ch)
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
close(ch)
|
|
||||||
|
|
||||||
mr := []MetricResult{}
|
|
||||||
for v := range ch {
|
|
||||||
mr = append(mr, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
Response(c, http.StatusOK, "success", mr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ResourceCount(c *gin.Context) {
|
|
||||||
overview := &Overview{}
|
|
||||||
//纳管资源域、纳管集群总计、容器创建数量 资源域
|
|
||||||
rows, _ := DB.Query("SELECT count( t.domain) from (select DISTINCT d.domain_id domain from domain d inner join domain_cluster dc on dc.domain_id = d.domain_id) t ")
|
|
||||||
for rows.Next() {
|
|
||||||
err := rows.Scan(&overview.Domain)
|
|
||||||
if err != nil {
|
|
||||||
glog.Errorf("query failed!,error %v", err)
|
|
||||||
Response(c, http.StatusBadRequest, "query failed!", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 集群数量
|
|
||||||
clusters, err := KarmadaClient.ClusterV1alpha1().Clusters().List(context.TODO(), metav1.ListOptions{})
|
|
||||||
if err != nil {
|
|
||||||
glog.Info("failed to retrieve cluster(%s). error: %v", clusters, err)
|
|
||||||
Response(c, http.StatusBadRequest, "failed to retrieve cluster", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
rows, _ = DB.Query("select count(*) as historyClusterCount from cluster_resource_history")
|
|
||||||
var historyClusterCount int
|
|
||||||
for rows.Next() {
|
|
||||||
err := rows.Scan(&historyClusterCount)
|
|
||||||
if err != nil {
|
|
||||||
glog.Errorf("query failed!,error %v", err)
|
|
||||||
Response(c, http.StatusBadRequest, "query failed!", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
overview.Cluster = len(clusters.Items) + historyClusterCount
|
|
||||||
//容器数量
|
|
||||||
overview.Pod = countPod()
|
|
||||||
//纳管算力总计
|
|
||||||
rows, _ = DB.Query(`select ifnull(sum(gpu_GFlops), 0) + ifnull(sum(cpu_GFlops), 0) as totalFlops from resources`)
|
|
||||||
var totalFlops float64
|
|
||||||
for rows.Next() {
|
|
||||||
err := rows.Scan(&totalFlops)
|
|
||||||
if err != nil {
|
|
||||||
glog.Errorf("query failed!,error %v", err)
|
|
||||||
Response(c, http.StatusBadRequest, "query failed!", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
totalFlops, _ = decimal.NewFromFloat(totalFlops / 1e6).Round(2).Float64()
|
|
||||||
overview.TotalFlops = totalFlops
|
|
||||||
Response(c, http.StatusOK, "success", *overview)
|
|
||||||
}
|
|
||||||
|
|
||||||
func countPod() int32 {
|
|
||||||
var podSum int32
|
|
||||||
opts := metav1.ListOptions{
|
|
||||||
TypeMeta: metav1.TypeMeta{},
|
|
||||||
LabelSelector: "jcce=true",
|
|
||||||
FieldSelector: "",
|
|
||||||
Watch: false,
|
|
||||||
AllowWatchBookmarks: false,
|
|
||||||
ResourceVersion: "",
|
|
||||||
ResourceVersionMatch: "",
|
|
||||||
TimeoutSeconds: nil,
|
|
||||||
Limit: 0,
|
|
||||||
Continue: "",
|
|
||||||
}
|
|
||||||
|
|
||||||
nsResult, _ := ClientSet.CoreV1().Namespaces().List(context.TODO(), opts)
|
|
||||||
|
|
||||||
for _, ns := range nsResult.Items {
|
|
||||||
deploymentList, err := ClientSet.AppsV1().Deployments(ns.Name).List(context.TODO(), metav1.ListOptions{})
|
|
||||||
if err != nil {
|
|
||||||
panic("解析失败")
|
|
||||||
}
|
|
||||||
var totalAvailable int32
|
|
||||||
for _, deploy := range deploymentList.Items {
|
|
||||||
replicaAvailable := deploy.Status.AvailableReplicas
|
|
||||||
totalAvailable += replicaAvailable
|
|
||||||
}
|
|
||||||
podSum += totalAvailable
|
|
||||||
}
|
|
||||||
return podSum
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary 获取大球监控数据
|
|
||||||
// @Description 获取大球监控数据
|
|
||||||
// @Tags namespace
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 400
|
|
||||||
// @Router /api/v1/sphere/getOverallMetrics [get]
|
|
||||||
func GetOverallMetrics(c *gin.Context) {
|
|
||||||
Response(c, http.StatusOK, "success", getOverallMetrics(metric_range_7d, steps_7d))
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary 获取大球监控数据1day
|
|
||||||
// @Description 获取大球监控数据1day
|
|
||||||
// @Tags namespace
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 400
|
|
||||||
// @Router /api/v1/sphere/GetOverallMetricsToday [get]
|
|
||||||
func GetOverallMetricsToday() {
|
|
||||||
getOverallMetrics(metric_range_1d, steps_7d)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getOverallMetrics(metric_range string, steps int64) []MetricResult {
|
|
||||||
|
|
||||||
cmap := make(map[string]map[string]string)
|
|
||||||
clusters := GetMonitoringClusters()
|
|
||||||
//clusters := []string{"fedjcce-fx001", "fedjcce-zj002", "fedjcce-zj003", "fedjcce-zj004", "fedjcce-zj005",
|
|
||||||
// "fedjcce-zj006", "host", "member-01", "member-02", "member-04", "member-05", "zhijiang-gpu4", "zhijiang-gpu5", "zhijiang-gpu7", "zhijiang-gpu8", "zhijiang-gpu9",
|
|
||||||
// "fedjcce-ten002", "kylin-edge", "nudt-ali", "nudt-hw", "nudt-tx", "test12077"}
|
|
||||||
for _, cluster := range clusters {
|
|
||||||
replaceMap := map[string]string{
|
|
||||||
ClusterName: cluster,
|
|
||||||
}
|
|
||||||
imap := make(map[string]string)
|
|
||||||
imap["cpu_avg_usage"] = replacePromql(cpu_avg_usage, replaceMap, ClusterName)
|
|
||||||
imap["mem_avg_usage"] = replacePromql(mem_avg_usage, replaceMap, ClusterName)
|
|
||||||
imap["cpu_total_usage"] = replacePromql(cpu_total_usage, replaceMap, ClusterName)
|
|
||||||
imap["mem_total_usage"] = replacePromql(mem_total_usage, replaceMap, ClusterName)
|
|
||||||
cmap[cluster] = imap
|
|
||||||
}
|
|
||||||
|
|
||||||
metricMap := make(map[string][]MetricUrl)
|
|
||||||
for _, cluster := range clusters {
|
|
||||||
var metricUrls1 []MetricUrl
|
|
||||||
var metricUrls2 []MetricUrl
|
|
||||||
var metricUrls3 []MetricUrl
|
|
||||||
var metricUrls4 []MetricUrl
|
|
||||||
metricUrls1 = append(metricUrls1, MetricUrl{ClusterName, "", GetMetricUrl(cmap[cluster]["cpu_avg_usage"], nil, "", metric_range, steps)})
|
|
||||||
metricMap[cluster+"#"+"cpu_avg_usage"] = metricUrls1
|
|
||||||
metricUrls2 = append(metricUrls2, MetricUrl{ClusterName, "", GetMetricUrl(cmap[cluster]["mem_avg_usage"], nil, "", metric_range, steps)})
|
|
||||||
metricMap[cluster+"#"+"mem_avg_usage"] = metricUrls2
|
|
||||||
metricUrls3 = append(metricUrls3, MetricUrl{ClusterName, "", GetMetricUrl(cmap[cluster]["cpu_total_usage"], nil, "", metric_range, steps)})
|
|
||||||
metricMap[cluster+"#"+"cpu_total_usage"] = metricUrls3
|
|
||||||
metricUrls4 = append(metricUrls4, MetricUrl{ClusterName, "", GetMetricUrl(cmap[cluster]["mem_total_usage"], nil, "", metric_range, steps)})
|
|
||||||
metricMap[cluster+"#"+"mem_total_usage"] = metricUrls4
|
|
||||||
}
|
|
||||||
|
|
||||||
ch := make(chan MetricResult, len(metricMap))
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
limit := make(chan bool, buffer)
|
|
||||||
|
|
||||||
for k, v := range metricMap {
|
|
||||||
limit <- true
|
|
||||||
wg.Add(1)
|
|
||||||
go HttpGetMetrics(k, v, &wg, ch)
|
|
||||||
<-limit
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
close(ch)
|
|
||||||
|
|
||||||
//aggregate metrics
|
|
||||||
metricsTotal := map[string]map[float64]float64{
|
|
||||||
"cpu_avg_usage": make(map[float64]float64),
|
|
||||||
"mem_avg_usage": make(map[float64]float64),
|
|
||||||
"cpu_total_usage": make(map[float64]float64),
|
|
||||||
"mem_total_usage": make(map[float64]float64),
|
|
||||||
}
|
|
||||||
for v := range ch {
|
|
||||||
format, ok := v.MetricData.Result[0]["values"].([]interface{})
|
|
||||||
arraySplit := strings.Split(v.MetricName, "#")
|
|
||||||
submap := metricsTotal[arraySplit[1]]
|
|
||||||
|
|
||||||
if ok {
|
|
||||||
for _, val := range format {
|
|
||||||
f, ok := val.([]interface{})
|
|
||||||
if ok {
|
|
||||||
timeUnix, ok1 := f[0].(float64)
|
|
||||||
value, ok2 := f[1].(string)
|
|
||||||
if ok1 && ok2 {
|
|
||||||
f_value, _ := strconv.ParseFloat(value, 64)
|
|
||||||
submap[timeUnix] += f_value
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
//var timeToValues TimeToValue
|
|
||||||
//t_byte, _ := json.Marshal(val)
|
|
||||||
//err := json.Unmarshal(t_byte, &timeToValues)
|
|
||||||
//if err != nil {
|
|
||||||
// fmt.Println("error")
|
|
||||||
//}
|
|
||||||
//fmt.Println(timeToValues)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
metricsTotal[arraySplit[1]] = submap
|
|
||||||
}
|
|
||||||
|
|
||||||
//avg指标取平均值
|
|
||||||
for i, _ := range metricsTotal {
|
|
||||||
metricName := i
|
|
||||||
if strings.Contains(metricName, metric_avg) {
|
|
||||||
var totalValue float64
|
|
||||||
for i2, _ := range metricsTotal[i] {
|
|
||||||
totalValue += metricsTotal[i][i2]
|
|
||||||
}
|
|
||||||
avgValue := round((totalValue / float64(len(metricsTotal[i]))), 3)
|
|
||||||
for i2, _ := range metricsTotal[i] {
|
|
||||||
metricsTotal[i][i2] = avgValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mrs := []MetricResult{}
|
|
||||||
for i, _ := range metricsTotal {
|
|
||||||
md := MetricData{
|
|
||||||
ResultType: "matrix",
|
|
||||||
}
|
|
||||||
var value [][]interface{}
|
|
||||||
|
|
||||||
for i2, _ := range metricsTotal[i] {
|
|
||||||
var v []interface{}
|
|
||||||
v = append(v, i2)
|
|
||||||
//v = append(v, strconv.FormatFloat(metricsTotal[i][i2]))
|
|
||||||
v = append(v, metricsTotal[i][i2])
|
|
||||||
value = append(value, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
r := map[string]interface{}{
|
|
||||||
"values": value,
|
|
||||||
}
|
|
||||||
md.Result = append(md.Result, r)
|
|
||||||
mr := MetricResult{
|
|
||||||
i, md,
|
|
||||||
}
|
|
||||||
mrs = append(mrs, mr)
|
|
||||||
}
|
|
||||||
|
|
||||||
return mrs
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetServerResources(c *gin.Context) {
|
|
||||||
var resources []model.Resources
|
|
||||||
sql := "select name, ip, public_ip, hpc_partition, cpu_name, cpu_GHz, cpu_cores, total_threads, memory, gpu_dcu, cpu_single_cycle, num, cpu_GFlops, gpu_GFlops, region, flops from resources order by id"
|
|
||||||
err := DB.Select(&resources, sql)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("exec failed, ", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
rows, _ := DB.Query(`select ifnull(sum(gpu_GFlops), 0) + ifnull(sum(cpu_GFlops), 0) as totalFlops from resources`)
|
|
||||||
var totalFlops float64
|
|
||||||
for rows.Next() {
|
|
||||||
err := rows.Scan(&totalFlops)
|
|
||||||
if err != nil {
|
|
||||||
glog.Errorf("query failed!,error %v", err)
|
|
||||||
Response(c, http.StatusBadRequest, "query failed!", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sumResource := model.Resources{
|
|
||||||
Name: "合计",
|
|
||||||
Flops: totalFlops,
|
|
||||||
}
|
|
||||||
resources = append(resources, sumResource)
|
|
||||||
Response(c, http.StatusOK, "success", resources)
|
|
||||||
}
|
|
||||||
|
|
||||||
func round(f float64, precision int) float64 {
|
|
||||||
p := math.Pow10(precision)
|
|
||||||
return math.Trunc((f+0.5/p)*p) / p
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary 批量获取容器集群资源监控
|
|
||||||
// @Description 批量获取容器集群资源监控
|
|
||||||
// @Tags overview
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param clusterName query string true "集群名称list"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 400
|
|
||||||
// @Router /api/v1/overview/getClusterMetrics [get]
|
|
||||||
func ListClusterMetrics(c *gin.Context) {
|
|
||||||
clusterNameStr := c.Query(ClusterName)
|
|
||||||
clusterNameList := strings.Split(clusterNameStr, ",")
|
|
||||||
listMr := make([]ListMetricResult, 0)
|
|
||||||
for _, clusterName := range clusterNameList {
|
|
||||||
|
|
||||||
replaceMap := make(map[string]string)
|
|
||||||
replaceMap[ClusterName] = clusterName
|
|
||||||
|
|
||||||
metricMap := map[string][]MetricUrl{
|
|
||||||
"cluster_pod_running_count": {{ClusterName, clusterName, GetMetricUrl(cluster_pod_running_count, replaceMap, ClusterName, metric_range_1s, steps_1s)}},
|
|
||||||
"cluster_memory_total": {{ClusterName, clusterName, GetMetricUrl(cluster_memory_total, replaceMap, ClusterName, metric_range_1s, steps_1s)}},
|
|
||||||
"cluster_cpu_total": {{ClusterName, clusterName, GetMetricUrl(cluster_cpu_total, replaceMap, ClusterName, metric_range_1s, steps_1s)}},
|
|
||||||
"cluster_disk_size_capacity": {{ClusterName, clusterName, GetMetricUrl(cluster_disk_size_capacity, replaceMap, ClusterName, metric_range_1s, steps_1s)}},
|
|
||||||
"cluster_cpu_usage": {{ClusterName, clusterName, GetMetricUrl(cluster_cpu_usage, replaceMap, ClusterName, metric_range_1s, steps_1s)}},
|
|
||||||
"cluster_pod_quota": {{ClusterName, clusterName, GetMetricUrl(cluster_pod_quota, replaceMap, ClusterName, metric_range_1s, steps_1s)}},
|
|
||||||
"cluster_memory_usage_wo_cache": {{ClusterName, clusterName, GetMetricUrl(cluster_memory_usage_wo_cache, replaceMap, ClusterName, metric_range_1s, steps_1s)}},
|
|
||||||
"cluster_disk_size_usage": {{ClusterName, clusterName, GetMetricUrl(cluster_disk_size_usage, replaceMap, ClusterName, metric_range_1s, steps_1s)}},
|
|
||||||
}
|
|
||||||
|
|
||||||
mr := []MetricResult{}
|
|
||||||
ch := make(chan MetricResult, len(metricMap))
|
|
||||||
limit := make(chan bool, buffer)
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
|
|
||||||
for k, v := range metricMap {
|
|
||||||
limit <- true
|
|
||||||
wg.Add(1)
|
|
||||||
go HttpGetMetrics(k, v, &wg, ch)
|
|
||||||
<-limit
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
close(ch)
|
|
||||||
|
|
||||||
for v := range ch {
|
|
||||||
mr = append(mr, v)
|
|
||||||
}
|
|
||||||
listMetricResult := ListMetricResult{}
|
|
||||||
listMetricResult.MetricResult = mr
|
|
||||||
listMr = append(listMr, listMetricResult)
|
|
||||||
}
|
|
||||||
Response(c, http.StatusOK, "success", listMr)
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,7 +1,5 @@
|
||||||
package app
|
package app
|
||||||
|
|
||||||
import "strconv"
|
|
||||||
|
|
||||||
type Page[T any] struct {
|
type Page[T any] struct {
|
||||||
// 当前页码
|
// 当前页码
|
||||||
PageNum int64 `json:"pageNum"`
|
PageNum int64 `json:"pageNum"`
|
||||||
|
@ -16,9 +14,7 @@ type Page[T any] struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Paginator 生成新的分页数据对象
|
// Paginator 生成新的分页数据对象
|
||||||
func Paginator[T any](items *Page[T], total int64, pageNumParam, pageSizeParam string) *Page[T] {
|
func Paginator[T any](items *Page[T], total, pageNum, pageSize int64) *Page[T] {
|
||||||
pageNum, _ := strconv.ParseInt(pageNumParam, 10, 64)
|
|
||||||
pageSize, _ := strconv.ParseInt(pageSizeParam, 10, 64)
|
|
||||||
var (
|
var (
|
||||||
pageStart int64
|
pageStart int64
|
||||||
pageEnd int64
|
pageEnd int64
|
||||||
|
|
503
app/pod.go
503
app/pod.go
|
@ -1,503 +0,0 @@
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
v12 "k8s.io/api/apps/v1"
|
|
||||||
v1 "k8s.io/api/core/v1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/metrics/pkg/apis/metrics"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
pod_cpu_usage = "irate(container_cpu_usage_seconds_total{cluster_name='clusterName', pod='podName', container='', prometheus_replica='prometheus-k8s-0'}[10m])"
|
|
||||||
pod_memery_usage_wo_cache = "container_memory_working_set_bytes{cluster_name='clusterName', pod='podName', container='', prometheus_replica='prometheus-k8s-0'}"
|
|
||||||
pod_net_bytes_transmitted = "irate(container_network_transmit_bytes_total{cluster_name='clusterName', pod='podName', interface='eth0', prometheus_replica='prometheus-k8s-0'}[10m])"
|
|
||||||
pod_net_bytes_received = "irate(container_network_receive_bytes_total{cluster_name='clusterName', pod='podName', interface='eth0',prometheus_replica='prometheus-k8s-0'}[10m])"
|
|
||||||
PodName = "podName"
|
|
||||||
pod_metric_range = "8.333h"
|
|
||||||
steps_pod = 600
|
|
||||||
)
|
|
||||||
|
|
||||||
type Pod struct {
|
|
||||||
PageNum string `json:"page_num"`
|
|
||||||
PageSize string `json:"page_size"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
ClusterName string `json:"cluster_name"`
|
|
||||||
DomainName string `json:"domain_name"`
|
|
||||||
Ready string `json:"ready"`
|
|
||||||
Status string `json:"status"`
|
|
||||||
Restarts int64 `json:"restarts"`
|
|
||||||
Age string `json:"age"`
|
|
||||||
IP string `json:"IP"`
|
|
||||||
Node string `json:"node"`
|
|
||||||
Namespace string `json:"namespace"`
|
|
||||||
ContainerImage string `json:"container_image"`
|
|
||||||
ContainerName string `json:"container_name"`
|
|
||||||
AliasName string `json:"alias_name"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
TemplateId string `json:"template_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type PodObject struct {
|
|
||||||
Kind string `json:"kind"`
|
|
||||||
ApiVersion string `json:"apiVersion"`
|
|
||||||
Metadata string `json:"metadata"`
|
|
||||||
ResourceVersion string `json:"resourceVersion"`
|
|
||||||
Items []v1.Pod `json:"items"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type DeploymentPods struct {
|
|
||||||
Replicas int32 `json:"replicas"`
|
|
||||||
AvailableReplicas int32 `json:"availableReplicas"`
|
|
||||||
Pods []v1.Pod `json:"pods"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ReplicaSetRes struct {
|
|
||||||
Kind string `json:"kind"`
|
|
||||||
ApiVersion string `json:"apiVersion"`
|
|
||||||
Metadata string `json:"metadata"`
|
|
||||||
ResourceVersion string `json:"resourceVersion"`
|
|
||||||
Items []v12.ReplicaSet `json:"items"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type StatefulSetRes struct {
|
|
||||||
Kind string `json:"kind"`
|
|
||||||
ApiVersion string `json:"apiVersion"`
|
|
||||||
Metadata string `json:"metadata"`
|
|
||||||
ResourceVersion string `json:"resourceVersion"`
|
|
||||||
Items []v12.StatefulSet `json:"items"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// PodMetrics 根据集群名称、命名空间和pod名称查询pod使用率
|
|
||||||
// @Summary 根据集群名称、命名空间和pod名称查询pod使用率
|
|
||||||
// @Description 根据集群名称、命名空间和pod名称查询pod使用率
|
|
||||||
// @Tags pod
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param namespace path string true "项目名称"
|
|
||||||
// @Param clusterName path string true "集群 "
|
|
||||||
// @Param name path string true "名称"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 500
|
|
||||||
// @Router /api/v1/pod/metrics/{clusterName}/{namespace}/{name} [get]
|
|
||||||
func PodMetrics(c *gin.Context) {
|
|
||||||
clusterName := c.Query("clusterName")
|
|
||||||
namespace := c.Query("namespace")
|
|
||||||
name := c.Query("name")
|
|
||||||
result := SearchObject(podMetrics, clusterName, namespace, name)
|
|
||||||
var metrics metrics.PodMetrics
|
|
||||||
raw, _ := result.Raw()
|
|
||||||
json.Unmarshal(raw, &metrics)
|
|
||||||
var cpu int64
|
|
||||||
var memory int64
|
|
||||||
for _, container := range metrics.Containers {
|
|
||||||
cpu = cpu + container.Usage.Cpu().Value()
|
|
||||||
memory = memory + container.Usage.Memory().Value()
|
|
||||||
}
|
|
||||||
Response(c, http.StatusOK, "success", map[string]string{
|
|
||||||
"cpu": decimal.NewFromInt(cpu).String(),
|
|
||||||
"memory": decimal.NewFromInt(memory).StringScaled(0),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// PodDetail 根据指定集群查询Pod详情
|
|
||||||
// @Summary 根据指定集群查询Pod详情
|
|
||||||
// @Description 根据指定集群查询Pod详情
|
|
||||||
// @Tags pod
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param clusterName path string true "集群名"
|
|
||||||
// @Param namespace path string true "命名空间名 "
|
|
||||||
// @Param name path string true "pod名"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 500
|
|
||||||
// @Router /api/v1/pod/detail/{clusterName}/{namespace}/{name} [get]
|
|
||||||
// PodDetail 根据指定集群查询Pod详情
|
|
||||||
func PodDetail(c *gin.Context) {
|
|
||||||
clusterName := c.Query("clusterName")
|
|
||||||
namespace := c.Query("namespace")
|
|
||||||
name := c.Query("name")
|
|
||||||
result := SearchObject(podListConst, clusterName, namespace, "")
|
|
||||||
var podRes PodObject
|
|
||||||
raw, _ := result.Raw()
|
|
||||||
json.Unmarshal(raw, &podRes)
|
|
||||||
for _, pod := range podRes.Items {
|
|
||||||
if strings.EqualFold(name, pod.Name) {
|
|
||||||
Response(c, http.StatusOK, "success", pod)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Response(c, http.StatusOK, "success", nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsExist 判断名称是否重复
|
|
||||||
func IsExist(c *gin.Context) {
|
|
||||||
clusterName := c.Query("clusterName")
|
|
||||||
namespace := c.Query("namespace")
|
|
||||||
name := c.Query("name")
|
|
||||||
result := SearchObject(podDetailConst, clusterName, namespace, name)
|
|
||||||
if result.Error() != nil {
|
|
||||||
Response(c, http.StatusOK, "success", false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var pod v1.Pod
|
|
||||||
raw, _ := result.Raw()
|
|
||||||
json.Unmarshal(raw, &pod)
|
|
||||||
if len(pod.Name) == 0 {
|
|
||||||
Response(c, http.StatusOK, "success", false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
Response(c, http.StatusOK, "success", true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListPod 根据指定集群查询Pod列表
|
|
||||||
// @Summary 根据指定集群查询Pod列表
|
|
||||||
// @Description 根据指定集群查询Pod列表
|
|
||||||
// @Tags pod
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 500
|
|
||||||
// @Router /api/v1/pod/list [get]
|
|
||||||
func ListPod(c *gin.Context) {
|
|
||||||
clusterName := c.Query("clusterName")
|
|
||||||
namespace := c.Query("namespace")
|
|
||||||
pageNum := c.Query("pageNum")
|
|
||||||
pageSize := c.Query("pageSize")
|
|
||||||
result := SearchObject(podListConst, clusterName, namespace, "")
|
|
||||||
if result.Error() != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "failed", result.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var podRes PodObject
|
|
||||||
raw, _ := result.Raw()
|
|
||||||
json.Unmarshal(raw, &podRes)
|
|
||||||
|
|
||||||
// 分页
|
|
||||||
page := &Page[v1.Pod]{}
|
|
||||||
// 排序
|
|
||||||
sort.SliceStable(podRes.Items, func(i, j int) bool {
|
|
||||||
return podRes.Items[i].CreationTimestamp.Time.After(podRes.Items[j].CreationTimestamp.Time)
|
|
||||||
})
|
|
||||||
page.List = podRes.Items
|
|
||||||
data := Paginator(page, int64(len(podRes.Items)), pageNum, pageSize)
|
|
||||||
Response(c, http.StatusOK, "success", data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListPod 根据指定集群查询Pod列表
|
|
||||||
// @Summary 根据指定集群查询Pod列表
|
|
||||||
// @Description 根据指定集群查询Pod列表
|
|
||||||
// @Param clusterName query string true "集群名称"
|
|
||||||
// @Param pageNum query int true "pageNum"
|
|
||||||
// @Param pageSize query int true "pageSize"
|
|
||||||
// @Tags pod
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 500
|
|
||||||
// @Router /api/v1/pod/all [get]
|
|
||||||
func ALLPod(c *gin.Context) {
|
|
||||||
clusterName := c.Query("clusterName")
|
|
||||||
pageNum := c.Query("pageNum")
|
|
||||||
pageSize := c.Query("pageSize")
|
|
||||||
result := SearchObject(allPodConst, clusterName, "", "")
|
|
||||||
if result.Error() != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "failed", result.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var podRes PodObject
|
|
||||||
raw, _ := result.Raw()
|
|
||||||
json.Unmarshal(raw, &podRes)
|
|
||||||
|
|
||||||
// 分页
|
|
||||||
page := &Page[v1.Pod]{}
|
|
||||||
// 排序
|
|
||||||
sort.SliceStable(podRes.Items, func(i, j int) bool {
|
|
||||||
return podRes.Items[i].CreationTimestamp.Time.After(podRes.Items[j].CreationTimestamp.Time)
|
|
||||||
})
|
|
||||||
page.List = podRes.Items
|
|
||||||
data := Paginator(page, int64(len(podRes.Items)), pageNum, pageSize)
|
|
||||||
Response(c, http.StatusOK, "success", data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreatePod 创建容器组
|
|
||||||
// @Summary 创建容器组
|
|
||||||
// @Description 创建容器组
|
|
||||||
// @Tags pod
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param param body Pod true "json"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 400
|
|
||||||
// @Router /api/v1/pod/create [post]
|
|
||||||
func CreatePod(c *gin.Context) {
|
|
||||||
var podParam Pod
|
|
||||||
if err := c.BindJSON(&podParam); err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "invalid request params.", "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
podListConst := v1.Pod{
|
|
||||||
TypeMeta: metav1.TypeMeta{},
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: podParam.Name,
|
|
||||||
},
|
|
||||||
Spec: v1.PodSpec{
|
|
||||||
Containers: []v1.Container{
|
|
||||||
{
|
|
||||||
Name: podParam.Name,
|
|
||||||
Image: podParam.ContainerImage,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Status: v1.PodStatus{},
|
|
||||||
}
|
|
||||||
|
|
||||||
podResult, err := ClientSet.CoreV1().Pods(podParam.Namespace).Create(context.TODO(), &podListConst, metav1.CreateOptions{})
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "create podListConst failed", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 创建调度实例
|
|
||||||
var clusterNameArray []string
|
|
||||||
clusterNameArray = append(clusterNameArray, podParam.ClusterName)
|
|
||||||
CreatePropagationPolicies(PropagationPolicy{
|
|
||||||
ClusterName: clusterNameArray,
|
|
||||||
TemplateId: podParam.TemplateId,
|
|
||||||
ResourceName: podParam.Name,
|
|
||||||
Name: "pod" + podParam.Namespace + podParam.Name,
|
|
||||||
Namespace: podParam.Namespace,
|
|
||||||
Kind: "Pod",
|
|
||||||
})
|
|
||||||
Response(c, http.StatusOK, "success", podResult)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeletePod 删除容器组
|
|
||||||
// @Summary 删除容器组
|
|
||||||
// @Description 删除容器组
|
|
||||||
// @Tags pod
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param namespace path string true "命名空间名"
|
|
||||||
// @param name path string true "Pod名"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 400
|
|
||||||
// @Router /api/v1/pod/delete/{namespace}/{name} [delete]
|
|
||||||
func DeletePod(c *gin.Context) {
|
|
||||||
clusterName := c.Param("clusterName")
|
|
||||||
namespace := c.Param("namespace")
|
|
||||||
name := c.Param("name")
|
|
||||||
label := c.Param("label")
|
|
||||||
if strings.EqualFold(label, "jcce") {
|
|
||||||
err := ClientSet.CoreV1().Pods(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{})
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "delete pod failed", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 删除调度策略
|
|
||||||
DeletePropagationPolicies(namespace, "pod"+namespace+name)
|
|
||||||
} else {
|
|
||||||
result := DeleteObject(detailPodConst, clusterName, namespace, name)
|
|
||||||
if result.Error() != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "delete pod failed", result.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Response(c, http.StatusOK, "success", nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdatePod 更新容器组
|
|
||||||
// @Summary 更新容器组
|
|
||||||
// @Description 更新容器组
|
|
||||||
// @Tags pod
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param param body Pod true "json"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 400
|
|
||||||
// @Router /api/v1/pod/updatePod [put]
|
|
||||||
func UpdatePod(c *gin.Context) {
|
|
||||||
var param Pod
|
|
||||||
if err := c.BindJSON(¶m); err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "invalid request params.", "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
pod := v1.Pod{
|
|
||||||
TypeMeta: metav1.TypeMeta{},
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: param.Name,
|
|
||||||
Annotations: map[string]string{
|
|
||||||
"kubesphere.io/alias-name": param.AliasName,
|
|
||||||
"kubesphere.io/description": param.Description,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Spec: v1.PodSpec{},
|
|
||||||
Status: v1.PodStatus{},
|
|
||||||
}
|
|
||||||
podResult, err := ClientSet.CoreV1().Pods(param.Namespace).Update(context.TODO(), &pod, metav1.UpdateOptions{})
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "update podListConst failed", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
Response(c, http.StatusOK, "success", podResult)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListPodFromNode 获取节点下的pod
|
|
||||||
func ListPodFromNode(c *gin.Context) {
|
|
||||||
clusterName := c.Query("clusterName")
|
|
||||||
nodeName := c.Query("nodeName")
|
|
||||||
podResult := SearchObject(allPodConst, clusterName, "", "")
|
|
||||||
if podResult.Error() != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "failed", podResult.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var podRes PodObject
|
|
||||||
raw, _ := podResult.Raw()
|
|
||||||
json.Unmarshal(raw, &podRes)
|
|
||||||
var result []v1.Pod
|
|
||||||
for i := range podRes.Items {
|
|
||||||
if strings.EqualFold(podRes.Items[i].Spec.NodeName, nodeName) {
|
|
||||||
result = append(result, podRes.Items[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Response(c, http.StatusOK, "success", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetPodForDeployment(clusterName string, namespace string, labelMap map[string]string) []v1.Pod {
|
|
||||||
|
|
||||||
// 查询pod当前命名空间下的列表
|
|
||||||
podResult := SearchObject(podListConst, clusterName, namespace, "")
|
|
||||||
var podObject PodObject
|
|
||||||
podRaw, _ := podResult.Raw()
|
|
||||||
json.Unmarshal(podRaw, &podObject)
|
|
||||||
// 查询label匹配的pod
|
|
||||||
var podsResult []v1.Pod
|
|
||||||
for _, pod := range podObject.Items {
|
|
||||||
for k, v := range labelMap {
|
|
||||||
if pod.Labels[k] != v {
|
|
||||||
goto loop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
podsResult = append(podsResult, pod)
|
|
||||||
loop:
|
|
||||||
}
|
|
||||||
return podsResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListPodFromDeployment 获取deployment下的pod
|
|
||||||
func ListPodFromDeployment(c *gin.Context) {
|
|
||||||
clusterName := c.Query("clusterName")
|
|
||||||
namespace := c.Query("namespace")
|
|
||||||
label := c.Query("label")
|
|
||||||
var labelMap = make(map[string]string)
|
|
||||||
labelArray := strings.Split(label, ",")
|
|
||||||
for _, label := range labelArray {
|
|
||||||
labelKV := strings.Split(label, "=")
|
|
||||||
labelMap[labelKV[0]] = labelKV[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
podList := GetPodForDeployment(clusterName, namespace, labelMap)
|
|
||||||
|
|
||||||
var replicas, availableReplicas int32
|
|
||||||
// 查询replicaSet
|
|
||||||
var replicaSetObject ReplicaSetObject
|
|
||||||
searchResult := SearchObject(replicaSetConst, clusterName, namespace, "")
|
|
||||||
raw, _ := searchResult.Raw()
|
|
||||||
json.Unmarshal(raw, &replicaSetObject)
|
|
||||||
for _, replicaSet := range replicaSetObject.Items {
|
|
||||||
for k, v := range labelMap {
|
|
||||||
if replicaSet.Labels[k] != v {
|
|
||||||
goto loop1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if replicaSet.Status.Replicas != 0 {
|
|
||||||
replicas = replicas + 1
|
|
||||||
if replicaSet.Status.AvailableReplicas != 0 {
|
|
||||||
availableReplicas = availableReplicas + 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
loop1:
|
|
||||||
}
|
|
||||||
|
|
||||||
Response(c, http.StatusOK, "success", DeploymentPods{
|
|
||||||
Pods: podList,
|
|
||||||
Replicas: replicas,
|
|
||||||
AvailableReplicas: availableReplicas,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPodDetail 获取容器组详情
|
|
||||||
// @Summary 获取容器组详情
|
|
||||||
// @Description 获取容器组详情
|
|
||||||
// @Tags pod
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param param body Pod true "json"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 400
|
|
||||||
// @Router /api/v1/pod/detail [put]
|
|
||||||
func GetPodDetail(c *gin.Context) {
|
|
||||||
clusterName := c.Query("clusterName")
|
|
||||||
namespace := c.Query("namespace")
|
|
||||||
name := c.Query("name")
|
|
||||||
rsRes := SearchObject(detailPodConst, clusterName, namespace, name)
|
|
||||||
raw, _ := rsRes.Raw()
|
|
||||||
var pod v1.Pod
|
|
||||||
json.Unmarshal(raw, &pod)
|
|
||||||
|
|
||||||
err_ := AddTypeMetaToObject(&pod)
|
|
||||||
if err_ != nil {
|
|
||||||
log.Println(err_)
|
|
||||||
}
|
|
||||||
Response(c, http.StatusOK, "success", pod)
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary 获取容器组监控
|
|
||||||
// @Description 获取容器组监控
|
|
||||||
// @Tags pod
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param clusterName query string true "集群名称"
|
|
||||||
// @Param podName query string true "容器组名称"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 400
|
|
||||||
// @Router /api/v1/pod/metrics [get]
|
|
||||||
func GetMetrics(c *gin.Context) {
|
|
||||||
clusterName := c.Query("clusterName")
|
|
||||||
podName := c.Query("podName")
|
|
||||||
|
|
||||||
replaceMap := make(map[string]string)
|
|
||||||
replaceMap[ClusterName] = clusterName
|
|
||||||
replaceMap[PodName] = podName
|
|
||||||
|
|
||||||
metricMap := map[string][]MetricUrl{
|
|
||||||
"pod_cpu_usage": {{PodName, podName, GetMetricUrl(pod_cpu_usage, replaceMap, PodName, pod_metric_range, steps_pod)}},
|
|
||||||
"pod_memory_usage_wo_cache": {{PodName, podName, GetMetricUrl(pod_memery_usage_wo_cache, replaceMap, PodName, pod_metric_range, steps_pod)}},
|
|
||||||
"pod_net_bytes_transmitted": {{PodName, podName, GetMetricUrl(pod_net_bytes_transmitted, replaceMap, PodName, pod_metric_range, steps_pod)}},
|
|
||||||
"pod_net_bytes_received": {{PodName, podName, GetMetricUrl(pod_net_bytes_received, replaceMap, PodName, pod_metric_range, steps_pod)}},
|
|
||||||
}
|
|
||||||
|
|
||||||
ch := make(chan MetricResult, len(metricMap))
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
|
|
||||||
for k, v := range metricMap {
|
|
||||||
wg.Add(1)
|
|
||||||
go HttpGetMetrics(k, v, &wg, ch)
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
close(ch)
|
|
||||||
|
|
||||||
mr := []MetricResult{}
|
|
||||||
for v := range ch {
|
|
||||||
mr = append(mr, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
Response(c, http.StatusOK, "success", mr)
|
|
||||||
}
|
|
|
@ -1,274 +0,0 @@
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"github.com/bitly/go-simplejson"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
policyv1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1"
|
|
||||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type PropagationPolicy struct {
|
|
||||||
TemplateId string `json:"template_id"`
|
|
||||||
TemplateName string `json:"template_name"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Namespace string `json:"namespace"`
|
|
||||||
ResourceName string `json:"resource_name"`
|
|
||||||
CrossDomain bool `json:"cross_domain"`
|
|
||||||
Kind string `json:"kind"`
|
|
||||||
Labels map[string]string `json:"labels"`
|
|
||||||
LabelString *string `json:"label_string"`
|
|
||||||
ReplicaSchedulingType *string `json:"replica_scheduling_type"`
|
|
||||||
ReplicaDivisionPreference *string `json:"replica_division_preference"`
|
|
||||||
ClusterPreference *string `json:"cluster_preference"`
|
|
||||||
ClusterName []string `json:"cluster_name"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreatePropagationPolicies 创建集群分发策略实例
|
|
||||||
func CreatePropagationPolicies(propagationPolicy PropagationPolicy) error {
|
|
||||||
var pp PropagationPolicy
|
|
||||||
//获取模板数据
|
|
||||||
sqlPolicyTemplate := "select template_id,template_name,labels,schedule_type,division_preference,cluster_preference from propagation_policy_template where template_id = ?"
|
|
||||||
queryErr := DB.QueryRow(sqlPolicyTemplate, propagationPolicy.TemplateId).Scan(&pp.TemplateId, &pp.TemplateName, &pp.LabelString, &pp.ReplicaSchedulingType, &pp.ReplicaDivisionPreference, &pp.ClusterPreference)
|
|
||||||
if queryErr != nil {
|
|
||||||
return queryErr
|
|
||||||
}
|
|
||||||
|
|
||||||
labelsJson, err := simplejson.NewJson([]byte(*pp.LabelString))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
labelsMapInterface, _ := labelsJson.Map()
|
|
||||||
labelsMap := make(map[string]string, len(labelsMapInterface))
|
|
||||||
for k, v := range labelsMapInterface {
|
|
||||||
labelsMap[k] = fmt.Sprint(v)
|
|
||||||
}
|
|
||||||
rst := policyv1alpha1.ReplicaSchedulingStrategy{}
|
|
||||||
|
|
||||||
//复制分配类型 否则为 Divided 根据下层策略分配
|
|
||||||
if *pp.ReplicaSchedulingType == "Duplicated" {
|
|
||||||
rst.ReplicaSchedulingType = policyv1alpha1.ReplicaSchedulingTypeDuplicated
|
|
||||||
} else if *pp.ReplicaDivisionPreference == "Aggregated" {
|
|
||||||
//划分-聚合 Aggregated 否则为 Weighted根据权重进行分配
|
|
||||||
rst.ReplicaSchedulingType = policyv1alpha1.ReplicaSchedulingTypeDivided
|
|
||||||
rst.ReplicaDivisionPreference = policyv1alpha1.ReplicaDivisionPreferenceAggregated
|
|
||||||
} else if *pp.ClusterPreference == "DynamicWeight" {
|
|
||||||
//划分-权重-动态 DynamicWeight 否则为静态
|
|
||||||
rst.ReplicaSchedulingType = policyv1alpha1.ReplicaSchedulingTypeDivided
|
|
||||||
rst.ReplicaDivisionPreference = policyv1alpha1.ReplicaDivisionPreferenceWeighted
|
|
||||||
rst.WeightPreference = &policyv1alpha1.ClusterPreferences{
|
|
||||||
DynamicWeight: policyv1alpha1.DynamicWeightByAvailableReplicas,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//划分-权重-静态
|
|
||||||
rst.ReplicaSchedulingType = policyv1alpha1.ReplicaSchedulingTypeDivided
|
|
||||||
rst.ReplicaDivisionPreference = policyv1alpha1.ReplicaDivisionPreferenceWeighted
|
|
||||||
}
|
|
||||||
if strings.EqualFold(propagationPolicy.Kind, "PersistentVolume") || strings.EqualFold(propagationPolicy.Kind, "Namespace") {
|
|
||||||
policy := &policyv1alpha1.ClusterPropagationPolicy{
|
|
||||||
ObjectMeta: v1.ObjectMeta{
|
|
||||||
Name: propagationPolicy.Name,
|
|
||||||
},
|
|
||||||
Spec: policyv1alpha1.PropagationSpec{
|
|
||||||
ResourceSelectors: []policyv1alpha1.ResourceSelector{
|
|
||||||
{
|
|
||||||
APIVersion: "v1",
|
|
||||||
Kind: propagationPolicy.Kind,
|
|
||||||
Name: propagationPolicy.ResourceName,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Placement: policyv1alpha1.Placement{
|
|
||||||
ClusterAffinity: &policyv1alpha1.ClusterAffinity{
|
|
||||||
LabelSelector: &v1.LabelSelector{
|
|
||||||
MatchLabels: labelsMap,
|
|
||||||
},
|
|
||||||
ClusterNames: propagationPolicy.ClusterName,
|
|
||||||
},
|
|
||||||
ReplicaScheduling: &rst,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
_, clusterPolicyErr := KarmadaClient.PolicyV1alpha1().ClusterPropagationPolicies().Create(context.TODO(), policy, v1.CreateOptions{})
|
|
||||||
if clusterPolicyErr != nil {
|
|
||||||
return clusterPolicyErr
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
policy := &policyv1alpha1.PropagationPolicy{
|
|
||||||
ObjectMeta: v1.ObjectMeta{
|
|
||||||
Name: propagationPolicy.Name,
|
|
||||||
Namespace: propagationPolicy.Namespace,
|
|
||||||
},
|
|
||||||
Spec: policyv1alpha1.PropagationSpec{
|
|
||||||
ResourceSelectors: []policyv1alpha1.ResourceSelector{
|
|
||||||
{
|
|
||||||
Kind: propagationPolicy.Kind,
|
|
||||||
Name: propagationPolicy.ResourceName,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Placement: policyv1alpha1.Placement{
|
|
||||||
ClusterAffinity: &policyv1alpha1.ClusterAffinity{
|
|
||||||
LabelSelector: &v1.LabelSelector{
|
|
||||||
MatchLabels: labelsMap,
|
|
||||||
},
|
|
||||||
ClusterNames: propagationPolicy.ClusterName,
|
|
||||||
},
|
|
||||||
ReplicaScheduling: &rst,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
switch propagationPolicy.Kind {
|
|
||||||
case "HorizontalPodAutoscaler":
|
|
||||||
policy.Spec.ResourceSelectors[0].APIVersion = "autoscaling/v1"
|
|
||||||
case "Pod", "Service", "PersistentVolumeClaim", "ConfigMap", "Namespace":
|
|
||||||
policy.Spec.ResourceSelectors[0].APIVersion = "v1"
|
|
||||||
case "Deployment", "StatefulSet", "DaemonSet":
|
|
||||||
policy.Spec.ResourceSelectors[0].APIVersion = "apps/v1"
|
|
||||||
}
|
|
||||||
_, policyErr := KarmadaClient.PolicyV1alpha1().PropagationPolicies(propagationPolicy.Namespace).Create(context.TODO(), policy, v1.CreateOptions{})
|
|
||||||
if policyErr != nil {
|
|
||||||
return policyErr
|
|
||||||
}
|
|
||||||
//数据库记录policy实例
|
|
||||||
_, err = DB.Exec(`INSERT INTO joint_domain.propagation_policy(template_id, policy_name, namespace, resource_name) VALUES(?,?,?,?)`, propagationPolicy.TemplateId, propagationPolicy.Name, propagationPolicy.Namespace, propagationPolicy.ResourceName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListPropagationPolicies 查询集群分发策略列表
|
|
||||||
// @Summary 查询分发策略模板
|
|
||||||
// @Description 查询分发策略模板
|
|
||||||
// @Tags propagationPolicy
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param label_key query string false "标签Key"
|
|
||||||
// @Param label_value query string false "标签Value"
|
|
||||||
// @Param policy_name query string false "policy_name"
|
|
||||||
// @Param pageNum query int true "页码"
|
|
||||||
// @Param pageSize query int true "每页数量"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 500
|
|
||||||
// @Router /api/v1/propagationPolicy/list [get]
|
|
||||||
func ListPropagationPolicies(c *gin.Context) {
|
|
||||||
|
|
||||||
labelKey := c.Query("label_key")
|
|
||||||
labelValue := c.Query("label_value")
|
|
||||||
policyName := c.Query("policy_name")
|
|
||||||
|
|
||||||
opts := v1.ListOptions{
|
|
||||||
TypeMeta: v1.TypeMeta{},
|
|
||||||
LabelSelector: labelKey + "=" + labelValue,
|
|
||||||
FieldSelector: "",
|
|
||||||
Watch: false,
|
|
||||||
AllowWatchBookmarks: false,
|
|
||||||
ResourceVersion: "",
|
|
||||||
ResourceVersionMatch: "",
|
|
||||||
TimeoutSeconds: nil,
|
|
||||||
Limit: 0,
|
|
||||||
Continue: "",
|
|
||||||
}
|
|
||||||
nsResult, _ := ClientSet.CoreV1().Namespaces().List(context.TODO(), opts)
|
|
||||||
proPolicyList := make([]PropagationPolicy, 0)
|
|
||||||
for i := 0; i < len(nsResult.Items); i++ {
|
|
||||||
|
|
||||||
propagationList, _ := KarmadaClient.PolicyV1alpha1().PropagationPolicies(nsResult.Items[i].Name).List(context.TODO(), v1.ListOptions{})
|
|
||||||
|
|
||||||
for i := 0; i < len(propagationList.Items); i++ {
|
|
||||||
name := propagationList.Items[i].ObjectMeta.Name
|
|
||||||
namespace := propagationList.Items[i].ObjectMeta.Namespace
|
|
||||||
labels := &propagationList.Items[i].Spec.Placement.ClusterAffinity.LabelSelector.MatchLabels
|
|
||||||
resourceName := propagationList.Items[i].Spec.ResourceSelectors[0].Name
|
|
||||||
kind := propagationList.Items[i].Spec.ResourceSelectors[0].Kind
|
|
||||||
rdp := string(propagationList.Items[i].Spec.Placement.ReplicaScheduling.ReplicaDivisionPreference)
|
|
||||||
rst := string(propagationList.Items[i].Spec.Placement.ReplicaScheduling.ReplicaSchedulingType)
|
|
||||||
|
|
||||||
//判断是否跨域
|
|
||||||
//deployJson := GetDeployFromOS(namespace, resourceName, "")
|
|
||||||
//hits, _ := deployJson.Get("hits").Get("hits").Array()
|
|
||||||
//
|
|
||||||
//var domainSet []string
|
|
||||||
//for i := 0; i < len(hits); i++ {
|
|
||||||
// cluster, _ := deployJson.Get("hits").Get("hits").GetIndex(i).Get("_source").Get("metadata").Get("annotations").Get("resource.karmada.io/cached-from-cluster").String()
|
|
||||||
// //获取域列表
|
|
||||||
// rows, _ := DB.Query("select domain_name,domain_id,cluster_name from domain_cluster where cluster_name = ?", cluster)
|
|
||||||
// for rows.Next() {
|
|
||||||
// var domainName string
|
|
||||||
// var clusterName string
|
|
||||||
// var domainId int32
|
|
||||||
// rows.Scan(&domainName, &domainId, &clusterName)
|
|
||||||
// domainSet = append(domainSet, domainName)
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//domainSet = removeDuplicateArr(domainSet)
|
|
||||||
//crossDomain := false
|
|
||||||
//if len(domainSet) > 1 {
|
|
||||||
// crossDomain = true
|
|
||||||
//}
|
|
||||||
|
|
||||||
pp := PropagationPolicy{
|
|
||||||
Name: name,
|
|
||||||
CrossDomain: true,
|
|
||||||
Namespace: namespace,
|
|
||||||
Labels: *labels,
|
|
||||||
ResourceName: resourceName,
|
|
||||||
Kind: kind,
|
|
||||||
ReplicaDivisionPreference: &rdp,
|
|
||||||
ReplicaSchedulingType: &rst,
|
|
||||||
}
|
|
||||||
|
|
||||||
//获取分发实例对应的模板信息
|
|
||||||
sqlPolicy := "SELECT ppt.template_id, ppt.template_name FROM joint_domain.propagation_policy pp,joint_domain.propagation_policy_template ppt WHERE pp.template_id =ppt.template_id and pp.policy_name= ?"
|
|
||||||
err := DB.QueryRow(sqlPolicy, name).Scan(&pp.TemplateId, &pp.TemplateName)
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "query failed!", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
//模糊查询
|
|
||||||
if strings.Contains(pp.Name, policyName) {
|
|
||||||
proPolicyList = append(proPolicyList, pp)
|
|
||||||
} else {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
total := len(proPolicyList)
|
|
||||||
page := &Page[PropagationPolicy]{}
|
|
||||||
page.List = proPolicyList
|
|
||||||
pageNum := c.Query("pageNum")
|
|
||||||
pageSize := c.Query("pageSize")
|
|
||||||
data := Paginator(page, int64(total), pageNum, pageSize)
|
|
||||||
Response(c, http.StatusOK, "success", data)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdatePropagationPolicies 更新集群分发策略
|
|
||||||
func UpdatePropagationPolicies(c *gin.Context) {
|
|
||||||
var propagationPolicy policyv1alpha1.PropagationPolicy
|
|
||||||
_, err := KarmadaClient.PolicyV1alpha1().PropagationPolicies(propagationPolicy.Namespace).Update(context.TODO(), &propagationPolicy, v1.UpdateOptions{})
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "update propagationPolicy failed", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
Response(c, http.StatusOK, "success", nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeletePropagationPolicies 删除集群分发策略
|
|
||||||
func DeletePropagationPolicies(namespace string, policyName string) {
|
|
||||||
err := KarmadaClient.PolicyV1alpha1().PropagationPolicies(namespace).Delete(context.TODO(), policyName, v1.DeleteOptions{})
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 删除数据库记录policy实例
|
|
||||||
_, err = DB.Exec(`delete from joint_domain.propagation_policy where namespace = ? and policy_name = ?`, namespace, policyName)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,138 +0,0 @@
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
type PropagationPolicyTemplate struct {
|
|
||||||
TemplateId int32 `json:"template_id"`
|
|
||||||
TemplateName string `json:"template_name"`
|
|
||||||
Labels *string `json:"labels"`
|
|
||||||
ScheduleType *string `json:"schedule_type"`
|
|
||||||
DivisionPreference *string `json:"division_preference"`
|
|
||||||
ClusterPreference *string `json:"cluster_preference"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary 创建策略模板
|
|
||||||
// @Description 创建策略模板
|
|
||||||
// @Tags propagationPolicyTemplate
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param param body PropagationPolicyTemplate true "json"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 500
|
|
||||||
// @Router /api/v1/propagationPolicyTemplate/create [post]
|
|
||||||
func CreatePropagationPolicyTemplate(c *gin.Context) {
|
|
||||||
var j PropagationPolicyTemplate
|
|
||||||
if err := c.BindJSON(&j); err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "invalid request params.", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, err := DB.Exec(`INSERT propagation_policy_template(template_name, labels, schedule_type, division_preference, cluster_preference)VALUES (?,?,?,?,?)`, j.TemplateName, j.Labels, j.ScheduleType, j.DivisionPreference, j.ClusterPreference)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "insert failed", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
Response(c, http.StatusOK, "success", nil)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary 删除策略模板
|
|
||||||
// @Description 删除策略模板
|
|
||||||
// @Tags propagationPolicyTemplate
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param param body PropagationPolicyTemplate true "json"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 500
|
|
||||||
// @Router /api/v1/propagationPolicyTemplate/delete [post]
|
|
||||||
func DeletePropagationPolicyTemplate(c *gin.Context) {
|
|
||||||
var j PropagationPolicyTemplate
|
|
||||||
if err := c.BindJSON(&j); err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "invalid request params.", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := DB.Exec(`DELETE FROM propagation_policy_template WHERE template_id = ?`, j.TemplateId)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "insert failed", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
Response(c, http.StatusOK, "success", nil)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary 查询分发策略模板
|
|
||||||
// @Description 查询分发策略模板
|
|
||||||
// @Tags propagationPolicyTemplate
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param template_name query string false "模板名称"
|
|
||||||
// @Param pageNum query int true "页码"
|
|
||||||
// @Param pageSize query int true "每页数量"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 500
|
|
||||||
// @Router /api/v1/propagationPolicyTemplate/list [get]
|
|
||||||
func ListPropagationPolicyTemplate(c *gin.Context) {
|
|
||||||
|
|
||||||
pptName := c.Query("template_name")
|
|
||||||
|
|
||||||
PptList := make([]PropagationPolicyTemplate, 0)
|
|
||||||
sqlStr := `SELECT template_name, labels, schedule_type, division_preference, cluster_preference ,template_id
|
|
||||||
FROM propagation_policy_template
|
|
||||||
where template_name like ?`
|
|
||||||
rows, err := DB.Query(sqlStr, "%"+pptName+"%")
|
|
||||||
defer rows.Close()
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "query failed", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for rows.Next() {
|
|
||||||
var ppt PropagationPolicyTemplate
|
|
||||||
err := rows.Scan(&ppt.TemplateName, &ppt.Labels, &ppt.ScheduleType, &ppt.DivisionPreference, &ppt.ClusterPreference, &ppt.TemplateId)
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "query failed!", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
PptList = append(PptList, ppt)
|
|
||||||
}
|
|
||||||
|
|
||||||
total := len(PptList)
|
|
||||||
page := &Page[PropagationPolicyTemplate]{}
|
|
||||||
page.List = PptList
|
|
||||||
pageNum := c.Query("pageNum")
|
|
||||||
pageSize := c.Query("pageSize")
|
|
||||||
data := Paginator(page, int64(total), pageNum, pageSize)
|
|
||||||
Response(c, http.StatusOK, "success", data)
|
|
||||||
//Response(c, http.StatusOK, "success", PptList)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary 更新策略模板
|
|
||||||
// @Description 更新策略模板
|
|
||||||
// @Tags propagationPolicyTemplate
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param param body PropagationPolicyTemplate true "json"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 500
|
|
||||||
// @Router /api/v1/propagationPolicyTemplate/update [post]
|
|
||||||
func UpdatePropagationPolicyTemplate(c *gin.Context) {
|
|
||||||
var j PropagationPolicyTemplate
|
|
||||||
if err := c.BindJSON(&j); err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "invalid request params.", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, err := DB.Exec(`UPDATE propagation_policy_template SET template_name = ?, labels = ?, schedule_type = ?, division_preference = ?, cluster_preference = ? WHERE template_id = ?`, j.TemplateName, j.Labels, j.ScheduleType, j.DivisionPreference, j.ClusterPreference, j.TemplateId)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "update failed", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
Response(c, http.StatusOK, "success", nil)
|
|
||||||
|
|
||||||
}
|
|
288
app/router.go
288
app/router.go
|
@ -1,288 +0,0 @@
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"crypto/tls"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/go-redis/redis/v8"
|
|
||||||
_ "github.com/go-sql-driver/mysql"
|
|
||||||
"github.com/jmoiron/sqlx"
|
|
||||||
karmadaclientset "github.com/karmada-io/karmada/pkg/generated/clientset/versioned"
|
|
||||||
"github.com/karmada-io/karmada/pkg/karmadactl"
|
|
||||||
"github.com/karmada-io/karmada/pkg/karmadactl/cmdinit/utils"
|
|
||||||
"github.com/opensearch-project/opensearch-go/v2"
|
|
||||||
swaggerFiles "github.com/swaggo/files"
|
|
||||||
ginSwagger "github.com/swaggo/gin-swagger"
|
|
||||||
v2 "gorm.io/driver/mysql"
|
|
||||||
"gorm.io/gorm"
|
|
||||||
"gorm.io/gorm/schema"
|
|
||||||
_ "jcc-schedule/docs"
|
|
||||||
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
|
||||||
kubeclient "k8s.io/client-go/kubernetes"
|
|
||||||
"k8s.io/client-go/rest"
|
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
var DB *sqlx.DB
|
|
||||||
var DBPCM *sqlx.DB
|
|
||||||
var KarmadaClient *karmadaclientset.Clientset
|
|
||||||
var KarmadaConfig karmadactl.KarmadaConfig
|
|
||||||
var ControlPlaneRestConfig *rest.Config
|
|
||||||
var ClientSet *kubeclient.Clientset
|
|
||||||
var OpenSearchClient *opensearch.Client
|
|
||||||
var CrDClient *clientset.Clientset
|
|
||||||
var MyRedis *redis.Client
|
|
||||||
var Gorm *gorm.DB
|
|
||||||
var ConfigNacos = GetNacosConfig()
|
|
||||||
|
|
||||||
func InitRouter() *gin.Engine {
|
|
||||||
|
|
||||||
r := gin.New()
|
|
||||||
r.Use(gin.Logger())
|
|
||||||
r.Use(gin.Recovery())
|
|
||||||
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
|
|
||||||
|
|
||||||
//mysql连接池
|
|
||||||
dsn := ConfigNacos.Mysql.Url
|
|
||||||
dsnPCM := ConfigNacos.Mysql.UrlPCM
|
|
||||||
|
|
||||||
DB, _ = sqlx.Connect("mysql", dsn)
|
|
||||||
DBPCM, _ = sqlx.Connect("mysql", dsnPCM)
|
|
||||||
|
|
||||||
DB.SetMaxOpenConns(ConfigNacos.Mysql.MaxOpenConn)
|
|
||||||
DB.SetMaxIdleConns(ConfigNacos.Mysql.MaxIdleConn)
|
|
||||||
|
|
||||||
DBPCM.SetMaxOpenConns(int(ConfigNacos.Mysql.MaxOpenConn))
|
|
||||||
DBPCM.SetMaxIdleConns(int(ConfigNacos.Mysql.MaxIdleConn))
|
|
||||||
|
|
||||||
//启动Gorm支持
|
|
||||||
gorm, _ := gorm.Open(v2.Open(dsn), &gorm.Config{
|
|
||||||
NamingStrategy: schema.NamingStrategy{
|
|
||||||
SingularTable: true, // 使用单数表名,启用该选项,此时,`User` 的表名应该是 `t_user`
|
|
||||||
},
|
|
||||||
})
|
|
||||||
Gorm = gorm
|
|
||||||
//redis连接
|
|
||||||
MyRedis = redis.NewClient(&redis.Options{
|
|
||||||
Addr: ConfigNacos.Redis.Host,
|
|
||||||
Password: ConfigNacos.Redis.Password, // no password set
|
|
||||||
DB: ConfigNacos.Redis.DB, // use default DB
|
|
||||||
})
|
|
||||||
MyRedis.Ping(context.Background()).Result()
|
|
||||||
|
|
||||||
//Karmada Client
|
|
||||||
dir, _ := os.Getwd()
|
|
||||||
KarmadaConfig = karmadactl.NewKarmadaConfig(clientcmd.NewDefaultPathOptions())
|
|
||||||
KarmadaConfig.GetClientConfig("", dir+"/karmadaConfig/karmada-host")
|
|
||||||
ControlPlaneRestConfig, _ = KarmadaConfig.GetRestConfig("", dir+"/karmadaConfig/karmada-host")
|
|
||||||
KarmadaClient = karmadaclientset.NewForConfigOrDie(ControlPlaneRestConfig)
|
|
||||||
ClientSet, _ = utils.NewClientSet(ControlPlaneRestConfig)
|
|
||||||
CrDClient, _ = utils.NewCRDsClient(ControlPlaneRestConfig)
|
|
||||||
// 初始化OpenSearch客户端
|
|
||||||
OpenSearchClient, _ = opensearch.NewClient(opensearch.Config{
|
|
||||||
Transport: &http.Transport{
|
|
||||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
|
||||||
},
|
|
||||||
Addresses: []string{ConfigNacos.OpenSearch.Url},
|
|
||||||
Username: ConfigNacos.OpenSearch.UserName,
|
|
||||||
Password: ConfigNacos.OpenSearch.PassWord,
|
|
||||||
})
|
|
||||||
|
|
||||||
//创建定时任务
|
|
||||||
|
|
||||||
//api分组
|
|
||||||
api := r.Group("/api")
|
|
||||||
v1 := api.Group("/v1")
|
|
||||||
{
|
|
||||||
//LabelType
|
|
||||||
labelType := v1.Group("labelType")
|
|
||||||
labelType.POST("/create", CreateLabelType)
|
|
||||||
labelType.POST("/delete", DeleteLabelType)
|
|
||||||
labelType.GET("/list", ListLabelType)
|
|
||||||
labelType.POST("/update", UpdateLabelType)
|
|
||||||
|
|
||||||
//Label
|
|
||||||
label := v1.Group("label")
|
|
||||||
label.POST("/create", CreateLabel)
|
|
||||||
label.POST("/delete", DeleteLabel)
|
|
||||||
label.GET("/list", ListLabel)
|
|
||||||
label.POST("/update", UpdateLabel)
|
|
||||||
|
|
||||||
//Namespace
|
|
||||||
namespace := v1.Group("namespace")
|
|
||||||
namespace.GET("/list", ListNamespace)
|
|
||||||
namespace.GET("/listPodsCount", ListPodsCount)
|
|
||||||
namespace.GET("/isExist", IsExistNamespace)
|
|
||||||
namespace.GET("/list/name", ListName)
|
|
||||||
namespace.GET("/listFromCluster", ListNamespaceFromCluster)
|
|
||||||
namespace.GET("/describe", DescribeNamespace)
|
|
||||||
namespace.GET("/detail", DetailNamespace)
|
|
||||||
namespace.POST("/create", CreateNamespace)
|
|
||||||
namespace.DELETE("/delete/:clusterName/:name/:label", DeleteNamespace)
|
|
||||||
namespace.PUT("/update", UpdateNamespace)
|
|
||||||
namespace.GET("/getBatchMetrics", GetBatchNamespaceMetrics)
|
|
||||||
namespace.GET("/getMetrics", GetNamespaceMetrics)
|
|
||||||
namespace.GET("/resources", GetNamespaceResources)
|
|
||||||
namespace.GET("/nameSpaceByRegion", ListNamespaceByDomain)
|
|
||||||
|
|
||||||
//Domain
|
|
||||||
domain := v1.Group("domain")
|
|
||||||
domain.POST("/create", CreateDomain)
|
|
||||||
domain.GET("/describe", DescribeDomain)
|
|
||||||
domain.GET("/list", ListDomain)
|
|
||||||
domain.GET("/listByDeployment", ListByDeployment)
|
|
||||||
domain.GET("/listClusters", ListClusters)
|
|
||||||
domain.GET("/usedRate", QueryDomainUsedRate)
|
|
||||||
|
|
||||||
//Pod
|
|
||||||
pod := v1.Group("pod")
|
|
||||||
pod.GET("/list", ListPod)
|
|
||||||
pod.GET("/all", ALLPod)
|
|
||||||
pod.GET("/isExist", IsExist)
|
|
||||||
pod.GET("/detail", PodDetail)
|
|
||||||
pod.GET("/cpuAndMemory", PodMetrics)
|
|
||||||
pod.POST("/create", CreatePod)
|
|
||||||
pod.DELETE("/delete/:clusterName/:namespace/:name/:label", DeletePod)
|
|
||||||
pod.PUT("/updatePod", UpdatePod)
|
|
||||||
pod.PUT("/detail", GetPodDetail)
|
|
||||||
pod.GET("/listFromDeployment", ListPodFromDeployment)
|
|
||||||
pod.GET("/listFromNode", ListPodFromNode)
|
|
||||||
pod.GET("/pod/metrics", ListPodFromNode)
|
|
||||||
pod.GET("/metrics", GetMetrics)
|
|
||||||
|
|
||||||
//Cluster
|
|
||||||
cluster := v1.Group("cluster")
|
|
||||||
cluster.GET("/list", ListCluster)
|
|
||||||
cluster.GET("/listWithLabel", ListClusterWithLabel)
|
|
||||||
cluster.POST("/tag", TagCluster)
|
|
||||||
cluster.POST("/unTag", UnTagCluster)
|
|
||||||
cluster.POST("/join", Join)
|
|
||||||
cluster.DELETE("/unJoin", UnJoin)
|
|
||||||
cluster.GET("/listByDomain", ListByDomain)
|
|
||||||
cluster.POST("/createCluster", CreateCluster)
|
|
||||||
cluster.GET("/listFree", ListFreeCluster)
|
|
||||||
cluster.POST("/proxyJoinCluster", ProxyJoinCluster)
|
|
||||||
cluster.GET("/:clusterName", ClusterExist)
|
|
||||||
|
|
||||||
//Node
|
|
||||||
node := v1.Group("node")
|
|
||||||
node.PUT("/update", UpdateNode)
|
|
||||||
node.GET("/list", ListNode)
|
|
||||||
node.GET("/count", NodeCount)
|
|
||||||
node.GET("/edge", ListEdgeNode)
|
|
||||||
node.GET("/metrics", ListNodeMetrics)
|
|
||||||
node.GET("/detail/pod", queryNodeInfo)
|
|
||||||
node.GET("/detail", DetailNode)
|
|
||||||
node.GET("/getNodeMetrics1h", GetNodeMetrics1h)
|
|
||||||
node.GET("/getNodeMetrics8h", GetNodeMetrics8h)
|
|
||||||
|
|
||||||
//DeploymentParam
|
|
||||||
deployment := v1.Group("deployments")
|
|
||||||
deployment.POST("/create", CreateDeployment)
|
|
||||||
deployment.DELETE("/delete/:clusterName/:namespace/:name/:label", DeleteDeployment)
|
|
||||||
deployment.GET("/list", ListDeployment)
|
|
||||||
deployment.GET("/listFromCluster", ListDeploymentFromCluster)
|
|
||||||
deployment.GET("/describe", DescribeDeployment)
|
|
||||||
deployment.GET("/detail", DetailDeployment)
|
|
||||||
deployment.GET("/listGlobal", ListClusterDeployment)
|
|
||||||
deployment.PUT("/redeploy", Redeploy)
|
|
||||||
deployment.PUT("/update", UpdateDeployment)
|
|
||||||
deployment.GET("/hpa", getHpa)
|
|
||||||
deployment.PUT("/autoScaling", AutoScaling)
|
|
||||||
deployment.POST("/hpa/create", CreateHpa)
|
|
||||||
deployment.GET("/getMetrics", GetDeploymentMetrics)
|
|
||||||
deployment.GET("isExist", IsExistDeployment)
|
|
||||||
deployment.GET("/controllerVersions", ControllerRevisions)
|
|
||||||
deployment.GET("/replicaSets", ListReplicaSets)
|
|
||||||
deployment.PUT("/back", BackVersion)
|
|
||||||
|
|
||||||
//daemonSet
|
|
||||||
daemonSet := v1.Group("daemonsets")
|
|
||||||
daemonSet.GET("/detail", DetailDaemonSet)
|
|
||||||
daemonSet.POST("/create", CreateDaemonSet)
|
|
||||||
daemonSet.DELETE("/delete/:clusterName/:namespace/:name/:label", DeleteDaemonSet)
|
|
||||||
daemonSet.PUT("/update", UpdateDaemonSet)
|
|
||||||
daemonSet.GET("isExist", IsExistDaemonSet)
|
|
||||||
daemonSet.GET("/listFromCluster", ListDaemonSetFromCluster)
|
|
||||||
daemonSet.PUT("/back", BackDaemonSetVersion)
|
|
||||||
|
|
||||||
//statefulSet
|
|
||||||
statefulSet := v1.Group("statefulsets")
|
|
||||||
statefulSet.GET("/listFromCluster", ListStatefulSetFromCluster)
|
|
||||||
statefulSet.GET("/detail", DetailStatefulSet)
|
|
||||||
statefulSet.DELETE("/delete/:clusterName/:namespace/:name/:label", DeleteStatefulSet)
|
|
||||||
statefulSet.POST("/create", CreateStatefulSet)
|
|
||||||
statefulSet.PUT("/update", UpdateStatefulSet)
|
|
||||||
statefulSet.GET("/isExist", IsExistStatefulSet)
|
|
||||||
statefulSet.PUT("/back", BackStatefulSetVersion)
|
|
||||||
|
|
||||||
// storage
|
|
||||||
storage := v1.Group("storage")
|
|
||||||
storage.POST("/pv", CreatePV)
|
|
||||||
storage.POST("/pvc", CreatePVC)
|
|
||||||
storage.PUT("/pvc", UpdatePVC)
|
|
||||||
storage.DELETE("/pvc/:clusterName/:namespace/:name/:label", DeletePVC)
|
|
||||||
storage.GET("/pvc/detail", DetailPVC)
|
|
||||||
storage.GET("/pvc/isExist", IsExistPVC)
|
|
||||||
storage.GET("/pv", ListPV)
|
|
||||||
storage.GET("/pvc", ListPVC)
|
|
||||||
storage.GET("/list", ListStorageClass)
|
|
||||||
|
|
||||||
// storage
|
|
||||||
crd := v1.Group("crd")
|
|
||||||
crd.GET("/list/:clusterName", ListCRD)
|
|
||||||
crd.GET("/detail/:clusterName/:namespace/:kind", DetailCRD)
|
|
||||||
crd.GET("/create", CreateCRD)
|
|
||||||
|
|
||||||
// configMap
|
|
||||||
configMap := v1.Group("config")
|
|
||||||
configMap.POST("/configMap/create", CreateConfigMap)
|
|
||||||
configMap.GET("/secret/list", ListSecret)
|
|
||||||
configMap.GET("/configMap/list", ListConfigMap)
|
|
||||||
|
|
||||||
// service
|
|
||||||
service := v1.Group("service")
|
|
||||||
service.POST("/create", CreateService)
|
|
||||||
service.PUT("/update", UpdateService)
|
|
||||||
service.DELETE("/delete/:clusterName/:namespace/:name/:label", DeleteService)
|
|
||||||
service.GET("/all", AllService)
|
|
||||||
service.GET("/list", ListService)
|
|
||||||
service.GET("/isExist", IsExistService)
|
|
||||||
service.GET("/detail", DetailService)
|
|
||||||
|
|
||||||
//PropagationPolicyTemplate
|
|
||||||
propagationPolicyTemplate := v1.Group("propagationPolicyTemplate")
|
|
||||||
propagationPolicyTemplate.POST("/create", CreatePropagationPolicyTemplate)
|
|
||||||
propagationPolicyTemplate.GET("/list", ListPropagationPolicyTemplate)
|
|
||||||
propagationPolicyTemplate.POST("/delete", DeletePropagationPolicyTemplate)
|
|
||||||
propagationPolicyTemplate.POST("/update", UpdatePropagationPolicyTemplate)
|
|
||||||
|
|
||||||
//PropagationPolicies
|
|
||||||
propagationPolicy := v1.Group("propagationPolicy")
|
|
||||||
propagationPolicy.GET("/list", ListPropagationPolicies)
|
|
||||||
propagationPolicy.POST("/update", UpdatePropagationPolicies)
|
|
||||||
|
|
||||||
//OverridePolicies
|
|
||||||
overridePolicy := v1.Group("overridePolicy")
|
|
||||||
overridePolicy.GET("/list", ListOverridePolicies)
|
|
||||||
overridePolicy.POST("/create", CreateOverridePolicies)
|
|
||||||
overridePolicy.POST("/delete", DeleteOverridePolicies)
|
|
||||||
overridePolicy.POST("/update", UpdateOverridePolicies)
|
|
||||||
|
|
||||||
//Overview
|
|
||||||
overview := v1.Group("resource")
|
|
||||||
overview.GET("/count", ResourceCount)
|
|
||||||
overview.GET("/getApiServerMetrics", GetApiServerMetrics)
|
|
||||||
overview.GET("/getNodeMetrics", GetNodeMetrics)
|
|
||||||
overview.GET("/getClusterMetrics", GetClusterMetrics)
|
|
||||||
overview.GET("/getScheduleMetrics", GetScheduleMetrics)
|
|
||||||
overview.GET("/getOverallMetrics", GetOverallMetrics)
|
|
||||||
overview.GET("/getServerResources", GetServerResources)
|
|
||||||
overview.GET("/listClusterMetrics", ListClusterMetrics)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return r
|
|
||||||
}
|
|
179
app/service.go
179
app/service.go
|
@ -1,179 +0,0 @@
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
v1 "k8s.io/api/core/v1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"net/http"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ServiceParam struct {
|
|
||||||
Service v1.Service `json:"service"`
|
|
||||||
TemplateId string `json:"templateId"`
|
|
||||||
ClusterName []string `json:"clusterName"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ServiceObject struct {
|
|
||||||
Kind string `json:"kind"`
|
|
||||||
ApiVersion string `json:"apiVersion"`
|
|
||||||
Metadata string `json:"metadata"`
|
|
||||||
ResourceVersion string `json:"resourceVersion"`
|
|
||||||
Items []v1.Service `json:"items"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateService 创建服务
|
|
||||||
// @Summary 创建服务
|
|
||||||
// @Description 创建服务
|
|
||||||
// @Tags service
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 500
|
|
||||||
// @Router /api/v1/service/create [post]
|
|
||||||
func CreateService(c *gin.Context) {
|
|
||||||
var sRequest ServiceParam
|
|
||||||
if err := c.BindJSON(&sRequest); err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "invalid request params.", "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if sRequest.Service.Labels != nil && sRequest.Service.Labels["jcce"] == "true" {
|
|
||||||
_, serviceErr := ClientSet.CoreV1().Services(sRequest.Service.Namespace).Create(context.TODO(), &sRequest.Service, metav1.CreateOptions{})
|
|
||||||
if serviceErr != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "create service error.", serviceErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 创建调度策略实例
|
|
||||||
err := CreatePropagationPolicies(PropagationPolicy{
|
|
||||||
ClusterName: sRequest.ClusterName,
|
|
||||||
TemplateId: sRequest.TemplateId,
|
|
||||||
ResourceName: sRequest.Service.Name,
|
|
||||||
Name: "service" + "." + sRequest.Service.Namespace + "." + sRequest.Service.Name,
|
|
||||||
Namespace: sRequest.Service.Namespace,
|
|
||||||
Kind: "Service",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "create service error.", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
result := PostObject(serviceConst, sRequest.ClusterName[0], sRequest.Service.Namespace, sRequest.Service.Name, sRequest.Service)
|
|
||||||
if result.Error() != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "failed", result.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Response(c, http.StatusOK, "success", nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateService 更新服务
|
|
||||||
func UpdateService(c *gin.Context) {
|
|
||||||
var sevice v1.Service
|
|
||||||
if err := c.BindJSON(&sevice); err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "invalid request params.", "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ClientSet.CoreV1().Services(sevice.Namespace).Update(context.TODO(), &sevice, metav1.UpdateOptions{})
|
|
||||||
Response(c, http.StatusOK, "success", nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteService 删除服务
|
|
||||||
func DeleteService(c *gin.Context) {
|
|
||||||
clusterName := c.Param("clusterName")
|
|
||||||
namespace := c.Param("namespace")
|
|
||||||
name := c.Param("name")
|
|
||||||
label := c.Param("label")
|
|
||||||
if strings.EqualFold(label, "jcce") {
|
|
||||||
err := ClientSet.CoreV1().Services(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{})
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "delete service failed", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 删除调度策略
|
|
||||||
DeletePropagationPolicies(namespace, "service"+"."+namespace+"."+name)
|
|
||||||
} else {
|
|
||||||
result := DeleteObject(detailServiceConst, clusterName, namespace, name)
|
|
||||||
if result.Error() != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "delete service failed", result.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Response(c, http.StatusOK, "success", nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsExistService 判断输入service名称是否重复
|
|
||||||
func IsExistService(c *gin.Context) {
|
|
||||||
clusterName := c.Query("clusterName")
|
|
||||||
namespace := c.Query("namespace")
|
|
||||||
name := c.Query("name")
|
|
||||||
result := SearchObject(detailServiceConst, clusterName, namespace, name)
|
|
||||||
if result.Error() != nil {
|
|
||||||
Response(c, http.StatusOK, "success", false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
raw, _ := result.Raw()
|
|
||||||
var service v1.Service
|
|
||||||
json.Unmarshal(raw, &service)
|
|
||||||
if len(service.Name) == 0 {
|
|
||||||
Response(c, http.StatusOK, "success", false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
Response(c, http.StatusOK, "success", true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DetailService 查询service详情
|
|
||||||
func DetailService(c *gin.Context) {
|
|
||||||
clusterName := c.Query("clusterName")
|
|
||||||
namespace := c.Query("namespace")
|
|
||||||
name := c.Query("name")
|
|
||||||
result := SearchObject(detailServiceConst, clusterName, namespace, name)
|
|
||||||
if result.Error() != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "failed", result.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
raw, _ := result.Raw()
|
|
||||||
var service v1.Service
|
|
||||||
json.Unmarshal(raw, &service)
|
|
||||||
Response(c, http.StatusOK, "success", service)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListService 查询命名空间下的service列表
|
|
||||||
func ListService(c *gin.Context) {
|
|
||||||
clusterName := c.Query("clusterName")
|
|
||||||
namespace := c.Query("namespace")
|
|
||||||
result := SearchObject(serviceConst, clusterName, namespace, "")
|
|
||||||
if result.Error() != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "failed", result.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
raw, _ := result.Raw()
|
|
||||||
var scRes SCRes
|
|
||||||
json.Unmarshal(raw, &scRes)
|
|
||||||
Response(c, http.StatusOK, "success", scRes.Items)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AllService 查询service列表
|
|
||||||
func AllService(c *gin.Context) {
|
|
||||||
clusterName := c.Query("clusterName")
|
|
||||||
pageNum := c.Query("pageNum")
|
|
||||||
pageSize := c.Query("pageSize")
|
|
||||||
result := SearchObject(allServiceConst, clusterName, "", "")
|
|
||||||
if result.Error() != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "failed", result.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
raw, _ := result.Raw()
|
|
||||||
var serviceRes ServiceObject
|
|
||||||
json.Unmarshal(raw, &serviceRes)
|
|
||||||
page := &Page[v1.Service]{}
|
|
||||||
page.List = serviceRes.Items
|
|
||||||
sort.SliceStable(serviceRes.Items, func(i, j int) bool {
|
|
||||||
return serviceRes.Items[i].CreationTimestamp.Time.After(serviceRes.Items[j].CreationTimestamp.Time)
|
|
||||||
})
|
|
||||||
data := Paginator(page, int64(len(serviceRes.Items)), pageNum, pageSize)
|
|
||||||
Response(c, http.StatusOK, "success", data)
|
|
||||||
}
|
|
388
app/storage.go
388
app/storage.go
|
@ -1,388 +0,0 @@
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
v1 "k8s.io/api/core/v1"
|
|
||||||
"k8s.io/api/storage/v1beta1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"net/http"
|
|
||||||
"reflect"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type PVRes struct {
|
|
||||||
Kind string `json:"kind"`
|
|
||||||
ApiVersion string `json:"apiVersion"`
|
|
||||||
Metadata string `json:"metadata"`
|
|
||||||
ResourceVersion string `json:"resourceVersion"`
|
|
||||||
Items []v1.PersistentVolume `json:"items"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type PVCObject struct {
|
|
||||||
Kind string `json:"kind"`
|
|
||||||
ApiVersion string `json:"apiVersion"`
|
|
||||||
Metadata string `json:"metadata"`
|
|
||||||
ResourceVersion string `json:"resourceVersion"`
|
|
||||||
Items []v1.PersistentVolumeClaim `json:"items"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type SCRes struct {
|
|
||||||
Kind string `json:"kind"`
|
|
||||||
ApiVersion string `json:"apiVersion"`
|
|
||||||
Metadata string `json:"metadata"`
|
|
||||||
ResourceVersion string `json:"resourceVersion"`
|
|
||||||
Items []v1beta1.StorageClass `json:"items"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type PersistentVolume struct {
|
|
||||||
TemplateId string `json:"templateId"`
|
|
||||||
ClusterName []string `json:"clusterName"`
|
|
||||||
PersistentVolume v1.PersistentVolume `json:"persistentVolume"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type PersistentVolume1 struct {
|
|
||||||
TemplateId string `json:"templateId"`
|
|
||||||
ClusterName []string `json:"clusterName"`
|
|
||||||
PersistentVolume JSONData1 `json:"persistentVolume"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type JSONData struct {
|
|
||||||
APIVersion string `json:"apiVersion"`
|
|
||||||
Kind string `json:"kind"`
|
|
||||||
Metadata struct {
|
|
||||||
Namespace string `json:"namespace"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Labels struct {
|
|
||||||
} `json:"labels"`
|
|
||||||
Annotations struct {
|
|
||||||
KubesphereIoCreator string `json:"kubesphere.io/creator"`
|
|
||||||
} `json:"annotations"`
|
|
||||||
} `json:"metadata"`
|
|
||||||
Spec struct {
|
|
||||||
AccessModes []string `json:"accessModes"`
|
|
||||||
Resources struct {
|
|
||||||
Requests struct {
|
|
||||||
Storage string `json:"storage"`
|
|
||||||
} `json:"requests"`
|
|
||||||
} `json:"resources"`
|
|
||||||
StorageClassName string `json:"storageClassName"`
|
|
||||||
} `json:"spec"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type JSONData1 struct {
|
|
||||||
APIVersion string `json:"apiVersion"`
|
|
||||||
Kind string `json:"kind"`
|
|
||||||
Metadata struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
} `json:"metadata"`
|
|
||||||
Spec struct {
|
|
||||||
Capacity struct {
|
|
||||||
Storage string `json:"storage"`
|
|
||||||
} `json:"capacity"`
|
|
||||||
VolumeMode string `json:"volumeMode"`
|
|
||||||
AccessModes []string `json:"accessModes"`
|
|
||||||
Nfs struct {
|
|
||||||
Path string `json:"path"`
|
|
||||||
Server string `json:"server"`
|
|
||||||
} `json:"nfs"`
|
|
||||||
} `json:"spec"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type PersistentVolumeClaim struct {
|
|
||||||
TemplateId string `json:"templateId"`
|
|
||||||
ClusterName []string `json:"clusterName"`
|
|
||||||
PersistentVolumeClaim v1.PersistentVolumeClaim `json:"persistentVolumeClaim"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreatePV 创建PV
|
|
||||||
type PersistentVolumeClaim1 struct {
|
|
||||||
TemplateId string `json:"templateId"`
|
|
||||||
ClusterName []string `json:"clusterName"`
|
|
||||||
PersistentVolumeClaim JSONData `json:"persistentVolumeClaim"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary 创建PV
|
|
||||||
// @Description 创建PV
|
|
||||||
// @Tags storage
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param param body PersistentVolume1 true "json"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 500
|
|
||||||
// @Router /api/v1/storage/pv [post]
|
|
||||||
func CreatePV(c *gin.Context) {
|
|
||||||
var pvRequest PersistentVolume
|
|
||||||
if err := c.BindJSON(&pvRequest); err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "invalid request params.", "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
persistentVolume, err := ClientSet.CoreV1().PersistentVolumes().Create(context.TODO(), &pvRequest.PersistentVolume, metav1.CreateOptions{})
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "create persistentVolume failed", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 创建调度策略实例
|
|
||||||
CreatePropagationPolicies(PropagationPolicy{
|
|
||||||
ClusterName: pvRequest.ClusterName,
|
|
||||||
TemplateId: pvRequest.TemplateId,
|
|
||||||
ResourceName: pvRequest.PersistentVolume.Name,
|
|
||||||
Name: "pv" + pvRequest.PersistentVolume.Name,
|
|
||||||
Kind: "PersistentVolume",
|
|
||||||
})
|
|
||||||
Response(c, http.StatusOK, "success", persistentVolume)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsExistPVC 查询pvc是否存在
|
|
||||||
func IsExistPVC(c *gin.Context) {
|
|
||||||
clusterName := c.Query("clusterName")
|
|
||||||
namespace := c.Query("namespace")
|
|
||||||
name := c.Query("name")
|
|
||||||
result := SearchObject(detailPvcConst, clusterName, namespace, name)
|
|
||||||
if result.Error() != nil {
|
|
||||||
Response(c, http.StatusOK, "success", false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
raw, _ := result.Raw()
|
|
||||||
var pvc v1.PersistentVolumeClaim
|
|
||||||
json.Unmarshal(raw, &pvc)
|
|
||||||
if len(pvc.Name) == 0 {
|
|
||||||
Response(c, http.StatusOK, "success", false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
Response(c, http.StatusOK, "success", true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DetailPVC 查询PVC详情
|
|
||||||
func DetailPVC(c *gin.Context) {
|
|
||||||
clusterName := c.Query("clusterName")
|
|
||||||
namespace := c.Query("namespace")
|
|
||||||
name := c.Query("name")
|
|
||||||
result := SearchObject(detailPvcConst, clusterName, namespace, name)
|
|
||||||
if result.Error() != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "failed", result.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
raw, _ := result.Raw()
|
|
||||||
var pvc v1.PersistentVolumeClaim
|
|
||||||
json.Unmarshal(raw, &pvc)
|
|
||||||
err_ := AddTypeMetaToObject(&pvc)
|
|
||||||
if err_ != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "failed", result.Error())
|
|
||||||
}
|
|
||||||
Response(c, http.StatusOK, "success", pvc)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeletePVC 删除存储卷
|
|
||||||
func DeletePVC(c *gin.Context) {
|
|
||||||
clusterName := c.Param("clusterName")
|
|
||||||
namespace := c.Param("namespace")
|
|
||||||
name := c.Param("name")
|
|
||||||
label := c.Param("label")
|
|
||||||
if strings.EqualFold(label, "jcce") {
|
|
||||||
err := ClientSet.CoreV1().PersistentVolumeClaims(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{})
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "delete persistentVolumeClaim failed", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 删除调度策略实例
|
|
||||||
DeletePropagationPolicies(namespace, "pvc"+"."+name)
|
|
||||||
} else {
|
|
||||||
result := DeleteObject(detailPvcConst, clusterName, namespace, name)
|
|
||||||
if result.Error() != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "delete persistentVolumeClaim failed", result.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Response(c, http.StatusOK, "success", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdatePVC 更新存储卷信息
|
|
||||||
func UpdatePVC(c *gin.Context) {
|
|
||||||
clusterName := c.Query("clusterName")
|
|
||||||
var pvc v1.PersistentVolumeClaim
|
|
||||||
if err := c.BindJSON(&pvc); err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "invalid request params.", "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(pvc.Labels["jcce"]) != 0 {
|
|
||||||
_, err := ClientSet.CoreV1().PersistentVolumeClaims(pvc.Namespace).Update(context.TODO(), &pvc, metav1.UpdateOptions{})
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "update persistentVolumeClaim failed", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
result := UpdateObject(detailPvcConst, clusterName, pvc.Namespace, pvc.Name, pvc)
|
|
||||||
if result.Error() != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "update persistentVolumeClaim failed", result.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Response(c, http.StatusOK, "success", "")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Summary 创建PVC
|
|
||||||
// @Description 创建PVC
|
|
||||||
// @Tags storage
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param param body PersistentVolumeClaim1 true "json"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 500
|
|
||||||
// @Router /api/v1/storage/pvc [post]
|
|
||||||
func CreatePVC(c *gin.Context) {
|
|
||||||
var pvcRequest PersistentVolumeClaim
|
|
||||||
if err := c.BindJSON(&pvcRequest); err != nil {
|
|
||||||
Response(c, http.StatusBadRequest, "invalid request params.", "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if pvcRequest.PersistentVolumeClaim.Labels != nil && pvcRequest.PersistentVolumeClaim.Labels["jcce"] == "true" {
|
|
||||||
_, err := ClientSet.CoreV1().PersistentVolumeClaims(pvcRequest.PersistentVolumeClaim.Namespace).Create(context.TODO(), &pvcRequest.PersistentVolumeClaim, metav1.CreateOptions{})
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "Create PersistentVolumeClaim Failed", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 创建调度策略实例
|
|
||||||
policyErr := CreatePropagationPolicies(PropagationPolicy{
|
|
||||||
ClusterName: pvcRequest.ClusterName,
|
|
||||||
TemplateId: pvcRequest.TemplateId,
|
|
||||||
ResourceName: pvcRequest.PersistentVolumeClaim.Name,
|
|
||||||
Name: "pvc" + "." + pvcRequest.PersistentVolumeClaim.Namespace + "." + pvcRequest.PersistentVolumeClaim.Name,
|
|
||||||
Namespace: pvcRequest.PersistentVolumeClaim.Namespace,
|
|
||||||
Kind: "PersistentVolumeClaim",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "create pvc failed", policyErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
result := PostObject(pvcListConst, pvcRequest.ClusterName[0], pvcRequest.PersistentVolumeClaim.Namespace, pvcRequest.PersistentVolumeClaim.Name, pvcRequest.PersistentVolumeClaim)
|
|
||||||
if result.Error() != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "failed", result.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Response(c, http.StatusOK, "success", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListPV 查询PV列表
|
|
||||||
// @Summary 查询PV列表
|
|
||||||
// @Description 查询PV列表
|
|
||||||
// @Tags storage
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param clusterName path string true "集群"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 500
|
|
||||||
// @Router /api/v1/storage/pv/{clusterName} [get]
|
|
||||||
func ListPV(c *gin.Context) {
|
|
||||||
clusterName := c.Query("clusterName")
|
|
||||||
result := SearchObject(pvConst, clusterName, "", "")
|
|
||||||
if result.Error() != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "failed", result.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
raw, _ := result.Raw()
|
|
||||||
var pvRes PVRes
|
|
||||||
json.Unmarshal(raw, &pvRes)
|
|
||||||
Response(c, http.StatusOK, "success", pvRes.Items)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListPVC 查询PVC列表
|
|
||||||
// @Summary 查询PV列表
|
|
||||||
// @Description 查询PV列表
|
|
||||||
// @Tags storage
|
|
||||||
// @accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param clusterName path string true "集群"
|
|
||||||
// @Param namespace path string true "命名空间"
|
|
||||||
// @Success 200
|
|
||||||
// @Failure 500
|
|
||||||
// @Router /api/v1/storage/pvc [get]
|
|
||||||
func ListPVC(c *gin.Context) {
|
|
||||||
clusterName := c.Query("clusterName")
|
|
||||||
pageNum := c.Query("pageNum")
|
|
||||||
pageSize := c.Query("pageSize")
|
|
||||||
name := c.Query("name")
|
|
||||||
status := c.Query("status")
|
|
||||||
result := SearchObject(pvcConst, clusterName, "", "")
|
|
||||||
if result.Error() != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "failed", result.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
raw, _ := result.Raw()
|
|
||||||
var pvcResult PVCObject
|
|
||||||
json.Unmarshal(raw, &pvcResult)
|
|
||||||
if len(name) != 0 {
|
|
||||||
count := 0
|
|
||||||
for i := range pvcResult.Items {
|
|
||||||
if !strings.Contains(pvcResult.Items[i-count].Name, name) {
|
|
||||||
pvcResult.Items = append(pvcResult.Items[:i-count], pvcResult.Items[i-count+1:]...)
|
|
||||||
count = count + 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
if len(status) != 0 {
|
|
||||||
count := 0
|
|
||||||
for i := range pvcResult.Items {
|
|
||||||
if !strings.EqualFold(reflect.ValueOf(pvcResult.Items[i-count].Status.Phase).String(), status) {
|
|
||||||
pvcResult.Items = append(pvcResult.Items[:i-count], pvcResult.Items[i-count+1:]...)
|
|
||||||
count = count + 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(pvcResult.Items) == 0 {
|
|
||||||
Response(c, http.StatusOK, "success", pvcResult.Items)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
page := &Page[v1.PersistentVolumeClaim]{}
|
|
||||||
page.List = pvcResult.Items
|
|
||||||
sort.SliceStable(pvcResult.Items, func(i, j int) bool {
|
|
||||||
return page.List[i].CreationTimestamp.Time.After(page.List[j].CreationTimestamp.Time)
|
|
||||||
})
|
|
||||||
data := Paginator(page, int64(len(pvcResult.Items)), pageNum, pageSize)
|
|
||||||
// 查询是否有pod挂载到当前pvc下
|
|
||||||
podResult := SearchObject(allPodConst, clusterName, "", "")
|
|
||||||
if podResult.Error() != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "failed", podResult.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var podRes PodObject
|
|
||||||
podRaw, _ := podResult.Raw()
|
|
||||||
json.Unmarshal(podRaw, &podRes)
|
|
||||||
for _, pvc := range data.List {
|
|
||||||
for _, pod := range podRes.Items {
|
|
||||||
for _, podPvc := range pod.Spec.Volumes {
|
|
||||||
if podPvc.PersistentVolumeClaim != nil && pvc.Annotations != nil && podPvc.PersistentVolumeClaim.ClaimName == pvc.Name {
|
|
||||||
pvc.Annotations["in-use"] = "true"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Response(c, http.StatusOK, "success", data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListStorageClass 查询StorageClass名称列表
|
|
||||||
func ListStorageClass(c *gin.Context) {
|
|
||||||
clusterName := c.Query("clusterName")
|
|
||||||
scResult := SearchObject(scConst, clusterName, "", "")
|
|
||||||
if scResult.Error() != nil {
|
|
||||||
Response(c, http.StatusInternalServerError, "failed", scResult.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
raw, _ := scResult.Raw()
|
|
||||||
var scRes SCRes
|
|
||||||
json.Unmarshal(raw, &scRes)
|
|
||||||
// 遍历获取名称集合
|
|
||||||
var result []string
|
|
||||||
for i := range scRes.Items {
|
|
||||||
result = append(result, scRes.Items[i].Name)
|
|
||||||
}
|
|
||||||
Response(c, http.StatusOK, "success", result)
|
|
||||||
}
|
|
89
app/utils.go
89
app/utils.go
|
@ -1,13 +1,8 @@
|
||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
gossh "golang.org/x/crypto/ssh"
|
gossh "golang.org/x/crypto/ssh"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Cli 连接信息
|
// Cli 连接信息
|
||||||
|
@ -53,87 +48,3 @@ func (c Cli) Run(shell string) (string, error) {
|
||||||
c.LastResult = string(buf)
|
c.LastResult = string(buf)
|
||||||
return c.LastResult, err
|
return c.LastResult, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func stringInclude(items []string, item string) bool {
|
|
||||||
for _, eachItem := range items {
|
|
||||||
if eachItem == item {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func LabelsConvertToMap(label string) map[string]string {
|
|
||||||
var labelMap = make(map[string]string)
|
|
||||||
labelArray := strings.Split(label, ",")
|
|
||||||
for _, label := range labelArray {
|
|
||||||
labelKV := strings.Split(label, "=")
|
|
||||||
labelMap[labelKV[0]] = labelKV[1]
|
|
||||||
}
|
|
||||||
return labelMap
|
|
||||||
}
|
|
||||||
|
|
||||||
func SetRedis(ctx context.Context, key string, value string, t int64) bool {
|
|
||||||
expire := time.Duration(t) * time.Second
|
|
||||||
if err := MyRedis.Set(ctx, key, value, expire).Err(); err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetRedis(ctx context.Context, key string) string {
|
|
||||||
result, err := MyRedis.Get(ctx, key).Result()
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func DelRedis(ctx context.Context, key string) bool {
|
|
||||||
_, err := MyRedis.Del(ctx, key).Result()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExpireRedis(ctx context.Context, key string, t int64) bool {
|
|
||||||
// 延长过期时间
|
|
||||||
expire := time.Duration(t) * time.Second
|
|
||||||
if err := MyRedis.Expire(ctx, key, expire).Err(); err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// PageParamInit 分页参数初始化
|
|
||||||
func PageParamInit(pageNumParam, pageSizeParam string) (pageNum, pageSize int64) {
|
|
||||||
pageNum, _ = strconv.ParseInt(pageNumParam, 10, 64)
|
|
||||||
pageSize, _ = strconv.ParseInt(pageSizeParam, 10, 64)
|
|
||||||
if pageNum <= 0 {
|
|
||||||
pageNum = 1
|
|
||||||
}
|
|
||||||
switch {
|
|
||||||
case pageSize > 100:
|
|
||||||
pageSize = 100 // 限制一下分页大小
|
|
||||||
case pageSize <= 0:
|
|
||||||
pageSize = 10
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func Paginate[T any](page *Page[T], pageNum, pageSize, total int64) *Page[T] {
|
|
||||||
totalPages := total / pageSize
|
|
||||||
if total%pageSize > 0 {
|
|
||||||
totalPages++
|
|
||||||
}
|
|
||||||
return &Page[T]{
|
|
||||||
PageNum: pageNum,
|
|
||||||
PageSize: pageSize,
|
|
||||||
TotalPage: totalPages,
|
|
||||||
Total: total,
|
|
||||||
List: page.List,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -16,10 +16,6 @@ spec:
|
||||||
labels:
|
labels:
|
||||||
k8s-app: jcce-schedule
|
k8s-app: jcce-schedule
|
||||||
spec:
|
spec:
|
||||||
hostAliases:
|
|
||||||
- hostnames:
|
|
||||||
- nacos.jcce.dev
|
|
||||||
ip: nacos_host
|
|
||||||
imagePullSecrets:
|
imagePullSecrets:
|
||||||
- name: SECRET_NAME
|
- name: SECRET_NAME
|
||||||
containers:
|
containers:
|
||||||
|
|
3082
docs/docs.go
3082
docs/docs.go
File diff suppressed because it is too large
Load Diff
3057
docs/swagger.json
3057
docs/swagger.json
File diff suppressed because it is too large
Load Diff
2010
docs/swagger.yaml
2010
docs/swagger.yaml
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,29 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ServerConfig struct {
|
||||||
|
Name string `mapstructure:"name"`
|
||||||
|
Port int `mapstructure:"port"`
|
||||||
|
Core Core `mapstructure:"core"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Core struct {
|
||||||
|
url string `mapstructure:"url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var ServerConf *ServerConfig
|
||||||
|
|
||||||
|
func InitConfig() {
|
||||||
|
v := viper.New()
|
||||||
|
v.SetConfigFile("etc/config/config.yaml")
|
||||||
|
if err := v.ReadInConfig(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
//serverConfig := ServerConfig{}
|
||||||
|
if err := v.Unmarshal(&ServerConf); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
name: pcm-kubernetes
|
||||||
|
port: 8022
|
||||||
|
|
||||||
|
core:
|
||||||
|
url: localhost:8080
|
183
go.mod
183
go.mod
|
@ -1,156 +1,83 @@
|
||||||
module jcc-schedule
|
module jcc-schedule
|
||||||
|
|
||||||
go 1.18
|
go 1.21
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/bitly/go-simplejson v0.5.0
|
|
||||||
github.com/gin-gonic/gin v1.8.1
|
github.com/gin-gonic/gin v1.8.1
|
||||||
github.com/go-redis/redis/v8 v8.11.5
|
github.com/go-resty/resty/v2 v2.10.0
|
||||||
github.com/go-sql-driver/mysql v1.7.0
|
github.com/golang/glog v1.1.2
|
||||||
github.com/golang/glog v1.0.0
|
github.com/json-iterator/go v1.1.12
|
||||||
github.com/jmoiron/sqlx v1.3.5
|
github.com/prometheus/client_golang v1.18.0
|
||||||
github.com/karmada-io/karmada v1.3.0
|
github.com/prometheus/common v0.45.0
|
||||||
github.com/nacos-group/nacos-sdk-go v1.1.2
|
|
||||||
github.com/opensearch-project/opensearch-go/v2 v2.1.0
|
|
||||||
github.com/robfig/cron/v3 v3.0.1
|
github.com/robfig/cron/v3 v3.0.1
|
||||||
github.com/shopspring/decimal v1.3.1
|
github.com/spf13/viper v1.10.1
|
||||||
github.com/sirupsen/logrus v1.8.1
|
github.com/zeromicro/go-zero v1.6.2
|
||||||
github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a
|
golang.org/x/crypto v0.18.0
|
||||||
github.com/swaggo/gin-swagger v1.5.3
|
k8s.io/api v0.29.1
|
||||||
github.com/swaggo/swag v1.8.5
|
k8s.io/apimachinery v0.29.1
|
||||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd
|
k8s.io/client-go v0.29.1
|
||||||
golang.org/x/exp v0.0.0-20221126150942-6ab00d035af9
|
gitlink.org.cn/JointCloud/pcm-coordinator v0.0.0-20240302013624-9de512ee32ab
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
|
||||||
gorm.io/driver/mysql v1.5.1
|
|
||||||
gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55
|
|
||||||
k8s.io/api v0.25.0
|
|
||||||
k8s.io/apiextensions-apiserver v0.25.0
|
|
||||||
k8s.io/apimachinery v0.25.0
|
|
||||||
k8s.io/client-go v0.25.0
|
|
||||||
k8s.io/metrics v0.24.2
|
|
||||||
sigs.k8s.io/yaml v1.3.0
|
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||||
github.com/MakeNowJust/heredoc v1.0.0 // indirect
|
github.com/fatih/color v1.16.0 // indirect
|
||||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
|
||||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
|
||||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.18 // indirect
|
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
|
||||||
github.com/blang/semver v3.5.1+incompatible // indirect
|
|
||||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
|
|
||||||
github.com/buger/jsonparser v1.1.1 // indirect
|
|
||||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
|
||||||
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 // indirect
|
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
|
||||||
github.com/emicklei/go-restful/v3 v3.8.0 // indirect
|
|
||||||
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
|
|
||||||
github.com/evanphx/json-patch/v5 v5.6.0 // indirect
|
|
||||||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
|
|
||||||
github.com/fatih/camelcase v1.0.0 // indirect
|
|
||||||
github.com/fsnotify/fsnotify v1.5.1 // indirect
|
github.com/fsnotify/fsnotify v1.5.1 // indirect
|
||||||
github.com/fvbommel/sortorder v1.0.1 // indirect
|
|
||||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
github.com/go-errors/errors v1.0.1 // indirect
|
github.com/go-logr/logr v1.3.0 // indirect
|
||||||
github.com/go-logr/logr v1.2.3 // indirect
|
github.com/go-openapi/jsonpointer v0.20.0 // indirect
|
||||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||||
github.com/go-openapi/jsonreference v0.19.6 // indirect
|
github.com/go-openapi/swag v0.22.4 // indirect
|
||||||
github.com/go-openapi/spec v0.20.4 // indirect
|
|
||||||
github.com/go-openapi/swag v0.19.15 // indirect
|
|
||||||
github.com/go-playground/locales v0.14.0 // indirect
|
github.com/go-playground/locales v0.14.0 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.0 // indirect
|
github.com/go-playground/universal-translator v0.18.0 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.10.0 // indirect
|
github.com/go-playground/validator/v10 v10.10.0 // indirect
|
||||||
github.com/goccy/go-json v0.9.7 // indirect
|
github.com/goccy/go-json v0.9.7 // indirect
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
github.com/golang/protobuf v1.5.3 // indirect
|
||||||
github.com/golang/protobuf v1.5.2 // indirect
|
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect
|
||||||
github.com/google/btree v1.0.1 // indirect
|
|
||||||
github.com/google/gnostic v0.5.7-v3refs // indirect
|
|
||||||
github.com/google/go-cmp v0.5.8 // indirect
|
|
||||||
github.com/google/gofuzz v1.2.0 // indirect
|
github.com/google/gofuzz v1.2.0 // indirect
|
||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/google/uuid v1.1.2 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect
|
|
||||||
github.com/imdario/mergo v0.3.12 // indirect
|
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
|
||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
|
||||||
github.com/jinzhu/now v1.1.5 // indirect
|
|
||||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
|
||||||
github.com/jonboulle/clockwork v0.2.2 // indirect
|
|
||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
|
||||||
github.com/kr/pretty v0.3.0 // indirect
|
|
||||||
github.com/kr/text v0.2.0 // indirect
|
|
||||||
github.com/leodido/go-urn v1.2.1 // indirect
|
github.com/leodido/go-urn v1.2.1 // indirect
|
||||||
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
|
github.com/magiconair/properties v1.8.5 // indirect
|
||||||
github.com/mailru/easyjson v0.7.6 // indirect
|
github.com/mailru/easyjson v0.7.7 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-sqlite3 v1.14.17 // indirect
|
github.com/mitchellh/mapstructure v1.4.3 // indirect
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
|
|
||||||
github.com/mitchellh/go-wordwrap v1.0.0 // indirect
|
|
||||||
github.com/moby/spdystream v0.2.0 // indirect
|
|
||||||
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
|
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/olekukonko/tablewriter v0.0.4 // indirect
|
github.com/pelletier/go-toml v1.9.4 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.0.1 // indirect
|
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
|
||||||
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
|
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/spf13/afero v1.8.0 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/spf13/cast v1.4.1 // indirect
|
||||||
github.com/prometheus/client_golang v1.12.1 // indirect
|
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||||
github.com/prometheus/client_model v0.2.0 // indirect
|
|
||||||
github.com/prometheus/common v0.32.1 // indirect
|
|
||||||
github.com/prometheus/procfs v0.7.3 // indirect
|
|
||||||
github.com/rivo/uniseg v0.2.0 // indirect
|
|
||||||
github.com/rogpeppe/go-internal v1.8.0 // indirect
|
|
||||||
github.com/russross/blackfriday v1.5.2 // indirect
|
|
||||||
github.com/spf13/cobra v1.4.0 // indirect
|
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/stretchr/testify v1.8.0 // indirect
|
github.com/subosito/gotenv v1.2.0 // indirect
|
||||||
github.com/ugorji/go/codec v1.2.7 // indirect
|
github.com/ugorji/go/codec v1.2.7 // indirect
|
||||||
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca // indirect
|
go.opentelemetry.io/otel v1.21.0 // indirect
|
||||||
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect
|
go.opentelemetry.io/otel/sdk v1.21.0 // indirect
|
||||||
go.uber.org/atomic v1.7.0 // indirect
|
go.opentelemetry.io/otel/trace v1.21.0 // indirect
|
||||||
go.uber.org/multierr v1.6.0 // indirect
|
go.uber.org/automaxprocs v1.5.3 // indirect
|
||||||
go.uber.org/zap v1.19.1 // indirect
|
golang.org/x/net v0.20.0 // indirect
|
||||||
golang.org/x/net v0.1.0 // indirect
|
golang.org/x/oauth2 v0.15.0 // indirect
|
||||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
|
golang.org/x/sys v0.16.0 // indirect
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
|
golang.org/x/term v0.16.0 // indirect
|
||||||
golang.org/x/sys v0.1.0 // indirect
|
golang.org/x/text v0.14.0 // indirect
|
||||||
golang.org/x/term v0.1.0 // indirect
|
golang.org/x/time v0.5.0 // indirect
|
||||||
golang.org/x/text v0.4.0 // indirect
|
google.golang.org/appengine v1.6.8 // indirect
|
||||||
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
|
google.golang.org/protobuf v1.32.0 // indirect
|
||||||
golang.org/x/tools v0.2.0 // indirect
|
|
||||||
gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
|
|
||||||
google.golang.org/appengine v1.6.7 // indirect
|
|
||||||
google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 // indirect
|
|
||||||
google.golang.org/grpc v1.47.0 // indirect
|
|
||||||
google.golang.org/protobuf v1.28.0 // indirect
|
|
||||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||||
gopkg.in/ini.v1 v1.66.3 // indirect
|
gopkg.in/ini.v1 v1.66.3 // indirect
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
|
||||||
gopkg.in/square/go-jose.v2 v2.2.2 // indirect
|
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
k8s.io/apiserver v0.25.0 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
k8s.io/cli-runtime v0.24.2 // indirect
|
k8s.io/klog/v2 v2.110.1 // indirect
|
||||||
k8s.io/cluster-bootstrap v0.24.2 // indirect
|
k8s.io/kube-openapi v0.0.0-20231206194836-bf4651e18aa8 // indirect
|
||||||
k8s.io/component-base v0.25.0 // indirect
|
k8s.io/utils v0.0.0-20231127182322-b307cd553661 // indirect
|
||||||
k8s.io/klog/v2 v2.70.1 // indirect
|
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
||||||
k8s.io/kube-aggregator v0.24.2 // indirect
|
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
|
||||||
k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect
|
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||||
k8s.io/kubectl v0.24.2 // indirect
|
|
||||||
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect
|
|
||||||
sigs.k8s.io/cluster-api v1.0.1 // indirect
|
|
||||||
sigs.k8s.io/controller-runtime v0.12.2 // indirect
|
|
||||||
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
|
|
||||||
sigs.k8s.io/kustomize/api v0.11.4 // indirect
|
|
||||||
sigs.k8s.io/kustomize/kyaml v0.13.6 // indirect
|
|
||||||
sigs.k8s.io/mcs-api v0.1.0 // indirect
|
|
||||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
|
|
||||||
)
|
)
|
||||||
|
|
8
main.go
8
main.go
|
@ -2,18 +2,18 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/robfig/cron/v3"
|
"github.com/robfig/cron/v3"
|
||||||
"jcc-schedule/app"
|
"jcc-schedule/pkg/apiserver"
|
||||||
|
"jcc-schedule/routers"
|
||||||
)
|
)
|
||||||
|
|
||||||
// @title jcc调度中心
|
// @title jcc调度中心
|
||||||
// @version 1.0
|
// @version 1.0
|
||||||
// @description jcc
|
// @description jcc
|
||||||
func main() {
|
func main() {
|
||||||
router := app.InitRouter()
|
router := routers.InitRouter()
|
||||||
|
apiserver.NewAPIServer()
|
||||||
c := cron.New()
|
c := cron.New()
|
||||||
app.QueryNodeEdgeInfo(c)
|
|
||||||
//app.SyncMonitor(c)
|
//app.SyncMonitor(c)
|
||||||
c.Start()
|
c.Start()
|
||||||
go app.Watch()
|
|
||||||
_ = router.Run(":8082")
|
_ = router.Run(":8082")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
package model
|
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
type Cluster struct {
|
|
||||||
Id int `json:"id" db:"id"`
|
|
||||||
ClusterName string `json:"clusterName" db:"cluster_name"`
|
|
||||||
Version string `json:"version" db:"version"`
|
|
||||||
UserName string `json:"userName" db:"user_name"`
|
|
||||||
CreateTime time.Time `json:"createTime" db:"create_time"`
|
|
||||||
ClusterIp string `json:"clusterIp" db:"cluster_ip"`
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
package model
|
|
||||||
|
|
||||||
type Resources struct {
|
|
||||||
Name string `db:"name" json:"name"`
|
|
||||||
Ip *string `db:"ip" json:"ip"`
|
|
||||||
PublicIp *string `db:"public_ip" json:"publicIp"`
|
|
||||||
HpcPartition *string `db:"hpc_partition" json:"hpcPartition"`
|
|
||||||
CpuName string `db:"cpu_name" json:"cpuName"`
|
|
||||||
CpuGHz string `db:"cpu_GHz" json:"cpuGHz"`
|
|
||||||
CpuCores string `db:"cpu_cores" json:"cpuCores"`
|
|
||||||
TotalThreads string `db:"total_threads" json:"totalThreads"`
|
|
||||||
Memory *string `db:"memory" json:"memory"`
|
|
||||||
GpuDcu *string `db:"gpu_dcu" json:"gpuDcu"`
|
|
||||||
CpuSingleCycle *string `db:"cpu_single_cycle" json:"cpuSingleCycle"`
|
|
||||||
Num string `db:"num" json:"num"`
|
|
||||||
CpuGFlops *float64 `db:"cpu_GFlops" json:"cpuGFlops"`
|
|
||||||
GpuGFlops *float64 `db:"gpu_GFlops" json:"gpuGFlops"`
|
|
||||||
Region string `db:"region" json:"region"`
|
|
||||||
Flops float64 `db:"flops" json:"flops"`
|
|
||||||
}
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
package apiserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
"jcc-schedule/pkg/monitoring"
|
||||||
|
"jcc-schedule/pkg/monitoring/prometheus"
|
||||||
|
"k8s.io/client-go/dynamic"
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
|
"k8s.io/client-go/rest"
|
||||||
|
"k8s.io/client-go/util/flowcontrol"
|
||||||
|
)
|
||||||
|
|
||||||
|
type APIServer struct {
|
||||||
|
MonitoringClientMap map[string]monitoring.Interface
|
||||||
|
ClientSetMap map[string]*kubernetes.Clientset
|
||||||
|
DynamicClientMap map[string]*dynamic.DynamicClient
|
||||||
|
Clusters []*Cluster
|
||||||
|
}
|
||||||
|
|
||||||
|
var ApiServer APIServer
|
||||||
|
|
||||||
|
func NewAPIServer() {
|
||||||
|
apiServer := &APIServer{}
|
||||||
|
apiServer.installClusters()
|
||||||
|
apiServer.installK8sClient()
|
||||||
|
apiServer.installMonitoring()
|
||||||
|
}
|
||||||
|
|
||||||
|
type Cluster struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Server string `json:"server"`
|
||||||
|
BearerToken string `json:"bearerToken"`
|
||||||
|
MonitorServer string `json:"monitorServer"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *APIServer) installClusters() {
|
||||||
|
|
||||||
|
client := resty.New()
|
||||||
|
resp, err := client.R().
|
||||||
|
SetQueryParams(map[string]string{
|
||||||
|
"adapterId": "1752857389213683712",
|
||||||
|
}).
|
||||||
|
SetResult(&s.Clusters).
|
||||||
|
ForceContentType("application/json").
|
||||||
|
Get("http://localhost:8999/pcm/v1/adapter/cluster/list")
|
||||||
|
println(resp.Status())
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *APIServer) installK8sClient() {
|
||||||
|
for _, cluster := range s.Clusters {
|
||||||
|
if len(cluster.Server) != 0 && len(cluster.BearerToken) != 0 {
|
||||||
|
restConfig := &rest.Config{
|
||||||
|
Host: cluster.Server,
|
||||||
|
RateLimiter: flowcontrol.NewTokenBucketRateLimiter(1000, 1000),
|
||||||
|
BearerToken: cluster.BearerToken,
|
||||||
|
TLSClientConfig: rest.TLSClientConfig{
|
||||||
|
Insecure: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
dynamicClient, _ := dynamic.NewForConfig(restConfig)
|
||||||
|
s.DynamicClientMap[cluster.Name] = dynamicClient
|
||||||
|
clientSet, _ := kubernetes.NewForConfig(restConfig)
|
||||||
|
s.ClientSetMap[cluster.Name] = clientSet
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *APIServer) installMonitoring() {
|
||||||
|
for _, cluster := range s.Clusters {
|
||||||
|
if len(cluster.MonitorServer) != 0 {
|
||||||
|
prometheusClient, err := prometheus.NewPrometheus(cluster.MonitorServer)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.MonitoringClientMap[cluster.Name] = prometheusClient
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package monitoring
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type Interface interface {
|
||||||
|
GetNamedMetrics(metrics []string, time time.Time, opt QueryOption) []Metric
|
||||||
|
GetNamedMetricsByTime(metrics []string, start, end string, step time.Duration, opt QueryOption) []Metric
|
||||||
|
}
|
|
@ -0,0 +1,189 @@
|
||||||
|
package prometheus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/prometheus/client_golang/api"
|
||||||
|
v1 "github.com/prometheus/client_golang/api/prometheus/v1"
|
||||||
|
"github.com/prometheus/common/model"
|
||||||
|
"jcc-schedule/pkg/monitoring"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Prometheus struct {
|
||||||
|
prometheus monitoring.Interface
|
||||||
|
client v1.API
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPrometheus Initialize the Prometheus client
|
||||||
|
func NewPrometheus(address string) (Prometheus, error) {
|
||||||
|
cfg := api.Config{
|
||||||
|
Address: address,
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := api.NewClient(cfg)
|
||||||
|
return Prometheus{client: v1.NewAPI(client)}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseTime(timestamp string) (time.Time, error) {
|
||||||
|
// Parse time params
|
||||||
|
startInt, err := strconv.ParseInt(timestamp, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return time.Now(), err
|
||||||
|
}
|
||||||
|
return time.Unix(startInt, 0), nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Prometheus) GetNamedMetricsByTime(metrics []string, start, end string, step time.Duration, o monitoring.QueryOption) []monitoring.Metric {
|
||||||
|
var res []monitoring.Metric
|
||||||
|
var mtx sync.Mutex
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
opts := monitoring.NewQueryOptions()
|
||||||
|
o.Apply(opts)
|
||||||
|
|
||||||
|
for _, metric := range metrics {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(metric string) {
|
||||||
|
parsedResp := monitoring.Metric{MetricName: metric}
|
||||||
|
startTimestamp, err := ParseTime(start)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
endTimestamp, err := ParseTime(end)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
timeRange := v1.Range{
|
||||||
|
Start: startTimestamp,
|
||||||
|
End: endTimestamp,
|
||||||
|
Step: step,
|
||||||
|
}
|
||||||
|
value, _, err := p.client.QueryRange(context.Background(), makeExpr(metric, *opts), timeRange)
|
||||||
|
if err != nil {
|
||||||
|
parsedResp.Error = err.Error()
|
||||||
|
} else {
|
||||||
|
parsedResp.MetricData = parseQueryRangeResp(value, genMetricFilter(o))
|
||||||
|
}
|
||||||
|
|
||||||
|
mtx.Lock()
|
||||||
|
res = append(res, parsedResp)
|
||||||
|
mtx.Unlock()
|
||||||
|
|
||||||
|
wg.Done()
|
||||||
|
}(metric)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseQueryRangeResp(value model.Value, metricFilter func(metric model.Metric) bool) monitoring.MetricData {
|
||||||
|
res := monitoring.MetricData{MetricType: monitoring.MetricTypeMatrix}
|
||||||
|
|
||||||
|
data, _ := value.(model.Matrix)
|
||||||
|
|
||||||
|
for _, v := range data {
|
||||||
|
if metricFilter != nil && !metricFilter(v.Metric) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
mv := monitoring.MetricValue{
|
||||||
|
Metadata: make(map[string]string),
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range v.Metric {
|
||||||
|
mv.Metadata[string(k)] = string(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, k := range v.Values {
|
||||||
|
mv.Series = append(mv.Series, monitoring.Point{float64(k.Timestamp) / 1000, float64(k.Value)})
|
||||||
|
}
|
||||||
|
|
||||||
|
res.MetricValues = append(res.MetricValues, mv)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Prometheus) GetNamedMetrics(metrics []string, ts time.Time, o monitoring.QueryOption) []monitoring.Metric {
|
||||||
|
var res []monitoring.Metric
|
||||||
|
var mtx sync.Mutex
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
opts := monitoring.NewQueryOptions()
|
||||||
|
o.Apply(opts)
|
||||||
|
|
||||||
|
for _, metric := range metrics {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(metric string) {
|
||||||
|
parsedResp := monitoring.Metric{MetricName: metric}
|
||||||
|
|
||||||
|
value, _, err := p.client.Query(context.Background(), makeExpr(metric, *opts), ts)
|
||||||
|
if err != nil {
|
||||||
|
parsedResp.Error = err.Error()
|
||||||
|
} else {
|
||||||
|
parsedResp.MetricData = parseQueryResp(value, genMetricFilter(o))
|
||||||
|
}
|
||||||
|
|
||||||
|
mtx.Lock()
|
||||||
|
res = append(res, parsedResp)
|
||||||
|
mtx.Unlock()
|
||||||
|
|
||||||
|
wg.Done()
|
||||||
|
}(metric)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
func parseQueryResp(value model.Value, metricFilter func(metric model.Metric) bool) monitoring.MetricData {
|
||||||
|
res := monitoring.MetricData{MetricType: monitoring.MetricTypeVector}
|
||||||
|
|
||||||
|
data, _ := value.(model.Vector)
|
||||||
|
|
||||||
|
for _, v := range data {
|
||||||
|
if metricFilter != nil && !metricFilter(v.Metric) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
mv := monitoring.MetricValue{
|
||||||
|
Metadata: make(map[string]string),
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range v.Metric {
|
||||||
|
mv.Metadata[string(k)] = string(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
mv.Sample = &monitoring.Point{float64(v.Timestamp) / 1000, float64(v.Value)}
|
||||||
|
|
||||||
|
res.MetricValues = append(res.MetricValues, mv)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func genMetricFilter(o monitoring.QueryOption) func(metric model.Metric) bool {
|
||||||
|
if o != nil {
|
||||||
|
if po, ok := o.(monitoring.PodOption); ok {
|
||||||
|
if po.NamespacedResourcesFilter != "" {
|
||||||
|
namespacedPodsMap := make(map[string]struct{})
|
||||||
|
for _, s := range strings.Split(po.NamespacedResourcesFilter, "|") {
|
||||||
|
namespacedPodsMap[s] = struct{}{}
|
||||||
|
}
|
||||||
|
return func(metric model.Metric) bool {
|
||||||
|
if len(metric) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
_, ok := namespacedPodsMap[string(metric["namespace"])+"/"+string(metric["pod"])]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return func(metric model.Metric) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,167 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) [2023] [pcm]
|
||||||
|
[pcm-coordinator] is licensed under Mulan PSL v2.
|
||||||
|
You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
|
||||||
|
http://license.coscl.org.cn/MulanPSL2
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
EITHER EXPaRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
package prometheus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"jcc-schedule/pkg/monitoring"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
StatefulSet = "StatefulSet"
|
||||||
|
DaemonSet = "DaemonSet"
|
||||||
|
Deployment = "Deployment"
|
||||||
|
)
|
||||||
|
|
||||||
|
var promQLTemplates = map[string]string{
|
||||||
|
|
||||||
|
//namespace
|
||||||
|
"namespace_cpu_usage": `round(namespace:container_cpu_usage_seconds_total:sum_rate{namespace!="", $1}, 0.001)`,
|
||||||
|
"namespace_memory_usage": `namespace:container_memory_usage_bytes:sum{namespace!="", $1}`,
|
||||||
|
"namespace_memory_usage_wo_cache": `namespace:container_memory_usage_bytes_wo_cache:sum{namespace!="", $1}`,
|
||||||
|
"namespace_net_bytes_transmitted": `sum by (namespace) (irate(container_network_transmit_bytes_total{namespace!="", pod!="", interface!~"^(cali.+|tunl.+|dummy.+|kube.+|flannel.+|cni.+|docker.+|veth.+|lo.*)", job="kubelet"}[5m]) * on (namespace) group_left(workspace) kube_namespace_labels{$1}) or on(namespace) max by(namespace) (kube_namespace_labels{$1} * 0)`,
|
||||||
|
"namespace_net_bytes_received": `sum by (namespace) (irate(container_network_receive_bytes_total{namespace!="", pod!="", interface!~"^(cali.+|tunl.+|dummy.+|kube.+|flannel.+|cni.+|docker.+|veth.+|lo.*)", job="kubelet"}[5m]) * on (namespace) group_left(workspace) kube_namespace_labels{$1}) or on(namespace) max by(namespace) (kube_namespace_labels{$1} * 0)`,
|
||||||
|
"namespace_pod_count": `sum by (namespace) (kube_pod_status_phase{phase!~"Failed|Succeeded", namespace!=""} * on (namespace) group_left(workspace) kube_namespace_labels{$1}) or on(namespace) max by(namespace) (kube_namespace_labels{$1} * 0)`,
|
||||||
|
"namespace_pod_running_count": `sum by (namespace) (kube_pod_status_phase{phase="Running", namespace!=""} * on (namespace) group_left(workspace) kube_namespace_labels{$1}) or on(namespace) max by(namespace) (kube_namespace_labels{$1} * 0)`,
|
||||||
|
"namespace_pod_succeeded_count": `sum by (namespace) (kube_pod_status_phase{phase="Succeeded", namespace!=""} * on (namespace) group_left(workspace) kube_namespace_labels{$1}) or on(namespace) max by(namespace) (kube_namespace_labels{$1} * 0)`,
|
||||||
|
"namespace_pod_abnormal_count": `namespace:pod_abnormal:count{namespace!="", $1}`,
|
||||||
|
"namespace_pod_abnormal_ratio": `namespace:pod_abnormal:ratio{namespace!="", $1}`,
|
||||||
|
"namespace_memory_limit_hard": `min by (namespace) (kube_resourcequota{resourcequota!="quota", type="hard", namespace!="", resource="limits.memory"} * on (namespace) group_left(workspace) kube_namespace_labels{$1})`,
|
||||||
|
"namespace_cpu_limit_hard": `min by (namespace) (kube_resourcequota{resourcequota!="quota", type="hard", namespace!="", resource="limits.cpu"} * on (namespace) group_left(workspace) kube_namespace_labels{$1})`,
|
||||||
|
"namespace_pod_count_hard": `min by (namespace) (kube_resourcequota{resourcequota!="quota", type="hard", namespace!="", resource="count/pods"} * on (namespace) group_left(workspace) kube_namespace_labels{$1})`,
|
||||||
|
"namespace_cronjob_count": `sum by (namespace) (kube_cronjob_labels{namespace!=""} * on (namespace) group_left(workspace) kube_namespace_labels{$1})`,
|
||||||
|
"namespace_pvc_count": `sum by (namespace) (kube_persistentvolumeclaim_info{namespace!=""} * on (namespace) group_left(workspace) kube_namespace_labels{$1})`,
|
||||||
|
"namespace_daemonset_count": `sum by (namespace) (kube_daemonset_labels{namespace!=""} * on (namespace) group_left(workspace) kube_namespace_labels{$1})`,
|
||||||
|
"namespace_deployment_count": `sum by (namespace) (kube_deployment_labels{namespace!=""} * on (namespace) group_left(workspace) kube_namespace_labels{$1})`,
|
||||||
|
"namespace_endpoint_count": `sum by (namespace) (kube_endpoint_labels{namespace!=""} * on (namespace) group_left(workspace) kube_namespace_labels{$1})`,
|
||||||
|
"namespace_hpa_count": `sum by (namespace) (kube_horizontalpodautoscaler_labels{namespace!=""} * on (namespace) group_left(workspace) kube_namespace_labels{$1})`,
|
||||||
|
"namespace_job_count": `sum by (namespace) (kube_job_labels{namespace!=""} * on (namespace) group_left(workspace) kube_namespace_labels{$1})`,
|
||||||
|
"namespace_statefulset_count": `sum by (namespace) (kube_statefulset_labels{namespace!=""} * on (namespace) group_left(workspace) kube_namespace_labels{$1})`,
|
||||||
|
"namespace_replicaset_count": `count by (namespace) (kube_replicaset_labels{namespace!=""} * on (namespace) group_left(workspace) kube_namespace_labels{$1})`,
|
||||||
|
"namespace_service_count": `sum by (namespace) (kube_service_info{namespace!=""} * on (namespace) group_left(workspace) kube_namespace_labels{$1})`,
|
||||||
|
"namespace_secret_count": `sum by (namespace) (kube_secret_info{namespace!=""} * on (namespace) group_left(workspace) kube_namespace_labels{$1})`,
|
||||||
|
"namespace_configmap_count": `sum by (namespace) (kube_configmap_info{namespace!=""} * on (namespace) group_left(workspace) kube_namespace_labels{$1})`,
|
||||||
|
"namespace_ingresses_extensions_count": `sum by (namespace) (kube_ingress_labels{namespace!=""} * on (namespace) group_left(workspace) kube_namespace_labels{$1})`,
|
||||||
|
"namespace_s2ibuilder_count": `sum by (namespace) (s2i_s2ibuilder_created{namespace!=""} * on (namespace) group_left(workspace) kube_namespace_labels{$1})`,
|
||||||
|
|
||||||
|
"controller_cpu_usage_rate": `sum( node_namespace_pod_container:container_cpu_usage_seconds_total:sum_irate{}* on(namespace,pod) group_left(workload) namespace_workload_pod:kube_pod_owner:relabel{$1}) by (workload)/sum( kube_pod_container_resource_limits{job="kube-state-metrics", resource="cpu"}* on(namespace,pod) group_left(workload) namespace_workload_pod:kube_pod_owner:relabel{ }) by (workload)`,
|
||||||
|
"controller_memory_usage_rate": `sum( container_memory_working_set_bytes{job="kubelet", metrics_path="/metrics/cadvisor", container!="", image!=""} * on(namespace,pod) group_left(workload) namespace_workload_pod:kube_pod_owner:relabel{$1}) by (workload)/sum( kube_pod_container_resource_limits{job="kube-state-metrics", resource="memory"}* on(namespace,pod) group_left(workload) namespace_workload_pod:kube_pod_owner:relabel{ }) by (workload)`,
|
||||||
|
// pod
|
||||||
|
"pod_cpu_usage": `round(sum by (namespace, pod) (irate(container_cpu_usage_seconds_total{job="kubelet", pod!="", image!=""}[5m])) * on (namespace, pod) group_left(owner_kind,owner_name) kube_pod_owner{$1} * on (namespace, pod) group_left(node) kube_pod_info{$2}, 0.001)`,
|
||||||
|
"pod_cpu_usage_rate": `sum(node_namespace_pod_container:container_cpu_usage_seconds_total:sum_irate{ $1}) by (pod) / sum(kube_pod_container_resource_limits{ $1,unit="core"}) by (pod)`,
|
||||||
|
"pod_memory_usage_rate": `sum(container_memory_working_set_bytes{job="kubelet", $1, container!="", image!=""}) by (pod) / sum(kube_pod_container_resource_limits{ $1,unit="byte"}) by (pod)`,
|
||||||
|
"pod_memory_usage": `sum by (namespace, pod) (container_memory_usage_bytes{job="kubelet", pod!="", image!=""}) * on (namespace, pod) group_left(owner_kind, owner_name) kube_pod_owner{$1} * on (namespace, pod) group_left(node) kube_pod_info{$2}`,
|
||||||
|
"pod_memory_usage_wo_cache": `sum by (namespace, pod) (container_memory_working_set_bytes{job="kubelet", pod!="", image!=""}) * on (namespace, pod) group_left(owner_kind, owner_name) kube_pod_owner{$1} * on (namespace, pod) group_left(node) kube_pod_info{$2}`,
|
||||||
|
"pod_net_bytes_transmitted": `sum by (namespace, pod) (irate(container_network_transmit_bytes_total{pod!="", interface!~"^(cali.+|tunl.+|dummy.+|kube.+|flannel.+|cni.+|docker.+|veth.+|lo.*)", job="kubelet"}[5m])) * on (namespace, pod) group_left(owner_kind, owner_name) kube_pod_owner{$1} * on (namespace, pod) group_left(node) kube_pod_info{$2}`,
|
||||||
|
"pod_net_bytes_received": `sum by (namespace, pod) (irate(container_network_receive_bytes_total{pod!="", interface!~"^(cali.+|tunl.+|dummy.+|kube.+|flannel.+|cni.+|docker.+|veth.+|lo.*)", job="kubelet"}[5m])) * on (namespace, pod) group_left(owner_kind, owner_name) kube_pod_owner{$1} * on (namespace, pod) group_left(node) kube_pod_info{$2}`,
|
||||||
|
"pod_cpu_resource_limits": `sum by (namespace, pod) (kube_pod_container_resource_limits{origin_prometheus=~"",resource="cpu",unit="core"}) * on (namespace, pod) group_left(owner_kind, owner_name) kube_pod_owner{$1} * on (namespace, pod) group_left(node) kube_pod_info{$2}`,
|
||||||
|
"pod_memory_resource_limits": `sum by (namespace, pod) (kube_pod_container_resource_limits{origin_prometheus=~"",resource="memory",unit="byte"}) * on (namespace, pod) group_left(owner_kind, owner_name) kube_pod_owner{$1} * on (namespace, pod) group_left(node) kube_pod_info{$2}`,
|
||||||
|
|
||||||
|
// container
|
||||||
|
"container_cpu_usage": `round(sum by (namespace, pod, container) (irate(container_cpu_usage_seconds_total{job="kubelet", container!="POD", container!="", image!="", $1}[5m])), 0.001)`,
|
||||||
|
"container_memory_usage": `sum by (namespace, pod, container) (container_memory_usage_bytes{job="kubelet", container!="POD", container!="", image!="", $1})`,
|
||||||
|
"container_memory_usage_wo_cache": `sum by (namespace, pod, container) (container_memory_working_set_bytes{job="kubelet", container!="POD", container!="", image!="", $1})`,
|
||||||
|
"container_processes_usage": `sum by (namespace, pod, container) (container_processes{job="kubelet", container!="POD", container!="", image!="", $1})`,
|
||||||
|
"container_threads_usage": `sum by (namespace, pod, container) (container_threads {job="kubelet", container!="POD", container!="", image!="", $1})`,
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeExpr(metric string, opts monitoring.QueryOptions) string {
|
||||||
|
tmpl := promQLTemplates[metric]
|
||||||
|
switch opts.Level {
|
||||||
|
case monitoring.LevelCluster:
|
||||||
|
return tmpl
|
||||||
|
case monitoring.LevelNode:
|
||||||
|
return makeNodeMetricExpr(tmpl, opts)
|
||||||
|
case monitoring.LevelWorkspace:
|
||||||
|
return makeWorkspaceMetricExpr(tmpl, opts)
|
||||||
|
case monitoring.LevelNamespace:
|
||||||
|
return makeNamespaceMetricExpr(tmpl, opts)
|
||||||
|
case monitoring.LevelController:
|
||||||
|
return makeControllerMetricExpr(tmpl, opts)
|
||||||
|
case monitoring.LevelPod:
|
||||||
|
return makePodMetricExpr(tmpl, opts)
|
||||||
|
case monitoring.LevelContainer:
|
||||||
|
return makeContainerMetricExpr(tmpl, opts)
|
||||||
|
case monitoring.LevelComponent:
|
||||||
|
return tmpl
|
||||||
|
default:
|
||||||
|
return tmpl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeNodeMetricExpr(tmpl string, o monitoring.QueryOptions) string {
|
||||||
|
var nodeSelector string
|
||||||
|
if o.NodeName != "" {
|
||||||
|
nodeSelector = fmt.Sprintf(`node="%s"`, o.NodeName)
|
||||||
|
} else {
|
||||||
|
nodeSelector = fmt.Sprintf(`node=~"%s"`, o.ResourceFilter)
|
||||||
|
}
|
||||||
|
return strings.Replace(tmpl, "$1", nodeSelector, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeWorkspaceMetricExpr(tmpl string, o monitoring.QueryOptions) string {
|
||||||
|
var workspaceSelector string
|
||||||
|
if o.WorkspaceName != "" {
|
||||||
|
workspaceSelector = fmt.Sprintf(`workspace="%s"`, o.WorkspaceName)
|
||||||
|
} else {
|
||||||
|
workspaceSelector = fmt.Sprintf(`workspace=~"%s", workspace!=""`, o.ResourceFilter)
|
||||||
|
}
|
||||||
|
return strings.Replace(tmpl, "$1", workspaceSelector, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeNamespaceMetricExpr(tmpl string, o monitoring.QueryOptions) string {
|
||||||
|
var namespaceSelector string
|
||||||
|
|
||||||
|
// For monitoring namespaces in the specific workspace
|
||||||
|
// GET /workspaces/{workspace}/namespaces
|
||||||
|
if o.WorkspaceName != "" {
|
||||||
|
namespaceSelector = fmt.Sprintf(`workspace="%s", namespace=~"%s"`, o.WorkspaceName, o.ResourceFilter)
|
||||||
|
return strings.Replace(tmpl, "$1", namespaceSelector, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// For monitoring the specific namespaces
|
||||||
|
// GET /namespaces/{namespace} or
|
||||||
|
// GET /namespaces
|
||||||
|
if o.Namespace != "" {
|
||||||
|
namespaceSelector = fmt.Sprintf(`namespace="%s"`, o.Namespace)
|
||||||
|
} else {
|
||||||
|
namespaceSelector = fmt.Sprintf(`namespace=~"%s"`, o.ResourceFilter)
|
||||||
|
}
|
||||||
|
return strings.Replace(tmpl, "$1", namespaceSelector, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeControllerMetricExpr(tmpl string, o monitoring.QueryOptions) string {
|
||||||
|
var workload string
|
||||||
|
|
||||||
|
workload = fmt.Sprintf(`workload="%s"`, o.WorkloadName)
|
||||||
|
return strings.NewReplacer("$1", workload).Replace(tmpl)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makePodMetricExpr(tmpl string, o monitoring.QueryOptions) string {
|
||||||
|
var podName string
|
||||||
|
|
||||||
|
podName = fmt.Sprintf(`pod="%s"`, o.PodName)
|
||||||
|
return strings.NewReplacer("$1", podName).Replace(tmpl)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeContainerMetricExpr(tmpl string, o monitoring.QueryOptions) string {
|
||||||
|
var containerSelector string
|
||||||
|
if o.ContainerName != "" {
|
||||||
|
containerSelector = fmt.Sprintf(`pod="%s", namespace="%s", container="%s"`, o.PodName, o.Namespace, o.ContainerName)
|
||||||
|
} else {
|
||||||
|
containerSelector = fmt.Sprintf(`pod="%s", namespace="%s", container=~"%s"`, o.PodName, o.Namespace, o.ResourceFilter)
|
||||||
|
}
|
||||||
|
return strings.Replace(tmpl, "$1", containerSelector, -1)
|
||||||
|
}
|
|
@ -0,0 +1,348 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
Copyright (c) [2023] [pcm]
|
||||||
|
[pcm-coordinator] is licensed under Mulan PSL v2.
|
||||||
|
You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
http://license.coscl.org.cn/MulanPSL2
|
||||||
|
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
EITHER EXPaRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
See the Mulan PSL v2 for more details.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
package monitoring
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Level int
|
||||||
|
|
||||||
|
const (
|
||||||
|
LevelCluster = 1 << iota
|
||||||
|
LevelNode
|
||||||
|
LevelWorkspace
|
||||||
|
LevelNamespace
|
||||||
|
LevelApplication
|
||||||
|
LevelOpenpitrix
|
||||||
|
LevelController
|
||||||
|
LevelService
|
||||||
|
LevelPod
|
||||||
|
LevelContainer
|
||||||
|
LevelPVC
|
||||||
|
LevelComponent
|
||||||
|
LevelIngress
|
||||||
|
)
|
||||||
|
|
||||||
|
var MeteringLevelMap = map[string]int{
|
||||||
|
"LevelCluster": LevelCluster,
|
||||||
|
"LevelNode": LevelNode,
|
||||||
|
"LevelWorkspace": LevelWorkspace,
|
||||||
|
"LevelNamespace": LevelNamespace,
|
||||||
|
"LevelApplication": LevelApplication,
|
||||||
|
"LevelController": LevelController,
|
||||||
|
"LevelService": LevelService,
|
||||||
|
"LevelPod": LevelPod,
|
||||||
|
"LevelContainer": LevelContainer,
|
||||||
|
"LevelPVC": LevelPVC,
|
||||||
|
"LevelComponent": LevelComponent,
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryOption interface {
|
||||||
|
Apply(*QueryOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Meteroptions struct {
|
||||||
|
Start time.Time
|
||||||
|
End time.Time
|
||||||
|
Step time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryOptions struct {
|
||||||
|
Level Level
|
||||||
|
|
||||||
|
NamespacedResourcesFilter string
|
||||||
|
QueryType string
|
||||||
|
ResourceFilter string
|
||||||
|
NodeName string
|
||||||
|
WorkspaceName string
|
||||||
|
Namespace string
|
||||||
|
WorkloadKind string
|
||||||
|
WorkloadName string
|
||||||
|
OwnerName string
|
||||||
|
PodName string
|
||||||
|
PodsName string
|
||||||
|
ContainerName string
|
||||||
|
StorageClassName string
|
||||||
|
PersistentVolumeClaimName string
|
||||||
|
PVCFilter string
|
||||||
|
ApplicationName string
|
||||||
|
ServiceName string
|
||||||
|
Ingress string
|
||||||
|
Job string
|
||||||
|
Duration *time.Duration
|
||||||
|
MeterOptions *Meteroptions
|
||||||
|
}
|
||||||
|
type ClusterOption struct{}
|
||||||
|
|
||||||
|
func (_ ClusterOption) Apply(o *QueryOptions) {
|
||||||
|
o.Level = LevelCluster
|
||||||
|
}
|
||||||
|
|
||||||
|
type NodeOption struct {
|
||||||
|
ResourceFilter string
|
||||||
|
NodeName string
|
||||||
|
PVCFilter string
|
||||||
|
StorageClassName string
|
||||||
|
QueryType string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (no NodeOption) Apply(o *QueryOptions) {
|
||||||
|
o.Level = LevelNode
|
||||||
|
o.ResourceFilter = no.ResourceFilter
|
||||||
|
o.NodeName = no.NodeName
|
||||||
|
o.PVCFilter = no.PVCFilter
|
||||||
|
o.StorageClassName = no.StorageClassName
|
||||||
|
o.QueryType = no.QueryType
|
||||||
|
}
|
||||||
|
|
||||||
|
type WorkspaceOption struct {
|
||||||
|
ResourceFilter string
|
||||||
|
WorkspaceName string
|
||||||
|
PVCFilter string
|
||||||
|
StorageClassName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wo WorkspaceOption) Apply(o *QueryOptions) {
|
||||||
|
o.Level = LevelWorkspace
|
||||||
|
o.ResourceFilter = wo.ResourceFilter
|
||||||
|
o.WorkspaceName = wo.WorkspaceName
|
||||||
|
o.PVCFilter = wo.PVCFilter
|
||||||
|
o.StorageClassName = wo.StorageClassName
|
||||||
|
}
|
||||||
|
|
||||||
|
type NamespaceOption struct {
|
||||||
|
ResourceFilter string
|
||||||
|
WorkspaceName string
|
||||||
|
NamespaceName string
|
||||||
|
PVCFilter string
|
||||||
|
StorageClassName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (no NamespaceOption) Apply(o *QueryOptions) {
|
||||||
|
o.Level = LevelNamespace
|
||||||
|
o.ResourceFilter = no.ResourceFilter
|
||||||
|
o.WorkspaceName = no.WorkspaceName
|
||||||
|
o.Namespace = no.NamespaceName
|
||||||
|
o.PVCFilter = no.PVCFilter
|
||||||
|
o.StorageClassName = no.StorageClassName
|
||||||
|
}
|
||||||
|
|
||||||
|
type ApplicationsOption struct {
|
||||||
|
NamespaceName string
|
||||||
|
Applications []string
|
||||||
|
StorageClassName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (aso ApplicationsOption) Apply(o *QueryOptions) {
|
||||||
|
// nothing should be done
|
||||||
|
//nolint:gosimple
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type OpenpitrixsOption struct {
|
||||||
|
Cluster string
|
||||||
|
NamespaceName string
|
||||||
|
Openpitrixs []string
|
||||||
|
StorageClassName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewQueryOptions() *QueryOptions {
|
||||||
|
return &QueryOptions{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (oso OpenpitrixsOption) Apply(o *QueryOptions) {
|
||||||
|
// nothing should be done
|
||||||
|
//nolint:gosimple
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplicationsOption & OpenpitrixsOption share the same ApplicationOption struct
|
||||||
|
type ApplicationOption struct {
|
||||||
|
NamespaceName string
|
||||||
|
Application string
|
||||||
|
ApplicationComponents []string
|
||||||
|
StorageClassName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ao ApplicationOption) Apply(o *QueryOptions) {
|
||||||
|
o.Level = LevelApplication
|
||||||
|
o.Namespace = ao.NamespaceName
|
||||||
|
o.ApplicationName = ao.Application
|
||||||
|
o.StorageClassName = ao.StorageClassName
|
||||||
|
|
||||||
|
app_components := strings.Join(ao.ApplicationComponents[:], "|")
|
||||||
|
|
||||||
|
if len(app_components) > 0 {
|
||||||
|
o.ResourceFilter = fmt.Sprintf(`namespace="%s", workload=~"%s"`, o.Namespace, app_components)
|
||||||
|
} else {
|
||||||
|
o.ResourceFilter = fmt.Sprintf(`namespace="%s", workload=~"%s"`, o.Namespace, ".*")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type WorkloadOption struct {
|
||||||
|
ResourceFilter string
|
||||||
|
NamespaceName string
|
||||||
|
WorkloadKind string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wo WorkloadOption) Apply(o *QueryOptions) {
|
||||||
|
o.Level = LevelController
|
||||||
|
o.ResourceFilter = wo.ResourceFilter
|
||||||
|
o.Namespace = wo.NamespaceName
|
||||||
|
o.WorkloadKind = wo.WorkloadKind
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServicesOption struct {
|
||||||
|
NamespaceName string
|
||||||
|
Services []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sso ServicesOption) Apply(o *QueryOptions) {
|
||||||
|
// nothing should be done
|
||||||
|
//nolint:gosimple
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServiceOption struct {
|
||||||
|
ResourceFilter string
|
||||||
|
NamespaceName string
|
||||||
|
ServiceName string
|
||||||
|
PodNames []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (so ServiceOption) Apply(o *QueryOptions) {
|
||||||
|
o.Level = LevelService
|
||||||
|
o.Namespace = so.NamespaceName
|
||||||
|
o.ServiceName = so.ServiceName
|
||||||
|
|
||||||
|
pod_names := strings.Join(so.PodNames, "|")
|
||||||
|
|
||||||
|
if len(pod_names) > 0 {
|
||||||
|
o.ResourceFilter = fmt.Sprintf(`pod=~"%s", namespace="%s"`, pod_names, o.Namespace)
|
||||||
|
} else {
|
||||||
|
o.ResourceFilter = fmt.Sprintf(`pod=~"%s", namespace="%s"`, ".*", o.Namespace)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type PodOption struct {
|
||||||
|
NamespacedResourcesFilter string
|
||||||
|
ResourceFilter string
|
||||||
|
NodeName string
|
||||||
|
NamespaceName string
|
||||||
|
WorkloadKind string
|
||||||
|
WorkloadName string
|
||||||
|
PodName string
|
||||||
|
}
|
||||||
|
|
||||||
|
type ControllerOption struct {
|
||||||
|
Namespace string
|
||||||
|
Kind string
|
||||||
|
WorkloadName string
|
||||||
|
Level string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (po PodOption) Apply(o *QueryOptions) {
|
||||||
|
o.Level = LevelPod
|
||||||
|
o.NamespacedResourcesFilter = po.NamespacedResourcesFilter
|
||||||
|
o.ResourceFilter = po.ResourceFilter
|
||||||
|
o.NodeName = po.NodeName
|
||||||
|
o.Namespace = po.NamespaceName
|
||||||
|
o.WorkloadKind = po.WorkloadKind
|
||||||
|
o.OwnerName = po.WorkloadName
|
||||||
|
o.PodName = po.PodName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (co ControllerOption) Apply(o *QueryOptions) {
|
||||||
|
o.Level = LevelController
|
||||||
|
o.Namespace = co.Namespace
|
||||||
|
o.WorkloadKind = co.Kind
|
||||||
|
o.WorkloadName = co.WorkloadName
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
type ContainerOption struct {
|
||||||
|
ResourceFilter string
|
||||||
|
NamespaceName string
|
||||||
|
PodName string
|
||||||
|
ContainerName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (co ContainerOption) Apply(o *QueryOptions) {
|
||||||
|
o.Level = LevelContainer
|
||||||
|
o.ResourceFilter = co.ResourceFilter
|
||||||
|
o.Namespace = co.NamespaceName
|
||||||
|
o.PodName = co.PodName
|
||||||
|
o.ContainerName = co.ContainerName
|
||||||
|
}
|
||||||
|
|
||||||
|
type PVCOption struct {
|
||||||
|
ResourceFilter string
|
||||||
|
NamespaceName string
|
||||||
|
StorageClassName string
|
||||||
|
PersistentVolumeClaimName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (po PVCOption) Apply(o *QueryOptions) {
|
||||||
|
o.Level = LevelPVC
|
||||||
|
o.ResourceFilter = po.ResourceFilter
|
||||||
|
o.Namespace = po.NamespaceName
|
||||||
|
o.StorageClassName = po.StorageClassName
|
||||||
|
o.PersistentVolumeClaimName = po.PersistentVolumeClaimName
|
||||||
|
|
||||||
|
// for meter
|
||||||
|
o.PVCFilter = po.PersistentVolumeClaimName
|
||||||
|
}
|
||||||
|
|
||||||
|
type IngressOption struct {
|
||||||
|
ResourceFilter string
|
||||||
|
NamespaceName string
|
||||||
|
Ingress string
|
||||||
|
Job string
|
||||||
|
Pod string
|
||||||
|
Duration *time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (no IngressOption) Apply(o *QueryOptions) {
|
||||||
|
o.Level = LevelIngress
|
||||||
|
o.ResourceFilter = no.ResourceFilter
|
||||||
|
o.Namespace = no.NamespaceName
|
||||||
|
o.Ingress = no.Ingress
|
||||||
|
o.Job = no.Job
|
||||||
|
o.PodName = no.Pod
|
||||||
|
o.Duration = no.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
type ComponentOption struct{}
|
||||||
|
|
||||||
|
func (_ ComponentOption) Apply(o *QueryOptions) {
|
||||||
|
o.Level = LevelComponent
|
||||||
|
}
|
||||||
|
|
||||||
|
type MeterOption struct {
|
||||||
|
Start time.Time
|
||||||
|
End time.Time
|
||||||
|
Step time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mo MeterOption) Apply(o *QueryOptions) {
|
||||||
|
o.MeterOptions = &Meteroptions{
|
||||||
|
Start: mo.Start,
|
||||||
|
End: mo.End,
|
||||||
|
Step: mo.Step,
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,120 @@
|
||||||
|
package monitoring
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
jsoniter "github.com/json-iterator/go"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
Copyright (c) [2023] [pcm]
|
||||||
|
[pcm-coordinator] is licensed under Mulan PSL v2.
|
||||||
|
You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
http://license.coscl.org.cn/MulanPSL2
|
||||||
|
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
EITHER EXPaRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
See the Mulan PSL v2 for more details.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
const (
|
||||||
|
MetricTypeMatrix = "matrix"
|
||||||
|
MetricTypeVector = "vector"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Metadata struct {
|
||||||
|
Metric string `json:"metric,omitempty" description:"metric name"`
|
||||||
|
Type string `json:"type,omitempty" description:"metric type"`
|
||||||
|
Help string `json:"help,omitempty" description:"metric description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Metric struct {
|
||||||
|
MetricName string `json:"metric_name,omitempty" description:"metric name, eg. scheduler_up_sum" csv:"metric_name"`
|
||||||
|
MetricData `json:"data,omitempty" description:"actual metric result"`
|
||||||
|
Error string `json:"error,omitempty" csv:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MetricValues []MetricValue
|
||||||
|
|
||||||
|
type MetricData struct {
|
||||||
|
MetricType string `json:"resultType,omitempty" description:"result type, one of matrix, vector" csv:"metric_type"`
|
||||||
|
MetricValues `json:"result,omitempty" description:"metric data including labels, time series and values" csv:"metric_values"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DashboardEntity struct {
|
||||||
|
GrafanaDashboardUrl string `json:"grafanaDashboardUrl,omitempty"`
|
||||||
|
GrafanaDashboardContent string `json:"grafanaDashboardContent,omitempty"`
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
Namespace string `json:"namespace,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// The first element is the timestamp, the second is the metric value.
|
||||||
|
// eg, [1585658599.195, 0.528]
|
||||||
|
type Point [2]float64
|
||||||
|
|
||||||
|
type MetricValue struct {
|
||||||
|
Metadata map[string]string `json:"metric,omitempty" description:"time series labels"`
|
||||||
|
// The type of Point is a float64 array with fixed length of 2.
|
||||||
|
// So Point will always be initialized as [0, 0], rather than nil.
|
||||||
|
// To allow empty Sample, we should declare Sample to type *Point
|
||||||
|
Sample *Point `json:"value,omitempty" description:"time series, values of vector type"`
|
||||||
|
Series []Point `json:"values,omitempty" description:"time series, values of matrix type"`
|
||||||
|
ExportSample *ExportPoint `json:"exported_value,omitempty" description:"exported time series, values of vector type"`
|
||||||
|
ExportedSeries []ExportPoint `json:"exported_values,omitempty" description:"exported time series, values of matrix type"`
|
||||||
|
|
||||||
|
MinValue string `json:"min_value" description:"minimum value from monitor points"`
|
||||||
|
MaxValue string `json:"max_value" description:"maximum value from monitor points"`
|
||||||
|
AvgValue string `json:"avg_value" description:"average value from monitor points"`
|
||||||
|
SumValue string `json:"sum_value" description:"sum value from monitor points"`
|
||||||
|
Fee string `json:"fee" description:"resource fee"`
|
||||||
|
ResourceUnit string `json:"resource_unit"`
|
||||||
|
CurrencyUnit string `json:"currency_unit"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Point) Timestamp() float64 {
|
||||||
|
return p[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Point) Value() float64 {
|
||||||
|
return p[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Point) transferToExported() ExportPoint {
|
||||||
|
return ExportPoint{p[0], p[1]}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Point) Add(other Point) Point {
|
||||||
|
return Point{p[0], p[1] + other[1]}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements json.Marshaler. It will be called when writing JSON to HTTP response
|
||||||
|
// Inspired by prometheus/client_golang
|
||||||
|
func (p Point) MarshalJSON() ([]byte, error) {
|
||||||
|
t, err := jsoniter.Marshal(p.Timestamp())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
v, err := jsoniter.Marshal(strconv.FormatFloat(p.Value(), 'f', -1, 64))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return []byte(fmt.Sprintf("[%s,%s]", t, v)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExportPoint [2]float64
|
||||||
|
|
||||||
|
func (p ExportPoint) Timestamp() string {
|
||||||
|
return time.Unix(int64(p[0]), 0).Format("2006-01-02 03:04:05 PM")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p ExportPoint) Value() float64 {
|
||||||
|
return p[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p ExportPoint) Format() string {
|
||||||
|
return p.Timestamp() + " " + strconv.FormatFloat(p.Value(), 'f', -1, 64)
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
package setting
|
|
@ -0,0 +1,210 @@
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
"jcc-schedule/pkg/apiserver"
|
||||||
|
"jcc-schedule/tool"
|
||||||
|
v12 "k8s.io/api/apps/v1"
|
||||||
|
v2 "k8s.io/api/autoscaling/v2"
|
||||||
|
v13 "k8s.io/api/batch/v1"
|
||||||
|
v1 "k8s.io/api/core/v1"
|
||||||
|
"k8s.io/api/extensions/v1beta1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type param struct {
|
||||||
|
Name string
|
||||||
|
Namespace string
|
||||||
|
}
|
||||||
|
|
||||||
|
type PodDetail struct {
|
||||||
|
clusterName string
|
||||||
|
podList []*v1.Pod
|
||||||
|
storeAmount float64
|
||||||
|
}
|
||||||
|
|
||||||
|
type AppDetail struct {
|
||||||
|
ClusterName string
|
||||||
|
Service *v1.Service
|
||||||
|
Deployment *v12.Deployment
|
||||||
|
Job *v13.Job
|
||||||
|
StatefulSet *v12.StatefulSet
|
||||||
|
IngressList *v1beta1.IngressList
|
||||||
|
ConfigMap *v1.ConfigMap
|
||||||
|
Secret *v1.Secret
|
||||||
|
HorizontalPodAutoscaler *v2.HorizontalPodAutoscaler
|
||||||
|
}
|
||||||
|
|
||||||
|
type App struct {
|
||||||
|
Deployment *v12.Deployment
|
||||||
|
StatefulSet *v12.StatefulSet
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAppDetail(c *gin.Context) {
|
||||||
|
clusterName := c.Param("clusterName")
|
||||||
|
var p = ¶m{}
|
||||||
|
if err := c.BindJSON(&p); err != nil {
|
||||||
|
Response(c, http.StatusBadRequest, "invalid request params.", "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
details := make([]*AppDetail, 0)
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(len(apiserver.ApiServer.ClientSetMap))
|
||||||
|
//查询指定集群下指定命名空间下和名字的deployment详情
|
||||||
|
for k := range apiserver.ApiServer.ClientSetMap {
|
||||||
|
cli := k
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
//转换赋值
|
||||||
|
var (
|
||||||
|
kDeployment v12.Deployment
|
||||||
|
kStatefulSets v12.StatefulSet
|
||||||
|
kIngresses v1beta1.IngressList
|
||||||
|
kService v1.Service
|
||||||
|
kConfigMap v1.ConfigMap
|
||||||
|
kHpa v2.HorizontalPodAutoscaler
|
||||||
|
kSecret v1.Secret
|
||||||
|
)
|
||||||
|
appDetail := AppDetail{
|
||||||
|
ClusterName: cli,
|
||||||
|
}
|
||||||
|
//获取指定命名空间下的deployment
|
||||||
|
deployment, err := apiserver.ApiServer.ClientSetMap[clusterName].AppsV1().Deployments(p.Namespace).Get(context.Background(), p.Name, metav1.GetOptions{})
|
||||||
|
if err == nil {
|
||||||
|
tool.Convert(deployment, &kDeployment)
|
||||||
|
appDetail.Deployment = &kDeployment
|
||||||
|
}
|
||||||
|
|
||||||
|
//查询指定集群下指定命名空间下和名字的statefulSets详情
|
||||||
|
statefulSets, err := apiserver.ApiServer.ClientSetMap[clusterName].AppsV1().StatefulSets(p.Namespace).Get(context.Background(), p.Name, metav1.GetOptions{})
|
||||||
|
if err == nil {
|
||||||
|
tool.Convert(statefulSets, &kStatefulSets)
|
||||||
|
appDetail.StatefulSet = &kStatefulSets
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询 Ingress 资源
|
||||||
|
labelSelector := fmt.Sprintf("cloud.sealos.io/app-deploy-manager=%s", p.Name)
|
||||||
|
// 执行查询操作,获取匹配标签的 Ingress 资源列表
|
||||||
|
ingresses, err := apiserver.ApiServer.ClientSetMap[clusterName].NetworkingV1().Ingresses(p.Namespace).List(context.Background(), metav1.ListOptions{LabelSelector: labelSelector})
|
||||||
|
if err == nil {
|
||||||
|
tool.Convert(ingresses, &kIngresses)
|
||||||
|
appDetail.IngressList = &kIngresses
|
||||||
|
}
|
||||||
|
|
||||||
|
//查询 Service 资源
|
||||||
|
service, err := apiserver.ApiServer.ClientSetMap[clusterName].CoreV1().Services(p.Namespace).Get(context.Background(), p.Name, metav1.GetOptions{})
|
||||||
|
if err == nil {
|
||||||
|
tool.Convert(service, &kService)
|
||||||
|
appDetail.Service = &kService
|
||||||
|
}
|
||||||
|
//查询configmap
|
||||||
|
configmap, err := apiserver.ApiServer.ClientSetMap[clusterName].CoreV1().ConfigMaps(p.Namespace).Get(context.Background(), p.Name, metav1.GetOptions{})
|
||||||
|
if err == nil {
|
||||||
|
tool.Convert(configmap, &kConfigMap)
|
||||||
|
appDetail.ConfigMap = &kConfigMap
|
||||||
|
}
|
||||||
|
//查询hpa
|
||||||
|
hpa, err := apiserver.ApiServer.ClientSetMap[clusterName].AutoscalingV1().HorizontalPodAutoscalers(p.Namespace).Get(context.Background(), p.Name, metav1.GetOptions{})
|
||||||
|
if err == nil {
|
||||||
|
tool.Convert(hpa, &kHpa)
|
||||||
|
appDetail.HorizontalPodAutoscaler = &kHpa
|
||||||
|
}
|
||||||
|
secret, err := apiserver.ApiServer.ClientSetMap[clusterName].CoreV1().Secrets(p.Namespace).Get(context.Background(), p.Name, metav1.GetOptions{})
|
||||||
|
if err == nil {
|
||||||
|
tool.Convert(secret, &kSecret)
|
||||||
|
appDetail.Secret = &kSecret
|
||||||
|
}
|
||||||
|
details = append(details, &appDetail)
|
||||||
|
}()
|
||||||
|
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
logx.Infof("查询app详情成功")
|
||||||
|
Response(c, http.StatusOK, "success", details)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAppByAppName(c *gin.Context) {
|
||||||
|
var p = ¶m{}
|
||||||
|
if err := c.BindJSON(&p); err != nil {
|
||||||
|
Response(c, http.StatusBadRequest, "invalid request params.", "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
app := App{}
|
||||||
|
var kDeployment v12.Deployment
|
||||||
|
var kStatefulSet v12.StatefulSet
|
||||||
|
for k, _ := range apiserver.ApiServer.ClientSetMap {
|
||||||
|
// 获取指定Deployment
|
||||||
|
deployment, err := apiserver.ApiServer.ClientSetMap[k].AppsV1().Deployments(p.Namespace).Get(c, p.Name, metav1.GetOptions{})
|
||||||
|
if err == nil {
|
||||||
|
tool.Convert(deployment, &kDeployment)
|
||||||
|
app.Deployment = &kDeployment
|
||||||
|
}
|
||||||
|
// 获取指定StatefulSet
|
||||||
|
statefulSet, err := apiserver.ApiServer.ClientSetMap[k].AppsV1().StatefulSets(p.Namespace).Get(c, p.Name, metav1.GetOptions{})
|
||||||
|
if err == nil {
|
||||||
|
tool.Convert(statefulSet, &kStatefulSet)
|
||||||
|
app.StatefulSet = &kStatefulSet
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Response(c, http.StatusOK, "success", app)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DelApp(c *gin.Context) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAppPodsByAppName(c *gin.Context) {
|
||||||
|
clusterName := c.Param("clusterName")
|
||||||
|
var p = ¶m{}
|
||||||
|
if err := c.BindJSON(&p); err != nil {
|
||||||
|
Response(c, http.StatusBadRequest, "invalid request params.", "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
details := make([]*PodDetail, 0)
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(len(apiserver.ApiServer.ClientSetMap))
|
||||||
|
//查询指定集群下指定命名空间下和名字的deployment详情
|
||||||
|
for k, _ := range apiserver.ApiServer.ClientSetMap {
|
||||||
|
cli := k
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
appDetail := PodDetail{}
|
||||||
|
var store int64 = 0
|
||||||
|
appSelector := fmt.Sprintf("app=%s", p.Name)
|
||||||
|
var KPodList []*v1.Pod
|
||||||
|
//查询pods
|
||||||
|
pods, err := apiserver.ApiServer.ClientSetMap[clusterName].CoreV1().Pods(p.Namespace).List(context.Background(), metav1.ListOptions{LabelSelector: appSelector})
|
||||||
|
if err != nil {
|
||||||
|
logx.Error(err.Error())
|
||||||
|
}
|
||||||
|
//查询指定集群下指定命名空间下和名字的statefulSets详情
|
||||||
|
statefulSets, _ := apiserver.ApiServer.ClientSetMap[clusterName].AppsV1().StatefulSets(p.Namespace).Get(context.Background(), p.Name, metav1.GetOptions{})
|
||||||
|
//计算存储卷大小
|
||||||
|
if statefulSets.Spec.VolumeClaimTemplates != nil {
|
||||||
|
//存储卷大小求和
|
||||||
|
for _, v := range statefulSets.Spec.VolumeClaimTemplates {
|
||||||
|
store += v.Spec.Resources.Requests.Storage().Value()
|
||||||
|
}
|
||||||
|
appDetail.storeAmount = float64(store) / 1024 / 1024 / 1024
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(pods.Items) > 0 {
|
||||||
|
tool.Convert(pods.Items, &KPodList)
|
||||||
|
appDetail.podList = KPodList
|
||||||
|
appDetail.clusterName = cli
|
||||||
|
details = append(details, &appDetail)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
Response(c, http.StatusOK, "success", details)
|
||||||
|
}
|
|
@ -0,0 +1,150 @@
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"jcc-schedule/pkg/apiserver"
|
||||||
|
"jcc-schedule/tool"
|
||||||
|
v1 "k8s.io/api/apps/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var deployMutex sync.Mutex
|
||||||
|
|
||||||
|
type Deployment struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Namespace string `json:"namespace"`
|
||||||
|
Labels map[string]string `json:"labels"`
|
||||||
|
Port int32 `json:"port"`
|
||||||
|
ContainerImage string `json:"container_image"`
|
||||||
|
ContainerName string `json:"container_name"`
|
||||||
|
Replicas int32 `json:"replicas"`
|
||||||
|
Cluster string `json:"cluster"`
|
||||||
|
Domain string `json:"domain"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteDeployment(c *gin.Context) {
|
||||||
|
clusterName := c.Param("clusterName")
|
||||||
|
name := c.Param("name")
|
||||||
|
namespace := c.Param("namespace")
|
||||||
|
err := apiserver.ApiServer.ClientSetMap[clusterName].AppsV1().Deployments(namespace).Delete(c, name, metav1.DeleteOptions{})
|
||||||
|
if err != nil {
|
||||||
|
Response(c, http.StatusInternalServerError, "failed", err)
|
||||||
|
}
|
||||||
|
Response(c, http.StatusOK, "success", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func PauseDeployment(c *gin.Context) {
|
||||||
|
clusterName := c.Param("clusterName")
|
||||||
|
name := c.Param("name")
|
||||||
|
namespace := c.Param("namespace")
|
||||||
|
// 缩放副本数量为0
|
||||||
|
rs := int32(0)
|
||||||
|
// 获取Deployment对象
|
||||||
|
deployment, err := apiserver.ApiServer.ClientSetMap[clusterName].AppsV1().Deployments(namespace).Get(c, name, metav1.GetOptions{})
|
||||||
|
if err == nil {
|
||||||
|
deployment.Spec.Replicas = &rs
|
||||||
|
annotations := deployment.GetAnnotations()
|
||||||
|
annotations["deploy.cloud.sealos.io/pause"] = `{"target":"","value":""}`
|
||||||
|
_, err = apiserver.ApiServer.ClientSetMap[clusterName].AppsV1().Deployments(namespace).Update(c, deployment, metav1.UpdateOptions{})
|
||||||
|
}
|
||||||
|
sts, stsErr := apiserver.ApiServer.ClientSetMap[clusterName].AppsV1().StatefulSets(namespace).Get(c, name, metav1.GetOptions{})
|
||||||
|
if stsErr == nil {
|
||||||
|
sts.Spec.Replicas = &rs
|
||||||
|
annotations := sts.GetAnnotations()
|
||||||
|
annotations["deploy.cloud.sealos.io/pause"] = `{"target":"","value":""}`
|
||||||
|
_, err = apiserver.ApiServer.ClientSetMap[clusterName].AppsV1().StatefulSets(namespace).Update(c, sts, metav1.UpdateOptions{})
|
||||||
|
}
|
||||||
|
Response(c, http.StatusOK, "success", nil)
|
||||||
|
}
|
||||||
|
func DeploymentList(c *gin.Context) {
|
||||||
|
clusterName := c.Param("clusterName")
|
||||||
|
namespace := c.Param("namespace")
|
||||||
|
deployments, err := apiserver.ApiServer.ClientSetMap[clusterName].AppsV1().Deployments(namespace).List(c, metav1.ListOptions{})
|
||||||
|
if err != nil {
|
||||||
|
Response(c, http.StatusInternalServerError, "failed", err)
|
||||||
|
}
|
||||||
|
Response(c, http.StatusOK, "success", deployments)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeploymentDetail(c *gin.Context) {
|
||||||
|
clusterName := c.Param("clusterName")
|
||||||
|
name := c.Param("name")
|
||||||
|
namespace := c.Param("namespace")
|
||||||
|
deployment, err := apiserver.ApiServer.ClientSetMap[clusterName].AppsV1().Deployments(namespace).Get(c, name, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
Response(c, http.StatusInternalServerError, "failed", err)
|
||||||
|
}
|
||||||
|
Response(c, http.StatusOK, "success", deployment)
|
||||||
|
}
|
||||||
|
func StartDeployment(c *gin.Context) {
|
||||||
|
clusterName := c.Param("clusterName")
|
||||||
|
name := c.Param("name")
|
||||||
|
namespace := c.Param("namespace")
|
||||||
|
deployment, err := apiserver.ApiServer.ClientSetMap[clusterName].AppsV1().Deployments(namespace).Get(c, name, metav1.GetOptions{})
|
||||||
|
//TODO 获取原始replicas数量,即metadata下annotations.deploy.cloud.sealos.io/minReplicas数量
|
||||||
|
minReplicas := deployment.Annotations["deploy.cloud.sealos.io/minReplicas"]
|
||||||
|
if minReplicas != "" {
|
||||||
|
rs := tool.StringToInt32(minReplicas)
|
||||||
|
deployment.Spec.Replicas = &rs
|
||||||
|
}
|
||||||
|
annotations := deployment.GetAnnotations()
|
||||||
|
delete(annotations, "deploy.cloud.sealos.io/pause")
|
||||||
|
deployment, err = apiserver.ApiServer.ClientSetMap[clusterName].AppsV1().Deployments(namespace).Update(c, deployment, metav1.UpdateOptions{})
|
||||||
|
if err != nil {
|
||||||
|
Response(c, http.StatusInternalServerError, "failed", err)
|
||||||
|
}
|
||||||
|
Response(c, http.StatusOK, "success", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RestartDeployment(c *gin.Context) {
|
||||||
|
clusterName := c.Param("clusterName")
|
||||||
|
name := c.Param("name")
|
||||||
|
namespace := c.Param("namespace")
|
||||||
|
patchOpt := metav1.PatchOptions{FieldManager: "kubectl-rollout"}
|
||||||
|
|
||||||
|
dt := time.Now()
|
||||||
|
|
||||||
|
patchInfo := fmt.Sprintf(`{"spec":{"template":{"metadata":{"annotations":{"kubectl.kubernetes.io/restartedAt":"%s"}}}}}`, dt.String())
|
||||||
|
|
||||||
|
_, err := apiserver.ApiServer.ClientSetMap[clusterName].AppsV1().Deployments(namespace).Patch(c, name, types.StrategicMergePatchType, []byte(patchInfo), patchOpt)
|
||||||
|
if err != nil {
|
||||||
|
Response(c, http.StatusInternalServerError, "failed", err)
|
||||||
|
}
|
||||||
|
Response(c, http.StatusOK, "success", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateDeploymentReplica(c *gin.Context) {
|
||||||
|
clusterName := c.Param("clusterName")
|
||||||
|
name := c.Param("name")
|
||||||
|
namespace := c.Param("namespace")
|
||||||
|
replica := c.Param("replica")
|
||||||
|
// 获取Deployment对象
|
||||||
|
deployment, err := apiserver.ApiServer.ClientSetMap[clusterName].AppsV1().Deployments(namespace).Get(c, name, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
Response(c, http.StatusInternalServerError, "failed", err)
|
||||||
|
}
|
||||||
|
// 缩放Deployment对象副本数量
|
||||||
|
rs := tool.StringToInt32(replica)
|
||||||
|
deployment.Spec.Replicas = &rs
|
||||||
|
annotations := deployment.GetAnnotations()
|
||||||
|
|
||||||
|
if rs == 0 {
|
||||||
|
annotations["deploy.cloud.sealos.io/pause"] = `{"target":"","value":""}`
|
||||||
|
} else {
|
||||||
|
delete(annotations, "deploy.cloud.sealos.io/pause")
|
||||||
|
annotations["deploy.cloud.sealos.io/maxReplicas"] = replica
|
||||||
|
annotations["deploy.cloud.sealos.io/minReplicas"] = replica
|
||||||
|
}
|
||||||
|
deployment, err = apiserver.ApiServer.ClientSetMap[clusterName].AppsV1().Deployments(namespace).Update(c, deployment, metav1.UpdateOptions{})
|
||||||
|
if err != nil {
|
||||||
|
Response(c, http.StatusInternalServerError, "failed", err)
|
||||||
|
}
|
||||||
|
var kDeployment v1.Deployment
|
||||||
|
tool.Convert(deployment, &kDeployment)
|
||||||
|
Response(c, http.StatusOK, "success", kDeployment)
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"jcc-schedule/pkg/apiserver"
|
||||||
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func JobDetail(c *gin.Context) {
|
||||||
|
clusterName := c.Param("clusterName")
|
||||||
|
name := c.Param("name")
|
||||||
|
namespace := c.Param("namespace")
|
||||||
|
job, err := apiserver.ApiServer.ClientSetMap[clusterName].BatchV1().Jobs(namespace).Get(c, name, v1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
Response(c, http.StatusInternalServerError, "failed", err)
|
||||||
|
}
|
||||||
|
Response(c, http.StatusOK, "success", job)
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"jcc-schedule/pkg/apiserver"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ListNamespace 查询Namespace列表
|
||||||
|
func ListNamespace(ctx *gin.Context) {
|
||||||
|
m := make(map[string]string)
|
||||||
|
for k, _ := range apiserver.ApiServer.ClientSetMap {
|
||||||
|
// 获取所有的命名空间
|
||||||
|
namespaces, err := apiserver.ApiServer.ClientSetMap[k].CoreV1().Namespaces().List(context.Background(), metav1.ListOptions{})
|
||||||
|
if err != nil {
|
||||||
|
Response(ctx, http.StatusBadRequest, "failed", err)
|
||||||
|
}
|
||||||
|
// 遍历命名空间并查找以 "ns-" 开头的命名空间
|
||||||
|
for _, ns := range namespaces.Items {
|
||||||
|
if strings.HasPrefix(ns.Name, "ns-") {
|
||||||
|
if m[ns.Name] != "" {
|
||||||
|
m[ns.Name] = m[ns.Name] + "," + k
|
||||||
|
} else {
|
||||||
|
m[ns.Name] = k
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Response(ctx, http.StatusOK, "success", m)
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"jcc-schedule/pkg/apiserver"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Pod struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
ClusterName string `json:"cluster_name"`
|
||||||
|
DomainName string `json:"domain_name"`
|
||||||
|
Ready string `json:"ready"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
Restarts int64 `json:"restarts"`
|
||||||
|
Age string `json:"age"`
|
||||||
|
IP string `json:"IP"`
|
||||||
|
Node string `json:"node"`
|
||||||
|
Namespace string `json:"namespace"`
|
||||||
|
ContainerImage string `json:"container_image"`
|
||||||
|
ContainerName string `json:"container_name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListPod 查询Pod列表
|
||||||
|
func PodList(c *gin.Context) {
|
||||||
|
clusterName := c.Param("clusterName")
|
||||||
|
podList, err := apiserver.ApiServer.ClientSetMap[clusterName].CoreV1().Pods("").List(c, metav1.ListOptions{})
|
||||||
|
if err != nil {
|
||||||
|
Response(c, http.StatusInternalServerError, "failed", err)
|
||||||
|
}
|
||||||
|
Response(c, http.StatusOK, "success", podList)
|
||||||
|
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package app
|
package v1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
|
@ -0,0 +1,37 @@
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"jcc-schedule/pkg/apiserver"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StatefulSet struct {
|
||||||
|
name string
|
||||||
|
namespace string
|
||||||
|
}
|
||||||
|
|
||||||
|
func RestartStatefulSet(c *gin.Context) {
|
||||||
|
clusterName := c.Param("clusterName")
|
||||||
|
var sts = &StatefulSet{}
|
||||||
|
if err := c.BindJSON(&sts); err != nil {
|
||||||
|
Response(c, http.StatusBadRequest, "invalid request params.", "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//实现kubectl rollout restart StatefulSets功能
|
||||||
|
patchOpt := metav1.PatchOptions{FieldManager: "kubectl-rollout"}
|
||||||
|
|
||||||
|
dt := time.Now()
|
||||||
|
|
||||||
|
patchInfo := fmt.Sprintf(`{"spec":{"template":{"metadata":{"annotations":{"kubectl.kubernetes.io/restartedAt":"%s"}}}}}`, dt.String())
|
||||||
|
_, err := apiserver.ApiServer.ClientSetMap[clusterName].AppsV1().StatefulSets(sts.namespace).Patch(context.TODO(), sts.name, types.StrategicMergePatchType, []byte(patchInfo), patchOpt)
|
||||||
|
if err != nil {
|
||||||
|
Response(c, http.StatusBadRequest, "failed", nil)
|
||||||
|
}
|
||||||
|
Response(c, http.StatusOK, "success", nil)
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
package routers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"jcc-schedule/routers/api/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func InitRouter() *gin.Engine {
|
||||||
|
r := gin.New()
|
||||||
|
r.Use(gin.Logger())
|
||||||
|
r.Use(gin.Recovery())
|
||||||
|
|
||||||
|
//api分组
|
||||||
|
apiv1 := r.Group("/api/v1")
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
//Namespace
|
||||||
|
namespace := apiv1.Group("namespace")
|
||||||
|
namespace.GET("/list", v1.ListNamespace)
|
||||||
|
|
||||||
|
//Pod
|
||||||
|
pod := apiv1.Group("pod")
|
||||||
|
pod.GET("/list", v1.PodList)
|
||||||
|
|
||||||
|
//Deployment
|
||||||
|
deployment := apiv1.Group("deployment")
|
||||||
|
deployment.GET("/list", v1.DeploymentList)
|
||||||
|
deployment.GET("/detail", v1.DeploymentDetail)
|
||||||
|
deployment.DELETE("", v1.DeleteDeployment)
|
||||||
|
deployment.PUT("/replica", v1.UpdateDeploymentReplica)
|
||||||
|
deployment.PUT("/restart", v1.RestartDeployment)
|
||||||
|
deployment.PUT("/start", v1.StartDeployment)
|
||||||
|
deployment.PUT("/pause", v1.PauseDeployment)
|
||||||
|
|
||||||
|
// job
|
||||||
|
job := apiv1.Group("job")
|
||||||
|
job.GET("/detail", v1.JobDetail)
|
||||||
|
//app
|
||||||
|
app := apiv1.Group("app")
|
||||||
|
app.GET("/detail", v1.GetAppDetail)
|
||||||
|
app.GET("/pods", v1.GetAppPodsByAppName)
|
||||||
|
app.GET("/info", v1.GetAppByAppName)
|
||||||
|
app.DELETE("", v1.DelApp)
|
||||||
|
//sts
|
||||||
|
statefulSet := apiv1.Group("statefulSet")
|
||||||
|
statefulSet.PUT("/restart", v1.RestartStatefulSet)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package tool
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Convert(source interface{}, target interface{}) {
|
||||||
|
jsonByte, _ := json.Marshal(source)
|
||||||
|
json.Unmarshal(jsonByte, &target)
|
||||||
|
}
|
||||||
|
|
||||||
|
func StringToInt32(str string) int32 {
|
||||||
|
j, _ := strconv.ParseInt(str, 10, 32)
|
||||||
|
return int32(j)
|
||||||
|
}
|
|
@ -1,11 +0,0 @@
|
||||||
package tool
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
func FloatConv(num float64) float64 {
|
|
||||||
num, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", num), 64)
|
|
||||||
return num
|
|
||||||
}
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
package tool
|
||||||
|
|
||||||
|
func Get() {
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue