feat: 仓库迁移,并添加CICD
This commit is contained in:
parent
369a2bf92d
commit
32e69ce521
|
@ -0,0 +1,28 @@
|
|||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
*.a
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Files generated by JetBrains IDEs, e.g. IntelliJ IDEA
|
||||
.idea/
|
||||
*.iml
|
||||
bin/
|
||||
|
||||
# Vscode files
|
||||
.vscode/
|
||||
__debug_bin
|
||||
|
||||
log/
|
||||
cache/
|
||||
tmp/
|
||||
testbin/
|
||||
karmadaConfig/
|
|
@ -0,0 +1,18 @@
|
|||
FROM alpine:3.16.2
|
||||
WORKDIR /home
|
||||
|
||||
# 修改alpine源为上海交通大学
|
||||
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.sjtug.sjtu.edu.cn/g' /etc/apk/repositories && \
|
||||
apk update && \
|
||||
apk upgrade && \
|
||||
apk add --no-cache ca-certificates && update-ca-certificates && \
|
||||
apk add --update tzdata && \
|
||||
rm -rf /var/cache/apk/*
|
||||
|
||||
|
||||
COPY jcce-schedule /home/
|
||||
ENV TZ=Asia/Shanghai
|
||||
|
||||
EXPOSE 8082
|
||||
|
||||
ENTRYPOINT ./jcce-schedule
|
|
@ -0,0 +1,72 @@
|
|||
def JOB_NAME = "${env.JOB_NAME}"
|
||||
def BUILD_NUMBER = "${env.BUILD_NUMBER}"
|
||||
def label = "jenkins-${JOB_NAME}-${BUILD_NUMBER}-${UUID.randomUUID().toString()}"
|
||||
def secret_name = "harbor-auth"
|
||||
|
||||
podTemplate(label: label, containers: [
|
||||
containerTemplate(name: 'golang', image: 'golang:1.18.5-alpine3.16', command: 'cat', ttyEnabled: true),
|
||||
containerTemplate(name: 'docker', image: 'docker:latest', command: 'cat', ttyEnabled: true),
|
||||
containerTemplate(name: 'kubectl', image: 'jcce/kubectl:1.23.7', command: 'cat', ttyEnabled: true)
|
||||
], serviceAccount: 'jenkins', volumes: [
|
||||
hostPathVolume(mountPath: '/var/run/docker.sock', hostPath: '/var/run/docker.sock')
|
||||
]) {
|
||||
node(label) {
|
||||
def myRepo = checkout scm
|
||||
// 获取 git commit id 作为镜像标签
|
||||
def imageTag = sh(script: "git rev-parse --short HEAD", returnStdout: true).trim()
|
||||
// 仓库地址
|
||||
def registryUrl = "hub.jcce-dev.net:8443"
|
||||
def imageEndpoint = "jcce/jcce-schedule"
|
||||
// 镜像
|
||||
def image = "${registryUrl}/${imageEndpoint}:${imageTag}"
|
||||
def imageLatest = "${registryUrl}/${imageEndpoint}:latest"
|
||||
|
||||
stage('单元测试') {
|
||||
echo "1.测试阶段"
|
||||
}
|
||||
stage('代码编译打包') {
|
||||
try {
|
||||
container('golang') {
|
||||
echo "2.代码编译打包阶段"
|
||||
sh """
|
||||
export GOPROXY=https://goproxy.cn
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o jcce-schedule ./main.go
|
||||
"""
|
||||
}
|
||||
} catch (exc) {
|
||||
println "构建失败 - ${currentBuild.fullDisplayName}"
|
||||
throw(exc)
|
||||
}
|
||||
}
|
||||
stage('构建 Docker 镜像') {
|
||||
withCredentials([[$class: 'UsernamePasswordMultiBinding',
|
||||
credentialsId: 'docker-auth',
|
||||
usernameVariable: 'DOCKER_USER',
|
||||
passwordVariable: 'DOCKER_PASSWORD']]) {
|
||||
container('docker') {
|
||||
echo "3. 构建 Docker 镜像阶段"
|
||||
sh('cat /etc/resolv.conf')
|
||||
sh("docker login '${registryUrl}' -u '${DOCKER_USER}' -p '${DOCKER_PASSWORD}' ")
|
||||
sh("docker build -t '${image}' -t '${imageLatest}' .")
|
||||
sh("docker push '${image}'")
|
||||
sh("docker push '${imageLatest}'")
|
||||
sh("docker rmi '${image}' '${imageLatest}'")
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('运行 Kubectl 部署到k8s平台') {
|
||||
withCredentials([file(credentialsId: 'kubeconfig', variable: 'KUBECONFIG')]) {
|
||||
container('kubectl') {
|
||||
echo "5.部署应用"
|
||||
sh('mkdir -p ~/.kube && cp ${KUBECONFIG} ~/.kube/config')
|
||||
sh("sed -i 's#IMAGE_NAME#${image}#' deploy/jcce-schedule-deployment.yaml")
|
||||
sh("sed -i 's#SECRET_NAME#${secret_name}#' deploy/jcce-schedule-deployment.yaml")
|
||||
sh('kubectl apply -f deploy/')
|
||||
sh('sleep 3')
|
||||
echo "6.查看应用"
|
||||
sh('kubectl get all -n jcce-system -l app=${JOB_NAME}')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,360 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"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"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"net/http"
|
||||
"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"`
|
||||
}
|
||||
|
||||
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"`
|
||||
}
|
||||
|
||||
// @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.GetQuery("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()
|
||||
|
||||
if len(clusterName) != 0 && !strings.Contains(cluster.ClusterName, clusterName) {
|
||||
continue
|
||||
} else {
|
||||
clusterList = append(clusterList, cluster)
|
||||
}
|
||||
|
||||
}
|
||||
total := len(clusterList)
|
||||
page := &Page[Cluster]{}
|
||||
page.List = clusterList
|
||||
pageNum, _ := c.GetQuery("pageNum")
|
||||
pageSize, _ := c.GetQuery("pageSize")
|
||||
num, _ := strconv.ParseInt(pageNum, 10, 64)
|
||||
size, _ := strconv.ParseInt(pageSize, 10, 64)
|
||||
data := Paginator(page, int64(total), num, size)
|
||||
Response(c, http.StatusOK, "success", data)
|
||||
}
|
||||
|
||||
// ListClusterWithLabel 查询有标签的集群列表
|
||||
func ListClusterWithLabel(c *gin.Context) {
|
||||
|
||||
clusterName, _ := c.GetQuery("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.GetQuery("pageNum")
|
||||
pageSize, _ := c.GetQuery("pageSize")
|
||||
num, _ := strconv.ParseInt(pageNum, 10, 64)
|
||||
size, _ := strconv.ParseInt(pageSize, 10, 64)
|
||||
data := Paginator(page, int64(total), num, size)
|
||||
Response(c, http.StatusOK, "success", data)
|
||||
}
|
||||
|
||||
// UnJoin 刪除集群
|
||||
func UnJoin(c *gin.Context) {
|
||||
copyContext := c.Copy()
|
||||
clusterName, _ := c.GetQuery("cluster_name")
|
||||
domainId, _ := c.GetQuery("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 func() {
|
||||
glog.Info("异步执行:" + copyContext.Request.URL.Path)
|
||||
err := karmadactl.UnJoinCluster(ControlPlaneRestConfig, nil, opts)
|
||||
if err != nil {
|
||||
glog.Errorf("failed to delete cluster. context: %s, kube-config: %s, error: %v", opts.KarmadaContext, opts.KubeConfig, err)
|
||||
Response(c, http.StatusInternalServerError, fmt.Sprintf("failed to delete cluster. context: %s, kube-config: %s, error: %v",
|
||||
opts.KarmadaContext, opts.KubeConfig, err), "")
|
||||
return
|
||||
}
|
||||
//解绑集群和域的关系
|
||||
_, err = DB.Exec(`delete from domain_cluster where domain_id= ? and cluster_name = ?`, domainId, clusterName)
|
||||
}()
|
||||
Response(c, http.StatusOK, "success", "")
|
||||
}
|
||||
|
||||
// Join 新集群加入联邦
|
||||
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 upload failed err %v", err)
|
||||
Response(c, http.StatusInternalServerError, fmt.Sprintf("File upload failed err: %s", err), "")
|
||||
return
|
||||
}
|
||||
//member集群标签
|
||||
Labels, _ = jsonToMap(c.PostForm("labels"))
|
||||
//资源域名称
|
||||
domainId := c.PostForm("domain_id")
|
||||
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)
|
||||
var memberProvider string
|
||||
for rows.Next() {
|
||||
var domainName string
|
||||
err := rows.Scan(&domainName)
|
||||
if err != nil {
|
||||
glog.Errorf("query failed!,error %v", err)
|
||||
Response(c, http.StatusBadRequest, "query failed!", err)
|
||||
return
|
||||
}
|
||||
memberProvider = domainName
|
||||
}
|
||||
rows.Close()
|
||||
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)
|
||||
if Labels != nil {
|
||||
// 给集群打上标签
|
||||
cluster, _, err := util.GetClusterWithKarmadaClient(KarmadaClient, memberName)
|
||||
if cluster == nil {
|
||||
Response(c, http.StatusBadRequest, "Get cluster failed", err)
|
||||
return
|
||||
}
|
||||
cluster.Labels = mergeMap(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", "")
|
||||
}
|
||||
|
||||
// TagCluster 集群打标签
|
||||
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 集群删除标签
|
||||
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查询集群
|
||||
func ListByDomain(c *gin.Context) {
|
||||
namespaceName, _ := c.GetQuery("namespace")
|
||||
deploymentName, _ := c.GetQuery("deployment_name")
|
||||
domainId, _ := c.GetQuery("domain_id")
|
||||
|
||||
//
|
||||
deployJson := GetDeployFromOS(namespaceName, deploymentName, "")
|
||||
hits, _ := deployJson.Get("hits").Get("hits").Array()
|
||||
var clusterSet []Cluster
|
||||
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 dc.cluster_name,d.domain_name,d.domain_id from domain_cluster dc,domain d where d.domain_id = ? and dc.domain_id = d.domain_id ", domainId)
|
||||
for rows.Next() {
|
||||
var clusterName string
|
||||
var domainName string
|
||||
var domainIdResult int32
|
||||
rows.Scan(&clusterName, &domainName, &domainId)
|
||||
|
||||
if clusterName == cluster {
|
||||
cluster := Cluster{
|
||||
ClusterName: clusterName,
|
||||
DomainId: domainIdResult,
|
||||
DomainName: domainName,
|
||||
}
|
||||
|
||||
clusterSet = append(clusterSet, cluster)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Response(c, http.StatusOK, "success", clusterSet)
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
type sliceError struct {
|
||||
msg string
|
||||
}
|
||||
|
||||
func (e *sliceError) Error() string {
|
||||
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 {
|
||||
result := make(map[string]string)
|
||||
for _, m := range maps {
|
||||
for k, v := range m {
|
||||
result[k] = v
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func removeDuplicateMap(originals []map[string]string) ([]map[string]string, error) {
|
||||
temp := map[string]struct{}{}
|
||||
|
||||
result := make([]map[string]string, 0, len(originals))
|
||||
|
||||
for _, item := range originals {
|
||||
key := fmt.Sprint(item)
|
||||
if _, ok := temp[key]; !ok {
|
||||
temp[key] = struct{}{}
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func removeDuplicateMaps(originals []map[string][]string) ([]map[string][]string, error) {
|
||||
temp := map[string]struct{}{}
|
||||
|
||||
result := make([]map[string][]string, 0, len(originals))
|
||||
|
||||
for _, item := range originals {
|
||||
key := fmt.Sprint(item)
|
||||
if _, ok := temp[key]; !ok {
|
||||
temp[key] = struct{}{}
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// JsonToMap string转map
|
||||
func jsonToMap(jsonStr string) (map[string]string, error) {
|
||||
m := make(map[string]string)
|
||||
err := json.Unmarshal([]byte(jsonStr), &m)
|
||||
if err != nil {
|
||||
glog.Errorf("JsonToMap error %v", err)
|
||||
return nil, err
|
||||
}
|
||||
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 {
|
||||
set := make(map[string]struct{}, len(arr))
|
||||
j := 0
|
||||
for _, v := range arr {
|
||||
_, ok := set[v]
|
||||
if ok {
|
||||
continue
|
||||
}
|
||||
set[v] = struct{}{}
|
||||
arr[j] = v
|
||||
j++
|
||||
}
|
||||
|
||||
return arr[:j]
|
||||
}
|
|
@ -0,0 +1,207 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/bitly/go-simplejson"
|
||||
"github.com/gin-gonic/gin"
|
||||
appv1 "k8s.io/api/apps/v1"
|
||||
coreV1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"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"`
|
||||
}
|
||||
|
||||
// CreateDeployment 创建工作负载
|
||||
func CreateDeployment(c *gin.Context) {
|
||||
var dpRequest Deployment
|
||||
if err := c.BindJSON(&dpRequest); err != nil {
|
||||
Response(c, http.StatusBadRequest, "invalid request params.", "")
|
||||
return
|
||||
}
|
||||
|
||||
deployMutex.Lock()
|
||||
defaultLabels := map[string]string{"jcce": "true"}
|
||||
deployMutex.Unlock()
|
||||
|
||||
namespace := dpRequest.Namespace
|
||||
if dpRequest.Port == 0 {
|
||||
dpRequest.Port = 80
|
||||
}
|
||||
var replicas = dpRequest.Replicas
|
||||
deployment := &appv1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: dpRequest.Name,
|
||||
Namespace: dpRequest.Namespace,
|
||||
},
|
||||
Spec: appv1.DeploymentSpec{
|
||||
Replicas: &replicas,
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: defaultLabels,
|
||||
},
|
||||
Template: coreV1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: dpRequest.Name,
|
||||
Labels: defaultLabels,
|
||||
},
|
||||
Spec: coreV1.PodSpec{
|
||||
Containers: []coreV1.Container{
|
||||
{
|
||||
Name: dpRequest.ContainerName,
|
||||
Image: dpRequest.ContainerImage,
|
||||
Ports: []coreV1.ContainerPort{
|
||||
{
|
||||
Name: "http",
|
||||
Protocol: coreV1.ProtocolTCP,
|
||||
ContainerPort: dpRequest.Port,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
deploymentList, err := ClientSet.AppsV1().Deployments(namespace).Create(context.TODO(), deployment, metav1.CreateOptions{})
|
||||
|
||||
if err != nil {
|
||||
Response(c, http.StatusInternalServerError, "create deployment failed", err)
|
||||
return
|
||||
}
|
||||
Response(c, http.StatusOK, "success", deploymentList)
|
||||
|
||||
}
|
||||
|
||||
// ListDeployment 根据查询工作负载列表(控制平面)
|
||||
func ListDeployment(c *gin.Context) {
|
||||
|
||||
namespace, _ := c.GetQuery("namespace")
|
||||
|
||||
dpList := make([]Deployment, 0)
|
||||
|
||||
deploymentList, err := ClientSet.AppsV1().Deployments(namespace).List(context.TODO(), metav1.ListOptions{})
|
||||
if err != nil {
|
||||
Response(c, http.StatusInternalServerError, "list deployment failed", err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, deploy := range deploymentList.Items {
|
||||
name := deploy.ObjectMeta.Name
|
||||
namespace := deploy.ObjectMeta.Namespace
|
||||
labels := deploy.Labels
|
||||
containerName := deploy.Spec.Template.Spec.Containers[0].Name
|
||||
containerImage := deploy.Spec.Template.Spec.Containers[0].Image
|
||||
replicas := deploy.Spec.Replicas
|
||||
|
||||
dp := Deployment{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Labels: labels,
|
||||
ContainerImage: containerImage,
|
||||
ContainerName: containerName,
|
||||
Replicas: *replicas,
|
||||
}
|
||||
dpList = append(dpList, dp)
|
||||
}
|
||||
|
||||
total := len(dpList)
|
||||
page := &Page[Deployment]{}
|
||||
page.List = dpList
|
||||
pageNum, _ := c.GetQuery("pageNum")
|
||||
pageSize, _ := c.GetQuery("pageSize")
|
||||
num, _ := strconv.ParseInt(pageNum, 10, 64)
|
||||
size, _ := strconv.ParseInt(pageSize, 10, 64)
|
||||
data := Paginator(page, int64(total), num, size)
|
||||
Response(c, http.StatusOK, "success", data)
|
||||
//Response(c, http.StatusOK, "success", dpList)
|
||||
|
||||
}
|
||||
|
||||
// ListClusterDeployment 查询工作负载列表(所有集群)
|
||||
func ListClusterDeployment(c *gin.Context) {
|
||||
//TODO 逻辑待完善
|
||||
ss, _ := KarmadaClient.SearchV1alpha1().ResourceRegistries().Get(context.TODO(), "clustercache-sample", metav1.GetOptions{})
|
||||
|
||||
Response(c, http.StatusOK, "success", ss)
|
||||
}
|
||||
|
||||
// DescribeDeployment 查询工作负载列表(所有集群)
|
||||
func DescribeDeployment(c *gin.Context) {
|
||||
|
||||
namespace, _ := c.GetQuery("namespace")
|
||||
deployName, _ := c.GetQuery("deploy_name")
|
||||
|
||||
podList := make([]Pod, 0)
|
||||
pods := GetPodFromOS(namespace, deployName)
|
||||
hits, _ := pods.Get("hits").Get("hits").Array()
|
||||
|
||||
for i := 0; i < len(hits); i++ {
|
||||
clusterName, _ := pods.Get("hits").Get("hits").GetIndex(i).Get("_source").Get("metadata").Get("annotations").Get("resource.karmada.io/cached-from-cluster").String()
|
||||
podName, _ := pods.Get("hits").Get("hits").GetIndex(i).Get("_source").Get("metadata").Get("name").String()
|
||||
|
||||
spec := pods.Get("hits").Get("hits").GetIndex(i).Get("_source").Get("spec")
|
||||
status := pods.Get("hits").Get("hits").GetIndex(i).Get("_source").Get("status")
|
||||
specString, _ := spec.String()
|
||||
specJson, _ := simplejson.NewJson([]byte(specString))
|
||||
statusString, _ := status.String()
|
||||
statusJson, _ := simplejson.NewJson([]byte(statusString))
|
||||
ready, _ := statusJson.Get("containerStatuses").Get("ready").String()
|
||||
restartCount, _ := statusJson.Get("containerStatuses").Get("restartCount").Int64()
|
||||
|
||||
startTimeString, _ := statusJson.Get("startTime").String()
|
||||
startTime, _ := time.Parse("2006-01-02T15:04:05Z", startTimeString)
|
||||
|
||||
podIP, _ := statusJson.Get("podIP").String()
|
||||
nodeName, _ := specJson.Get("nodeName").String()
|
||||
podStatus, _ := statusJson.Get("phase").String()
|
||||
containerName, _ := specJson.Get("containers").GetIndex(0).Get("name").String()
|
||||
containerImage, _ := specJson.Get("containers").GetIndex(0).Get("image").String()
|
||||
|
||||
pod := Pod{
|
||||
Name: podName,
|
||||
ClusterName: clusterName,
|
||||
Ready: ready,
|
||||
Status: podStatus,
|
||||
Restarts: restartCount,
|
||||
Age: strconv.FormatFloat(time.Now().Sub(startTime).Hours()/24, 'f', 0, 64) + "d",
|
||||
IP: podIP,
|
||||
Node: nodeName,
|
||||
Namespace: namespace,
|
||||
ContainerName: containerName,
|
||||
ContainerImage: containerImage,
|
||||
}
|
||||
rows, _ := DB.Query("select dc.domain_id,dc.domain_name,d.longitude,d.latitude from joint_domain.domain_cluster dc,joint_domain.domain d where dc.domain_id = d.domain_id and dc.cluster_name = ?", 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 {
|
||||
Response(c, http.StatusBadRequest, "query failed!", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
pod.DomainName = domainName
|
||||
podList = append(podList, pod)
|
||||
|
||||
}
|
||||
|
||||
Response(c, http.StatusOK, "success", podList)
|
||||
}
|
|
@ -0,0 +1,251 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/karmada-io/karmada/pkg/util"
|
||||
"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"`
|
||||
}
|
||||
|
||||
// CreateDomain 创建域
|
||||
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列表
|
||||
func ListDomain(c *gin.Context) {
|
||||
|
||||
domainName, _ := c.GetQuery("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数组
|
||||
*/
|
||||
func DescribeDomain(c *gin.Context) {
|
||||
|
||||
var domain Domain
|
||||
domainId, _ := c.GetQuery("domain_id")
|
||||
namespace, _ := c.GetQuery("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)
|
||||
}
|
||||
|
||||
// ListByDeployment 根据项目名称和工作负载查询域列表
|
||||
func ListByDeployment(c *gin.Context) {
|
||||
domainList := make([]Domain, 0)
|
||||
namespaceName, _ := c.GetQuery("namespace")
|
||||
deploymentName, _ := c.GetQuery("deployment_name")
|
||||
|
||||
domainList = getDomainsByDeployment(namespaceName, deploymentName)
|
||||
|
||||
Response(c, http.StatusOK, "success", domainList)
|
||||
}
|
||||
|
||||
// 通过ns名称+deploy名称查询deploy所在域的信息,单独方法供复用
|
||||
func getDomainsByDeployment(namespaceName string, deploymentName string) (domainList []Domain) {
|
||||
|
||||
deployJson := GetDeployFromOS(namespaceName, deploymentName, "")
|
||||
hits, _ := deployJson.Get("hits").Get("hits").Array()
|
||||
|
||||
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()
|
||||
domain := Domain{}
|
||||
//获取域列表
|
||||
rows, _ := DB.Query("select domain_name,domain_id,cluster_name from domain_cluster where cluster_name = ?", cluster)
|
||||
var clusters []string
|
||||
for rows.Next() {
|
||||
var domainName string
|
||||
var clusterName string
|
||||
var domainId int32
|
||||
err := rows.Scan(&domainName, &domainId, &clusterName)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
domain.DomainName = domainName
|
||||
domain.DomainId = domainId
|
||||
clusters = append(clusters, clusterName)
|
||||
}
|
||||
domain.Clusters = clusters
|
||||
domainList = append(domainList, domain)
|
||||
}
|
||||
//去重
|
||||
domainList = RemoveRepeatedDomain(domainList)
|
||||
|
||||
return domainList
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"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"`
|
||||
}
|
||||
|
||||
// CreateLabel 创建域标
|
||||
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)
|
||||
|
||||
}
|
||||
|
||||
// DeleteLabel 删除域标
|
||||
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)
|
||||
|
||||
}
|
||||
|
||||
// ListLabel 查询域标列表
|
||||
func ListLabel(c *gin.Context) {
|
||||
|
||||
labelName, _ := c.GetQuery("label_name")
|
||||
|
||||
LabelList := make([]Label, 0)
|
||||
rows, err := DB.Query(`SELECT l.label_id,l.label_type_id ,lt.label_type ,l.label_name, l.create_time,l.update_time FROM label l,label_type lt WHERE l.label_type_id = lt.label_type_id and l.label_name like ?`, "%"+labelName+"%")
|
||||
defer rows.Close()
|
||||
|
||||
if err != nil {
|
||||
Response(c, http.StatusBadRequest, "query failed", err)
|
||||
return
|
||||
}
|
||||
for rows.Next() {
|
||||
var lt Label
|
||||
var createTimeString string
|
||||
var updateTimeString string
|
||||
err := rows.Scan(<.LabelId, <.LabelTypeId, <.LabelType, <.LabelName, &createTimeString, &updateTimeString)
|
||||
if err != nil {
|
||||
Response(c, http.StatusBadRequest, "query failed!", err)
|
||||
return
|
||||
}
|
||||
|
||||
lt.CreateTime = &createTimeString
|
||||
lt.UpdateTime = &updateTimeString
|
||||
|
||||
LabelList = append(LabelList, lt)
|
||||
}
|
||||
|
||||
total := len(LabelList)
|
||||
page := &Page[Label]{}
|
||||
page.List = LabelList
|
||||
pageNum, _ := c.GetQuery("pageNum")
|
||||
pageSize, _ := c.GetQuery("pageSize")
|
||||
num, _ := strconv.ParseInt(pageNum, 10, 64)
|
||||
size, _ := strconv.ParseInt(pageSize, 10, 64)
|
||||
data := Paginator(page, int64(total), num, size)
|
||||
Response(c, http.StatusOK, "success", data)
|
||||
//Response(c, http.StatusOK, "success", LabelList)
|
||||
|
||||
}
|
||||
|
||||
// UpdateLabel 更新域标
|
||||
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)
|
||||
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type LabelType struct {
|
||||
LabelTypeId int32 `json:"label_type_id"`
|
||||
LabelType string `json:"label_type"`
|
||||
MultiCheck *bool `json:"multi_check"`
|
||||
}
|
||||
|
||||
// CreateLabelType 创建域标类型
|
||||
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)
|
||||
|
||||
}
|
||||
|
||||
// DeleteLabelType 删除域标类型
|
||||
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 查询域标类型
|
||||
func ListLabelType(c *gin.Context) {
|
||||
|
||||
labelType, _ := c.GetQuery("label_type")
|
||||
|
||||
LabelTypeList := make([]LabelType, 0)
|
||||
rows, err := DB.Query(`SELECT label_type_id,label_type,multi_check FROM label_type WHERE label_type like ?`, "%"+labelType+"%")
|
||||
if err != nil {
|
||||
Response(c, http.StatusBadRequest, "query failed", err)
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var lt LabelType
|
||||
err := rows.Scan(<.LabelTypeId, <.LabelType, <.MultiCheck)
|
||||
if err != nil {
|
||||
Response(c, http.StatusBadRequest, "query failed!", err)
|
||||
return
|
||||
}
|
||||
LabelTypeList = append(LabelTypeList, lt)
|
||||
}
|
||||
|
||||
total := len(LabelTypeList)
|
||||
page := &Page[LabelType]{}
|
||||
page.List = LabelTypeList
|
||||
pageNum, _ := c.GetQuery("pageNum")
|
||||
pageSize, _ := c.GetQuery("pageSize")
|
||||
num, _ := strconv.ParseInt(pageNum, 10, 64)
|
||||
size, _ := strconv.ParseInt(pageSize, 10, 64)
|
||||
data := Paginator(page, int64(total), num, size)
|
||||
Response(c, http.StatusOK, "success", data)
|
||||
//Response(c, http.StatusOK, "success", LabelTypeList)
|
||||
|
||||
}
|
||||
|
||||
// UpdateLabelType 更新域标类型
|
||||
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)
|
||||
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
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"`
|
||||
MaxOpenConn int `yaml:"max-open-conn"`
|
||||
MaxIdleConn int `yaml:"max-idle-conn"`
|
||||
}
|
||||
Karmada struct {
|
||||
ConfigPath string `yaml:"config-path"`
|
||||
MemberConfigPath string `yaml:"member-config-path"`
|
||||
}
|
||||
}
|
||||
|
||||
func GetNacosConfig() Config {
|
||||
configClient := GetClient()
|
||||
// 获取配置
|
||||
dataId := "dispatch_center_test"
|
||||
group := "dispatch"
|
||||
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 {
|
||||
serverConfig := []constant.ServerConfig{
|
||||
{
|
||||
IpAddr: "10.101.15.6",
|
||||
Port: 8848,
|
||||
},
|
||||
}
|
||||
// 创建clientConfig
|
||||
clientConfig := constant.ClientConfig{
|
||||
NamespaceId: "zqj", // 如果需要支持多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: "dispatch",
|
||||
})
|
||||
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))
|
||||
}
|
|
@ -0,0 +1,280 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/karmada-io/karmada/pkg/util"
|
||||
coreV1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
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"`
|
||||
}
|
||||
|
||||
type DomainResult struct {
|
||||
DomainName string `json:"domain_name"`
|
||||
Location [2]float64 `json:"location"`
|
||||
}
|
||||
|
||||
// CreateNamespace 创建命名空间(项目)
|
||||
func CreateNamespace(c *gin.Context) {
|
||||
var j Namespace
|
||||
labels := make(map[string]string)
|
||||
labels["jcce"] = "true"
|
||||
if err := c.BindJSON(&j); err != nil {
|
||||
Response(c, http.StatusBadRequest, "invalid request params.", "")
|
||||
return
|
||||
}
|
||||
sqlStr1 := "INSERT INTO joint_domain.namespace (namespace_name, alias, `describe`) VALUES(?,?,?)"
|
||||
_, err := DB.Exec(sqlStr1, j.NsName, j.Alias, j.Describe)
|
||||
if err != nil {
|
||||
Response(c, http.StatusBadRequest, "query failed!", err)
|
||||
return
|
||||
}
|
||||
|
||||
ns := coreV1.Namespace{
|
||||
TypeMeta: metav1.TypeMeta{},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: j.NsName,
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: coreV1.NamespaceSpec{},
|
||||
Status: coreV1.NamespaceStatus{},
|
||||
}
|
||||
|
||||
nsResult, err := ClientSet.CoreV1().Namespaces().Create(context.TODO(), &ns, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
println(err)
|
||||
}
|
||||
Response(c, http.StatusOK, "success", nsResult)
|
||||
|
||||
}
|
||||
|
||||
// ListNamespace 查询Namespace列表
|
||||
func ListNamespace(ctx *gin.Context) {
|
||||
|
||||
opts := metav1.ListOptions{
|
||||
TypeMeta: metav1.TypeMeta{},
|
||||
LabelSelector: "jcce=true",
|
||||
Watch: false,
|
||||
AllowWatchBookmarks: false,
|
||||
TimeoutSeconds: nil,
|
||||
Limit: 0,
|
||||
}
|
||||
|
||||
nsResult, _ := ClientSet.CoreV1().Namespaces().List(context.TODO(), opts)
|
||||
var nsList []Namespace
|
||||
for _, ns := range nsResult.Items {
|
||||
deploymentList, err := ClientSet.AppsV1().Deployments(ns.Name).List(context.TODO(), metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var totalRequired int32
|
||||
var totalAvailable int32
|
||||
domainList := make([]Domain, 0)
|
||||
for _, deploy := range deploymentList.Items {
|
||||
|
||||
//从OpenSearch中获取deployment相关信息
|
||||
domains := getDomainsByDeployment(ns.Name, deploy.Name)
|
||||
for _, domain := range domains {
|
||||
domainList = append(domainList, domain)
|
||||
}
|
||||
replicaRequired := *deploy.Spec.Replicas
|
||||
replicaAvailable := deploy.Status.AvailableReplicas
|
||||
totalRequired += replicaRequired
|
||||
totalAvailable += replicaAvailable
|
||||
}
|
||||
domainList = RemoveRepeatedDomain(domainList)
|
||||
crossDomain := false
|
||||
if len(domainList) > 1 {
|
||||
crossDomain = true
|
||||
}
|
||||
|
||||
ns := Namespace{
|
||||
NsName: ns.Name,
|
||||
State: string(ns.Status.Phase),
|
||||
Age: strconv.FormatFloat(time.Now().Sub(ns.CreationTimestamp.Time).Hours()/24, 'f', 0, 64) + "d",
|
||||
CrossDomain: crossDomain,
|
||||
RequirePodNum: totalRequired,
|
||||
AvailablePodNum: totalAvailable,
|
||||
}
|
||||
nsList = append(nsList, ns)
|
||||
}
|
||||
|
||||
total := len(nsList)
|
||||
page := &Page[Namespace]{}
|
||||
page.List = nsList
|
||||
pageNum, _ := ctx.GetQuery("pageNum")
|
||||
pageSize, _ := ctx.GetQuery("pageSize")
|
||||
num, _ := strconv.ParseInt(pageNum, 10, 64)
|
||||
size, _ := strconv.ParseInt(pageSize, 10, 64)
|
||||
data := Paginator(page, int64(total), num, size)
|
||||
Response(ctx, http.StatusOK, "success", data)
|
||||
|
||||
}
|
||||
|
||||
// DescribeNamespace 查询Namespace详情
|
||||
func DescribeNamespace(c *gin.Context) {
|
||||
|
||||
namespace, _ := c.GetQuery("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)
|
||||
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 更新命名空间(项目)
|
||||
func UpdateNamespace(c *gin.Context) {
|
||||
var j Namespace
|
||||
if err := c.BindJSON(&j); err != nil {
|
||||
Response(c, http.StatusBadRequest, "invalid request params.", "")
|
||||
return
|
||||
}
|
||||
|
||||
ns := coreV1.Namespace{
|
||||
TypeMeta: metav1.TypeMeta{},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: j.NsName,
|
||||
},
|
||||
Spec: coreV1.NamespaceSpec{},
|
||||
Status: coreV1.NamespaceStatus{},
|
||||
}
|
||||
|
||||
nsResult, err := ClientSet.CoreV1().Namespaces().Update(context.TODO(), &ns, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
println(err)
|
||||
}
|
||||
Response(c, http.StatusOK, "success", nsResult)
|
||||
|
||||
}
|
||||
|
||||
// DeleteNamespace 删除命名空间(项目)
|
||||
func DeleteNamespace(c *gin.Context) {
|
||||
var j Namespace
|
||||
if err := c.BindJSON(&j); err != nil {
|
||||
Response(c, http.StatusBadRequest, "invalid request params.", "")
|
||||
return
|
||||
}
|
||||
|
||||
err := ClientSet.CoreV1().Namespaces().Delete(context.TODO(), j.NsName, metav1.DeleteOptions{})
|
||||
if err != nil {
|
||||
Response(c, http.StatusInternalServerError, "delete namespace "+j.NsName+"failed", "")
|
||||
println(err)
|
||||
}
|
||||
Response(c, http.StatusOK, "success", "")
|
||||
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type Node struct {
|
||||
NodeName string `json:"nodeName"`
|
||||
ClusterName string `json:"clusterName"`
|
||||
State string `json:"state"`
|
||||
Age string `json:"age"`
|
||||
Roles string `json:"roles"`
|
||||
Version string `json:"version"`
|
||||
Ip string `json:"ip"`
|
||||
}
|
||||
|
||||
// ListNode 查询节点列表
|
||||
func ListNode(c *gin.Context) {
|
||||
|
||||
//TODO 调整为karmada-search
|
||||
//nodes := make([]Node, 0)
|
||||
//cmdKmd := karmadactl.NewKarmadaCtlCommand("karmadactl", "karmadactl")
|
||||
//cmdKmd.SetArgs([]string{"get", "nodes"})
|
||||
//if err := cmdKmd.Execute(); err != nil {
|
||||
// println(err)
|
||||
//}
|
||||
//result := karmadactl.GetResult
|
||||
//for i := 0; i < len(result); i++ {
|
||||
// cluster := result[i].Cluster
|
||||
// listData, err := json.Marshal(result[i].Info.Object)
|
||||
// var respNode *simplejson.Json
|
||||
// respNode, err = simplejson.NewJson(listData)
|
||||
// if err != nil {
|
||||
// panic("解析失败")
|
||||
// }
|
||||
//
|
||||
// for i := 0; i < len(respNode.Get("rows").MustArray()); i++ {
|
||||
// nodeJson := respNode.Get("rows").GetIndex(i).Get("cells")
|
||||
// nodeName, _ := nodeJson.GetIndex(0).String()
|
||||
// status, _ := nodeJson.GetIndex(1).String()
|
||||
// role, _ := nodeJson.GetIndex(2).String()
|
||||
// age, _ := nodeJson.GetIndex(3).String()
|
||||
// version, _ := nodeJson.GetIndex(4).String()
|
||||
// ip, _ := nodeJson.GetIndex(5).String()
|
||||
// node := Node{
|
||||
// NodeName: nodeName,
|
||||
// ClusterName: cluster,
|
||||
// State: status,
|
||||
// Roles: role,
|
||||
// Age: age,
|
||||
// Version: version,
|
||||
// Ip: ip,
|
||||
// }
|
||||
// nodes = append(nodes, node)
|
||||
// }
|
||||
//}
|
||||
//total := len(nodes)
|
||||
//page := &Page[Node]{}
|
||||
//page.List = nodes
|
||||
//pageNum, _ := c.GetQuery("pageNum")
|
||||
//pageSize, _ := c.GetQuery("pageSize")
|
||||
//num, _ := strconv.ParseInt(pageNum, 10, 64)
|
||||
//size, _ := strconv.ParseInt(pageSize, 10, 64)
|
||||
//data := Paginator(page, int64(total), num, size)
|
||||
//Response(c, http.StatusOK, "success", data)
|
||||
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"github.com/bitly/go-simplejson"
|
||||
"github.com/opensearch-project/opensearch-go/v2/opensearchapi"
|
||||
"strings"
|
||||
)
|
||||
|
||||
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-pod"},
|
||||
Body: content,
|
||||
}
|
||||
searchResp, _ := searchReq.Do(context.Background(), OpenSearchClient)
|
||||
bufPod := new(bytes.Buffer)
|
||||
bufPod.ReadFrom(searchResp.Body)
|
||||
|
||||
podJson, _ := simplejson.NewJson([]byte(bufPod.String()))
|
||||
|
||||
return podJson
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
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"`
|
||||
}
|
||||
|
||||
// CreateOverridePolicies 查询override策略列表
|
||||
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 查询重写策略列表
|
||||
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", nil)
|
||||
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang/glog"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type Overview struct {
|
||||
Domain int `json:"domain"`
|
||||
Cluster int `json:"cluster"`
|
||||
Pod int32 `json:"pod"`
|
||||
}
|
||||
|
||||
func ResourceCount(c *gin.Context) {
|
||||
overview := &Overview{}
|
||||
//纳管资源域、纳管集群总计、容器创建数量 资源域
|
||||
rows, _ := DB.Query("select count(*) domain from domain")
|
||||
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)
|
||||
}
|
||||
overview.Cluster = len(clusters.Items)
|
||||
//容器数量
|
||||
overview.Pod = countPod()
|
||||
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
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package app
|
||||
|
||||
type Page[T any] struct {
|
||||
// 当前页码
|
||||
PageNum int64 `json:"pageNum"`
|
||||
//每页显示条数
|
||||
PageSize int64 `json:"pageSize"`
|
||||
// 总数量
|
||||
Total int64 `json:"total"`
|
||||
// 总页数
|
||||
TotalPage int64 `json:"totalPage"`
|
||||
// 数据列表
|
||||
List []T `json:"list"`
|
||||
}
|
||||
|
||||
// Paginator 生成新的分页数据对象
|
||||
func Paginator[T any](items *Page[T], total, pageNum, pageSize int64) *Page[T] {
|
||||
var (
|
||||
pageStart int64
|
||||
pageEnd int64
|
||||
)
|
||||
|
||||
if pageNum <= 0 {
|
||||
pageNum = 1
|
||||
}
|
||||
if pageSize <= 0 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
//总页数
|
||||
totalPages := total / pageSize
|
||||
if (total % pageSize) > 0 {
|
||||
totalPages += 1
|
||||
}
|
||||
|
||||
if pageNum*pageSize < total {
|
||||
pageEnd = pageNum * pageSize
|
||||
pageStart = pageEnd - pageSize
|
||||
} else {
|
||||
pageEnd = total
|
||||
pageStart = pageSize * (totalPages - 1)
|
||||
}
|
||||
|
||||
if pageEnd > total {
|
||||
pageEnd = total
|
||||
}
|
||||
if total <= 0 {
|
||||
items.List = make([]T, 0)
|
||||
|
||||
} else {
|
||||
items.List = items.List[pageStart:pageEnd]
|
||||
}
|
||||
|
||||
return &Page[T]{
|
||||
PageNum: pageNum,
|
||||
PageSize: pageSize,
|
||||
TotalPage: totalPages,
|
||||
Total: total,
|
||||
List: items.List,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"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 ListPod(c *gin.Context) {
|
||||
|
||||
Response(c, http.StatusOK, "success", nil)
|
||||
|
||||
}
|
|
@ -0,0 +1,235 @@
|
|||
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"
|
||||
"strconv"
|
||||
"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"`
|
||||
}
|
||||
|
||||
// CreatePropagationPolicies 创建集群分发策略实例
|
||||
func CreatePropagationPolicies(c *gin.Context) {
|
||||
|
||||
var policyRequest PropagationPolicy
|
||||
if err := c.BindJSON(&policyRequest); err != nil {
|
||||
Response(c, http.StatusBadRequest, "invalid request params.", "")
|
||||
return
|
||||
}
|
||||
|
||||
var pp PropagationPolicy
|
||||
//获取模板数据
|
||||
sqlPolicyTemplate := "select template_id,template_name,labels,schedule_type,division_preference,cluster_preference from propagation_policy_template where template_id = ?"
|
||||
err := DB.QueryRow(sqlPolicyTemplate, policyRequest.TemplateId).Scan(&pp.TemplateId, &pp.TemplateName, &pp.LabelString, &pp.ReplicaSchedulingType, &pp.ReplicaDivisionPreference, &pp.ClusterPreference)
|
||||
if err != nil {
|
||||
Response(c, http.StatusBadRequest, "query failed!", err)
|
||||
return
|
||||
}
|
||||
|
||||
labelsJson, err := simplejson.NewJson([]byte(*pp.LabelString))
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
policy := &policyv1alpha1.PropagationPolicy{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: policyRequest.Name,
|
||||
Namespace: policyRequest.Namespace,
|
||||
},
|
||||
Spec: policyv1alpha1.PropagationSpec{
|
||||
ResourceSelectors: []policyv1alpha1.ResourceSelector{
|
||||
{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: policyRequest.Kind,
|
||||
Name: policyRequest.ResourceName,
|
||||
},
|
||||
},
|
||||
Placement: policyv1alpha1.Placement{
|
||||
ClusterAffinity: &policyv1alpha1.ClusterAffinity{
|
||||
LabelSelector: &v1.LabelSelector{
|
||||
MatchLabels: labelsMap,
|
||||
},
|
||||
},
|
||||
ReplicaScheduling: &rst,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
propagationList, err := KarmadaClient.PolicyV1alpha1().PropagationPolicies(policyRequest.Namespace).Create(context.TODO(), policy, v1.CreateOptions{})
|
||||
if err != nil {
|
||||
Response(c, http.StatusInternalServerError, "create policy failed", err)
|
||||
return
|
||||
}
|
||||
|
||||
//数据库记录policy实例
|
||||
_, err = DB.Exec(`INSERT INTO joint_domain.propagation_policy(template_id, policy_name, namespace, resource_name) VALUES(?,?,?,?)`, policyRequest.TemplateId, policyRequest.Name, policyRequest.Namespace, policyRequest.ResourceName)
|
||||
if err != nil {
|
||||
Response(c, http.StatusInternalServerError, "insert db failed", err)
|
||||
return
|
||||
}
|
||||
Response(c, http.StatusOK, "success", propagationList)
|
||||
}
|
||||
|
||||
// ListPropagationPolicies 查询集群分发策略列表
|
||||
func ListPropagationPolicies(c *gin.Context) {
|
||||
|
||||
labelKey, _ := c.GetQuery("label_key")
|
||||
labelValue, _ := c.GetQuery("label_value")
|
||||
policyName, _ := c.GetQuery("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: crossDomain,
|
||||
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.GetQuery("pageNum")
|
||||
pageSize, _ := c.GetQuery("pageSize")
|
||||
num, _ := strconv.ParseInt(pageNum, 10, 64)
|
||||
size, _ := strconv.ParseInt(pageSize, 10, 64)
|
||||
data := Paginator(page, int64(total), num, size)
|
||||
Response(c, http.StatusOK, "success", data)
|
||||
|
||||
}
|
||||
|
||||
// UpdatePropagationPolicies 更新集群分发策略
|
||||
func UpdatePropagationPolicies(c *gin.Context) {
|
||||
|
||||
Response(c, http.StatusOK, "success", nil)
|
||||
}
|
||||
|
||||
// DeletePropagationPolicies 删除集群分发策略
|
||||
func DeletePropagationPolicies(c *gin.Context) {
|
||||
|
||||
var policyRequest PropagationPolicy
|
||||
if err := c.BindJSON(&policyRequest); err != nil {
|
||||
Response(c, http.StatusBadRequest, "invalid request params.", "")
|
||||
return
|
||||
}
|
||||
|
||||
err := KarmadaClient.PolicyV1alpha1().PropagationPolicies(policyRequest.Namespace).Delete(context.TODO(), policyRequest.Name, v1.DeleteOptions{})
|
||||
if err != nil {
|
||||
Response(c, http.StatusInternalServerError, "delete policy failed", err)
|
||||
return
|
||||
}
|
||||
Response(c, http.StatusOK, "success", nil)
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
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"`
|
||||
}
|
||||
|
||||
// CreatePropagationPolicyTemplate 创建策略模板
|
||||
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)
|
||||
|
||||
}
|
||||
|
||||
// DeletePropagationPolicyTemplate 删除策略模板
|
||||
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)
|
||||
|
||||
}
|
||||
|
||||
// ListPropagationPolicyTemplate 查询分发策略模板
|
||||
func ListPropagationPolicyTemplate(c *gin.Context) {
|
||||
|
||||
pptName, _ := c.GetQuery("template_name")
|
||||
|
||||
PptList := make([]PropagationPolicyTemplate, 0)
|
||||
rows, err := DB.Query(`SELECT * FROM propagation_policy_template ppt where ppt.template_name like ?`, "%"+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.TemplateId, &ppt.TemplateName, &ppt.Labels, &ppt.ScheduleType, &ppt.DivisionPreference, &ppt.ClusterPreference)
|
||||
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.GetQuery("pageNum")
|
||||
pageSize, _ := c.GetQuery("pageSize")
|
||||
num, _ := strconv.ParseInt(pageNum, 10, 64)
|
||||
size, _ := strconv.ParseInt(pageSize, 10, 64)
|
||||
data := Paginator(page, int64(total), num, size)
|
||||
Response(c, http.StatusOK, "success", data)
|
||||
//Response(c, http.StatusOK, "success", PptList)
|
||||
|
||||
}
|
||||
|
||||
// UpdatePropagationPolicyTemplate 更新策略模板
|
||||
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)
|
||||
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func Response(c *gin.Context, code int, msg interface{}, data interface{}) {
|
||||
c.JSON(http.StatusOK, map[string]interface{}{
|
||||
"code": code,
|
||||
"msg": msg,
|
||||
"data": data,
|
||||
})
|
||||
return
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"database/sql"
|
||||
"github.com/gin-gonic/gin"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
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"
|
||||
_ "jcc-schedule/docs"
|
||||
kubeclient "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
var DB *sql.DB
|
||||
var KarmadaClient *karmadaclientset.Clientset
|
||||
var KarmadaConfig karmadactl.KarmadaConfig
|
||||
var ControlPlaneRestConfig *rest.Config
|
||||
var ClientSet *kubeclient.Clientset
|
||||
var OpenSearchClient *opensearch.Client
|
||||
|
||||
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
|
||||
DB, _ = sql.Open("mysql", dsn)
|
||||
DB.SetMaxOpenConns(int(ConfigNacos.Mysql.MaxOpenConn))
|
||||
DB.SetMaxIdleConns(int(ConfigNacos.Mysql.MaxIdleConn))
|
||||
|
||||
//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)
|
||||
|
||||
// 初始化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")
|
||||
{
|
||||
//Label
|
||||
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("/describe", DescribeNamespace)
|
||||
namespace.POST("/create", CreateNamespace)
|
||||
namespace.POST("/delete", DeleteNamespace)
|
||||
namespace.POST("/update", UpdateNamespace)
|
||||
|
||||
//Domain
|
||||
domain := v1.Group("domain")
|
||||
domain.POST("/create", CreateDomain)
|
||||
domain.GET("/describe", DescribeDomain)
|
||||
domain.GET("/list", ListDomain)
|
||||
domain.GET("/listByDeployment", ListByDeployment)
|
||||
|
||||
//Pod
|
||||
pod := v1.Group("pod")
|
||||
pod.GET("/list", ListPod)
|
||||
|
||||
//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)
|
||||
|
||||
//Node
|
||||
node := v1.Group("node")
|
||||
node.GET("/list", ListNode)
|
||||
|
||||
//Deployment
|
||||
deployment := v1.Group("deployment")
|
||||
deployment.POST("/create", CreateDeployment)
|
||||
deployment.GET("/list", ListDeployment)
|
||||
deployment.GET("/describe", DescribeDeployment)
|
||||
deployment.GET("/listGlobal", ListClusterDeployment)
|
||||
|
||||
//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.POST("/create", CreatePropagationPolicies)
|
||||
propagationPolicy.GET("/list", ListPropagationPolicies)
|
||||
propagationPolicy.POST("/delete", DeletePropagationPolicies)
|
||||
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)
|
||||
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
kind: Deployment
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
name: jcce-schedule-deployment
|
||||
namespace: jcce-system
|
||||
labels:
|
||||
k8s-app: jcce-schedule
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
k8s-app: jcce-schedule
|
||||
template:
|
||||
metadata:
|
||||
name: jcce-schedule
|
||||
labels:
|
||||
k8s-app: jcce-schedule
|
||||
spec:
|
||||
imagePullSecrets:
|
||||
- name: SECRET_NAME
|
||||
containers:
|
||||
- name: jcce-schedule
|
||||
image: IMAGE_NAME
|
||||
resources: {}
|
||||
imagePullPolicy: Always
|
||||
securityContext:
|
||||
privileged: false
|
||||
procMount: Default
|
||||
ports:
|
||||
- containerPort: 80
|
||||
volumeMounts: []
|
||||
volumes: []
|
||||
restartPolicy: Always
|
||||
terminationGracePeriodSeconds: 30
|
||||
dnsPolicy: ClusterFirst
|
||||
securityContext: {}
|
||||
schedulerName: default-scheduler
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxUnavailable: 25%
|
||||
maxSurge: 25%
|
||||
revisionHistoryLimit: 10
|
||||
progressDeadlineSeconds: 600
|
|
@ -0,0 +1,16 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
namespace: jcce-system
|
||||
name: jcce-schedule-service
|
||||
labels:
|
||||
k8s-service: jcce-schedule
|
||||
spec:
|
||||
selector:
|
||||
k8s-app: jcce-schedule
|
||||
ports:
|
||||
- name: web
|
||||
protocol: TCP
|
||||
port: 8082
|
||||
targetPort: 8082
|
||||
type: ClusterIP
|
|
@ -0,0 +1,75 @@
|
|||
// Package docs GENERATED BY SWAG; DO NOT EDIT
|
||||
// This file was generated by swaggo/swag
|
||||
package docs
|
||||
|
||||
import "github.com/swaggo/swag"
|
||||
|
||||
const docTemplate = `{
|
||||
"schemes": {{ marshal .Schemes }},
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"description": "{{escape .Description}}",
|
||||
"title": "{{.Title}}",
|
||||
"contact": {},
|
||||
"version": "{{.Version}}"
|
||||
},
|
||||
"host": "{{.Host}}",
|
||||
"basePath": "{{.BasePath}}",
|
||||
"paths": {
|
||||
"/api/v1/cluster/list": {
|
||||
"get": {
|
||||
"description": "查询集群列表",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"cluster"
|
||||
],
|
||||
"summary": "查询集群列表",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "页码",
|
||||
"name": "pageNum",
|
||||
"in": "query",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "每页数量",
|
||||
"name": "pageSize",
|
||||
"in": "query",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
// SwaggerInfo holds exported Swagger Info so clients can modify it
|
||||
var SwaggerInfo = &swag.Spec{
|
||||
Version: "1.0",
|
||||
Host: "",
|
||||
BasePath: "",
|
||||
Schemes: []string{},
|
||||
Title: "jcc调度中心",
|
||||
Description: "jcc",
|
||||
InfoInstanceName: "swagger",
|
||||
SwaggerTemplate: docTemplate,
|
||||
}
|
||||
|
||||
func init() {
|
||||
swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"description": "jcc",
|
||||
"title": "jcc调度中心",
|
||||
"contact": {},
|
||||
"version": "1.0"
|
||||
},
|
||||
"paths": {
|
||||
"/api/v1/cluster/list": {
|
||||
"get": {
|
||||
"description": "查询集群列表",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"cluster"
|
||||
],
|
||||
"summary": "查询集群列表",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "页码",
|
||||
"name": "pageNum",
|
||||
"in": "query",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "每页数量",
|
||||
"name": "pageSize",
|
||||
"in": "query",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
info:
|
||||
contact: {}
|
||||
description: jcc
|
||||
title: jcc调度中心
|
||||
version: "1.0"
|
||||
paths:
|
||||
/api/v1/cluster/list:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 查询集群列表
|
||||
parameters:
|
||||
- description: 页码
|
||||
in: query
|
||||
name: pageNum
|
||||
required: true
|
||||
type: integer
|
||||
- description: 每页数量
|
||||
in: query
|
||||
name: pageSize
|
||||
required: true
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
summary: 查询集群列表
|
||||
tags:
|
||||
- cluster
|
||||
swagger: "2.0"
|
|
@ -0,0 +1,143 @@
|
|||
module jcc-schedule
|
||||
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/bitly/go-simplejson v0.5.0
|
||||
github.com/gin-gonic/gin v1.8.1
|
||||
github.com/go-sql-driver/mysql v1.6.0
|
||||
github.com/golang/glog v1.0.0
|
||||
github.com/karmada-io/karmada v1.3.0
|
||||
github.com/nacos-group/nacos-sdk-go v1.1.2
|
||||
github.com/opensearch-project/opensearch-go/v2 v2.1.0
|
||||
github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a
|
||||
github.com/swaggo/gin-swagger v1.5.3
|
||||
github.com/swaggo/swag v1.8.5
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
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
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/MakeNowJust/heredoc v1.0.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/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/fvbommel/sortorder v1.0.1 // 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.2.3 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.19.6 // 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/universal-translator v0.18.0 // indirect
|
||||
github.com/go-playground/validator/v10 v10.10.0 // indirect
|
||||
github.com/goccy/go-json v0.9.7 // 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.2 // 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/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/google/uuid v1.1.2 // 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/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/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/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
|
||||
github.com/mailru/easyjson v0.7.6 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.13 // 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/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/olekukonko/tablewriter v0.0.4 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.1 // indirect
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_golang v1.12.1 // 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/stretchr/testify v1.8.0 // indirect
|
||||
github.com/ugorji/go/codec v1.2.7 // indirect
|
||||
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca // indirect
|
||||
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
go.uber.org/zap v1.19.1 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
|
||||
golang.org/x/tools v0.1.12 // 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/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
|
||||
k8s.io/apiserver v0.25.0 // indirect
|
||||
k8s.io/cli-runtime v0.24.2 // indirect
|
||||
k8s.io/cluster-bootstrap v0.24.2 // indirect
|
||||
k8s.io/component-base v0.25.0 // indirect
|
||||
k8s.io/klog/v2 v2.70.1 // indirect
|
||||
k8s.io/kube-aggregator v0.24.2 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // 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
|
||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||
)
|
Loading…
Reference in New Issue