This commit is contained in:
zhangwei 2024-03-08 17:23:45 +08:00
parent 4fdef4d47a
commit 480c1b553f
55 changed files with 1754 additions and 16627 deletions

View File

@ -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(&param); 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)
}

12
app/clusterClient.go Normal file
View File

@ -0,0 +1,12 @@
package app
import "k8s.io/client-go/kubernetes"
type ClusterClient struct {
KubeClient *kubernetes.Clientset
ClusterName string
}
func NewClusterClientSet(clusterName string) {
}

View File

@ -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)
// 启动 informerList & 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
}
}

View File

@ -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

View File

@ -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)
}

View File

@ -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(&param); err != nil {
Response(c, http.StatusBadRequest, "invalid request params.", "")
return
}
crdList, err := CrDClient.ApiextensionsV1().CustomResourceDefinitions().Create(context.TODO(), &param.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)
}

View File

@ -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)
}

File diff suppressed because it is too large Load Diff

View File

@ -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)
}

View File

@ -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(&lt.LabelId, &lt.LabelTypeId, &lt.LabelType, &lt.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)
}

View File

@ -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(&lt.LabelTypeId, &lt.LabelType, &lt.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)
}

View File

@ -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()
}

View File

@ -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
}

View File

@ -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(&param); 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(), &param.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)
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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

View File

@ -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(&param); 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)
}

View File

@ -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
}
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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,
}
}

View File

@ -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:

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

29
etc/config/config.go Normal file
View File

@ -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)
}
}

5
etc/config/config.yaml Normal file
View File

@ -0,0 +1,5 @@
name: pcm-kubernetes
port: 8022
core:
url: localhost:8080

183
go.mod
View File

@ -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
) )

1218
go.sum

File diff suppressed because it is too large Load Diff

View File

@ -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")
} }

View File

@ -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"`
}

View File

@ -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"`
}

View File

@ -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
}
}
}

View File

@ -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
}

View File

@ -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
}
}

View File

@ -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)
}

View File

@ -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,
}
}

120
pkg/monitoring/types.go Normal file
View File

@ -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)
}

1
pkg/setting/setting.go Normal file
View File

@ -0,0 +1 @@
package setting

210
routers/api/v1/app.go Normal file
View File

@ -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 = &param{}
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 = &param{}
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 = &param{}
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)
}

View File

@ -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)
}

19
routers/api/v1/job.go Normal file
View File

@ -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)
}

View File

@ -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)
}

34
routers/api/v1/pod.go Normal file
View File

@ -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)
}

View File

@ -1,4 +1,4 @@
package app package v1
import ( import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"

View File

@ -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)
}

52
routers/router.go Normal file
View File

@ -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
}

16
tool/convert.go Normal file
View File

@ -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)
}

View File

@ -1,11 +0,0 @@
package tool
import (
"fmt"
"strconv"
)
func FloatConv(num float64) float64 {
num, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", num), 64)
return num
}

5
tool/resty.go Normal file
View File

@ -0,0 +1,5 @@
package tool
func Get() {
}