# kms-backend
**Repository Path**: bigberg/kms-backend
## Basic Information
- **Project Name**: kms-backend
- **Description**: No description available
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2024-04-03
- **Last Updated**: 2024-04-11
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
工程化目录
```
这是一个脚手架项目,可以根据这个项目生成一个基础的框架
```
### 一、目录结构
main.go: 程序入口
config : 配置层,存放配置文件
controllers: 控制器层,实现路由的逻辑处理
routers: 路由层,管理程序的路由信息
middlewares: 中间件层
model: 模型层
utils: 工具层
### 二、JWT
JSON Web Token(JSON Web令牌),是一个开放标准(rfc7519),它定义了一种紧凑的、自包含的方式,用于在各方之间以JSON对象安全地传输信息。此信息可以验证和信任,因为它是数字签名的。jwt可以使用秘密〈使用HNAC算法)或使用RSA或ECDSA的公钥/私钥对进行签名。
JWT作用:
- 授权:一旦用户登录,每个后续请求将包括JWT,从而允许用户访问该令牌允许的路由,服务和资源。它的开销很小并且可以在不同的域中使用。如:单点登录。
- 信息交换:在各方之间安全地传输信息。JWT可进行签名(如使用公钥/私钥对),因此可确保发件人。由于签名是使用标头和有效负载计算的,因此还可验证内容是否被篡改。
#### 2.1 封装生成JWT的函数
下载jtw
go get -u github.com/golang-jwt/jwt/v5
在config.go 定义一些配置信息
```
var (
JwtSignKey string
JwtExpireTime int64 // jwt token 过期时间,单位: 分钟
)
func init() {
// 设置jwt环境变量
viper.SetDefault("JWT_SIGN_KEY", "Bigberg")
JwtSignKey = viper.GetString("JWT_SIGN_KEY")
// 设置jwt过期时间,默认值120分钟
viper.SetDefault("JWT_EXPIRE_TIME", 120)
JwtExpireTime = viper.GetInt64("JWT_EXPIRE_TIME")
}
```
在utils中定义token封装函数
```
package jwtutils
import (
"kms-backend/config"
"time"
"github.com/golang-jwt/jwt/v5"
)
// 定义一个byte类型的变量
var jwtSignKey = []byte(config.JwtSignKey)
// 1.自定义声明类型
type MyCustomClaims struct {
Username string `json:"username"`
jwt.RegisteredClaims
}
// 2. 封装生成token的函数
func GenToken(username string) (string, error) {
claims := MyCustomClaims{
"bar",
jwt.RegisteredClaims{
// A usual scenario is to set the expiration time relative to the current time
// 生成时间加上一个设置的分钟数为过期时间
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Minute * time.Duration(config.JwtExpireTime))),
IssuedAt: jwt.NewNumericDate(time.Now()),
NotBefore: jwt.NewNumericDate(time.Now()),
Issuer: "bergcom", // 颁发机构
Subject: "Bigberg",
},
}
// 生成token
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
ss, err := token.SignedString(jwtSignKey)
return ss, err
}
```
在main函数中调用封装函数,生成token。
```
func main() {
r := gin.Default()
logs.Info(nil, "程序启动成功!")
// 测试生成jwt的 token 是否可用
ss, _ := jwtutils.GenToken("bigberg")
logs.Info(nil, ss)
r.Run(config.Port)
}
```
输出
```
{"file":"E:/code/kms-backend/utils/logs/logs.go:14","func":"kms-backend/utils/logs.Info","level":"info","msg":"程序启动成功!","time":"2023-09-11 10:24:13"}
{"file":"E:/code/kms-backend/utils/logs/logs.go:14","func":"kms-backend/utils/logs.Info","level":"info","msg":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImJhciIsImlzcyI6ImJlcmdjb20iLCJzdWIiOiJCaWdiZXJnIiwiZXhwIjoxNjk0NDA2MjUzLCJuYmYiOjE2OTQzOTkwNTMsImlhdCI6MTY5NDM5OTA1M30.944BRCSVApjo3iYTQyEsl0J8V6pZ9VAuXS7Mg8nQUoc","time":"2023-09-11 10:24:13"}
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :8888
```
#### 2.2 校验Token
生成token后,还需要校验token是否合法,在utils中定义token解析函数
```
// 3. 解析token
func ParseToker(ss string) (*MyCustomClaims, error) {
token, err := jwt.ParseWithClaims(ss, &MyCustomClaims{}, func(token *jwt.Token) (interface{}, error) {
return jwtSignKey, nil
})
if err != nil {
// 解析失败
logs.Error(nil, "token解析失败")
}
if claims, ok := token.Claims.(*MyCustomClaims); ok && token.Valid {
// token合法
return claims, nil
} else {
// token不合法
logs.Warning(nil, "Token 不合法")
return nil, errors.New("Token不合法: invalid token")
}
}
```
验证,main函数中添加
```
claim, err := jwtutils.ParseToker(ss)
if err != nil {
fmt.Println("解析token失败:", err.Error())
} else {
fmt.Println(claim)
}
```
### 三、client-go
#### 3.1 安装client-go
```
go git k8s.io/client-go@latest
```
#### 3.2 client-go 初体验
`kms-backend/client-go.go`
```
package main
import (
"context"
"fmt"
"kms-backend/utils/logs"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
)
func main() {
// 1. 初始化config实例
config, err := clientcmd.BuildConfigFromFlags("", "./config/cls-ik7lvowm-config")
if err != nil {
panic((err.Error()))
}
// 2. 创建客户端工具,clientset
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
panic(err.Error())
}
// 3. 操作集群
// Pods("default") 查询 namespace default 下的pod数量
// 如果为空,查询所有namespace下pods总数
// 查询api-resource
// kubectl api-resources | grep deployment
// clientset.AppsV1()
// clientset.NetworkingV1()
pods, err := clientset.CoreV1().Pods("default").List(context.TODO(), metav1.ListOptions{})
if err != nil {
logs.Error(nil, "查询Pods失败")
} else {
fmt.Printf("There are %d pods in th cluster\n", len(pods.Items))
}
// 查询deployment
deployments, _ := clientset.AppsV1().Deployments("saas").List(context.TODO(), metav1.ListOptions{})
deploymentsItems := deployments.Items
for _, deploy := range deploymentsItems {
fmt.Printf("当前deployment名称是: %s\n", deploy.Name)
}
}
```
#### 3.3 Get方法
```
...
...
func main() {
...
// Get方法
// Get方法中需要传入一个名称参数
podDetail, _ := clientset.CoreV1().Pods("default").Get(context.TODO(), "nginx-deployment-77dd77c74d-69gzc", metav1.GetOptions{})
// 输出pod中第一个容器镜像的名称
fmt.Printf("pod中第一个镜像名称: %s", podDetail.Spec.Containers[0].Image)
// 获取namespace详情
namespaceDetail, _ := clientset.CoreV1().Namespaces().Get(context.TODO(), "saas", metav1.GetOptions{})
fmt.Print(namespaceDetail)
}
```
#### 3.4 Update更新操作
```
import (
"context"
"fmt"
"kms-backend/utils/logs"
appsv1 "k8s.io/api/apps/v1"
apiCorev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
)
func main(){
...
...
// 更新操作
// 获取deployment并更新
deploymentDetail, _ := clientset.AppsV1().Deployments("default").Get(context.TODO(), "nginx-deployment", metav1.GetOptions{})
fmt.Print("该denployment的名称是:", deploymentDetail.Name)
// 获取当前label
// labels := deploymentDetail.Labels
// labels["newlabel"] = "new-label"
// 修改annotination
// deploymentDetail.Annotations["new-anno"] = "new-anno"
// 如果labels 和annotination 原本是空,会报空指针错误,需要提前初始化map
// 修改副本数量
// replicas 需要一个指针类型
// newReplicas := int32(2)
// deploymentDetail.Spec.Replicas = &newReplicas
// 修改镜像版本
deploymentDetail.Spec.Template.Spec.Containers[0].Image = "nginx:1.22.0"
_, err = clientset.AppsV1().Deployments("default").Update(context.TODO(), deploymentDetail, metav1.UpdateOptions{})
if err != nil {
fmt.Print("deployment更新失败: ", err.Error())
}
}
```
#### 3.5 Delete删除操作
```
import (
"context"
"fmt"
"kms-backend/utils/logs"
appsv1 "k8s.io/api/apps/v1"
apiCorev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
)
func main(){
...
// 删除资源
err = clientset.AppsV1().Deployments("default").Delete(context.TODO(), "nginx-deployment", metav1.DeleteOptions{})
if err != nil {
fmt.Print("删除资源失败: ", err.Error())
}
}
```
#### 3.6 创建资源
```
import (
"context"
"fmt"
"kms-backend/utils/logs"
appsv1 "k8s.io/api/apps/v1"
apiCorev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
)
func main(){
...
// 创建资源
// 创建一个Namespace
// 声明一个namespace的类型
// var newNamespace apiCorev1.Namespace
// newNamespace.Name = "mytest"
// _, err = clientset.CoreV1().Namespaces().Create(context.TODO(), &newNamespace, metav1.CreateOptions{})
// if err != nil {
// fmt.Print("创建资源失败: ", err.Error())
// }
// 创建一个deployment
// kubect create deployment nginx --image=nginx --dry-run -oymal
// 上述命令可以导出一个创建deploymnet必备的参数文档
var newDeployment appsv1.Deployment
newDeployment.Name = "nginx"
newDeployment.Namespace = "mytest"
// 初始化一个label,并将其赋值给 selector Matchlabels 和 deployment 本身metadata中的labels
label := make(map[string]string)
label["app"] = "nginx"
label["version"] = "v1"
// selector 需要进行初始化
newDeployment.Spec.Selector = &metav1.LabelSelector{}
newDeployment.Spec.Selector.MatchLabels = label
newDeployment.Labels = label
// 此处更改pod的标签
newDeployment.Spec.Template.Labels = label
// 初始化容器
var newContainers []apiCorev1.Container
// 声明一个容器
var container apiCorev1.Container
container.Image = "nginx"
container.Name = "nginx"
newContainers = append(newContainers, container)
// 再创建一个容器
container.Image = "redis"
container.Name = "redis"
newContainers = append(newContainers, container)
newDeployment.Spec.Template.Spec.Containers = newContainers
_, err = clientset.AppsV1().Deployments("mytest").Create(context.TODO(), &newDeployment, metav1.CreateOptions{})
if err != nil {
fmt.Print("创建Deployment资源失败: ", err.Error())
}
}
```
#### 3.7 通过json串创建资源
```
import (
"context"
"encoding/json"
"fmt"
"kms-backend/utils/logs"
appsv1 "k8s.io/api/apps/v1"
// apiCorev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
)
fun main() {
// 通过json创建资源
// kubectl create deploy redis --image=redis --dry-run=client -ojson
deployJson := `{
"kind": "Deployment",
"apiVersion": "apps/v1",
"metadata": {
"name": "redis",
"creationTimestamp": null,
"labels": {
"app": "redis"
}
},
"spec": {
"replicas": 1,
"selector": {
"matchLabels": {
"app": "redis"
}
},
"template": {
"metadata": {
"creationTimestamp": null,
"labels": {
"app": "redis"
}
},
"spec": {
"containers": [
{
"name": "redis",
"image": "redis",
"resources": {}
}
]
}
},
"strategy": {}
},
"status": {}
}`
// 声明创建k8s的类型
var newDeployment2 appsv1.Deployment
// 把json转换成string
err = json.Unmarshal([]byte(deployJson), &newDeployment2)
if err != nil {
fmt.Print("json转换失败: ", err.Error())
}
fmt.Print("json转换struct后配置详情: ", newDeployment2)
// 创建
_, err = clientset.AppsV1().Deployments("default").Create(context.TODO(), &newDeployment2, metav1.CreateOptions{})
if err != nil {
fmt.Print("创建Deployment资源失败: ", err.Error())
}
}
```
### 四、K8s多集群管理设计
#### 4.1 定义元数据命名空间
`config/config.go`
```
var MetadataNamespace string
func init(){
...
// 元数据的metadatanamespace
viper.SetDefault("METADATA_NAMESPACE", "kms")
MetadataNamespace = viper.GetString("METADATA_NAMESPACE")
}
```
#### 4.2 初始化元数据命名空间
在controller目录下
`controller/initcontroller/initcontroller.go`
```
package initcontroller
import (
"kms-backend/utils/logs"
)
func init() {
logs.Debug(nil, "初始化基本数据")
// 1. 通过kubeconfig 文件去创建client-go的客户端
// 2. 检查元数据明明空间师傅创建
// a. 已经创建: 元数据空间已经存在
// b. 未创建: 创建命名空间
metadataInit()
}
```
具体实现功能在incluster.go中实现
`controller/initcontroller/incluster.go`
```
package initcontroller
import (
"context"
"kms-backend/config"
"kms-backend/utils/logs"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
)
func metadataInit() {
logs.Debug(nil, "初始化元数据命名空间")
// 1. 初始化config实例
kmsConfig, err := clientcmd.BuildConfigFromFlags("", "config/cls-ik7lvowm-config")
if err != nil {
logs.Error(map[string]interface{}{"msg": err.Error()}, "加载incluster kubeconfig配置文件失败")
panic((err.Error()))
}
// 2. 创建客户端工具,clientset
clientset, err := kubernetes.NewForConfig(kmsConfig)
if err != nil {
logs.Error(map[string]interface{}{"msg": err.Error()}, "incluster创建客户端clientSet失败")
panic(err.Error())
}
// 3. 检查元数据空间是否存在
_, err = clientset.CoreV1().Namespaces().Get(context.TODO(), config.MetadataNamespace, metav1.GetOptions{})
if err != nil {
// 不存在元数据空间
// 需要创建元数据空间
var metadataNamespace corev1.Namespace
metadataNamespace.Name = config.MetadataNamespace
_, err = clientset.CoreV1().Namespaces().Create(context.TODO(), &metadataNamespace, metav1.CreateOptions{})
if err != nil {
logs.Error(map[string]interface{}{"msg": err.Error()}, "元数据命名空间创建失败")
panic(err.Error())
}
logs.Info(nil, "元数据命名空间创建成功")
} else {
// 已经存在元数据空间
logs.Info(map[string]interface{}{"Namespace": config.MetadataNamespace}, "元数据空间已经存在")
}
}
```
#### 4.3 集群路由配置
配置路由
`routers/cluster/cluster.go`
```
package cluster
import (
"kms-backend/controllers/cluster"
"github.com/gin-gonic/gin"
)
// 新增
func add(clusterGroup *gin.RouterGroup) {
clusterGroup.POST("/add", cluster.Add)
}
// 更新
func update(clusterGroup *gin.RouterGroup) {
clusterGroup.POST("/update", cluster.Update)
}
// 详情
func detail(clusterGroup *gin.RouterGroup) {
clusterGroup.GET("/detail", cluster.Detail)
}
func list(clusterGroup *gin.RouterGroup) {
clusterGroup.GET("/list", cluster.List)
}
func delete(clusterGroup *gin.RouterGroup) {
clusterGroup.GET("/delete", cluster.Delete)
}
func RegisterSubRouter(g *gin.RouterGroup) {
// 配置路由策略
clusterGroup := g.Group("/cluster")
add(clusterGroup)
update(clusterGroup)
detail(clusterGroup)
list(clusterGroup)
delete(clusterGroup)
}
```
注册路由
`routers/routers.go`
```
import (
"kms-backend/routers/auth"
"kms-backend/routers/cluster"
"github.com/gin-gonic/gin"
)
// 注册路由的方法
func RegisterRouters(r *gin.Engine) {
apiGroup := r.Group("/api")
auth.RegisterSubRouter(apiGroup)
cluster.RegisterSubRouter(apiGroup)
}
```
### 五、集群路由功能的具体实现
#### 5.1 新增集群功能
新增集群就是添加集群的一些信息,这些信息会存储在元数据空间的`Secret`中,主要包括`kubeconfig`里面的内容
定义数据结构体
`controller/cluster/cluster.go`
```
package cluster
type ClusterInfo struct {
Id string `json:"id"`
DisplayName string `json:"displayName"`
City string `json:"city"`
District string `json:"district"`
}
// 定义一个结构体,用于描述创建集群的配置信息
type ClusterConfing struct {
ClusterInfo
kubeconfig string `json:"kubeconfig"`
}
```
添加集群功能
`controller/cluster/add.go`
```
package cluster
import (
"context"
"kms-backend/config"
"kms-backend/utils/logs"
"github.com/gin-gonic/gin"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func Add(r *gin.Context) {
logs.Debug(nil, "添加集群")
returnData := config.NewReturnData()
clusterConfig := ClusterConfing{}
if err := r.ShouldBindJSON(&clusterConfig); err != nil {
msg := "添加集群失败,集群信息不完整" + err.Error()
returnData.Message = msg
returnData.Status = 400
r.JSON(200, returnData)
return
}
logs.Info(map[string]interface{}{"集群名称": clusterConfig.DisplayName, "集群ID": clusterConfig.Id}, "开始添加集群")
// 创建一个存储集群配置的secret
var clusterConfigSecret corev1.Secret
clusterConfigSecret.Name = clusterConfig.Id
// labels
clusterConfigSecret.Labels = make(map[string]string)
clusterConfigSecret.Labels["kubeasy.com/cluster.metadata"] = "true"
// annotinations, 添加注释,保存集群配置信息
clusterConfigSecret.Annotations = make(map[string]string)
clusterConfigSecret.Annotations["displayName"] = clusterConfig.DisplayName
clusterConfigSecret.Annotations["city"] = clusterConfig.City
clusterConfigSecret.Annotations["district"] = clusterConfig.District
// 保存kubeconfig 信息
clusterConfigSecret.StringData = make(map[string]string)
clusterConfigSecret.StringData["kubeconfig"] = clusterConfig.kubeconfig
// 调用clientSet 创建Secret
// config配置文件中定义一个clientSet类型的变量,controller 中init 方法中创建的clientSet赋值给该变量
_, err = config.InClusterClientSet.CoreV1().Secrets(config.MetadataNamespace).Create(context.TODO(), &clusterConfigSecret, metav1.CreateOptions{})
if err != nil {
logs.Error(map[string]interface{}{"集群ID": clusterConfig.Id, "集群名称": clusterConfig.DisplayName, "msg": err.Error()}, "添加集群Secret失败")
msg := "添加集群失败" + err.Error()
returnData.Message = msg
returnData.Status = 400
r.JSON(200, returnData)
return
}
logs.Info(map[string]interface{}{"集群ID": clusterConfig.Id, "集群名称": clusterConfig.DisplayName}, "添加集群成功")
returnData.Message = "集群添加成功"
returnData.Status = 200
r.JSON(200, returnData)
}
```
#### 5.2 验证新增的集群状态
如果添加的集群状态有问题,则不会新增成功,这需要判断集群状态
先给`clusterConfig`定义一个结构体的方法
`controller/cluster/cluster.go`
```
// 描述集群的状态
type ClusterStatus struct {
ClusterInfo
Version string `json:"version"`
Status string `json:"status"`
}
// 结构体的方法,用于判断集群的状态
func (c *ClusterConfing) getClusterStatus() (ClusterStatus, error) {
// 判断集群是不是正常
ClusterStatus := ClusterStatus{}
ClusterStatus.ClusterInfo = c.ClusterInfo
// 创建一个clientSet
// 通过字符串形式接收kubeconfig
restConfig, err := clientcmd.RESTConfigFromKubeConfig([]byte(c.kubeconfig))
if err != nil {
return ClusterStatus, err
}
clientset, err := kubernetes.NewForConfig(restConfig)
if err != nil {
return ClusterStatus, err
}
serverVersion, err := clientset.Discovery().ServerVersion()
if err != nil {
return ClusterStatus, err
}
// 能够正常获取集群信息,代表集群是正常的
ClusterStatus.Version = serverVersion.String()
ClusterStatus.Status = "Active"
return ClusterStatus, nil
}
```
在添加集群的功能中应用该方法
`controller/cluster/add.go`
```
...
...
// 判断集群是否正常, 在集群添加前判断该集群是否正常
clusterStatus, err := clusterConfig.getClusterStatus()
if err != nil {
msg := "无法获取集群信息: " + err.Error()
returnData.Message = msg
returnData.Status = 400
r.JSON(http.StatusOK, returnData)
logs.Error(map[string]interface{}{"error": err.Error()}, "添加集群失败,无法获取集群信息")
return
}
logs.Info(map[string]interface{}{"集群名称": clusterConfig.DisplayName, "集群ID": clusterConfig.Id}, "开始添加集群")
```
#### 5.3 优化annotations的添加代码
新增集群功能中,`clusterConfigSecret.Annotations`是一个map类型,而它的数据在`clusterStatus`结构体中都存在,所以只要将`clusterStatus`结构体转换成`map`,再赋值给`clusterConfigSecret.Annotations`就可以了
先实现一个`struct`转换成`map`的功能
`utils/tools/structmap.go`
```
package tools
import "encoding/json"
func StructToMap(s interface{}) map[string]string {
j, _ := json.Marshal(s)
m := make(map[string]string)
json.Unmarshal(j, &m)
return m
}
```
修改原来`clusterConfigSecret.Annotations`赋值的代码
`controller/cluster/add.go`
```
...
...
// annotations, 添加注释,保存集群配置信息
clusterConfigSecret.Annotations = make(map[string]string)
// clusterConfigSecret.Annotations["displayName"] = clusterConfig.DisplayName
// clusterConfigSecret.Annotations["city"] = clusterConfig.City
// clusterConfigSecret.Annotations["district"] = clusterConfig.District
// 优化以上代码
// 把集群状态结构体转换成map
m := tools.StructToMap(clusterStatus)
clusterConfigSecret.Annotations = m
```
以上新增集群功能完成
#### 5.4 删除集群功能
`controller/cluster/delete.go`
```
package cluster
import (
"context"
"kms-backend/config"
"kms-backend/utils/logs"
"net/http"
"github.com/gin-gonic/gin"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func Delete(r *gin.Context) {
logs.Debug(nil, "删除集群")
// 1. 获取集群Id
// 前端传递参数clusterId
clusterId := r.Query("clusterId")
returnData := config.ReturnData{}
// 2. 删除集群,即删除Secrect
err := config.InClusterClientSet.CoreV1().Secrets(config.MetadataNamespace).Delete(context.TODO(), clusterId, metav1.DeleteOptions{})
if err != nil {
// 删除失败
logs.Error(nil, "集群删除失败: "+err.Error())
msg := "删除集群失败: " + err.Error()
returnData.Message = msg
returnData.Status = 400
} else {
// 删除成功
logs.Info(map[string]interface{}{"当前被删除集群Id": clusterId}, "集群删除成功")
returnData.Message = "集群删除成功"
returnData.Status = 200
}
r.JSON(http.StatusOK, returnData)
}
```
#### 5.5 集群列表展示功能
`controller/cluster/list.go`
```
package cluster
import (
"context"
"kms-backend/config"
"kms-backend/utils/logs"
"net/http"
"github.com/gin-gonic/gin"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func List(r *gin.Context) {
logs.Debug(nil, "展示集群")
// namespace中有很多其他的Secret
// ListOptions可以根据LabelSelector 筛选出我们需要的Secret
returnData := config.ReturnData{}
listOptions := metav1.ListOptions{
LabelSelector: "kubeasy.com/cluster.metadata=true",
}
secretList, err := config.InClusterClientSet.CoreV1().Secrets(config.MetadataNamespace).List(context.TODO(), listOptions)
if err != nil {
// 查询失败
logs.Error(nil, "查询集群列表失败: "+err.Error())
msg := "查询失败: " + err.Error()
returnData.Message = msg
returnData.Status = 400
r.JSON(http.StatusOK, returnData)
return
} else {
logs.Info(nil, "查询集群列表成功")
returnData.Status = 200
returnData.Message = "查询集群列表成功"
// 将secretList存入retrunData.Data 返回给前端
// Secret 里面的信息也只需要Annotations的内容
clusterList := []map[string]string{}
for _, v := range secretList.Items {
anno := v.Annotations
clusterList = append(clusterList, anno)
}
returnData.Data = make(map[string]interface{})
returnData.Data["items"] = clusterList
r.JSON(200, returnData)
}
}
```
#### 5.6 更新集群信息
新增和更新集群部分代码重复,可以将两个功能合并来写
`controller/cluster/addorupadte.go`
```
package cluster
import (
"context"
"kms-backend/config"
"kms-backend/utils/logs"
"kms-backend/utils/tools"
"net/http"
"github.com/gin-gonic/gin"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// 新增和更新集群信息的功能相识,可以统一使用
func addOrUpdate(r *gin.Context, method string) {
var flag string
if method == "Create" {
flag = "添加"
} else {
flag = "更新"
}
logs.Debug(nil, flag+"集群")
returnData := config.NewReturnData()
clusterConfig := ClusterConfing{}
if err := r.ShouldBindJSON(&clusterConfig); err != nil {
msg := flag + "集群失败,集群信息不完整" + err.Error()
returnData.Message = msg
returnData.Status = 400
r.JSON(200, returnData)
return
}
// 判断集群是否正常
clusterStatus, err := clusterConfig.getClusterStatus()
if err != nil {
msg := "无法获取集群信息: " + err.Error()
returnData.Message = msg
returnData.Status = 400
r.JSON(http.StatusOK, returnData)
logs.Error(map[string]interface{}{"error": err.Error()}, flag+"集群失败,无法获取集群信息")
return
}
logs.Info(map[string]interface{}{"集群名称": clusterConfig.DisplayName, "集群ID": clusterConfig.Id}, "开始"+flag+"集群")
// 创建一个存储集群配置的secret
var clusterConfigSecret corev1.Secret
clusterConfigSecret.Name = clusterConfig.Id
// labels
clusterConfigSecret.Labels = make(map[string]string)
clusterConfigSecret.Labels["kubeasy.com/cluster.metadata"] = "true"
// annotations, 添加注释,保存集群配置信息
clusterConfigSecret.Annotations = make(map[string]string)
// clusterConfigSecret.Annotations["displayName"] = clusterConfig.DisplayName
// clusterConfigSecret.Annotations["city"] = clusterConfig.City
// clusterConfigSecret.Annotations["district"] = clusterConfig.District
// 优化以上代码
// 把集群状态结构体转换成map
m := tools.StructToMap(clusterStatus)
clusterConfigSecret.Annotations = m
// 保存kubeconfig 信息
clusterConfigSecret.StringData = make(map[string]string)
clusterConfigSecret.StringData["kubeconfig"] = clusterConfig.KubeConfig
// 调用clientSet 创建Secret
// config配置文件中定义一个clientSet类型的变量,controller 中init 方法中创建的clientSet赋值给该变量
if method == "Create" {
_, err = config.InClusterClientSet.CoreV1().Secrets(config.MetadataNamespace).Create(context.TODO(), &clusterConfigSecret, metav1.CreateOptions{})
} else {
_, err = config.InClusterClientSet.CoreV1().Secrets(config.MetadataNamespace).Update(context.TODO(), &clusterConfigSecret, metav1.UpdateOptions{})
}
if err != nil {
logs.Error(map[string]interface{}{"集群ID": clusterConfig.Id, "集群名称": clusterConfig.DisplayName, "msg": err.Error()}, flag+"集群Secret失败")
msg := flag + "集群失败" + err.Error()
returnData.Message = msg
returnData.Status = 400
r.JSON(200, returnData)
return
}
logs.Info(map[string]interface{}{"集群ID": clusterConfig.Id, "集群名称": clusterConfig.DisplayName}, flag+"集群成功")
returnData.Message = "集群" + flag + "成功"
returnData.Status = 200
r.JSON(200, returnData)
}
```
原先`add.go`的代码
```
package cluster
import (
"github.com/gin-gonic/gin"
)
func Add(r *gin.Context) {
addOrUpdate(r, "Create")
}
```
`update.go`的内容
```
package cluster
import (
"github.com/gin-gonic/gin"
)
func Update(r *gin.Context) {
addOrUpdate(r, "Update")
}
```
#### 5.7 获取集群详情
`controller/cluster/detail.go`
```
package cluster
import (
"context"
"kms-backend/config"
"kms-backend/utils/logs"
"github.com/gin-gonic/gin"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func Detail(r *gin.Context) {
logs.Debug(nil, "获取集群详情")
clusterId := r.Query("clusterId")
returnData := config.ReturnData{}
// 获取集群配置Secret
clusterSecret, err := config.InClusterClientSet.CoreV1().Secrets(config.MetadataNamespace).Get(context.TODO(), clusterId, metav1.GetOptions{})
if err != nil {
// 获取集群信息失败
logs.Error(nil, "获取集群详情失败: "+err.Error())
msg := "获取集群详情失败"
returnData.Message = msg
returnData.Status = 400
} else {
logs.Info(map[string]interface{}{"集群Id": clusterId}, "查询集群详情成功")
returnData.Status = 200
returnData.Message = "查询集群详情成功"
returnData.Data = make(map[string]interface{})
// 使用一个map获取所需详情信息
clusterConfigMap := clusterSecret.Annotations
clusterConfigMap["kubeconfig"] = string(clusterSecret.Data["kubeconfig"])
returnData.Data["item"] = clusterConfigMap
}
r.JSON(200, returnData)
}
```
### 六、命名空间管理
#### 6.1 新增指向命名空间的路由
可以拷贝`controller/cluster`的目录为`controller/namespace`,修改文件中的`package cluster`为`package namespace`。拷贝目录`router/cluster`为`routers/namespace`,修改关键字`cluster`为`namespace`。再`routers/routers.go`中新增`namespace`的路由。
`routers/namespace/namespace.go`
新增路由
```
package namespace
import (
"kms-backend/controllers/namespace"
"github.com/gin-gonic/gin"
)
// 新增
func create(namespaceGroup *gin.RouterGroup) {
namespaceGroup.POST("/create", namespace.Create)
}
// 更新
func update(namespaceGroup *gin.RouterGroup) {
namespaceGroup.POST("/update", namespace.Update)
}
func detail(namespaceGroup *gin.RouterGroup) {
namespaceGroup.GET("/detail", namespace.Detail)
}
func list(namespaceGroup *gin.RouterGroup) {
namespaceGroup.GET("/list", namespace.List)
}
func delete(namespaceGroup *gin.RouterGroup) {
namespaceGroup.GET("/delete", namespace.Delete)
}
func RegisterSubRouter(g *gin.RouterGroup) {
// 配置路由策略
namespaceGroup := g.Group("/namespace")
create(namespaceGroup)
update(namespaceGroup)
detail(namespaceGroup)
list(namespaceGroup)
delete(namespaceGroup)
}
```
注册路由
`routers/routers.go`
```
func RegisterRouters(r *gin.Engine) {
apiGroup := r.Group("/api")
auth.RegisterSubRouter(apiGroup)
cluster.RegisterSubRouter(apiGroup)
namespace.RegisterSubRouter(apiGroup)
}
```
#### 6.2 如何获取现有集群的kubeconfig
```
1. 首先需要知道对哪个集群中的哪个Namespace进行管理
2. 获取具体Namespace的信息
a. 通过kubeconfig 获取MetadataNamespace中的Secret,
b. 通过该Secert获取clusterId,即获取到具体集群信息
c. 通过该集群信息中kubeconfig 字段获取 kubeconfig信息
d. 通过kubeconfig信息创建clientSet
3. kubeconfig 信息可以存在一个变量当中,因为会一直用到,这样就不用每次创建clientset时就去获取kubeconfig
4. 创建clientset,操作Namespace
```
我们需要在初始化的时候,获取所有集群的信息,将这些已经存在的集群Id 和kubeconfig存储在一个map中。这样我们就可以通过集群Id确定具体的集群,并
通过该集群的kubeconfig来创建clientset客户端工具
定义一个`ClusterKubeConfig`的变量
`config/config.go`
```
ClusterKubeConfig map[string]string // 存储集群kubeconfig
```
获取ClusterKubeConfig
`controller/initcontroller/incluster.go`
```
package initcontroller
import (
"context"
"kms-backend/config"
"kms-backend/utils/logs"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
)
func metadataInit() {
...
...
// 初始化Clusterkubeconfig
config.ClusterKubeConfig = make(map[string]string)
// 查询当前已经存在的集群配置
listOptions := metav1.ListOptions{
LabelSelector: config.ClusterConfingSecretKey + "=" + config.ClusterConfingSecretValue,
}
secretList, _ := config.InClusterClientSet.CoreV1().Secrets(config.MetadataNamespace).List(context.TODO(), listOptions)
for _, secret := range secretList.Items {
// 获取集群ID
clusterId := secret.Name
kubeconfig := secret.Data["kubeconfig"]
config.ClusterKubeConfig[clusterId] = string(kubeconfig)
}
}
```
新增集群时,我们需要在ClusterKubeConfig中加入新集群的clusterId和kubeconfig
`controller/cluster/addorupdate.go`
```
...
...
// 初始化中获取的集群信息中,加入新增的集群
config.ClusterKubeConfig[clusterConfig.Id] = clusterConfig.KubeConfig
// 测试输出config.ClusterKubeConfig的信息
// fmt.Print(config.ClusterKubeConfig)
logs.Info(map[string]interface{}{"集群ID": clusterConfig.Id, "集群名称": clusterConfig.DisplayName}, flag+"集群成功")
returnData.Message = "集群" + flag + "成功"
returnData.Status = 200
r.JSON(200, returnData)
```
删除集群时,需要在ClusterKubeConfig中删除该集群的clusterId
`controller/cluster/delte.go`
```
...
...
else {
// 删除成功
logs.Info(map[string]interface{}{"当前被删除集群Id": clusterId}, "集群删除成功")
returnData.Message = "集群删除成功"
returnData.Status = 200
// 删除初始化时获取的集群信息中现在删除的集群
delete(config.ClusterKubeConfig, clusterId)
// 测试输出config.ClusterKubeConfig的信息
// fmt.Print(config.ClusterKubeConfig)
}
r.JSON(http.StatusOK, returnData)
```
#### 6.3 创建namespace
定义一个全局基础数据结构,接收前端页面传递的基础数据
`controller/controllers.go`
```
package controllers
// 定义全局的数据结构
type BasicInfo struct {
ClusterId string `json:"clusterId"`
Namespace string `json:"namespace"`
Name string `json:"name"`
}
```
具体实现逻辑
`controller/namespace/create.go`
```
package namespace
import (
"context"
"kms-backend/config"
"kms-backend/controllers"
"kms-backend/utils/logs"
"github.com/gin-gonic/gin"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
)
func Create(r *gin.Context) {
// 新增
logs.Debug(nil, "创建命名空间")
// 初始化数据
returnData := config.ReturnData{}
basicInfo := controllers.BasicInfo{}
if err := r.ShouldBindJSON(&basicInfo); err != nil {
msg := "请求出错" + err.Error()
logs.Error(nil, msg)
returnData.Message = msg
returnData.Status = 400
r.JSON(200, returnData)
return
}
// 获取kubeconfig
kubeconfig := config.ClusterKubeConfig[basicInfo.ClusterId]
restConfig, err := clientcmd.RESTConfigFromKubeConfig([]byte(kubeconfig))
if err != nil {
msg := "解析kubeconfig出错" + err.Error()
logs.Error(nil, msg)
returnData.Status = 400
returnData.Message = msg
r.JSON(200, returnData)
return
}
// 创建clientset客户端工具
clientSet, err := kubernetes.NewForConfig(restConfig)
if err != nil {
msg := "创建clientset失败" + err.Error()
logs.Error(nil, msg)
returnData.Status = 400
returnData.Message = msg
r.JSON(200, returnData)
return
}
// 操作资源
var namespace corev1.Namespace
namespace.Name = basicInfo.Name
_, err = clientSet.CoreV1().Namespaces().Create(context.TODO(), &namespace, metav1.CreateOptions{})
if err != nil {
msg := "命名空间创建失败" + err.Error()
logs.Error(nil, "集群"+basicInfo.ClusterId+msg)
returnData.Message = msg
returnData.Status = 400
} else {
msg := "创建namespace成功"
logs.Info(nil, "集群"+basicInfo.ClusterId+"中命名空间"+namespace.Name+"创建成功")
returnData.Message = msg
returnData.Status = 200
}
r.JSON(200, returnData)
}
```
#### 6.4 优化namespace生成clientset的方法
在操作`namespace`资源时,都需要生成`clientSet`客户端,可以将这个生成功能独立出来
`controllers/controllers.go`
```
func BasicInit(r *gin.Context) (clientSet *kubernetes.Clientset, basicInfo BasicInfo, err error) {
// 初始化数据
if err := r.ShouldBindJSON(&basicInfo); err != nil {
msg := "绑定basicInfo时请求出错" + err.Error()
logs.Error(nil, msg)
return nil, basicInfo, err
}
// 获取kubeconfig
kubeconfig := config.ClusterKubeConfig[basicInfo.ClusterId]
restConfig, err := clientcmd.RESTConfigFromKubeConfig([]byte(kubeconfig))
if err != nil {
msg := "解析kubeconfig出错" + err.Error()
logs.Error(nil, msg)
return nil, basicInfo, err
}
// 创建clientset客户端工具
clientSet, err = kubernetes.NewForConfig(restConfig)
if err != nil {
msg := "创建clientset失败" + err.Error()
logs.Error(nil, msg)
return nil, basicInfo, err
}
return clientSet, basicInfo, nil
}
```
创建新命名空间功能修改
`controllers/namespace/create.go`
```
package namespace
import (
"context"
"kms-backend/config"
"kms-backend/controllers"
"kms-backend/utils/logs"
"github.com/gin-gonic/gin"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func Create(r *gin.Context) {
// 新增
logs.Debug(nil, "创建命名空间")
returnData := config.ReturnData{}
// 生成clientSet, 基础数据basicInfo
clientSet, basicInfo, err := controllers.BasicInit(r)
if err != nil {
returnData.Message = err.Error()
returnData.Status = 400
r.JSON(200, returnData)
return
}
// 操作资源
var namespace corev1.Namespace
namespace.Name = basicInfo.Name
_, err = clientSet.CoreV1().Namespaces().Create(context.TODO(), &namespace, metav1.CreateOptions{})
if err != nil {
msg := "命名空间创建失败" + err.Error()
logs.Error(nil, "集群"+basicInfo.ClusterId+msg)
returnData.Message = msg
returnData.Status = 400
} else {
msg := "创建namespace成功"
logs.Info(nil, "集群"+basicInfo.ClusterId+"中命名空间"+namespace.Name+"创建成功")
returnData.Message = msg
returnData.Status = 200
}
r.JSON(200, returnData)
}
```
#### 6.5 删除namespace
`controllers/namespace/delete.go`
```
package namespace
import (
"context"
"kms-backend/config"
"kms-backend/controllers"
"kms-backend/utils/logs"
"github.com/gin-gonic/gin"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func Delete(r *gin.Context) {
logs.Debug(nil, "开始删除命名空间")
returnData := config.ReturnData{}
// 生成clientSet, 基础数据basicInfo
clientSet, basicInfo, err := controllers.BasicInit(r)
if err != nil {
returnData.Message = err.Error()
returnData.Status = 400
r.JSON(200, returnData)
return
}
// 删除操作
err = clientSet.CoreV1().Namespaces().Delete(context.TODO(), basicInfo.Name, metav1.DeleteOptions{})
if err != nil {
msg := "删除命名空间失败" + err.Error()
logs.Error(nil, "集群"+basicInfo.ClusterId+msg)
returnData.Message = msg
returnData.Status = 400
} else {
msg := "删除命名空间成功"
logs.Info(nil, "集群"+basicInfo.ClusterId+"中命名空间"+basicInfo.Name+"删除成功")
returnData.Message = msg
returnData.Status = 200
}
r.JSON(200, returnData)
}
```
在绑定前端传递的数据时,因为删除操作是`GET`方法,而新建namespace使用的是`POST`方法,所以绑定数据的功能需要优化
`controllers/controllers.go`
```
package controllers
import (
"errors"
"kms-backend/config"
"kms-backend/utils/logs"
"github.com/gin-gonic/gin"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
)
// 定义全局的数据结构
// 绑定Get方法传递的数据,需要使用form形式
type BasicInfo struct {
ClusterId string `json:"clusterId" form:"clusterId"`
Namespace string `json:"namespace" form:"namespace"`
Name string `json:"name" form:"name"`
}
// namespace在操作时,都需要生成一个clientSet来具体实施
// 所有可以实现一个基础功能,来生成clientSet
func BasicInit(r *gin.Context) (clientSet *kubernetes.Clientset, basicInfo BasicInfo, err error) {
// 初始化数据
// 绑定前端传递的json数据,也需要根据调用方法来区别,因为绑定方法不一样
requestMethod := r.Request.Method
if requestMethod == "GET" {
err = r.ShouldBindQuery(&basicInfo)
} else if requestMethod == "POST" {
err = r.ShouldBindJSON(&basicInfo)
} else {
err = errors.New("不支持该请求方式")
}
if err != nil {
msg := "绑定basicInfo时请求出错" + err.Error()
logs.Error(nil, msg)
return nil, basicInfo, err
}
// 获取kubeconfig
kubeconfig := config.ClusterKubeConfig[basicInfo.ClusterId]
restConfig, err := clientcmd.RESTConfigFromKubeConfig([]byte(kubeconfig))
if err != nil {
msg := "解析kubeconfig出错" + err.Error()
logs.Error(nil, msg)
return nil, basicInfo, err
}
// 禁止删除的namespace
if basicInfo.Name == "kube-system" {
returnData.Message = "禁止删除kube-system"
returnData.Status = 400
r.JSON(200, returnData)
return
}
// 创建clientset客户端工具
clientSet, err = kubernetes.NewForConfig(restConfig)
if err != nil {
msg := "创建clientset失败" + err.Error()
logs.Error(nil, msg)
return nil, basicInfo, err
}
return clientSet, basicInfo, nil
}
```
#### 6.6 展示集群中的命名空间
`controllers/namespace/list.go`
```
package namespace
import (
"context"
"kms-backend/config"
"kms-backend/controllers"
"kms-backend/utils/logs"
"github.com/gin-gonic/gin"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func List(r *gin.Context) {
logs.Debug(nil, "展示命名空间")
returnData := config.ReturnData{}
// 生成clientSet, 基础数据basicInfo
clientSet, basicInfo, err := controllers.BasicInit(r)
if err != nil {
returnData.Message = err.Error()
returnData.Status = 400
r.JSON(200, returnData)
return
}
// 获取namespace列表
namespaceList, err := clientSet.CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{})
if err != nil {
msg := "获取集群中命名空间失败" + err.Error()
logs.Error(nil, "集群"+basicInfo.ClusterId+msg)
returnData.Message = msg
returnData.Status = 400
} else {
msg := "获取集群中命名空间成功"
logs.Info(nil, "集群"+basicInfo.ClusterId+"中命名空间获取成功")
returnData.Message = msg
returnData.Status = 200
returnData.Data = make(map[string]interface{})
returnData.Data["items"] = namespaceList.Items
}
r.JSON(200, returnData)
}
```
#### 6.7 获取命名空间详情
`controllers/namespace/detail.go`
```
package namespace
import (
"context"
"kms-backend/config"
"kms-backend/controllers"
"kms-backend/utils/logs"
"github.com/gin-gonic/gin"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func Detail(r *gin.Context) {
logs.Debug(nil, "获取详情")
returnData := config.ReturnData{}
// 生成clientSet, 基础数据basicInfo
clientSet, basicInfo, err := controllers.BasicInit(r)
if err != nil {
returnData.Message = err.Error()
returnData.Status = 400
r.JSON(200, returnData)
return
}
// 获取namespace列表
namespace, err := clientSet.CoreV1().Namespaces().Get(context.TODO(), basicInfo.Name, metav1.GetOptions{})
if err != nil {
msg := "获取命名空间详情失败" + err.Error()
logs.Error(nil, "集群"+basicInfo.ClusterId+msg)
returnData.Message = msg
returnData.Status = 400
} else {
msg := "获取命名空间详情成功"
logs.Info(nil, "集群"+basicInfo.ClusterId+"中命名空间详情获取成功")
returnData.Message = msg
returnData.Status = 200
returnData.Data = make(map[string]interface{})
returnData.Data["item"] = namespace
}
r.JSON(200, returnData)
}
```
#### 6.8 更新命名空间信息
```
// 更新操作主要是更新namespace的 labels 和 annotations
// 更新操作时Update方法中需要传入一个*v1.Namespace的指针对象,这个对象内容是前端传入的数据
// 所以函数BasicInit中需要加入一个参数,这个参数是interface{}类型,即不仅能接收namespace对象,
// 也可以接收其他比如deployment、statefulset等类型
```
优化`controllers.go`的内容
```
package controllers
import (
"errors"
"kms-backend/config"
"kms-backend/utils/logs"
"github.com/gin-gonic/gin"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
)
// 定义全局的数据结构
// 绑定Get方法传递的数据,需要使用form形式
type BasicInfo struct {
ClusterId string `json:"clusterId" form:"clusterId"`
Namespace string `json:"namespace" form:"namespace"`
Name string `json:"name" form:"name"`
Item interface{} `json:"item"`
}
// namespace在操作时,都需要生成一个clientSet来具体实施
// 所有可以实现一个基础功能,来生成clientSet
func BasicInit(r *gin.Context, item interface{}) (clientSet *kubernetes.Clientset, basicInfo BasicInfo, err error) {
// 初始化数据
// BasicInit接收一个item参数,可以是任意类型,并将其赋值给basicInfo.Item
// 前端将具体数据绑定到basicInfo.Item上,如果是namespace类型,item里面就是namespace的json格式的数据
basicInfo = BasicInfo{}
basicInfo.Item = item
...
...
}
```
更新操作的功能
```
package namespace
import (
"context"
"kms-backend/config"
"kms-backend/controllers"
"kms-backend/utils/logs"
"github.com/gin-gonic/gin"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func Update(r *gin.Context) {
logs.Debug(nil, "更新命名空间")
var namespace corev1.Namespace
returnData := config.ReturnData{}
// 生成clientSet, 基础数据basicInfo
clientSet, basicInfo, err := controllers.BasicInit(r, &namespace)
if err != nil {
returnData.Message = err.Error()
returnData.Status = 400
r.JSON(200, returnData)
return
}
// 操作资源
_, err = clientSet.CoreV1().Namespaces().Update(context.TODO(), &namespace, metav1.UpdateOptions{})
if err != nil {
msg := "命名空间更新失败" + err.Error()
logs.Error(nil, "集群"+basicInfo.ClusterId+msg)
returnData.Message = msg
returnData.Status = 400
} else {
msg := "更新namespace成功"
logs.Info(nil, "集群"+basicInfo.ClusterId+"中命名空间"+namespace.Name+"更新成功")
returnData.Message = msg
returnData.Status = 200
}
r.JSON(200, returnData)
}
```
其他namespace功能代码修改,它们都缺失一个参数
```
clientSet, basicInfo, err := controllers.BasicInit(r, nil)
```
### 七、Pod的管理
和`namespace`操作一致,先配置路由和controller里面的内容,可以复制`namespace`的代码文件稍作修改
#### 7.1 Pod路由配置
`routers/pod/pod.go`
```
package pod
import (
"kms-backend/controllers/pod"
"github.com/gin-gonic/gin"
)
// 新增
func create(podGroup *gin.RouterGroup) {
podGroup.POST("/create", pod.Create)
}
// 更新
func update(podGroup *gin.RouterGroup) {
podGroup.POST("/update", pod.Update)
}
func detail(podGroup *gin.RouterGroup) {
podGroup.GET("/detail", pod.Detail)
}
func list(podGroup *gin.RouterGroup) {
podGroup.GET("/list", pod.List)
}
func delete(podGroup *gin.RouterGroup) {
podGroup.GET("/delete", pod.Delete)
}
func RegisterSubRouter(g *gin.RouterGroup) {
// 配置路由策略
podGroup := g.Group("/pod")
create(podGroup)
update(podGroup)
detail(podGroup)
list(podGroup)
delete(podGroup)
}
```
注册路由
`routers/routers.go`
```
package routers
import (
"kms-backend/routers/auth"
"kms-backend/routers/cluster"
"kms-backend/routers/namespace"
"kms-backend/routers/pod"
"github.com/gin-gonic/gin"
)
// 注册路由的方法
func RegisterRouters(r *gin.Engine) {
apiGroup := r.Group("/api")
auth.RegisterSubRouter(apiGroup)
cluster.RegisterSubRouter(apiGroup)
namespace.RegisterSubRouter(apiGroup)
pod.RegisterSubRouter(apiGroup)
}
```
#### 7.2 创建pod的功能
`controllers/pod/create.go`
```
package pod
import (
"context"
"kms-backend/config"
"kms-backend/controllers"
"kms-backend/utils/logs"
"github.com/gin-gonic/gin"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func Create(r *gin.Context) {
// 新增
logs.Debug(nil, "创建Pod")
var pod corev1.Pod
returnData := config.ReturnData{}
// 生成clientSet, 基础数据basicInfo
clientSet, basicInfo, err := controllers.BasicInit(r, &pod)
if err != nil {
returnData.Message = err.Error()
returnData.Status = 400
r.JSON(200, returnData)
return
}
// 操作资源
_, err = clientSet.CoreV1().Pods(basicInfo.Namespace).Create(context.TODO(), &pod, metav1.CreateOptions{})
if err != nil {
msg := "Pod创建失败" + err.Error()
logs.Error(nil, basicInfo.ClusterId+"集群中"+basicInfo.Namespace+"命名空间下"+msg)
returnData.Message = msg
returnData.Status = 400
} else {
msg := "Pod创建成功"
logs.Info(nil, basicInfo.ClusterId+"集群中"+basicInfo.Namespace+"命名空间下"+"Pod创建成功")
returnData.Message = msg
returnData.Status = 200
}
r.JSON(200, returnData)
}
```
#### 7.3 删除单个Pod功能
`controllers/pod/delete.go`
```
package pod
import (
"context"
"kms-backend/config"
"kms-backend/controllers"
"kms-backend/utils/logs"
"github.com/gin-gonic/gin"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func Delete(r *gin.Context) {
logs.Debug(nil, "开始删除Pod")
returnData := config.ReturnData{}
// 生成clientSet, 基础数据basicInfo
clientSet, basicInfo, err := controllers.BasicInit(r, nil)
if err != nil {
returnData.Message = err.Error()
returnData.Status = 400
r.JSON(200, returnData)
return
}
// 删除操作
err = clientSet.CoreV1().Pods(basicInfo.Namespace).Delete(context.TODO(), basicInfo.Name, metav1.DeleteOptions{})
if err != nil {
msg := "Pod删除失败" + err.Error()
logs.Error(nil, basicInfo.ClusterId+"集群中"+basicInfo.Namespace+"命名空间下"+basicInfo.Name+" "+msg)
returnData.Message = msg
returnData.Status = 400
} else {
msg := "Pod删除成功"
logs.Info(nil, basicInfo.ClusterId+"集群中"+basicInfo.Namespace+"命名空间下"+basicInfo.Name+" "+msg)
returnData.Message = msg
returnData.Status = 200
}
r.JSON(200, returnData)
}
```
#### 7.4 同时删除多个Pod
删除多个Pod就不能使用`GET`方法,需要修改为`POST`方法。同时`BasicInfo`中需要新增一个`DeleteList`的切片
新增`DeleteList`
`controllers/controllers.go`
```
type BasicInfo struct {
ClusterId string `json:"clusterId" form:"clusterId"`
Namespace string `json:"namespace" form:"namespace"`
Name string `json:"name" form:"name"`
Item interface{} `json:"item"`
DeleteList []string `json:"deletelist"`
}
```
修改`GET`方法为`POST`
`routers/pod/pod.go`
```
func delete(podGroup *gin.RouterGroup) {
podGroup.POST("/delete", pod.Delete)
}
```
修改delete功能
`controllers/pod/delete.go`
```
package pod
import (
"context"
"kms-backend/config"
"kms-backend/controllers"
"kms-backend/utils/logs"
"github.com/gin-gonic/gin"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func Delete(r *gin.Context) {
logs.Debug(nil, "开始删除Pod")
returnData := config.ReturnData{}
// 生成clientSet, 基础数据basicInfo
clientSet, basicInfo, err := controllers.BasicInit(r, nil)
if err != nil {
returnData.Message = err.Error()
returnData.Status = 400
r.JSON(200, returnData)
return
}
// 删除操作
// 多个pod删除
returnData.Data = make(map[string]interface{})
failPod := []string{}
for _, v := range basicInfo.DeleteList {
err = clientSet.CoreV1().Pods(basicInfo.Namespace).Delete(context.TODO(), v, metav1.DeleteOptions{})
if err != nil {
failPod = append(failPod, v)
}
}
returnData.Data["FailPods"] = failPod
if len(failPod) > 0 {
returnData.Message = "存在Pod删除失败"
returnData.Status = 400
r.JSON(200, returnData)
return
} else {
msg := "Pod删除成功"
logs.Info(nil, basicInfo.ClusterId+"集群中"+basicInfo.Namespace+"命名空间下"+msg)
returnData.Message = msg
returnData.Status = 200
r.JSON(200, returnData)
}
}
```
#### 7.5 展示命名空间中pods
`controllers/pods/list.go`
```
package pod
import (
"context"
"kms-backend/config"
"kms-backend/controllers"
"kms-backend/utils/logs"
"github.com/gin-gonic/gin"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func List(r *gin.Context) {
logs.Debug(nil, "展示Pod")
returnData := config.ReturnData{}
// 生成clientSet, 基础数据basicInfo
clientSet, basicInfo, err := controllers.BasicInit(r, nil)
if err != nil {
returnData.Message = err.Error()
returnData.Status = 400
r.JSON(200, returnData)
return
}
// 获取pod列表
if basicInfo.Namespace == "" {
basicInfo.Namespace = "default"
}
podsList, err := clientSet.CoreV1().Pods(basicInfo.Namespace).List(context.TODO(), metav1.ListOptions{})
if err != nil {
msg := "获取Pods失败" + err.Error()
logs.Error(nil, "获取"+basicInfo.ClusterId+"集群中Pods失败")
returnData.Message = msg
returnData.Status = 400
} else {
msg := "获取集群Pods成功"
logs.Info(nil, "获取"+basicInfo.ClusterId+"集群中"+basicInfo.Namespace+"命名空间下Pods成功")
returnData.Message = msg
returnData.Status = 200
returnData.Data = make(map[string]interface{})
returnData.Data["items"] = podsList.Items
}
r.JSON(200, returnData)
}
```
#### 7.6 获取pod详情
`controllers/pod/detail.go`
```
package pod
import (
"context"
"kms-backend/config"
"kms-backend/controllers"
"kms-backend/utils/logs"
"github.com/gin-gonic/gin"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func Detail(r *gin.Context) {
logs.Debug(nil, "获取Pod详情")
returnData := config.ReturnData{}
// 生成clientSet, 基础数据basicInfo
clientSet, basicInfo, err := controllers.BasicInit(r, nil)
if err != nil {
returnData.Message = err.Error()
returnData.Status = 400
r.JSON(200, returnData)
return
}
// 获取pod
pod, err := clientSet.CoreV1().Pods(basicInfo.Namespace).Get(context.TODO(), basicInfo.Name, metav1.GetOptions{})
if err != nil {
msg := "获取Pod详情失败" + err.Error()
logs.Error(nil, "获取集群"+basicInfo.ClusterId+"命名空间"+basicInfo.Namespace+"中"+basicInfo.Name+"详情失败")
returnData.Message = msg
returnData.Status = 400
} else {
msg := "获取Pod详情成功"
logs.Info(nil, "获取集群"+basicInfo.ClusterId+"命名空间"+basicInfo.Namespace+"中"+basicInfo.Name+"详情成功")
returnData.Message = msg
returnData.Status = 200
returnData.Data = make(map[string]interface{})
returnData.Data["item"] = pod
}
r.JSON(200, returnData)
}
```
### 八、优化代码
#### 8.1 下载接口工具
使用接口工具简化创建、更新、删除等操作
```
go get github.com/dotbalo/kubeutils
```
#### 8.2 构造函数获取kubeconfig
kubeutils有个kubeconfig的返回值,可以使用一个构造函数获取kubeconfig
`controllers/controllers.go`
```
...
...
type BasicInfo struct {
ClusterId string `json:"clusterId" form:"clusterId"`
Namespace string `json:"namespace" form:"namespace"`
Name string `json:"name" form:"name"`
Item interface{} `json:"item"`
DeleteList []string `json:"deletelist"`
}
type Info struct {
BasicInfo
ReturnData config.ReturnData
}
// 优化代码使用的接口工具kubeutils需要一个kubeconfig,此处用构造函数获取kubeconfig
func NewInfo(r *gin.Context, info *Info, returnDataMsg string) (kubeconfig string) {
// 绑定前端传递的json数据,也需要根据调用方法来区别,因为绑定方法不一样
requestMethod := r.Request.Method
var err error
info.ReturnData.Message = returnDataMsg
info.ReturnData.Status = 200
if requestMethod == "GET" {
err = r.ShouldBindQuery(&info)
} else if requestMethod == "POST" {
err = r.ShouldBindJSON(&info)
} else {
err = errors.New("不支持该请求方式")
}
if err != nil {
msg := "绑定basicInfo时请求出错" + err.Error()
info.ReturnData.Message = msg
info.ReturnData.Status = 400
logs.Error(nil, msg)
r.JSON(http.StatusOK, info.ReturnData)
return
}
// 默认去default命名空间
if info.Namespace == "" {
info.Namespace = "default"
}
// 获取kubeconfig
kubeconfig = config.ClusterKubeConfig[info.ClusterId]
return kubeconfig
}
```
#### 8.3 用结构体方法生成创建功能
`controllers/controllers.go`
```
...
...
// 创建方法
func (c *Info) Create(r *gin.Context, kubeUtilsInterface kubeutils.KubeUtilser) {
err := kubeUtilsInterface.Create(c.Namespace)
if err != nil {
msg := "创建失败: " + err.Error()
c.ReturnData.Message = msg
c.ReturnData.Status = 400
logs.Error(nil, msg)
}
r.JSON(200, c.ReturnData)
}
```
#### 8.4 修改创建pod的功能
`controllers/pod/create.go`
```
package pod
import (
"kms-backend/controllers"
"kms-backend/utils/logs"
"github.com/dotbalo/kubeutils/kubeutils"
"github.com/gin-gonic/gin"
corev1 "k8s.io/api/core/v1"
)
func Create(r *gin.Context) {
// 新增
logs.Debug(nil, "开始创建Pod")
var pod corev1.Pod
info := controllers.Info{}
info.Item = &pod
kubeconfig := controllers.NewInfo(r, &info, "创建Pod成功")
// 使用kubeutils接口创建pod
var kubeUtilser kubeutils.KubeUtilser
instance := kubeutils.NewPod(kubeconfig, &pod)
// 把实例赋值给kubeUtilser
kubeUtilser = instance
// 使用kubeUtilser创建
info.Create(r, kubeUtilser)
}
```
#### 8.5 使用结构体方法生成其他功能
`controllers/controllers.go`
```
package controllers
import (
"errors"
"kms-backend/config"
"kms-backend/utils/logs"
"net/http"
"github.com/dotbalo/kubeutils/kubeutils"
"github.com/gin-gonic/gin"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
)
// 定义全局的数据结构
// 绑定Get方法传递的数据,需要使用form形式
type BasicInfo struct {
ClusterId string `json:"clusterId" form:"clusterId"`
Namespace string `json:"namespace" form:"namespace"`
Name string `json:"name" form:"name"`
Item interface{} `json:"item"`
DeleteList []string `json:"deletelist"`
}
type Info struct {
BasicInfo
ReturnData config.ReturnData
LabelSelector string `json:"labelselector" form:"labelselector"`
FieldSelector string `json:"fieldSelector" form:"fieldSelector"`
// 判断是否强制删除
Force bool `json:"force" form:"force"`
}
// 创建方法
func (c *Info) Create(r *gin.Context, kubeUtilsInterface kubeutils.KubeUtilser) {
err := kubeUtilsInterface.Create(c.Namespace)
if err != nil {
msg := "创建失败: " + err.Error()
c.ReturnData.Message = msg
c.ReturnData.Status = 400
logs.Error(nil, msg)
}
r.JSON(200, c.ReturnData)
}
// 更新方法
func (c *Info) Update(r *gin.Context, kubeUtilsInterface kubeutils.KubeUtilser) {
err := kubeUtilsInterface.Update(c.Namespace)
if err != nil {
msg := "更新失败: " + err.Error()
c.ReturnData.Message = msg
c.ReturnData.Status = 400
logs.Error(nil, msg)
}
r.JSON(200, c.ReturnData)
}
// List方法
func (c *Info) List(r *gin.Context, kubeUtilsInterface kubeutils.KubeUtilser) {
// List需要3个参数:namespace、lableselector、fieldselector
// 直接返回items 和error
items, err := kubeUtilsInterface.List(c.Namespace, c.LabelSelector, c.FieldSelector)
if err != nil {
msg := "查询失败: " + err.Error()
c.ReturnData.Message = msg
c.ReturnData.Status = 400
logs.Error(nil, msg)
} else {
c.ReturnData.Data = make(map[string]interface{})
c.ReturnData.Data["items"] = items
}
r.JSON(200, c.ReturnData)
}
// Get方法
func (c *Info) Get(r *gin.Context, kubeUtilsInterface kubeutils.KubeUtilser) {
item, err := kubeUtilsInterface.Get(c.Namespace, c.Name)
if err != nil {
msg := "查询失败: " + err.Error()
c.ReturnData.Message = msg
c.ReturnData.Status = 400
logs.Error(nil, msg)
} else {
c.ReturnData.Data = make(map[string]interface{})
c.ReturnData.Data["items"] = item
}
r.JSON(200, c.ReturnData)
}
// Delete方法
func (c *Info) Delete(r *gin.Context, kubeUtilsInterface kubeutils.KubeUtilser) {
var gracePeriodSeconds int64
if c.Force {
// 强制删除, 0 表示立即删除
var s int64 = 0
gracePeriodSeconds = s
}
// 需要3个参数: namespace name int64
// int64 表示是否强制删除的参数
err := kubeUtilsInterface.Delete(c.Namespace, c.Name, &gracePeriodSeconds)
if err != nil {
msg := "删除失败: " + err.Error()
c.ReturnData.Message = msg
c.ReturnData.Status = 400
logs.Error(nil, msg)
}
r.JSON(200, c.ReturnData)
}
// Delete列表方法
func (c *Info) DeleteList(r *gin.Context, kubeUtilsInterface kubeutils.KubeUtilser) {
var gracePeriodSeconds int64
if c.Force {
// 强制删除, 0 表示立即删除
var s int64 = 0
gracePeriodSeconds = s
}
// 需要3个参数: namespace name int64
// int64 表示是否强制删除的参数
err := kubeUtilsInterface.DeleteList(c.Namespace, c.BasicInfo.DeleteList, &gracePeriodSeconds)
if err != nil {
msg := "删除失败: " + err.Error()
c.ReturnData.Message = msg
c.ReturnData.Status = 400
logs.Error(nil, msg)
}
r.JSON(200, c.ReturnData)
}
```
#### 8.6 改造Delete功能
`controllers/pod/delete.go`
```
package pod
import (
"kms-backend/controllers"
"kms-backend/utils/logs"
"github.com/dotbalo/kubeutils/kubeutils"
"github.com/gin-gonic/gin"
)
func Delete(r *gin.Context) {
logs.Debug(nil, "开始删除Pod")
info := controllers.Info{}
kubeconfig := controllers.NewInfo(r, &info, "删除Pod成功")
// 使用kubeutils接口创建一个实例
var kubeUtilser kubeutils.KubeUtilser
instance := kubeutils.NewPod(kubeconfig, nil)
// 把实例赋值给kubeUtilser
kubeUtilser = instance
// 使用kubeUtilser创建
info.Delete(r, kubeUtilser)
}
func DeleteList(r *gin.Context) {
logs.Debug(nil, "开始删除Pod")
info := controllers.Info{}
kubeconfig := controllers.NewInfo(r, &info, "删除Pod成功")
// 使用kubeutils接口创建一个实例
var kubeUtilser kubeutils.KubeUtilser
instance := kubeutils.NewPod(kubeconfig, nil)
// 把实例赋值给kubeUtilser
kubeUtilser = instance
// 使用kubeUtilser创建
info.DeleteList(r, kubeUtilser)
}
```
#### 8.7 改造List功能
`controllers/pod/list.go`
```
package pod
import (
"kms-backend/controllers"
"kms-backend/utils/logs"
"github.com/dotbalo/kubeutils/kubeutils"
"github.com/gin-gonic/gin"
)
func List(r *gin.Context) {
logs.Debug(nil, "开始查询Pod列表")
info := controllers.Info{}
kubeconfig := controllers.NewInfo(r, &info, "查询pod列表详情成功")
// 使用kubeutils接口创建一个实例
var kubeUtilser kubeutils.KubeUtilser
instance := kubeutils.NewPod(kubeconfig, nil)
// 把实例赋值给kubeUtilser
kubeUtilser = instance
// 使用kubeUtilser创建
info.List(r, kubeUtilser)
}
```
#### 8.8 改造detail功能
`controllers/pod/detail.go`
```
package pod
import (
"kms-backend/controllers"
"kms-backend/utils/logs"
"github.com/dotbalo/kubeutils/kubeutils"
"github.com/gin-gonic/gin"
)
func Detail(r *gin.Context) {
logs.Debug(nil, "获取Pod详情")
info := controllers.Info{}
kubeconfig := controllers.NewInfo(r, &info, "获取Pod详情成功")
// 使用kubeutils接口创建一个实例
var kubeUtilser kubeutils.KubeUtilser
instance := kubeutils.NewPod(kubeconfig, nil)
// 把实例赋值给kubeUtilser
kubeUtilser = instance
// 使用kubeUtilser创建
info.Get(r, kubeUtilser)
}
```
#### 8.8 更新功能
`controllers/pod/update.go`
```
package pod
import (
"kms-backend/config"
"kms-backend/utils/logs"
"github.com/gin-gonic/gin"
)
func Update(r *gin.Context) {
logs.Debug(nil, "更新Pod")
var returnData config.ReturnData
returnData.Message = "Pod暂不支持更新操作"
returnData.Status = 200
r.JSON(200, returnData)
}
```
#### 8.9 新增一个DeleteList路由
`routers/pod/pod.go`
```
...
...
func deleteList(podGroup *gin.RouterGroup) {
podGroup.POST("/deleteList", pod.DeleteList)
}
func RegisterSubRouter(g *gin.RouterGroup) {
// 配置路由策略
podGroup := g.Group("/pod")
create(podGroup)
update(podGroup)
detail(podGroup)
list(podGroup)
delete(podGroup)
deleteList(podGroup)
```
### 九、Deployment
#### 9.1 生成Deployment路由
复制`routers/pod`的内容为`routers/deployment`,修改`pod`字段为`deployment`。新增`deployment`的路由
`routers/routers.go`
```
deployment.RegisterSubRouter(apiGroup)
```
#### 9.2 Deployment 控制器功能
同上操作`controllers/pod`目录
#### 9.2 Deployment具体实现功能
`controllers/deployment/create.go`
```
package deployment
import (
"kms-backend/controllers"
"kms-backend/utils/logs"
"github.com/dotbalo/kubeutils/kubeutils"
"github.com/gin-gonic/gin"
appsv1 "k8s.io/api/apps/v1"
)
func Create(r *gin.Context) {
// 新增
logs.Debug(nil, "开始创建deployment")
var deployment appsv1.Deployment
info := controllers.Info{}
info.Item = &deployment
kubeconfig := controllers.NewInfo(r, &info, "创建deployment成功")
// 使用kubeutils接口创建deployment
var kubeUtilser kubeutils.KubeUtilser
instance := kubeutils.NewDeployment(kubeconfig, &deployment)
// 把实例赋值给kubeUtilser
kubeUtilser = instance
// 使用kubeUtilser创建
info.Create(r, kubeUtilser)
}
```
`controllers/deployment/update.go`
```
package deployment
import (
"kms-backend/controllers"
"kms-backend/utils/logs"
"github.com/dotbalo/kubeutils/kubeutils"
"github.com/gin-gonic/gin"
appsv1 "k8s.io/api/apps/v1"
)
func Update(r *gin.Context) {
logs.Debug(nil, "更新deployment")
var deployment appsv1.Deployment
info := controllers.Info{}
info.Item = &deployment
kubeconfig := controllers.NewInfo(r, &info, "更新deployment成功")
// 使用kubeutils接口创建deployment
var kubeUtilser kubeutils.KubeUtilser
instance := kubeutils.NewDeployment(kubeconfig, &deployment)
// 把实例赋值给kubeUtilser
kubeUtilser = instance
// 使用kubeUtilser创建
info.Update(r, kubeUtilser)
}
```
`controllers/deployment/list.go`
```
package deployment
import (
"kms-backend/controllers"
"kms-backend/utils/logs"
"github.com/dotbalo/kubeutils/kubeutils"
"github.com/gin-gonic/gin"
)
func List(r *gin.Context) {
logs.Debug(nil, "开始查询deployment列表")
info := controllers.Info{}
kubeconfig := controllers.NewInfo(r, &info, "查询deployment列表详情成功")
// 使用kubeutils接口创建一个实例
var kubeUtilser kubeutils.KubeUtilser
instance := kubeutils.NewDeployment(kubeconfig, nil)
// 把实例赋值给kubeUtilser
kubeUtilser = instance
// 使用kubeUtilser创建
info.List(r, kubeUtilser)
}
```
`controllers/deployment/detail.go`
```
package deployment
import (
"kms-backend/controllers"
"kms-backend/utils/logs"
"github.com/dotbalo/kubeutils/kubeutils"
"github.com/gin-gonic/gin"
)
func Detail(r *gin.Context) {
logs.Debug(nil, "获取deployment详情")
info := controllers.Info{}
kubeconfig := controllers.NewInfo(r, &info, "获取deployment详情成功")
// 使用kubeutils接口创建一个实例
var kubeUtilser kubeutils.KubeUtilser
instance := kubeutils.NewDeployment(kubeconfig, nil)
// 把实例赋值给kubeUtilser
kubeUtilser = instance
// 使用kubeUtilser创建
info.Get(r, kubeUtilser)
}
```
`controllers/deployment/delete.go`
```
package pod
import (
"kms-backend/controllers"
"kms-backend/utils/logs"
"github.com/dotbalo/kubeutils/kubeutils"
"github.com/gin-gonic/gin"
)
func Delete(r *gin.Context) {
logs.Debug(nil, "开始删除Pod")
info := controllers.Info{}
kubeconfig := controllers.NewInfo(r, &info, "删除Pod成功")
// 使用kubeutils接口创建一个实例
var kubeUtilser kubeutils.KubeUtilser
instance := kubeutils.NewPod(kubeconfig, nil)
// 把实例赋值给kubeUtilser
kubeUtilser = instance
// 使用kubeUtilser创建
info.Delete(r, kubeUtilser)
}
```
### 十、StatefuSet
同`Deployment`的操作
### 十一 DaemonSet
同上
### 十二、CronJob
同上
```
kubectl create cronjob my-job --image=busybox --schedule="*/1 * * * *" --dry-run=client -o json
```
### 十三、ReplicaSet
在`Deployment`等回滚的时候,要查询以前的版本,这就需要`ReplicaSet`的查询功能
同上操作但是只需要`get`和`list`功能
### Service
同上
### Ingress
同上
获取ingress的json格式
```
kubectl create ingress annotated --class=default --rule="foo.com/bar=svc:port" --annotation ingress.annotation1=foo --annotation ingress.annotation2=bla --dry-run=client -o json
```
### ConfigMap
同上
### Secret
同上
```
kubectl create secret generic my-secret --from-literal=key1=supersecret --from-literal=key2=topsecret --dry-run=client -ojson
```
### pv
同上
### pvc
同上
### StorageClass
只需要`list`功能