diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 35410ca..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# 默认忽略的文件 -/shelf/ -/workspace.xml -# 基于编辑器的 HTTP 客户端请求 -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/.idea/k8s-platform.iml b/.idea/k8s-platform.iml deleted file mode 100644 index 5e764c4..0000000 --- a/.idea/k8s-platform.iml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index e430014..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/config/config.go b/config/config.go index 8ad3708..2de14cc 100644 --- a/config/config.go +++ b/config/config.go @@ -4,8 +4,8 @@ import "time" const ( ListenAddr = "0.0.0.0:9090" - //k8s集群kube-config文件存放位置 - Kubeconfig = "H:\\project\\kubeconfig\\config" + Kubeconfig = "E:\\kubeconfig\\config" + //tail的日志行数 PodLogTailLine = 2000 @@ -17,11 +17,12 @@ const ( DbUser = "root" DbPwd = "123456" - //打印Mysql debug的sql日志 LogMode = false - //连接池的配置 - MaxIdleConns = 10 //最大空闲连接 - MaxOpenConns = 100 //最大连接数 - MaxLifeTime = 30 * time.Second //最大生存时间 + MaxIdleConns = 10 + MaxOpenConns = 100 + MaxLifeTime = 30 * time.Second + + AdminUser = "admin" + AdminPwd = "123456" ) diff --git a/controller/configmap.go b/controller/configmap.go new file mode 100644 index 0000000..d72444e --- /dev/null +++ b/controller/configmap.go @@ -0,0 +1,130 @@ +package controller + +import ( + "dkube/service" + "github.com/gin-gonic/gin" + "github.com/wonderivan/logger" + "net/http" +) + +var ConfigMap configMap + +type configMap struct{} + +func (c *configMap) GetConfigMaps(ctx *gin.Context) { + params := new(struct { + FilterName string `form:"filter_name"` + Namespace string `form:"namespace"` + Page int `form:"page"` + Limit int `form:"limit"` + }) + if err := ctx.Bind(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + data, err := service.ConfigMap.GetConfigMaps(params.FilterName, params.Namespace, params.Limit, params.Page) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "msg": "获取ConfigMap列表成功", + "data": data, + }) +} + +func (c *configMap) GetConfigMapDetail(ctx *gin.Context) { + params := new(struct { + ConfigMapName string `form:"configmap_name"` + Namespace string `form:"namespace"` + }) + if err := ctx.Bind(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + data, err := service.ConfigMap.GetConfigMapDetail(params.ConfigMapName, params.Namespace) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "msg": "获取ConfigMap详情成功", + "data": data, + }) +} + +func (c *configMap) DeleteConfigMap(ctx *gin.Context) { + params := new(struct { + ConfigMapName string `json:"configmap_name"` + Namespace string `json:"namespace"` + }) + + if err := ctx.ShouldBindJSON(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + err := service.ConfigMap.DeleteConfigMap(params.ConfigMapName, params.Namespace) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + ctx.JSON(http.StatusOK, gin.H{ + "msg": "删除ConfigMap成功", + "data": nil, + }) +} + +func (c *configMap) UpdateConfigMap(ctx *gin.Context) { + params := new(struct { + Namespace string `json:"namespace"` + Content string `json:"content"` + }) + + if err := ctx.ShouldBindJSON(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + err := service.ConfigMap.UpdateConfigMap(params.Namespace, params.Content) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + ctx.JSON(http.StatusOK, gin.H{ + "msg": "更新ConfigMap成功", + "data": nil, + }) +} diff --git a/controller/daemonset.go b/controller/daemonset.go new file mode 100644 index 0000000..e741de1 --- /dev/null +++ b/controller/daemonset.go @@ -0,0 +1,130 @@ +package controller + +import ( + "dkube/service" + "github.com/gin-gonic/gin" + "github.com/wonderivan/logger" + "net/http" +) + +var DaemonSet daemonSet + +type daemonSet struct{} + +func (d *daemonSet) GetDaemonSets(ctx *gin.Context) { + params := new(struct { + FilterName string `form:"filter_name"` + Namespace string `form:"namespace"` + Page int `form:"page"` + Limit int `form:"limit"` + }) + if err := ctx.Bind(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + data, err := service.DaemonSet.GetDaemonSets(params.FilterName, params.Namespace, params.Limit, params.Page) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "msg": "获取DaemonSet列表成功", + "data": data, + }) +} + +func (d *daemonSet) GetDaemonSetDetail(ctx *gin.Context) { + params := new(struct { + DaemonSetName string `form:"daemonset_name"` + Namespace string `form:"namespace"` + }) + if err := ctx.Bind(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + data, err := service.DaemonSet.GetDaemonSetDetail(params.DaemonSetName, params.Namespace) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "msg": "获取DaemonSet详情成功", + "data": data, + }) +} + +func (d *daemonSet) DeleteDaemonSet(ctx *gin.Context) { + params := new(struct { + DaemonSetName string `json:"daemonset_name"` + Namespace string `json:"namespace"` + }) + + if err := ctx.ShouldBindJSON(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + err := service.DaemonSet.DeleteDaemonSet(params.DaemonSetName, params.Namespace) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + ctx.JSON(http.StatusOK, gin.H{ + "msg": "删除DaemonSet成功", + "data": nil, + }) +} + +func (d *daemonSet) UpdateDaemonSet(ctx *gin.Context) { + params := new(struct { + Namespace string `json:"namespace"` + Content string `json:"content"` + }) + + if err := ctx.ShouldBindJSON(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + err := service.DaemonSet.UpdateDaemonSet(params.Namespace, params.Content) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + ctx.JSON(http.StatusOK, gin.H{ + "msg": "更新DaemonSet成功", + "data": nil, + }) +} diff --git a/controller/deployment.go b/controller/deployment.go new file mode 100644 index 0000000..e3dc0ea --- /dev/null +++ b/controller/deployment.go @@ -0,0 +1,232 @@ +package controller + +import ( + "dkube/service" + "fmt" + "github.com/gin-gonic/gin" + "github.com/wonderivan/logger" + "net/http" +) + +var Deployment deployment + +type deployment struct{} + +func (d *deployment) GetDeployments(ctx *gin.Context) { + params := new(struct { + FilterName string `form:"filter_name"` + Namespace string `form:"namespace"` + Page int `form:"page"` + Limit int `form:"limit"` + }) + if err := ctx.Bind(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + data, err := service.Deployment.GetDeployments(params.FilterName, params.Namespace, params.Limit, params.Page) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "msg": "获取Deployment列表成功", + "data": data, + }) +} + +func (d *deployment) GetDeploymentDetail(ctx *gin.Context) { + params := new(struct { + DeploymentName string `form:"deployment_name"` + Namespace string `form:"namespace"` + }) + if err := ctx.Bind(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + data, err := service.Deployment.GetDeploymentDetail(params.DeploymentName, params.Namespace) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + ctx.JSON(http.StatusOK, gin.H{ + "msg": "获取Deployment详情成功", + "data": data, + }) +} + +func (d *deployment) CreateDeployment(ctx *gin.Context) { + var ( + deployCreate = new(service.DeployCreate) + err error + ) + + if err = ctx.ShouldBindJSON(deployCreate); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + if err = service.Deployment.CreateDeployment(deployCreate); err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + } + + ctx.JSON(http.StatusOK, gin.H{ + "msg": "创建Deployment成功", + "data": nil, + }) +} + +func (d *deployment) ScaleDeployment(ctx *gin.Context) { + params := new(struct { + DeploymentName string `json:"deployment_name"` + Namespace string `json:"namespace"` + ScaleNum int `json:"scale_num"` + }) + + if err := ctx.ShouldBindJSON(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + data, err := service.Deployment.ScaleDeployment(params.DeploymentName, params.Namespace, params.ScaleNum) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + ctx.JSON(http.StatusOK, gin.H{ + "msg": "设置Deployment副本数成功", + "data": fmt.Sprintf("最新副本数: %d", data), + }) +} + +func (d *deployment) DeleteDeployment(ctx *gin.Context) { + params := new(struct { + DeploymentName string `json:"deployment_name"` + Namespace string `json:"namespace"` + }) + + if err := ctx.ShouldBindJSON(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + err := service.Deployment.DeleteDeployment(params.DeploymentName, params.Namespace) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + ctx.JSON(http.StatusOK, gin.H{ + "msg": "删除Deployment成功", + "data": nil, + }) +} + +func (d *deployment) RestartDeployment(ctx *gin.Context) { + params := new(struct { + DeploymentName string `json:"deployment_name"` + Namespace string `json:"namespace"` + }) + + if err := ctx.ShouldBindJSON(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + err := service.Deployment.RestartDeployment(params.DeploymentName, params.Namespace) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + ctx.JSON(http.StatusOK, gin.H{ + "msg": "重启Deployment成功", + "data": nil, + }) +} + +func (d *deployment) UpdateDeployment(ctx *gin.Context) { + params := new(struct { + Namespace string `json:"namespace"` + Content string `json:"content"` + }) + + if err := ctx.ShouldBindJSON(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + err := service.Deployment.UpdateDeployment(params.Namespace, params.Content) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + ctx.JSON(http.StatusOK, gin.H{ + "msg": "更新Deployment成功", + "data": nil, + }) +} + +func (d *deployment) GetDeployNumPerNp(ctx *gin.Context) { + data, err := service.Deployment.GetDeployNumPerNp() + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "msg": "获取每个namespace的deployment数量成功", + "data": data, + }) +} diff --git a/controller/ingress.go b/controller/ingress.go new file mode 100644 index 0000000..2ceb835 --- /dev/null +++ b/controller/ingress.go @@ -0,0 +1,159 @@ +package controller + +import ( + "dkube/service" + "github.com/gin-gonic/gin" + "github.com/wonderivan/logger" + "net/http" +) + +var Ingress ingress + +type ingress struct{} + +func (i *ingress) GetIngresses(ctx *gin.Context) { + params := new(struct { + FilterName string `form:"filter_name"` + Namespace string `form:"namespace"` + Page int `form:"page"` + Limit int `form:"limit"` + }) + if err := ctx.Bind(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + data, err := service.Ingress.GetIngresses(params.FilterName, params.Namespace, params.Limit, params.Page) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "msg": "获取Ingresst列表成功", + "data": data, + }) +} + +func (i *ingress) GetIngressDetail(ctx *gin.Context) { + params := new(struct { + IngressName string `form:"ingress_name"` + Namespace string `form:"namespace"` + }) + if err := ctx.Bind(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + data, err := service.Ingress.GetIngresstDetail(params.IngressName, params.Namespace) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "msg": "获取Ingress详情成功", + "data": data, + }) +} + +func (i *ingress) DeleteIngress(ctx *gin.Context) { + params := new(struct { + IngressName string `json:"ingress_name"` + Namespace string `json:"namespace"` + }) + + if err := ctx.ShouldBindJSON(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + err := service.Ingress.DeleteIngress(params.IngressName, params.Namespace) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + ctx.JSON(http.StatusOK, gin.H{ + "msg": "删除Ingress成功", + "data": nil, + }) +} + +func (i *ingress) CreateIngress(ctx *gin.Context) { + var ( + ingressCreate = new(service.IngressCreate) + err error + ) + + if err = ctx.ShouldBindJSON(ingressCreate); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + if err = service.Ingress.CreateIngress(ingressCreate); err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "msg": "创建Ingress成功", + "data": nil, + }) +} + +func (i *ingress) UpdateIngress(ctx *gin.Context) { + params := new(struct { + Namespace string `json:"namespace"` + Content string `json:"content"` + }) + + if err := ctx.ShouldBindJSON(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + err := service.Ingress.UpdateIngress(params.Namespace, params.Content) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + ctx.JSON(http.StatusOK, gin.H{ + "msg": "更新Ingress成功", + "data": nil, + }) +} diff --git a/controller/login.go b/controller/login.go new file mode 100644 index 0000000..720d0e8 --- /dev/null +++ b/controller/login.go @@ -0,0 +1,41 @@ +package controller + +import ( + "dkube/service" + "github.com/gin-gonic/gin" + "github.com/wonderivan/logger" + "net/http" +) + +var Login login + +type login struct{} + +func (l *login) Auth(ctx *gin.Context) { + params := new(struct { + UserName string `json:"username"` + Password string `json:"password"` + }) + if err := ctx.ShouldBindJSON(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + err := service.Login.Auth(params.UserName, params.Password) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "msg": "登录成功", + "data": nil, + }) +} diff --git a/controller/namespace.go b/controller/namespace.go new file mode 100644 index 0000000..100a9d3 --- /dev/null +++ b/controller/namespace.go @@ -0,0 +1,98 @@ +package controller + +import ( + "dkube/service" + "github.com/gin-gonic/gin" + "github.com/wonderivan/logger" + "net/http" +) + +var Namespace namespace + +type namespace struct{} + +func (n *namespace) GetNamespaces(ctx *gin.Context) { + params := new(struct { + FilterName string `form:"filter_name"` + Page int `form:"page"` + Limit int `form:"limit"` + }) + if err := ctx.Bind(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + data, err := service.Namespace.GetNamespaces(params.FilterName, params.Limit, params.Page) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "msg": "获取Namespace列表成功", + "data": data, + }) +} + +func (n *namespace) GetNamespaceDetail(ctx *gin.Context) { + params := new(struct { + NamespaceName string `form:"namespace_name"` + }) + if err := ctx.Bind(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + data, err := service.Namespace.GetNamespaceDetail(params.NamespaceName) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "msg": "获取Namespace详情成功", + "data": data, + }) +} + +func (n *namespace) DeleteNamespace(ctx *gin.Context) { + params := new(struct { + NamespaceName string `json:"namespace_name"` + }) + + if err := ctx.ShouldBindJSON(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + err := service.Namespace.DeleteNamespace(params.NamespaceName) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + ctx.JSON(http.StatusOK, gin.H{ + "msg": "删除Namespace成功", + "data": nil, + }) +} diff --git a/controller/node.go b/controller/node.go new file mode 100644 index 0000000..2183555 --- /dev/null +++ b/controller/node.go @@ -0,0 +1,70 @@ +package controller + +import ( + "dkube/service" + "github.com/gin-gonic/gin" + "github.com/wonderivan/logger" + "net/http" +) + +var Node node + +type node struct{} + +func (n *node) GetNodes(ctx *gin.Context) { + params := new(struct { + FilterName string `form:"filter_name"` + Page int `form:"page"` + Limit int `form:"limit"` + }) + if err := ctx.Bind(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + data, err := service.Node.GetNodes(params.FilterName, params.Limit, params.Page) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "msg": "获取Node列表成功", + "data": data, + }) +} + +func (n *node) GetNodeDetail(ctx *gin.Context) { + params := new(struct { + NodeName string `form:"node_name"` + }) + if err := ctx.Bind(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + data, err := service.Node.GetNodeDetail(params.NodeName) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "msg": "获取Node详情成功", + "data": data, + }) +} diff --git a/controller/pod.go b/controller/pod.go index 186fb98..ec35fc0 100644 --- a/controller/pod.go +++ b/controller/pod.go @@ -11,7 +11,6 @@ var Pod pod type pod struct{} -// GetPods 获取Pod列表,支持分页、过滤、排序 func (p *pod) GetPods(ctx *gin.Context) { params := new(struct { FilterName string `form:"filter_name"` @@ -19,15 +18,15 @@ func (p *pod) GetPods(ctx *gin.Context) { Limit int `form:"limit"` Page int `form:"page"` }) + if err := ctx.Bind(params); err != nil { logger.Error("Bind绑定参数失败" + err.Error()) ctx.JSON(http.StatusInternalServerError, gin.H{ - "msg": "Bind参数绑定失败" + err.Error(), + "msg": "Bind绑定参数失败" + err.Error(), "data": nil, }) return } - data, err := service.Pod.GetPods(params.FilterName, params.Namespace, params.Limit, params.Page) if err != nil { ctx.JSON(http.StatusInternalServerError, gin.H{ @@ -36,27 +35,27 @@ func (p *pod) GetPods(ctx *gin.Context) { }) return } + ctx.JSON(http.StatusOK, gin.H{ "msg": "获取Pod列表成功", "data": data, }) } -//获取Pod详情 func (p *pod) GetPodDetail(ctx *gin.Context) { params := new(struct { PodName string `form:"pod_name"` Namespace string `form:"namespace"` }) + if err := ctx.Bind(params); err != nil { logger.Error("Bind绑定参数失败" + err.Error()) ctx.JSON(http.StatusInternalServerError, gin.H{ - "msg": "Bind参数绑定失败" + err.Error(), + "msg": "Bind绑定参数失败" + err.Error(), "data": nil, }) return } - data, err := service.Pod.GetPodDetail(params.PodName, params.Namespace) if err != nil { ctx.JSON(http.StatusInternalServerError, gin.H{ @@ -65,13 +64,13 @@ func (p *pod) GetPodDetail(ctx *gin.Context) { }) return } + ctx.JSON(http.StatusOK, gin.H{ "msg": "获取Pod列表成功", "data": data, }) } -//删除Pod func (p *pod) DeletePod(ctx *gin.Context) { params := new(struct { PodName string `json:"pod_name"` @@ -81,12 +80,11 @@ func (p *pod) DeletePod(ctx *gin.Context) { if err := ctx.ShouldBindJSON(params); err != nil { logger.Error("Bind绑定参数失败" + err.Error()) ctx.JSON(http.StatusInternalServerError, gin.H{ - "msg": "Bind参数绑定失败" + err.Error(), + "msg": "Bind绑定参数失败" + err.Error(), "data": nil, }) return } - err := service.Pod.DeletePod(params.PodName, params.Namespace) if err != nil { ctx.JSON(http.StatusInternalServerError, gin.H{ @@ -95,13 +93,13 @@ func (p *pod) DeletePod(ctx *gin.Context) { }) return } + ctx.JSON(http.StatusOK, gin.H{ "msg": "删除Pod成功", "data": nil, }) } -//更新Pod func (p *pod) UpdatePod(ctx *gin.Context) { params := new(struct { Namespace string `json:"namespace"` @@ -109,14 +107,13 @@ func (p *pod) UpdatePod(ctx *gin.Context) { }) if err := ctx.ShouldBindJSON(params); err != nil { - logger.Error("Bind绑定参数失败" + err.Error()) + logger.Error("Bind请求参数失败, " + err.Error()) ctx.JSON(http.StatusInternalServerError, gin.H{ - "msg": "Bind参数绑定失败" + err.Error(), + "msg": err.Error(), "data": nil, }) return } - err := service.Pod.UpdatePod(params.Namespace, params.Content) if err != nil { ctx.JSON(http.StatusInternalServerError, gin.H{ @@ -131,21 +128,20 @@ func (p *pod) UpdatePod(ctx *gin.Context) { }) } -//获取Pod容器 func (p *pod) GetPodContainer(ctx *gin.Context) { params := new(struct { PodName string `form:"pod_name"` Namespace string `form:"namespace"` }) + if err := ctx.Bind(params); err != nil { - logger.Error("Bind请求参数失败" + err.Error()) + logger.Error("Bind请求参数失败, " + err.Error()) ctx.JSON(http.StatusInternalServerError, gin.H{ "msg": err.Error(), "data": nil, }) return } - data, err := service.Pod.GetPodContainer(params.PodName, params.Namespace) if err != nil { ctx.JSON(http.StatusInternalServerError, gin.H{ @@ -160,22 +156,21 @@ func (p *pod) GetPodContainer(ctx *gin.Context) { }) } -//获取Pod容器日志 func (p *pod) GetPodLog(ctx *gin.Context) { params := new(struct { ContainerName string `form:"container_name"` PodName string `form:"pod_name"` Namespace string `form:"namespace"` }) + if err := ctx.Bind(params); err != nil { - logger.Error("Bind请求参数失败" + err.Error()) + logger.Error("Bind请求参数失败, " + err.Error()) ctx.JSON(http.StatusInternalServerError, gin.H{ "msg": err.Error(), "data": nil, }) return } - data, err := service.Pod.GetPodLog(params.ContainerName, params.PodName, params.Namespace) if err != nil { ctx.JSON(http.StatusInternalServerError, gin.H{ @@ -185,12 +180,11 @@ func (p *pod) GetPodLog(ctx *gin.Context) { return } ctx.JSON(http.StatusOK, gin.H{ - "msg": "获取Pod容器日志成功", + "msg": "获取Pod中容器日志成功", "data": data, }) } -//获取每个Namespace的Pod数量 func (p *pod) GetPodNumPerNp(ctx *gin.Context) { data, err := service.Pod.GetPodNumPerNp() if err != nil { @@ -200,8 +194,9 @@ func (p *pod) GetPodNumPerNp(ctx *gin.Context) { }) return } + ctx.JSON(http.StatusOK, gin.H{ - "msg": "获取每个namespace的Pod数量成功", + "msg": "获取每个namespace的pod数量成功", "data": data, }) } diff --git a/controller/pv.go b/controller/pv.go new file mode 100644 index 0000000..435e1b4 --- /dev/null +++ b/controller/pv.go @@ -0,0 +1,98 @@ +package controller + +import ( + "dkube/service" + "github.com/gin-gonic/gin" + "github.com/wonderivan/logger" + "net/http" +) + +var Pv pv + +type pv struct{} + +func (p *pv) GetPvs(ctx *gin.Context) { + params := new(struct { + FilterName string `form:"filter_name"` + Page int `form:"page"` + Limit int `form:"limit"` + }) + if err := ctx.Bind(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + data, err := service.Pv.GetPvs(params.FilterName, params.Limit, params.Page) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "msg": "获取Pv列表成功", + "data": data, + }) +} + +func (p *pv) GetPvDetail(ctx *gin.Context) { + params := new(struct { + PvName string `form:"pv_name"` + }) + if err := ctx.Bind(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + data, err := service.Pv.GetPvDetail(params.PvName) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "msg": "获取Pv详情成功", + "data": data, + }) +} + +func (p *pv) DeletePv(ctx *gin.Context) { + params := new(struct { + PvName string `json:"pv_name"` + }) + + if err := ctx.ShouldBindJSON(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + err := service.Pv.DeletePv(params.PvName) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + ctx.JSON(http.StatusOK, gin.H{ + "msg": "删除Pv成功", + "data": nil, + }) +} diff --git a/controller/pvc.go b/controller/pvc.go new file mode 100644 index 0000000..d892bee --- /dev/null +++ b/controller/pvc.go @@ -0,0 +1,130 @@ +package controller + +import ( + "dkube/service" + "github.com/gin-gonic/gin" + "github.com/wonderivan/logger" + "net/http" +) + +var Pvc pvc + +type pvc struct{} + +func (p *pvc) GetPvcs(ctx *gin.Context) { + params := new(struct { + FilterName string `form:"filter_name"` + Namespace string `form:"namespace"` + Page int `form:"page"` + Limit int `form:"limit"` + }) + if err := ctx.Bind(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + data, err := service.Pvc.GetPvcs(params.FilterName, params.Namespace, params.Limit, params.Page) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "msg": "获取Pvc列表成功", + "data": data, + }) +} + +func (p *pvc) GetPvcDetail(ctx *gin.Context) { + params := new(struct { + PvcName string `form:"pvc_name"` + Namespace string `form:"namespace"` + }) + if err := ctx.Bind(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + data, err := service.Pvc.GetPvcDetail(params.PvcName, params.Namespace) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "msg": "获取Pvc详情成功", + "data": data, + }) +} + +func (p *pvc) DeletePvc(ctx *gin.Context) { + params := new(struct { + PvcName string `json:"pvc_name"` + Namespace string `json:"namespace"` + }) + + if err := ctx.ShouldBindJSON(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + err := service.Pvc.DeletePvc(params.PvcName, params.Namespace) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + ctx.JSON(http.StatusOK, gin.H{ + "msg": "删除Pvc成功", + "data": nil, + }) +} + +func (p *pvc) UpdatePvc(ctx *gin.Context) { + params := new(struct { + Namespace string `json:"namespace"` + Content string `json:"content"` + }) + + if err := ctx.ShouldBindJSON(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + err := service.Pvc.UpdatePvc(params.Namespace, params.Content) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + ctx.JSON(http.StatusOK, gin.H{ + "msg": "更新Pvc成功", + "data": nil, + }) +} diff --git a/controller/router.go b/controller/router.go index 775ee6d..e727b56 100644 --- a/controller/router.go +++ b/controller/router.go @@ -6,18 +6,62 @@ import ( var Router router -//声明router结构体 type router struct{} -// InitApiRouter 初始化路由规则,创建api测试接口 func (r *router) InitApiRouter(router *gin.Engine) { router. - //pod操作 + POST("/api/login", Login.Auth). + POST("/api/k8s/workflow/create", Workflow.Create). GET("/api/k8s/pods", Pod.GetPods). GET("/api/k8s/pod/detail", Pod.GetPodDetail). DELETE("/api/k8s/pod/del", Pod.DeletePod). PUT("/api/k8s/pod/update", Pod.UpdatePod). GET("/api/k8s/pod/container", Pod.GetPodContainer). GET("/api/k8s/pod/log", Pod.GetPodLog). - GET("/api/k8s/pod/numnp", Pod.GetPodNumPerNp) + GET("/api/k8s/pod/numnp", Pod.GetPodNumPerNp). + GET("/api/k8s/deployments", Deployment.GetDeployments). + GET("/api/k8s/deployment/detail", Deployment.GetDeploymentDetail). + PUT("/api/k8s/deployment/scale", Deployment.ScaleDeployment). + DELETE("/api/k8s/deployment/del", Deployment.DeleteDeployment). + PUT("/api/k8s/deployment/restart", Deployment.RestartDeployment). + PUT("/api/k8s/deployment/update", Deployment.UpdateDeployment). + GET("/api/k8s/deployment/numnp", Deployment.GetDeployNumPerNp). + POST("/api/k8s/deployment/create", Deployment.CreateDeployment). + GET("/api/k8s/daemonsets", DaemonSet.GetDaemonSets). + GET("/api/k8s/daemonset/detail", DaemonSet.GetDaemonSetDetail). + DELETE("/api/k8s/daemonset/del", DaemonSet.DeleteDaemonSet). + PUT("/api/k8s/daemonset/update", DaemonSet.UpdateDaemonSet). + GET("/api/k8s/statefulsets", StatefulSet.GetStatefulSets). + GET("/api/k8s/statefulset/detail", StatefulSet.GetStatefulSetDetail). + DELETE("/api/k8s/statefulset/del", StatefulSet.DeleteStatefulSet). + PUT("/api/k8s/statefulset/update", StatefulSet.UpdateStatefulSet). + GET("/api/k8s/services", Servicev1.GetServices). + GET("/api/k8s/service/detail", Servicev1.GetServiceDetail). + DELETE("/api/k8s/service/del", Servicev1.DeleteService). + PUT("/api/k8s/service/update", Servicev1.UpdateService). + POST("/api/k8s/service/create", Servicev1.CreateService). + GET("/api/k8s/ingresses", Ingress.GetIngresses). + GET("/api/k8s/ingress/detail", Ingress.GetIngressDetail). + DELETE("/api/k8s/ingress/del", Ingress.DeleteIngress). + PUT("/api/k8s/ingress/update", Ingress.UpdateIngress). + POST("/api/k8s/ingress/create", Ingress.CreateIngress). + GET("/api/k8s/configmaps", ConfigMap.GetConfigMaps). + GET("/api/k8s/configmap/detail", ConfigMap.GetConfigMapDetail). + DELETE("/api/k8s/configmap/del", ConfigMap.DeleteConfigMap). + PUT("/api/k8s/configmap/update", ConfigMap.UpdateConfigMap). + GET("/api/k8s/secrets", Secret.GetSecrets). + GET("/api/k8s/secret/detail", Secret.GetSecretDetail). + DELETE("/api/k8s/secret/del", Secret.DeleteSecret). + PUT("/api/k8s/secret/update", Secret.UpdateSecret). + GET("/api/k8s/pvcs", Pvc.GetPvcs). + GET("/api/k8s/pvc/detail", Pvc.GetPvcDetail). + DELETE("/api/k8s/pvc/del", Pvc.DeletePvc). + PUT("/api/k8s/pvc/update", Pvc.UpdatePvc). + GET("/api/k8s/nodes", Node.GetNodes). + GET("/api/k8s/node/detail", Node.GetNodeDetail). + GET("/api/k8s/namespaces", Namespace.GetNamespaces). + GET("/api/k8s/namespace/detail", Namespace.GetNamespaceDetail). + GET("/api/k8s/namespace/del", Namespace.DeleteNamespace). + GET("/api/k8s/pvs", Pv.GetPvs). + GET("/api/k8s/pv/detail", Pv.GetPvDetail) } diff --git a/controller/secret.go b/controller/secret.go new file mode 100644 index 0000000..baff8cd --- /dev/null +++ b/controller/secret.go @@ -0,0 +1,130 @@ +package controller + +import ( + "dkube/service" + "github.com/gin-gonic/gin" + "github.com/wonderivan/logger" + "net/http" +) + +var Secret secret + +type secret struct{} + +func (s *secret) GetSecrets(ctx *gin.Context) { + params := new(struct { + FilterName string `form:"filter_name"` + Namespace string `form:"namespace"` + Page int `form:"page"` + Limit int `form:"limit"` + }) + if err := ctx.Bind(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + data, err := service.Secret.GetSecrets(params.FilterName, params.Namespace, params.Limit, params.Page) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "msg": "获取Secret列表成功", + "data": data, + }) +} + +func (s *secret) GetSecretDetail(ctx *gin.Context) { + params := new(struct { + SecretName string `form:"secret_name"` + Namespace string `form:"namespace"` + }) + if err := ctx.Bind(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + data, err := service.Secret.GetSecretDetail(params.SecretName, params.Namespace) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "msg": "获取Secret详情成功", + "data": data, + }) +} + +func (s *secret) DeleteSecret(ctx *gin.Context) { + params := new(struct { + SecretName string `json:"secret_name"` + Namespace string `json:"namespace"` + }) + + if err := ctx.ShouldBindJSON(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + err := service.Secret.DeleteSecret(params.SecretName, params.Namespace) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + ctx.JSON(http.StatusOK, gin.H{ + "msg": "删除Secret成功", + "data": nil, + }) +} + +func (s *secret) UpdateSecret(ctx *gin.Context) { + params := new(struct { + Namespace string `json:"namespace"` + Content string `json:"content"` + }) + + if err := ctx.ShouldBindJSON(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + err := service.Secret.UpdateSecret(params.Namespace, params.Content) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + ctx.JSON(http.StatusOK, gin.H{ + "msg": "更新Secret成功", + "data": nil, + }) +} diff --git a/controller/service.go b/controller/service.go new file mode 100644 index 0000000..4446b98 --- /dev/null +++ b/controller/service.go @@ -0,0 +1,158 @@ +package controller + +import ( + "dkube/service" + "github.com/gin-gonic/gin" + "github.com/wonderivan/logger" + "net/http" +) + +var Servicev1 servicev1 + +type servicev1 struct{} + +func (s *servicev1) GetServices(ctx *gin.Context) { + params := new(struct { + FilterName string `form:"filter_name"` + Namespace string `form:"namespace"` + Page int `form:"page"` + Limit int `form:"limit"` + }) + if err := ctx.Bind(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + data, err := service.Servicev1.GetServices(params.FilterName, params.Namespace, params.Limit, params.Page) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "msg": "获取Servicet列表成功", + "data": data, + }) +} + +func (s *servicev1) GetServiceDetail(ctx *gin.Context) { + params := new(struct { + ServiceName string `form:"service_name"` + Namespace string `form:"namespace"` + }) + if err := ctx.Bind(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + data, err := service.Servicev1.GetServicetDetail(params.ServiceName, params.Namespace) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "msg": "获取Service详情成功", + "data": data, + }) +} + +func (s *servicev1) CreateService(ctx *gin.Context) { + var ( + serviceCreate = new(service.ServiceCreate) + err error + ) + + if err = ctx.ShouldBindJSON(serviceCreate); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + if err = service.Servicev1.CreateService(serviceCreate); err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + } + + ctx.JSON(http.StatusOK, gin.H{ + "msg": "创建Service成功", + "data": nil, + }) +} + +func (s *servicev1) DeleteService(ctx *gin.Context) { + params := new(struct { + ServiceName string `json:"service_name"` + Namespace string `json:"namespace"` + }) + + if err := ctx.ShouldBindJSON(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + err := service.Servicev1.DeleteService(params.ServiceName, params.Namespace) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + ctx.JSON(http.StatusOK, gin.H{ + "msg": "删除Service成功", + "data": nil, + }) +} + +func (s *servicev1) UpdateService(ctx *gin.Context) { + params := new(struct { + Namespace string `json:"namespace"` + Content string `json:"content"` + }) + + if err := ctx.ShouldBindJSON(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + err := service.Servicev1.UpdateService(params.Namespace, params.Content) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + ctx.JSON(http.StatusOK, gin.H{ + "msg": "更新Service成功", + "data": nil, + }) +} diff --git a/controller/statefulset.go b/controller/statefulset.go new file mode 100644 index 0000000..fc32b39 --- /dev/null +++ b/controller/statefulset.go @@ -0,0 +1,130 @@ +package controller + +import ( + "dkube/service" + "github.com/gin-gonic/gin" + "github.com/wonderivan/logger" + "net/http" +) + +var StatefulSet statefulSet + +type statefulSet struct{} + +func (s *statefulSet) GetStatefulSets(ctx *gin.Context) { + params := new(struct { + FilterName string `form:"filter_name"` + Namespace string `form:"namespace"` + Page int `form:"page"` + Limit int `form:"limit"` + }) + if err := ctx.Bind(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + data, err := service.StatefulSet.GetStatefulSets(params.FilterName, params.Namespace, params.Limit, params.Page) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "msg": "获取StatefulSet列表成功", + "data": data, + }) +} + +func (s *statefulSet) GetStatefulSetDetail(ctx *gin.Context) { + params := new(struct { + StatefulSetName string `form:"statefulset_name"` + Namespace string `form:"namespace"` + }) + if err := ctx.Bind(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + data, err := service.StatefulSet.GetStatefulSetDetail(params.StatefulSetName, params.Namespace) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "msg": "获取StatefulSet详情成功", + "data": data, + }) +} + +func (s *statefulSet) DeleteStatefulSet(ctx *gin.Context) { + params := new(struct { + StatefulSetName string `json:"statefulset_name"` + Namespace string `json:"namespace"` + }) + + if err := ctx.ShouldBindJSON(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + err := service.StatefulSet.DeleteStatefulSet(params.StatefulSetName, params.Namespace) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + ctx.JSON(http.StatusOK, gin.H{ + "msg": "删除StatefulSet成功", + "data": nil, + }) +} + +func (s *statefulSet) UpdateStatefulSet(ctx *gin.Context) { + params := new(struct { + Namespace string `json:"namespace"` + Content string `json:"content"` + }) + + if err := ctx.ShouldBindJSON(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + err := service.StatefulSet.UpdateStatefulSet(params.Namespace, params.Content) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + ctx.JSON(http.StatusOK, gin.H{ + "msg": "更新StatefulSet成功", + "data": nil, + }) +} diff --git a/controller/workflow.go b/controller/workflow.go new file mode 100644 index 0000000..5d2d0ef --- /dev/null +++ b/controller/workflow.go @@ -0,0 +1,131 @@ +package controller + +import ( + "dkube/service" + "github.com/gin-gonic/gin" + "github.com/wonderivan/logger" + "net/http" +) + +var Workflow workflow + +type workflow struct{} + +func (w *workflow) GetList(ctx *gin.Context) { + params := new(struct { + Name string `form:"name"` + Namespace string `form:"namespace"` + Page int `form:"page"` + Limit int `form:"limit"` + }) + if err := ctx.Bind(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + data, err := service.Workflow.GetList(params.Name, params.Namespace, params.Limit, params.Page) + if err != nil { + logger.Error("获取Workflow列表失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "msg": "获取Workflow列表成功", + "data": data, + }) +} + +func (w *workflow) GetById(ctx *gin.Context) { + params := new(struct { + ID int `form:"id"` + }) + if err := ctx.Bind(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + data, err := service.Workflow.GetById(params.ID) + if err != nil { + logger.Error("查询Workflow单条数据失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "msg": "查询Workflow单条数据成功", + "data": data, + }) +} + +func (w *workflow) Create(ctx *gin.Context) { + var ( + wc = &service.WorkflowCreate{} + err error + ) + + if err = ctx.ShouldBindJSON(wc); err != nil { + logger.Error("Bind请求参数dc失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + if err = service.Workflow.CreateWorkFlow(wc); err != nil { + logger.Error("创建Workflow失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "msg": "创建Workflow成功", + "data": nil, + }) + +} + +func (w *workflow) DelById(ctx *gin.Context) { + params := new(struct { + ID int `json:"id"` + }) + if err := ctx.ShouldBindJSON(params); err != nil { + logger.Error("Bind请求参数失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + if err := service.Workflow.DelById(params.ID); err != nil { + logger.Error("删除Workflow失败, " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "msg": err.Error(), + "data": nil, + }) + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "msg": "删除Workflow成功", + "data": nil, + }) +} diff --git a/dao/workflow.go b/dao/workflow.go new file mode 100644 index 0000000..bb179d9 --- /dev/null +++ b/dao/workflow.go @@ -0,0 +1,70 @@ +package dao + +import ( + "dkube/db" + "dkube/model" + "errors" + "github.com/wonderivan/logger" +) + +var Workflow workflow + +type workflow struct{} + +type WorkflowResp struct { + Items []*model.Workflow `json:"items"` + Total int `json:"total"` +} + +func (w *workflow) GetWorkflows(filterName, namespace string, limit, page int) (data *WorkflowResp, err error) { + startSet := (page - 1) * limit + var ( + workflowList []*model.Workflow + total int + ) + + tx := db.GORM. + Model(&model.Workflow{}). + Where("name like ?", "%"+filterName+"%"). + Count(&total). + Limit(limit). + Offset(startSet). + Order("id desc"). + Find(&workflowList) + if tx.Error != nil && tx.Error.Error() != "record not found" { + logger.Error("获取Workflow列表失败, " + tx.Error.Error()) + return nil, errors.New("获取Workflow列表失败, " + tx.Error.Error()) + } + return &WorkflowResp{ + Items: workflowList, + Total: total, + }, nil +} + +func (w *workflow) GetById(id int) (workflow *model.Workflow, err error) { + workflow = &model.Workflow{} + tx := db.GORM.Where("id = ?", id).First(&workflow) + if tx.Error != nil && tx.Error.Error() != "record not found" { + logger.Error("获取Workflow详情失败, " + tx.Error.Error()) + return nil, errors.New("获取Workflow详情失败, " + tx.Error.Error()) + } + return workflow, nil +} + +func (w *workflow) Add(workflow *model.Workflow) (err error) { + tx := db.GORM.Create(&workflow) + if tx.Error != nil && tx.Error.Error() != "record not found" { + logger.Error("创建Workflow失败, " + tx.Error.Error()) + return errors.New("创建Workflow失败, " + tx.Error.Error()) + } + return nil +} + +func (w *workflow) DelById(id int) (err error) { + tx := db.GORM.Where("id = ?", id).Delete(&model.Workflow{}) + if tx.Error != nil && tx.Error.Error() != "record not found" { + logger.Error("获取Workflow详情失败, " + tx.Error.Error()) + return errors.New("获取Workflow详情失败, " + tx.Error.Error()) + } + return nil +} diff --git a/db/init.go b/db/init.go index 4b4b0cd..504c6dc 100644 --- a/db/init.go +++ b/db/init.go @@ -30,10 +30,8 @@ func Init() { panic("数据库连接失败," + err.Error()) } - //打印sql语句 GORM.LogMode(config.LogMode) - //开启连接池 GORM.DB().SetMaxIdleConns(config.MaxIdleConns) GORM.DB().SetMaxOpenConns(config.MaxOpenConns) GORM.DB().SetConnMaxLifetime(time.Duration(config.MaxLifeTime)) @@ -42,7 +40,6 @@ func Init() { logger.Info("数据库初始化成功!") } -//关闭数据库连接 func Close() error { fmt.Println("测试db是否关闭") return GORM.Close() diff --git a/go.mod b/go.mod index b5f02dd..75397a8 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,9 @@ module dkube go 1.18 require ( + github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/gin-gonic/gin v1.8.1 + github.com/gorilla/websocket v1.4.2 github.com/jinzhu/gorm v1.9.16 github.com/wonderivan/logger v1.0.0 k8s.io/api v0.25.2 @@ -37,11 +39,13 @@ require ( github.com/leodido/go-urn v1.2.1 // indirect github.com/mailru/easyjson v0.7.6 // indirect github.com/mattn/go-isatty v0.0.14 // indirect + github.com/moby/spdystream v0.2.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pelletier/go-toml/v2 v2.0.1 // 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 golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect diff --git a/go.sum b/go.sum index 061e290..4a2aa77 100644 --- a/go.sum +++ b/go.sum @@ -39,6 +39,7 @@ github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= @@ -51,7 +52,10 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM= github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/emicklei/go-restful/v3 v3.8.0 h1:eCZ8ulSerjdAiaNpF7GxXIE7ZCMo1moN1qX+S609eVw= github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -150,6 +154,8 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -190,6 +196,8 @@ github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9 github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA= github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= +github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= +github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -215,13 +223,15 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= diff --git a/main.go b/main.go index 0e65e5d..33a48df 100644 --- a/main.go +++ b/main.go @@ -3,16 +3,25 @@ package main import ( "dkube/config" "dkube/controller" + "dkube/db" + "dkube/middle" "dkube/service" "github.com/gin-gonic/gin" + "net/http" ) func main() { - //初始化k8s client + db.Init() service.K8s.Init() - //初始化Gin对象 r := gin.Default() + r.Use(middle.Cors()) controller.Router.InitApiRouter(r) - //启动gin程序 + + go func() { + http.HandleFunc("/ws", service.Terminal.WsHandler) + http.ListenAndServe(":8081", nil) + }() + r.Run(config.ListenAddr) + db.Close() } diff --git a/middle/cors.go b/middle/cors.go new file mode 100644 index 0000000..ef1f8fd --- /dev/null +++ b/middle/cors.go @@ -0,0 +1,24 @@ +package middle + +import ( + "github.com/gin-gonic/gin" + "net/http" +) + +func Cors() gin.HandlerFunc { + return func(c *gin.Context) { + + method := c.Request.Method + c.Header("Content-Type", "application/json") + c.Header("Access-Control-Allow-Origin", "*") + c.Header("Access-Control-Max-Age", "86400") + c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE") + c.Header("Access-Control-Allow-Headers", "X-Token, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, X-Max") + c.Header("Access-Control-Allow-Credentials", "false") + + if method == "OPTIONS" { + c.AbortWithStatus(http.StatusNoContent) + } + c.Next() + } +} diff --git a/middle/jwt.go b/middle/jwt.go new file mode 100644 index 0000000..4c174c3 --- /dev/null +++ b/middle/jwt.go @@ -0,0 +1,45 @@ +package middle + +import ( + "dkube/utils" + "github.com/gin-gonic/gin" + "net/http" +) + +func JWTAuth() gin.HandlerFunc { + return func(c *gin.Context) { + if len(c.Request.URL.String()) >= 10 && c.Request.URL.String()[0:10] == "/api/login" { + c.Next() + } else { + token := c.Request.Header.Get("Authorization") + if token == "" { + c.JSON(http.StatusBadRequest, gin.H{ + "msg": "请求未携带token,无权限访问", + "data": nil, + }) + c.Abort() + return + } + + claims, err := utils.JWTToken.ParseToken(token) + if err != nil { + if err.Error() == "TokenExpired" { + c.JSON(http.StatusBadRequest, gin.H{ + "msg": "授权已过期", + "data": nil, + }) + c.Abort() + return + } + c.JSON(http.StatusBadRequest, gin.H{ + "msg": err.Error(), + "data": nil, + }) + c.Abort() + return + } + c.Set("claims", claims) + c.Next() + } + } +} diff --git a/model/workflow.go b/model/workflow.go new file mode 100644 index 0000000..c6f4e52 --- /dev/null +++ b/model/workflow.go @@ -0,0 +1,22 @@ +package model + +import "time" + +type Workflow struct { + ID uint `json:"id" gorm:"primaryKey"` + CreatedAt *time.Time `json:"created_at"` + UpdatedAt *time.Time `json:"updated_at"` + DeletedAt *time.Time `json:"deleted_at"` + + Name string `json:"name"` + Namespace string `json:"namespace"` + Replicas int32 `json:"replicas"` + Deployment string `json:"deployment"` + Service string `json:"service"` + Ingress string `json:"ingress"` + Type string `json:"type" gorm:"column:type"` +} + +func (*Workflow) TableName() string { + return "workflow" +} diff --git a/service/configmap.go b/service/configmap.go new file mode 100644 index 0000000..233070c --- /dev/null +++ b/service/configmap.go @@ -0,0 +1,102 @@ +package service + +import ( + "context" + "encoding/json" + "errors" + "github.com/wonderivan/logger" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var ConfigMap configMap + +type configMap struct{} + +type ConfigMapsResp struct { + Items []corev1.ConfigMap `json:"items"` + Total int `json:"total"` +} + +func (c *configMap) GetConfigMaps(filterName, namespace string, limit, page int) (configMapsResp *ConfigMapsResp, err error) { + configMapList, err := K8s.ClientSet.CoreV1().ConfigMaps(namespace).List(context.TODO(), metav1.ListOptions{}) + if err != nil { + logger.Error(errors.New("获取ConfigMap列表失败, " + err.Error())) + return nil, errors.New("获取ConfigMap列表失败, " + err.Error()) + } + selectableData := &dataSelector{ + GenericDataList: c.toCells(configMapList.Items), + DataSelect: &DataSelectQuery{ + Filter: &FilterQuery{Name: filterName}, + Paginate: &PaginateQuery{ + Limit: limit, + Page: page, + }, + }, + } + + filtered := selectableData.Filter() + total := len(filtered.GenericDataList) + data := filtered.Sort().Paginate() + + configMaps := c.fromCells(data.GenericDataList) + + return &ConfigMapsResp{ + Items: configMaps, + Total: total, + }, nil +} + +func (c *configMap) GetConfigMapDetail(configMapName, namespace string) (configMap *corev1.ConfigMap, err error) { + configMap, err = K8s.ClientSet.CoreV1().ConfigMaps(namespace).Get(context.TODO(), configMapName, metav1.GetOptions{}) + if err != nil { + logger.Error(errors.New("获取ConfigMap详情失败, " + err.Error())) + return nil, errors.New("获取ConfigMap详情失败, " + err.Error()) + } + + return configMap, nil +} + +func (c *configMap) DeleteConfigMap(configMapName, namespace string) (err error) { + err = K8s.ClientSet.CoreV1().ConfigMaps(namespace).Delete(context.TODO(), configMapName, metav1.DeleteOptions{}) + if err != nil { + logger.Error(errors.New("删除ConfigMap失败, " + err.Error())) + return errors.New("删除ConfigMap失败, " + err.Error()) + } + + return nil +} + +func (c *configMap) UpdateConfigMap(namespace, content string) (err error) { + var configMap = &corev1.ConfigMap{} + + err = json.Unmarshal([]byte(content), configMap) + if err != nil { + logger.Error(errors.New("反序列化失败, " + err.Error())) + return errors.New("反序列化失败, " + err.Error()) + } + + _, err = K8s.ClientSet.CoreV1().ConfigMaps(namespace).Update(context.TODO(), configMap, metav1.UpdateOptions{}) + if err != nil { + logger.Error(errors.New("更新ConfigMap失败, " + err.Error())) + return errors.New("更新ConfigMap失败, " + err.Error()) + } + return nil +} + +func (c *configMap) toCells(std []corev1.ConfigMap) []DataCell { + cells := make([]DataCell, len(std)) + for i := range std { + cells[i] = configMapCell(std[i]) + } + return cells +} + +func (c *configMap) fromCells(cells []DataCell) []corev1.ConfigMap { + configMaps := make([]corev1.ConfigMap, len(cells)) + for i := range cells { + configMaps[i] = corev1.ConfigMap(cells[i].(configMapCell)) + } + + return configMaps +} diff --git a/service/daemonset.go b/service/daemonset.go new file mode 100644 index 0000000..f6ba283 --- /dev/null +++ b/service/daemonset.go @@ -0,0 +1,102 @@ +package service + +import ( + "context" + "encoding/json" + "errors" + "github.com/wonderivan/logger" + appsv1 "k8s.io/api/apps/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var DaemonSet daemonSet + +type daemonSet struct{} + +type DaemonSetsResp struct { + Items []appsv1.DaemonSet `json:"items"` + Total int `json:"total"` +} + +func (d *daemonSet) GetDaemonSets(filterName, namespace string, limit, page int) (daemonSetsResp *DaemonSetsResp, err error) { + daemonSetList, err := K8s.ClientSet.AppsV1().DaemonSets(namespace).List(context.TODO(), metav1.ListOptions{}) + if err != nil { + logger.Error(errors.New("获取DaemonSet列表失败, " + err.Error())) + return nil, errors.New("获取DaemonSet列表失败, " + err.Error()) + } + selectableData := &dataSelector{ + GenericDataList: d.toCells(daemonSetList.Items), + DataSelect: &DataSelectQuery{ + Filter: &FilterQuery{Name: filterName}, + Paginate: &PaginateQuery{ + Limit: limit, + Page: page, + }, + }, + } + + filtered := selectableData.Filter() + total := len(filtered.GenericDataList) + data := filtered.Sort().Paginate() + + daemonSets := d.fromCells(data.GenericDataList) + + return &DaemonSetsResp{ + Items: daemonSets, + Total: total, + }, nil +} + +func (d *daemonSet) GetDaemonSetDetail(daemonSetName, namespace string) (daemonSet *appsv1.DaemonSet, err error) { + daemonSet, err = K8s.ClientSet.AppsV1().DaemonSets(namespace).Get(context.TODO(), daemonSetName, metav1.GetOptions{}) + if err != nil { + logger.Error(errors.New("获取DaemonSet详情失败, " + err.Error())) + return nil, errors.New("获取DaemonSet详情失败, " + err.Error()) + } + + return daemonSet, nil +} + +func (d *daemonSet) DeleteDaemonSet(daemonSetName, namespace string) (err error) { + err = K8s.ClientSet.AppsV1().DaemonSets(namespace).Delete(context.TODO(), daemonSetName, metav1.DeleteOptions{}) + if err != nil { + logger.Error(errors.New("删除DaemonSet失败, " + err.Error())) + return errors.New("删除DaemonSet失败, " + err.Error()) + } + + return nil +} + +func (d *daemonSet) UpdateDaemonSet(namespace, content string) (err error) { + var daemonSet = &appsv1.DaemonSet{} + + err = json.Unmarshal([]byte(content), daemonSet) + if err != nil { + logger.Error(errors.New("反序列化失败, " + err.Error())) + return errors.New("反序列化失败, " + err.Error()) + } + + _, err = K8s.ClientSet.AppsV1().DaemonSets(namespace).Update(context.TODO(), daemonSet, metav1.UpdateOptions{}) + if err != nil { + logger.Error(errors.New("更新DaemonSet失败, " + err.Error())) + return errors.New("更新DaemonSet失败, " + err.Error()) + } + return nil +} + +func (d *daemonSet) toCells(std []appsv1.DaemonSet) []DataCell { + cells := make([]DataCell, len(std)) + for i := range std { + cells[i] = daemonSetCell(std[i]) + } + return cells +} + +func (d *daemonSet) fromCells(cells []DataCell) []appsv1.DaemonSet { + daemonSets := make([]appsv1.DaemonSet, len(cells)) + for i := range cells { + daemonSets[i] = appsv1.DaemonSet(cells[i].(daemonSetCell)) + } + + return daemonSets +} diff --git a/service/dataselector.go b/service/dataselector.go index 1cc33f1..db74b57 100644 --- a/service/dataselector.go +++ b/service/dataselector.go @@ -9,19 +9,16 @@ import ( "time" ) - type dataSelector struct { GenericDataList []DataCell DataSelect *DataSelectQuery } - type DataCell interface { GetCreation() time.Time GetName() string } - type DataSelectQuery struct { Filter *FilterQuery Paginate *PaginateQuery @@ -36,12 +33,10 @@ type PaginateQuery struct { Page int } - func (d *dataSelector) Len() int { return len(d.GenericDataList) } - func (d *dataSelector) Swap(i, j int) { d.GenericDataList[i], d.GenericDataList[j] = d.GenericDataList[j], d.GenericDataList[i] } @@ -49,6 +44,7 @@ func (d *dataSelector) Swap(i, j int) { func (d *dataSelector) Less(i, j int) bool { a := d.GenericDataList[i].GetCreation() b := d.GenericDataList[j].GetCreation() + return b.Before(a) } @@ -65,7 +61,7 @@ func (d *dataSelector) Filter() *dataSelector { for _, value := range d.GenericDataList { matches := true objName := value.GetName() - if strings.Contains(objName, d.DataSelect.Filter.Name) { + if !strings.Contains(objName, d.DataSelect.Filter.Name) { matches = false continue } @@ -73,7 +69,6 @@ func (d *dataSelector) Filter() *dataSelector { filtered = append(filtered, value) } } - d.GenericDataList = filtered return d } @@ -81,11 +76,9 @@ func (d *dataSelector) Filter() *dataSelector { func (d *dataSelector) Paginate() *dataSelector { limit := d.DataSelect.Paginate.Limit page := d.DataSelect.Paginate.Page - if limit <= 0 || page <= 0 { return d } - startIndex := limit * (page - 1) endIndex := limit*page - 1 @@ -118,6 +111,56 @@ func (d deploymentCell) GetName() string { return d.Name } +type daemonSetCell appsv1.DaemonSet + +func (d daemonSetCell) GetCreation() time.Time { + return d.CreationTimestamp.Time +} + +func (d daemonSetCell) GetName() string { + return d.Name +} + +type statefulSetCell appsv1.StatefulSet + +func (s statefulSetCell) GetCreation() time.Time { + return s.CreationTimestamp.Time +} + +func (s statefulSetCell) GetName() string { + return s.Name +} + +type nodeCell corev1.Node + +func (n nodeCell) GetCreation() time.Time { + return n.CreationTimestamp.Time +} + +func (n nodeCell) GetName() string { + return n.Name +} + +type namespaceCell corev1.Namespace + +func (n namespaceCell) GetCreation() time.Time { + return n.CreationTimestamp.Time +} + +func (n namespaceCell) GetName() string { + return n.Name +} + +type pvCell corev1.PersistentVolume + +func (p pvCell) GetCreation() time.Time { + return p.CreationTimestamp.Time +} + +func (p pvCell) GetName() string { + return p.Name +} + type serviceCell corev1.Service func (s serviceCell) GetCreation() time.Time { @@ -137,3 +180,33 @@ func (i ingressCell) GetCreation() time.Time { func (i ingressCell) GetName() string { return i.Name } + +type configMapCell corev1.ConfigMap + +func (c configMapCell) GetCreation() time.Time { + return c.CreationTimestamp.Time +} + +func (c configMapCell) GetName() string { + return c.Name +} + +type secretCell corev1.Secret + +func (s secretCell) GetCreation() time.Time { + return s.CreationTimestamp.Time +} + +func (s secretCell) GetName() string { + return s.Name +} + +type pvcCell corev1.PersistentVolumeClaim + +func (p pvcCell) GetCreation() time.Time { + return p.CreationTimestamp.Time +} + +func (p pvcCell) GetName() string { + return p.Name +} diff --git a/service/deployment.go b/service/deployment.go index 88b71b7..0fe390a 100644 --- a/service/deployment.go +++ b/service/deployment.go @@ -41,7 +41,6 @@ type DeploysNp struct { DeployNum int `json:"deployment_num"` } -//获取deployment列表,支持过滤、排序、分页 func (d *deployment) GetDeployments(filterName, namespace string, limit, page int) (deploymentsResp *DeploymentsResp, err error) { deploymentList, err := K8s.ClientSet.AppsV1().Deployments(namespace).List(context.TODO(), metav1.ListOptions{}) if err != nil { @@ -71,7 +70,6 @@ func (d *deployment) GetDeployments(filterName, namespace string, limit, page in }, nil } -//获取deployment详情 func (d *deployment) GetDeploymentDetail(deploymentName, namespace string) (deployment *appsv1.Deployment, err error) { deployment, err = K8s.ClientSet.AppsV1().Deployments(namespace).Get(context.TODO(), deploymentName, metav1.GetOptions{}) if err != nil { @@ -82,14 +80,12 @@ func (d *deployment) GetDeploymentDetail(deploymentName, namespace string) (depl return deployment, nil } -//设置deployment副本数 func (d *deployment) ScaleDeployment(deploymentName, namespace string, scaleNum int) (replica int32, err error) { scale, err := K8s.ClientSet.AppsV1().Deployments(namespace).GetScale(context.TODO(), deploymentName, metav1.GetOptions{}) if err != nil { logger.Error(errors.New("获取Deployment副本数信息失败, " + err.Error())) return 0, errors.New("获取Deployment副本数信息失败, " + err.Error()) } - scale.Spec.Replicas = int32(scaleNum) newScale, err := K8s.ClientSet.AppsV1().Deployments(namespace).UpdateScale(context.TODO(), deploymentName, scale, metav1.UpdateOptions{}) if err != nil { @@ -100,7 +96,6 @@ func (d *deployment) ScaleDeployment(deploymentName, namespace string, scaleNum return newScale.Spec.Replicas, nil } -//创建deployment,接收DeployCreate对象 func (d *deployment) CreateDeployment(data *DeployCreate) (err error) { deployment := &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ @@ -137,7 +132,7 @@ func (d *deployment) CreateDeployment(data *DeployCreate) (err error) { }, Status: appsv1.DeploymentStatus{}, } - //判断健康检查功能是否打开,若打开,则增加健康检查功能 + if data.HealthCheck { deployment.Spec.Template.Spec.Containers[0].ReadinessProbe = &corev1.Probe{ ProbeHandler: corev1.ProbeHandler{ @@ -168,7 +163,7 @@ func (d *deployment) CreateDeployment(data *DeployCreate) (err error) { PeriodSeconds: 5, } } - //定义容器的limit和request资源 + deployment.Spec.Template.Spec.Containers[0].Resources.Limits = map[corev1.ResourceName]resource.Quantity{ corev1.ResourceCPU: resource.MustParse(data.Cpu), corev1.ResourceMemory: resource.MustParse(data.Memory), @@ -188,7 +183,6 @@ func (d *deployment) CreateDeployment(data *DeployCreate) (err error) { return nil } -//删除deployment func (d *deployment) DeleteDeployment(deploymentName, namespace string) (err error) { err = K8s.ClientSet.AppsV1().Deployments(namespace).Delete(context.TODO(), deploymentName, metav1.DeleteOptions{}) if err != nil { @@ -199,7 +193,6 @@ func (d *deployment) DeleteDeployment(deploymentName, namespace string) (err err return nil } -//重启deployment func (d *deployment) RestartDeployment(deploymentName, namespace string) (err error) { patchData := map[string]interface{}{ "spec": map[string]interface{}{ @@ -217,7 +210,6 @@ func (d *deployment) RestartDeployment(deploymentName, namespace string) (err er }, }, } - patchByte, err := json.Marshal(patchData) if err != nil { logger.Error(errors.New("json序列化失败, " + err.Error())) @@ -232,7 +224,6 @@ func (d *deployment) RestartDeployment(deploymentName, namespace string) (err er return nil } -//更新deployment func (d *deployment) UpdateDeployment(namespace, content string) (err error) { var deploy = &appsv1.Deployment{} @@ -250,7 +241,6 @@ func (d *deployment) UpdateDeployment(namespace, content string) (err error) { return nil } -//获取每个namespace的deployment数量 func (d *deployment) GetDeployNumPerNp() (deploysNps []*DeploysNp, err error) { namespaceList, err := K8s.ClientSet.CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{}) if err != nil { @@ -272,7 +262,6 @@ func (d *deployment) GetDeployNumPerNp() (deploysNps []*DeploysNp, err error) { return deploysNps, nil } -//类型转换 func (d *deployment) toCells(deployments []appsv1.Deployment) []DataCell { cells := make([]DataCell, len(deployments)) for i := range deployments { @@ -284,7 +273,6 @@ func (d *deployment) toCells(deployments []appsv1.Deployment) []DataCell { func (d *deployment) fromCells(cells []DataCell) []appsv1.Deployment { deployments := make([]appsv1.Deployment, len(cells)) for i := range cells { - //cells[i].(podCell)是将DataCell类型转成podCell deployments[i] = appsv1.Deployment(cells[i].(deploymentCell)) } return deployments diff --git a/service/ingress.go b/service/ingress.go index aca9a2e..0d17e6b 100644 --- a/service/ingress.go +++ b/service/ingress.go @@ -18,7 +18,6 @@ type IngressesResp struct { Total int `json:"total"` } -//定义ServiceCreate结构体,用于创建service需要的参数属性的定义 type IngressCreate struct { Name string `json:"name"` Namespace string `json:"namespace"` @@ -26,7 +25,6 @@ type IngressCreate struct { Hosts map[string][]*HttpPath `json:"hosts"` } -//定义ingress的path结构体 type HttpPath struct { Path string `json:"path"` PathType nwv1.PathType `json:"path_type"` @@ -34,15 +32,12 @@ type HttpPath struct { ServicePort int32 `json:"service_port"` } -//获取ingress列表,支持过滤、排序、分页 func (i *ingress) GetIngresses(filterName, namespace string, limit, page int) (ingressesResp *IngressesResp, err error) { - //获取ingressList类型的ingress列表 ingressList, err := K8s.ClientSet.NetworkingV1().Ingresses(namespace).List(context.TODO(), metav1.ListOptions{}) if err != nil { logger.Error(errors.New("获取Ingress列表失败, " + err.Error())) return nil, errors.New("获取Ingress列表失败, " + err.Error()) } - //将ingressList中的ingress列表(Items),放进dataselector对象中,进行排序 selectableData := &dataSelector{ GenericDataList: i.toCells(ingressList.Items), DataSelect: &DataSelectQuery{ @@ -58,7 +53,6 @@ func (i *ingress) GetIngresses(filterName, namespace string, limit, page int) (i total := len(filtered.GenericDataList) data := filtered.Sort().Paginate() - //将[]DataCell类型的ingress列表转为v1.ingress列表 ingresss := i.fromCells(data.GenericDataList) return &IngressesResp{ @@ -67,7 +61,6 @@ func (i *ingress) GetIngresses(filterName, namespace string, limit, page int) (i }, nil } -//获取ingress详情 func (i *ingress) GetIngresstDetail(ingressName, namespace string) (ingress *nwv1.Ingress, err error) { ingress, err = K8s.ClientSet.NetworkingV1().Ingresses(namespace).Get(context.TODO(), ingressName, metav1.GetOptions{}) if err != nil { @@ -78,12 +71,9 @@ func (i *ingress) GetIngresstDetail(ingressName, namespace string) (ingress *nwv return ingress, nil } -//创建ingress func (i *ingress) CreateIngress(data *IngressCreate) (err error) { - //声明nwv1.IngressRule和nwv1.HTTPIngressPath变量,后面组装数据于鏊用到 var ingressRules []nwv1.IngressRule var httpIngressPATHs []nwv1.HTTPIngressPath - //将data中的数据组装成nwv1.Ingress对象 ingress := &nwv1.Ingress{ ObjectMeta: metav1.ObjectMeta{ Name: data.Name, @@ -92,41 +82,33 @@ func (i *ingress) CreateIngress(data *IngressCreate) (err error) { }, Status: nwv1.IngressStatus{}, } - //第一层for循环是将host组装成nwv1.IngressRule类型的对象 - // 一个host对应一个ingressrule,每个ingressrule中包含一个host和多个path + for key, value := range data.Hosts { ir := nwv1.IngressRule{ Host: key, - //这里现将nwv1.HTTPIngressRuleValue类型中的Paths置为空,后面组装好数据再赋值 IngressRuleValue: nwv1.IngressRuleValue{ HTTP: &nwv1.HTTPIngressRuleValue{Paths: nil}, }, } - //第二层for循环是将path组装成nwv1.HTTPIngressPath类型的对象 for _, httpPath := range value { hip := nwv1.HTTPIngressPath{ Path: httpPath.Path, PathType: &httpPath.PathType, Backend: nwv1.IngressBackend{ Service: &nwv1.IngressServiceBackend{ - Name: httpPath.ServiceName, + Name: getServiceName(httpPath.ServiceName), Port: nwv1.ServiceBackendPort{ Number: httpPath.ServicePort, }, }, }, } - //将每个hip对象组装成数组 httpIngressPATHs = append(httpIngressPATHs, hip) } - //给Paths赋值,前面置为空了 ir.IngressRuleValue.HTTP.Paths = httpIngressPATHs - //将每个ir对象组装成数组,这个ir对象就是IngressRule,每个元素是一个host和多个path ingressRules = append(ingressRules, ir) } - //将ingressRules对象加入到ingress的规则中 ingress.Spec.Rules = ingressRules - //创建ingress _, err = K8s.ClientSet.NetworkingV1().Ingresses(data.Namespace).Create(context.TODO(), ingress, metav1.CreateOptions{}) if err != nil { logger.Error(errors.New("创建Ingress失败, " + err.Error())) @@ -136,7 +118,6 @@ func (i *ingress) CreateIngress(data *IngressCreate) (err error) { return nil } -//删除ingress func (i *ingress) DeleteIngress(ingressName, namespace string) (err error) { err = K8s.ClientSet.NetworkingV1().Ingresses(namespace).Delete(context.TODO(), ingressName, metav1.DeleteOptions{}) if err != nil { @@ -147,7 +128,6 @@ func (i *ingress) DeleteIngress(ingressName, namespace string) (err error) { return nil } -//更新ingress func (i *ingress) UpdateIngress(namespace, content string) (err error) { var ingress = &nwv1.Ingress{} diff --git a/service/init.go b/service/init.go index c5c0aac..9ae62e0 100644 --- a/service/init.go +++ b/service/init.go @@ -7,25 +7,22 @@ import ( "k8s.io/client-go/tools/clientcmd" ) -//用于初始化k8s clientset var K8s k8s type k8s struct { ClientSet *kubernetes.Clientset } -//初始化方法 func (k *k8s) Init() { conf, err := clientcmd.BuildConfigFromFlags("", config.Kubeconfig) if err != nil { - panic("获取k8s clinet配置失败," + err.Error()) + logger.Error("获取k8s client配置失败," + err.Error()) } - clientset, err := kubernetes.NewForConfig(conf) if err != nil { - panic("创建k8s clinet失败," + err.Error()) + logger.Error("创建k8s client失败," + err.Error()) } else { - logger.Info("k8s client 初始化成功") + logger.Info("k8s client 初始化成功!") } k.ClientSet = clientset diff --git a/service/login.go b/service/login.go new file mode 100644 index 0000000..d37bcab --- /dev/null +++ b/service/login.go @@ -0,0 +1,21 @@ +package service + +import ( + "dkube/config" + "errors" + "github.com/wonderivan/logger" +) + +var Login login + +type login struct{} + +func (l *login) Auth(username, password string) (err error) { + if username == config.AdminUser && password == config.AdminPwd { + return nil + } else { + logger.Error("登录失败, 用户名或密码错误") + return errors.New("登录失败, 用户名或密码错误") + } + return nil +} diff --git a/service/namespace.go b/service/namespace.go new file mode 100644 index 0000000..9774636 --- /dev/null +++ b/service/namespace.go @@ -0,0 +1,84 @@ +package service + +import ( + "context" + "errors" + "github.com/wonderivan/logger" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var Namespace namespace + +type namespace struct{} + +type NamespacesResp struct { + Items []corev1.Namespace `json:"items"` + Total int `json:"total"` +} + +func (n *namespace) GetNamespaces(filterName string, limit, page int) (namespacesResp *NamespacesResp, err error) { + namespaceList, err := K8s.ClientSet.CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{}) + if err != nil { + logger.Error(errors.New("获取Namespace列表失败, " + err.Error())) + return nil, errors.New("获取Namespace列表失败, " + err.Error()) + } + selectableData := &dataSelector{ + GenericDataList: n.toCells(namespaceList.Items), + DataSelect: &DataSelectQuery{ + Filter: &FilterQuery{Name: filterName}, + Paginate: &PaginateQuery{ + Limit: limit, + Page: page, + }, + }, + } + + filtered := selectableData.Filter() + total := len(filtered.GenericDataList) + data := filtered.Sort().Paginate() + + namespaces := n.fromCells(data.GenericDataList) + + return &NamespacesResp{ + Items: namespaces, + Total: total, + }, nil +} + +func (n *namespace) GetNamespaceDetail(namespaceName string) (namespace *corev1.Namespace, err error) { + namespace, err = K8s.ClientSet.CoreV1().Namespaces().Get(context.TODO(), namespaceName, metav1.GetOptions{}) + if err != nil { + logger.Error(errors.New("获取Namespace详情失败, " + err.Error())) + return nil, errors.New("获取Namespace详情失败, " + err.Error()) + } + + return namespace, nil +} + +func (n *namespace) DeleteNamespace(namespaceName string) (err error) { + err = K8s.ClientSet.CoreV1().Namespaces().Delete(context.TODO(), namespaceName, metav1.DeleteOptions{}) + if err != nil { + logger.Error(errors.New("删除Namespace失败, " + err.Error())) + return errors.New("删除Namespace失败, " + err.Error()) + } + + return nil +} + +func (n *namespace) toCells(std []corev1.Namespace) []DataCell { + cells := make([]DataCell, len(std)) + for i := range std { + cells[i] = namespaceCell(std[i]) + } + return cells +} + +func (n *namespace) fromCells(cells []DataCell) []corev1.Namespace { + namespaces := make([]corev1.Namespace, len(cells)) + for i := range cells { + namespaces[i] = corev1.Namespace(cells[i].(namespaceCell)) + } + + return namespaces +} diff --git a/service/node.go b/service/node.go new file mode 100644 index 0000000..a9533e3 --- /dev/null +++ b/service/node.go @@ -0,0 +1,74 @@ +package service + +import ( + "context" + "errors" + "github.com/wonderivan/logger" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var Node node + +type node struct{} + +type NodesResp struct { + Items []corev1.Node `json:"items"` + Total int `json:"total"` +} + +func (n *node) GetNodes(filterName string, limit, page int) (nodesResp *NodesResp, err error) { + nodeList, err := K8s.ClientSet.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{}) + if err != nil { + logger.Error(errors.New("获取Node列表失败, " + err.Error())) + return nil, errors.New("获取Node列表失败, " + err.Error()) + } + selectableData := &dataSelector{ + GenericDataList: n.toCells(nodeList.Items), + DataSelect: &DataSelectQuery{ + Filter: &FilterQuery{Name: filterName}, + Paginate: &PaginateQuery{ + Limit: limit, + Page: page, + }, + }, + } + + filtered := selectableData.Filter() + total := len(filtered.GenericDataList) + data := filtered.Sort().Paginate() + + nodes := n.fromCells(data.GenericDataList) + + return &NodesResp{ + Items: nodes, + Total: total, + }, nil +} + +func (n *node) GetNodeDetail(nodeName string) (node *corev1.Node, err error) { + node, err = K8s.ClientSet.CoreV1().Nodes().Get(context.TODO(), nodeName, metav1.GetOptions{}) + if err != nil { + logger.Error(errors.New("获取Node详情失败, " + err.Error())) + return nil, errors.New("获取Node详情失败, " + err.Error()) + } + + return node, nil +} + +func (n *node) toCells(std []corev1.Node) []DataCell { + cells := make([]DataCell, len(std)) + for i := range std { + cells[i] = nodeCell(std[i]) + } + return cells +} + +func (n *node) fromCells(cells []DataCell) []corev1.Node { + nodes := make([]corev1.Node, len(cells)) + for i := range cells { + nodes[i] = corev1.Node(cells[i].(nodeCell)) + } + + return nodes +} diff --git a/service/pod.go b/service/pod.go index 59fa1e8..83cbc39 100644 --- a/service/pod.go +++ b/service/pod.go @@ -21,13 +21,11 @@ type PodsResp struct { Items []corev1.Pod `json:"items"` } - type PodsNp struct { Namespace string PodNum int } - func (p *pod) GetPods(filterName, namespace string, limit, page int) (podsResp *PodsResp, err error) { podList, err := K8s.ClientSet.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{}) if err != nil { @@ -44,42 +42,37 @@ func (p *pod) GetPods(filterName, namespace string, limit, page int) (podsResp * }, }, } - filtered := selectableData.Filter() total := len(filtered.GenericDataList) - //排序和分页 data := filtered.Sort().Paginate() - pods := p.fromCells(data.GenericDataList) return &PodsResp{ Total: total, Items: pods, }, nil - } -//获取Pod详情 func (p *pod) GetPodDetail(podName, namespace string) (pod *corev1.Pod, err error) { pod, err = K8s.ClientSet.CoreV1().Pods(namespace).Get(context.TODO(), podName, metav1.GetOptions{}) if err != nil { logger.Error("获取Pod详情失败," + err.Error()) return nil, errors.New("获取Pod详情失败," + err.Error()) } + return pod, nil } -//删除Pod func (p *pod) DeletePod(podName, namespace string) (err error) { err = K8s.ClientSet.CoreV1().Pods(namespace).Delete(context.TODO(), podName, metav1.DeleteOptions{}) if err != nil { logger.Error("删除Pod失败," + err.Error()) return errors.New("删除Pod失败," + err.Error()) } + return nil } -//更新Pod func (p *pod) UpdatePod(namespace, content string) (err error) { pod := &corev1.Pod{} err = json.Unmarshal([]byte(content), pod) @@ -92,12 +85,11 @@ func (p *pod) UpdatePod(namespace, content string) (err error) { logger.Error("更新Pod失败," + err.Error()) return errors.New("更新Pod失败," + err.Error()) } + return nil } -//获取Pod容器名列表 func (p *pod) GetPodContainer(podName, namespace string) (containers []string, err error) { - //获取Pod详情 pod, err := p.GetPodDetail(podName, namespace) if err != nil { return nil, err @@ -105,43 +97,39 @@ func (p *pod) GetPodContainer(podName, namespace string) (containers []string, e for _, container := range pod.Spec.Containers { containers = append(containers, container.Name) } + return containers, nil } -//获取Pod内容器日志 func (p *pod) GetPodLog(containerName, podName, namespace string) (log string, err error) { lineLimit := int64(config.PodLogTailLine) option := &corev1.PodLogOptions{ Container: containerName, TailLines: &lineLimit, } - //获取request实例 req := K8s.ClientSet.CoreV1().Pods(namespace).GetLogs(podName, option) podLogs, err := req.Stream(context.TODO()) if err != nil { - logger.Error(errors.New("获取PodLogs失败," + err.Error())) - return "", errors.New("获取PodLogs失败," + err.Error()) + logger.Error(errors.New("获取PodLog失败, " + err.Error())) + return "", errors.New("获取PodLog失败, " + err.Error()) } - defer podLogs.Close() //处理完关闭 + defer podLogs.Close() buf := new(bytes.Buffer) _, err = io.Copy(buf, podLogs) if err != nil { - logger.Error(errors.New("获取PodLogs失败," + err.Error())) - return "", errors.New("获取PodLogs失败," + err.Error()) + logger.Error(errors.New("复制PodLog失败, " + err.Error())) + return "", errors.New("复制PodLog失败, " + err.Error()) } return buf.String(), nil } -//获取每个namespace的pod数量 func (p *pod) GetPodNumPerNp() (podsNps []*PodsNp, err error) { - //获取namespace列表 namespaceList, err := K8s.ClientSet.CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{}) if err != nil { return nil, err } for _, namespace := range namespaceList.Items { - //获取pod列表 podList, err := K8s.ClientSet.CoreV1().Pods(namespace.Name).List(context.TODO(), metav1.ListOptions{}) if err != nil { return nil, err @@ -169,4 +157,4 @@ func (p *pod) fromCells(cells []DataCell) []corev1.Pod { pods[i] = corev1.Pod(cells[i].(podCell)) } return pods -} +} \ No newline at end of file diff --git a/service/pv.go b/service/pv.go new file mode 100644 index 0000000..b27076f --- /dev/null +++ b/service/pv.go @@ -0,0 +1,84 @@ +package service + +import ( + "context" + "errors" + "github.com/wonderivan/logger" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var Pv pv + +type pv struct{} + +type PvsResp struct { + Items []corev1.PersistentVolume `json:"items"` + Total int `json:"total"` +} + +func (p *pv) GetPvs(filterName string, limit, page int) (pvsResp *PvsResp, err error) { + pvList, err := K8s.ClientSet.CoreV1().PersistentVolumes().List(context.TODO(), metav1.ListOptions{}) + if err != nil { + logger.Error(errors.New("获取Pv列表失败, " + err.Error())) + return nil, errors.New("获取Pv列表失败, " + err.Error()) + } + selectableData := &dataSelector{ + GenericDataList: p.toCells(pvList.Items), + DataSelect: &DataSelectQuery{ + Filter: &FilterQuery{Name: filterName}, + Paginate: &PaginateQuery{ + Limit: limit, + Page: page, + }, + }, + } + + filtered := selectableData.Filter() + total := len(filtered.GenericDataList) + data := filtered.Sort().Paginate() + + pvs := p.fromCells(data.GenericDataList) + + return &PvsResp{ + Items: pvs, + Total: total, + }, nil +} + +func (p *pv) GetPvDetail(pvName string) (pv *corev1.PersistentVolume, err error) { + pv, err = K8s.ClientSet.CoreV1().PersistentVolumes().Get(context.TODO(), pvName, metav1.GetOptions{}) + if err != nil { + logger.Error(errors.New("获取Pv详情失败, " + err.Error())) + return nil, errors.New("获取Pv详情失败, " + err.Error()) + } + + return pv, nil +} + +func (p *pv) DeletePv(pvName string) (err error) { + err = K8s.ClientSet.CoreV1().PersistentVolumes().Delete(context.TODO(), pvName, metav1.DeleteOptions{}) + if err != nil { + logger.Error(errors.New("删除Pv失败, " + err.Error())) + return errors.New("删除Pv失败, " + err.Error()) + } + + return nil +} + +func (p *pv) toCells(std []corev1.PersistentVolume) []DataCell { + cells := make([]DataCell, len(std)) + for i := range std { + cells[i] = pvCell(std[i]) + } + return cells +} + +func (p *pv) fromCells(cells []DataCell) []corev1.PersistentVolume { + pvs := make([]corev1.PersistentVolume, len(cells)) + for i := range cells { + pvs[i] = corev1.PersistentVolume(cells[i].(pvCell)) + } + + return pvs +} diff --git a/service/pvc.go b/service/pvc.go new file mode 100644 index 0000000..d3c7465 --- /dev/null +++ b/service/pvc.go @@ -0,0 +1,101 @@ +package service + +import ( + "context" + "encoding/json" + "errors" + "github.com/wonderivan/logger" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var Pvc pvc + +type pvc struct{} + +type PvcsResp struct { + Items []corev1.PersistentVolumeClaim `json:"items"` + Total int `json:"total"` +} + +func (p *pvc) GetPvcs(filterName, namespace string, limit, page int) (pvcsResp *PvcsResp, err error) { + pvcList, err := K8s.ClientSet.CoreV1().PersistentVolumeClaims(namespace).List(context.TODO(), metav1.ListOptions{}) + if err != nil { + logger.Error(errors.New("获取Pvc列表失败, " + err.Error())) + return nil, errors.New("获取Pvc列表失败, " + err.Error()) + } + selectableData := &dataSelector{ + GenericDataList: p.toCells(pvcList.Items), + DataSelect: &DataSelectQuery{ + Filter: &FilterQuery{Name: filterName}, + Paginate: &PaginateQuery{ + Limit: limit, + Page: page, + }, + }, + } + + filtered := selectableData.Filter() + total := len(filtered.GenericDataList) + data := filtered.Sort().Paginate() + pvcs := p.fromCells(data.GenericDataList) + + return &PvcsResp{ + Items: pvcs, + Total: total, + }, nil +} + +func (p *pvc) GetPvcDetail(pvcName, namespace string) (pvc *corev1.PersistentVolumeClaim, err error) { + pvc, err = K8s.ClientSet.CoreV1().PersistentVolumeClaims(namespace).Get(context.TODO(), pvcName, metav1.GetOptions{}) + if err != nil { + logger.Error(errors.New("获取Pvc详情失败, " + err.Error())) + return nil, errors.New("获取Pvc详情失败, " + err.Error()) + } + + return pvc, nil +} + +func (p *pvc) DeletePvc(pvcName, namespace string) (err error) { + err = K8s.ClientSet.CoreV1().PersistentVolumeClaims(namespace).Delete(context.TODO(), pvcName, metav1.DeleteOptions{}) + if err != nil { + logger.Error(errors.New("删除Pvc失败, " + err.Error())) + return errors.New("删除Pvc失败, " + err.Error()) + } + + return nil +} + +func (p *pvc) UpdatePvc(namespace, content string) (err error) { + var pvc = &corev1.PersistentVolumeClaim{} + + err = json.Unmarshal([]byte(content), pvc) + if err != nil { + logger.Error(errors.New("反序列化失败, " + err.Error())) + return errors.New("反序列化失败, " + err.Error()) + } + + _, err = K8s.ClientSet.CoreV1().PersistentVolumeClaims(namespace).Update(context.TODO(), pvc, metav1.UpdateOptions{}) + if err != nil { + logger.Error(errors.New("更新Pvc失败, " + err.Error())) + return errors.New("更新Pvc失败, " + err.Error()) + } + return nil +} + +func (p *pvc) toCells(std []corev1.PersistentVolumeClaim) []DataCell { + cells := make([]DataCell, len(std)) + for i := range std { + cells[i] = pvcCell(std[i]) + } + return cells +} + +func (p *pvc) fromCells(cells []DataCell) []corev1.PersistentVolumeClaim { + pvcs := make([]corev1.PersistentVolumeClaim, len(cells)) + for i := range cells { + pvcs[i] = corev1.PersistentVolumeClaim(cells[i].(pvcCell)) + } + + return pvcs +} diff --git a/service/secret.go b/service/secret.go new file mode 100644 index 0000000..686532d --- /dev/null +++ b/service/secret.go @@ -0,0 +1,100 @@ +package service + +import ( + "context" + "encoding/json" + "errors" + "github.com/wonderivan/logger" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var Secret secret + +type secret struct{} + +type SecretsResp struct { + Items []corev1.Secret `json:"items"` + Total int `json:"total"` +} + +func (s *secret) GetSecrets(filterName, namespace string, limit, page int) (secretsResp *SecretsResp, err error) { + secretList, err := K8s.ClientSet.CoreV1().Secrets(namespace).List(context.TODO(), metav1.ListOptions{}) + if err != nil { + logger.Error(errors.New("获取Secret列表失败, " + err.Error())) + return nil, errors.New("获取Secret列表失败, " + err.Error()) + } + selectableData := &dataSelector{ + GenericDataList: s.toCells(secretList.Items), + DataSelect: &DataSelectQuery{ + Filter: &FilterQuery{Name: filterName}, + Paginate: &PaginateQuery{ + Limit: limit, + Page: page, + }, + }, + } + + filtered := selectableData.Filter() + total := len(filtered.GenericDataList) + data := filtered.Sort().Paginate() + secrets := s.fromCells(data.GenericDataList) + return &SecretsResp{ + Items: secrets, + Total: total, + }, nil +} + +func (s *secret) GetSecretDetail(secretName, namespace string) (secret *corev1.Secret, err error) { + secret, err = K8s.ClientSet.CoreV1().Secrets(namespace).Get(context.TODO(), secretName, metav1.GetOptions{}) + if err != nil { + logger.Error(errors.New("获取Secret详情失败, " + err.Error())) + return nil, errors.New("获取Secret详情失败, " + err.Error()) + } + + return secret, nil +} + +func (s *secret) DeleteSecret(secretName, namespace string) (err error) { + err = K8s.ClientSet.CoreV1().Secrets(namespace).Delete(context.TODO(), secretName, metav1.DeleteOptions{}) + if err != nil { + logger.Error(errors.New("删除Secret失败, " + err.Error())) + return errors.New("删除Secret失败, " + err.Error()) + } + + return nil +} + +func (s *secret) UpdateSecret(namespace, content string) (err error) { + var secret = &corev1.Secret{} + + err = json.Unmarshal([]byte(content), secret) + if err != nil { + logger.Error(errors.New("反序列化失败, " + err.Error())) + return errors.New("反序列化失败, " + err.Error()) + } + + _, err = K8s.ClientSet.CoreV1().Secrets(namespace).Update(context.TODO(), secret, metav1.UpdateOptions{}) + if err != nil { + logger.Error(errors.New("更新Secret失败, " + err.Error())) + return errors.New("更新Secret失败, " + err.Error()) + } + return nil +} + +func (s *secret) toCells(std []corev1.Secret) []DataCell { + cells := make([]DataCell, len(std)) + for i := range std { + cells[i] = secretCell(std[i]) + } + return cells +} + +func (s *secret) fromCells(cells []DataCell) []corev1.Secret { + secrets := make([]corev1.Secret, len(cells)) + for i := range cells { + secrets[i] = corev1.Secret(cells[i].(secretCell)) + } + + return secrets +} diff --git a/service/service.go b/service/service.go index e75d38e..f4953b8 100644 --- a/service/service.go +++ b/service/service.go @@ -29,7 +29,6 @@ type ServiceCreate struct { Label map[string]string `json:"label"` } -//获取service列表,支持过滤、排序、分页 func (s *servicev1) GetServices(filterName, namespace string, limit, page int) (servicesResp *ServicesResp, err error) { serviceList, err := K8s.ClientSet.CoreV1().Services(namespace).List(context.TODO(), metav1.ListOptions{}) if err != nil { @@ -50,8 +49,6 @@ func (s *servicev1) GetServices(filterName, namespace string, limit, page int) ( filtered := selectableData.Filter() total := len(filtered.GenericDataList) data := filtered.Sort().Paginate() - - //将[]DataCell类型的service列表转为v1.service列表 services := s.fromCells(data.GenericDataList) return &ServicesResp{ @@ -60,7 +57,6 @@ func (s *servicev1) GetServices(filterName, namespace string, limit, page int) ( }, nil } -//获取service详情 func (s *servicev1) GetServicetDetail(serviceName, namespace string) (service *corev1.Service, err error) { service, err = K8s.ClientSet.CoreV1().Services(namespace).Get(context.TODO(), serviceName, metav1.GetOptions{}) if err != nil { @@ -73,13 +69,11 @@ func (s *servicev1) GetServicetDetail(serviceName, namespace string) (service *c func (s *servicev1) CreateService(data *ServiceCreate) (err error) { service := &corev1.Service{ - //ObjectMeta中定义资源名、命名空间以及标签 ObjectMeta: metav1.ObjectMeta{ Name: data.Name, Namespace: data.Namespace, Labels: data.Label, }, - Spec: corev1.ServiceSpec{ Type: corev1.ServiceType(data.Type), Ports: []corev1.ServicePort{ @@ -99,7 +93,6 @@ func (s *servicev1) CreateService(data *ServiceCreate) (err error) { if data.NodePort != 0 && data.Type == "NodePort" { service.Spec.Ports[0].NodePort = data.NodePort } - //创建Service _, err = K8s.ClientSet.CoreV1().Services(data.Namespace).Create(context.TODO(), service, metav1.CreateOptions{}) if err != nil { logger.Error(errors.New("创建Service失败, " + err.Error())) @@ -109,7 +102,6 @@ func (s *servicev1) CreateService(data *ServiceCreate) (err error) { return nil } -//删除service func (s *servicev1) DeleteService(serviceName, namespace string) (err error) { err = K8s.ClientSet.CoreV1().Services(namespace).Delete(context.TODO(), serviceName, metav1.DeleteOptions{}) if err != nil { @@ -120,7 +112,6 @@ func (s *servicev1) DeleteService(serviceName, namespace string) (err error) { return nil } -//更新service func (s *servicev1) UpdateService(namespace, content string) (err error) { var service = &corev1.Service{} diff --git a/service/statefulset.go b/service/statefulset.go new file mode 100644 index 0000000..74df248 --- /dev/null +++ b/service/statefulset.go @@ -0,0 +1,101 @@ +package service + +import ( + "context" + "encoding/json" + "errors" + "github.com/wonderivan/logger" + appsv1 "k8s.io/api/apps/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var StatefulSet statefulSet + +type statefulSet struct{} + +type StatusfulSetsResp struct { + Items []appsv1.StatefulSet `json:"items"` + Total int `json:"total"` +} + +func (s *statefulSet) GetStatefulSets(filterName, namespace string, limit, page int) (statusfulSetsResp *StatusfulSetsResp, err error) { + statefulSetList, err := K8s.ClientSet.AppsV1().StatefulSets(namespace).List(context.TODO(), metav1.ListOptions{}) + if err != nil { + logger.Error(errors.New("获取StatefulSet列表失败, " + err.Error())) + return nil, errors.New("获取StatefulSet列表失败, " + err.Error()) + } + selectableData := &dataSelector{ + GenericDataList: s.toCells(statefulSetList.Items), + DataSelect: &DataSelectQuery{ + Filter: &FilterQuery{Name: filterName}, + Paginate: &PaginateQuery{ + Limit: limit, + Page: page, + }, + }, + } + + filtered := selectableData.Filter() + total := len(filtered.GenericDataList) + data := filtered.Sort().Paginate() + statefulSets := s.fromCells(data.GenericDataList) + + return &StatusfulSetsResp{ + Items: statefulSets, + Total: total, + }, nil +} + +func (s *statefulSet) GetStatefulSetDetail(statefulSetName, namespace string) (statefulSet *appsv1.StatefulSet, err error) { + statefulSet, err = K8s.ClientSet.AppsV1().StatefulSets(namespace).Get(context.TODO(), statefulSetName, metav1.GetOptions{}) + if err != nil { + logger.Error(errors.New("获取StatefulSet详情失败, " + err.Error())) + return nil, errors.New("获取StatefulSet详情失败, " + err.Error()) + } + + return statefulSet, nil +} + +func (s *statefulSet) DeleteStatefulSet(statefulSetName, namespace string) (err error) { + err = K8s.ClientSet.AppsV1().StatefulSets(namespace).Delete(context.TODO(), statefulSetName, metav1.DeleteOptions{}) + if err != nil { + logger.Error(errors.New("删除StatefulSet失败, " + err.Error())) + return errors.New("删除StatefulSet失败, " + err.Error()) + } + + return nil +} + +func (s *statefulSet) UpdateStatefulSet(namespace, content string) (err error) { + var statefulSet = &appsv1.StatefulSet{} + + err = json.Unmarshal([]byte(content), statefulSet) + if err != nil { + logger.Error(errors.New("反序列化失败, " + err.Error())) + return errors.New("反序列化失败, " + err.Error()) + } + + _, err = K8s.ClientSet.AppsV1().StatefulSets(namespace).Update(context.TODO(), statefulSet, metav1.UpdateOptions{}) + if err != nil { + logger.Error(errors.New("更新StatefulSet失败, " + err.Error())) + return errors.New("更新StatefulSet失败, " + err.Error()) + } + return nil +} + +func (s *statefulSet) toCells(std []appsv1.StatefulSet) []DataCell { + cells := make([]DataCell, len(std)) + for i := range std { + cells[i] = statefulSetCell(std[i]) + } + return cells +} + +func (s *statefulSet) fromCells(cells []DataCell) []appsv1.StatefulSet { + statefulSets := make([]appsv1.StatefulSet, len(cells)) + for i := range cells { + statefulSets[i] = appsv1.StatefulSet(cells[i].(statefulSetCell)) + } + + return statefulSets +} diff --git a/service/terminal.go b/service/terminal.go new file mode 100644 index 0000000..76db01b --- /dev/null +++ b/service/terminal.go @@ -0,0 +1,175 @@ +package service + +import ( + "dkube/config" + "encoding/json" + "errors" + "fmt" + "github.com/gorilla/websocket" + "github.com/wonderivan/logger" + v1 "k8s.io/api/core/v1" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/client-go/tools/remotecommand" + "log" + "net/http" + "time" +) + +var Terminal terminal + +type terminal struct{} + +func (t *terminal) WsHandler(w http.ResponseWriter, r *http.Request) { + conf, err := clientcmd.BuildConfigFromFlags("", config.Kubeconfig) + if err != nil { + logger.Error("加载k8s配置失败, " + err.Error()) + return + } + if err := r.ParseForm(); err != nil { + logger.Error("解析参数失败, " + err.Error()) + return + } + namespace := r.Form.Get("namespace") + podName := r.Form.Get("pod_name") + containerName := r.Form.Get("container_name") + logger.Info("exec pod: %s, container: %s, namespace: %s\n", podName, containerName, namespace) + + pty, err := NewTerminalSession(w, r, nil) + if err != nil { + logger.Error("实例化TerminalSession失败, " + err.Error()) + return + } + defer func() { + logger.Info("关闭TerminalSession") + pty.Close() + }() + req := K8s.ClientSet.CoreV1().RESTClient().Post(). + Resource("pods"). + Name(podName). + Namespace(namespace). + SubResource("exec"). + VersionedParams(&v1.PodExecOptions{ + Stdin: true, + Stdout: true, + Stderr: true, + TTY: true, + Container: containerName, + Command: []string{"/bin/bash"}, + }, scheme.ParameterCodec) + logger.Info("exec post request url: ", req) + + executor, err := remotecommand.NewSPDYExecutor(conf, "POST", req.URL()) + if err != nil { + logger.Error("建立SPDY连接失败, " + err.Error()) + return + } + err = executor.Stream(remotecommand.StreamOptions{ + Stdin: pty, + Stdout: pty, + Stderr: pty, + Tty: true, + TerminalSizeQueue: pty, + }) + + if err != nil { + logger.Error("执行 pod 命令失败, " + err.Error()) + pty.Write([]byte("执行 pod 命令失败, " + err.Error())) + pty.Done() + } +} + +type terminalMessage struct { + Operation string `json:"operation"` + Data string `json:"data"` + Rows uint16 `json:"rows"` + Cols uint16 `json:"cols"` +} + +type TerminalSession struct { + wsConn *websocket.Conn + sizeChan chan remotecommand.TerminalSize + doneChan chan struct{} +} + +var upgrader = func() websocket.Upgrader { + upgrader := websocket.Upgrader{} + upgrader.HandshakeTimeout = time.Second * 2 + upgrader.CheckOrigin = func(r *http.Request) bool { + return true + } + return upgrader +}() + +func NewTerminalSession(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*TerminalSession, error) { + conn, err := upgrader.Upgrade(w, r, responseHeader) + if err != nil { + return nil, errors.New("升级websocket失败," + err.Error()) + } + session := &TerminalSession{ + wsConn: conn, + sizeChan: make(chan remotecommand.TerminalSize), + doneChan: make(chan struct{}), + } + + return session, nil +} + +func (t *TerminalSession) Read(p []byte) (int, error) { + _, message, err := t.wsConn.ReadMessage() + if err != nil { + log.Printf("read message err: %v", err) + return 0, err + } + var msg terminalMessage + if err := json.Unmarshal(message, &msg); err != nil { + log.Printf("read parse message err: %v", err) + return 0, err + } + switch msg.Operation { + case "stdin": + return copy(p, msg.Data), nil + case "resize": + t.sizeChan <- remotecommand.TerminalSize{Width: msg.Cols, Height: msg.Rows} + return 0, nil + case "ping": + return 0, nil + default: + log.Printf("unknown message type '%s'", msg.Operation) + return 0, fmt.Errorf("unknown message type '%s'", msg.Operation) + } +} + +func (t *TerminalSession) Write(p []byte) (int, error) { + msg, err := json.Marshal(terminalMessage{ + Operation: "stdout", + Data: string(p), + }) + if err != nil { + log.Printf("write parse message err: %v", err) + return 0, err + } + if err := t.wsConn.WriteMessage(websocket.TextMessage, msg); err != nil { + log.Printf("write message err: %v", err) + return 0, err + } + + return len(p), nil +} + +func (t *TerminalSession) Done() { + close(t.doneChan) +} + +func (t *TerminalSession) Close() { + t.wsConn.Close() +} + +func (t *TerminalSession) Next() *remotecommand.TerminalSize { + select { + case size := <-t.sizeChan: + return &size + case <-t.doneChan: + return nil + } +} diff --git a/service/workflow.go b/service/workflow.go new file mode 100644 index 0000000..a8e5369 --- /dev/null +++ b/service/workflow.go @@ -0,0 +1,167 @@ +package service + +import ( + "dkube/dao" + "dkube/model" +) + +var Workflow workflow + +type workflow struct{} + +type WorkflowCreate struct { + Name string `json:"name"` + Namespace string `json:"namespace"` + Replicas int32 `json:"replicas"` + Image string `json:"image"` + Label map[string]string `json:"label"` + Cpu string `json:"cpu"` + Memory string `json:"memory"` + ContainerPort int32 `json:"container_port"` + HealthCheck bool `json:"health_check"` + HealthPath string `json:"health_path"` + Type string `json:"type"` + Port int32 `json:"port"` + NodePort int32 `json:"node_port"` + Hosts map[string][]*HttpPath `json:"hosts"` +} + +func (w *workflow) GetList(name, namespace string, page, limit int) (data *dao.WorkflowResp, err error) { + data, err = dao.Workflow.GetWorkflows(name, namespace, page, limit) + if err != nil { + return nil, err + } + return data, nil +} + +func (w *workflow) GetById(id int) (data *model.Workflow, err error) { + data, err = dao.Workflow.GetById(id) + if err != nil { + return nil, err + } + return data, nil +} + +func (w *workflow) CreateWorkFlow(data *WorkflowCreate) (err error) { + var ingressName string + if data.Type == "Ingress" { + ingressName = getIngressName(data.Name) + } else { + ingressName = "" + } + + workflow := &model.Workflow{ + Name: data.Name, + Namespace: data.Namespace, + Replicas: data.Replicas, + Deployment: data.Name, + Service: getServiceName(data.Name), + Ingress: ingressName, + Type: data.Type, + } + err = dao.Workflow.Add(workflow) + if err != nil { + return err + } + err = createWorkflowRes(data) + if err != nil { + return err + } + return err +} + +func (w *workflow) DelById(id int) (err error) { + workflow, err := dao.Workflow.GetById(id) + if err != nil { + return err + } + err = delWorkflowRes(workflow) + if err != nil { + return err + } + err = dao.Workflow.DelById(id) + if err != nil { + return err + } + return +} + +func delWorkflowRes(workflow *model.Workflow) (err error) { + err = Deployment.DeleteDeployment(workflow.Name, workflow.Namespace) + if err != nil { + return err + } + err = Servicev1.DeleteService(getServiceName(workflow.Name), workflow.Namespace) + if err != nil { + return err + } + + if workflow.Type == "Ingress" { + err = Ingress.DeleteIngress(getIngressName(workflow.Name), workflow.Namespace) + if err != nil { + return err + } + } + + return nil +} + +func createWorkflowRes(data *WorkflowCreate) (err error) { + dc := &DeployCreate{ + Name: data.Name, + Namespace: data.Namespace, + Replicas: data.Replicas, + Image: data.Image, + Label: data.Label, + Cpu: data.Cpu, + Memory: data.Memory, + ContainerPort: data.ContainerPort, + HealthCheck: data.HealthCheck, + HealthPath: data.HealthPath, + } + err = Deployment.CreateDeployment(dc) + if err != nil { + return err + } + var serviceType string + if data.Type != "Ingress" { + serviceType = data.Type + } else { + serviceType = "ClusterIP" + } + sc := &ServiceCreate{ + Name: getServiceName(data.Name), + Namespace: data.Namespace, + Type: serviceType, + ContainerPort: data.ContainerPort, + Port: data.Port, + NodePort: data.NodePort, + Label: data.Label, + } + if err := Servicev1.CreateService(sc); err != nil { + return err + } + var ic *IngressCreate + if data.Type == "Ingress" { + ic = &IngressCreate{ + Name: getIngressName(data.Name), + Namespace: data.Namespace, + Label: data.Label, + Hosts: data.Hosts, + } + err = Ingress.CreateIngress(ic) + if err != nil { + return err + } + } + + return nil +} + +func getServiceName(workflowName string) (serviceName string) { + return workflowName + "-svc" +} + +func getIngressName(workflowName string) (ingressName string) { + return workflowName + "-ing" +} diff --git a/utils/jwt.go b/utils/jwt.go new file mode 100644 index 0000000..7416567 --- /dev/null +++ b/utils/jwt.go @@ -0,0 +1,46 @@ +package utils + +import ( + "errors" + "github.com/dgrijalva/jwt-go" + "github.com/wonderivan/logger" +) + +var JWTToken jwtToken + +type jwtToken struct{} + +type CustomClaims struct { + UserName string `json:"username"` + Password string `json:"password"` + jwt.StandardClaims +} + +const ( + SECRET = "devops" +) + +func (*jwtToken) ParseToken(tokenString string) (claims *CustomClaims, err error) { + token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) { + return []byte(SECRET), nil + }) + if err != nil { + logger.Error("parse token failed ", err) + if ve, ok := err.(*jwt.ValidationError); ok { + if ve.Errors&jwt.ValidationErrorMalformed != 0 { + return nil, errors.New("TokenMalformed") + } else if ve.Errors&jwt.ValidationErrorExpired != 0 { + // Token is expired + return nil, errors.New("TokenExpired") + } else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 { + return nil, errors.New("TokenNotValidYet") + } else { + return nil, errors.New("TokenInvalid") + } + } + } + if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid { + return claims, nil + } + return nil, errors.New("解析Token失败") +}