K8s 管理系统项目[API部分–Workflow]
年终于算过完了,身体也康复了,继续学习
1. DB设置
1.1 连接配置
service/config.go
package config
import "time"
const (
ListenAddr = "0.0.0.0:9091"
KubeConfig = "D:\\golang\\k8s-plantform\\config\\cka"
//KubeConfig = "D:\\golang\\k8s-plantform\\config\\config"
// tail 的日志行数
PodLogTailLine = 2000
// DB Config
DbType = "mysql"
DbHost = "192.168.31.24"
DbPort = 3306
DbName = "k8s_dashboard"
DbUser = "root"
DbPass = "123456"
// 打印mysql debug的sql日志
LogMode = false
// 连接池配置
MaxIdleConns = 10 // 最大空闲连接
MaxOpenConns = 100 // 最大连接数
MaxLifeTime = 30 * time.Second // 会话时间
)
1.2 初始化
db/init.go
package db
import (
"fmt"
"k8s-plantform/config"
"time"
"github.com/wonderivan/logger"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
)
var (
isInit bool
GORM *gorm.DB
err error
)
// DB的初始化函数,与数据库建立连接
func Init() {
// 判断是否已经初始化
if isInit {
return
}
// 组装连接配置
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8&parseTime=True&loc=Local",
config.DbUser,
config.DbPass,
config.DbHost,
config.DbPort,
config.DbName)
GORM, err = gorm.Open(config.DbType, dsn)
if err != nil {
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))
isInit = true
logger.Info("数据库初始化成功")
}
// 关闭数据库连接
func Close() error {
return GORM.Close()
}
1.3 初始化
main.go
package main
import (
"fmt"
"k8s-plantform/config"
"k8s-plantform/controller"
"k8s-plantform/db"
"k8s-plantform/service"
"github.com/gin-gonic/gin"
)
func main() {
// 初始化数据库
db.Init()
// 初始化k8s client
service.K8s.Init()
// 初始化gin
r := gin.Default()
controller.Router.InitApiRouter(r)
// gin 程序启动
//r.Run(config.ListenAdd)
fmt.Println("http://192.168.31.1:9091/")
r.Run(config.ListenAddr)
// 关闭数据库
db.Close()
}
1.4 创建数据库
创建数据库
重启服务确认数据库连接正常
1.5 表结构
model/workflow.go
package model
import "time"
//定义结构体,属性与mysql表字段对齐
type Workflow struct {
//gorm:"primaryKey"用于声明主键
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"`
//gorm:"column:type"用于声明mysql中表的字段名
Type string `json:"type" gorm:"column:type"`
}
//定义TableName方法,返回mysql表名,以此来定义mysql中的表名
func(*Workflow) TableName() string {
return "workflow"
}
1.6 创建workflow表
连接数据库,在命令行或管理工具中创建workflow表
CREATE TABLE `workflow` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(32) COLLATE utf8mb4_general_ci NOT NULL,
`namespace` varchar(32) COLLATE utf8mb4_general_ci DEFAULT NULL,
`replicas` int DEFAULT NULL,
`deployment` varchar(32) COLLATE utf8mb4_general_ci DEFAULT NULL,
`service` varchar(32) COLLATE utf8mb4_general_ci DEFAULT NULL,
`ingress` varchar(32) COLLATE utf8mb4_general_ci DEFAULT NULL,
`type` varchar(32) COLLATE utf8mb4_general_ci DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
`deleted_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
2. Workflow dao
dao/workflow.go
package dao
import (
"errors"
"k8s-plantform/db"
"k8s-plantform/model"
"github.com/wonderivan/logger"
)
var Workflow workflow
type workflow struct{}
type WorkflowResp struct {
Items []*model.Workflow `json:"items"`
Total int `json:"total"`
}
// 获取workflow列表
func (w *workflow) GetWorkflows(filterName, namespace string, limit, page int) (data *WorkflowResp, err error) {
//定义分页的起始位置
startSet := (page - 1) * limit
//定义数据库查询返回的内容
var (
workflowList []*model.Workflow
total int
)
//数据库查询,Limit方法用于限制条数,Offset方法用于设置起始位置
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
}
3. Workflow Service
service/workflow.go
package service
import (
"k8s-plantform/dao"
"k8s-plantform/model"
)
var Workflow workflow
type workflow struct {}
//定义workflowCreate类型
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
}
//查询workflow单条数据
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
}
//创建workflow
func(w *workflow) CreateWorkFlow(data *WorkflowCreate) (err error) {
//定义ingress名字
var ingressName string
if data.Type == "Ingress" {
ingressName = getIngressName(data.Name)
} else {
ingressName = ""
}
//workflow数据落库
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
}
//创建k8s资源
err = createWorkflowRes(data)
if err != nil {
return err
}
return err
}
//删除workflow
func(w *workflow) DelById(id int) (err error) {
//获取数据库数据
workflow, err := dao.Workflow.GetById(id)
if err != nil {
return err
}
//删除k8s资源
err = delWorkflowRes(workflow)
if err != nil {
return err
}
//删除数据库数据
err = dao.Workflow.DelById(id)
if err != nil {
return err
}
return
}
//删除k8s资源 deployment service ingress
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
}
//创建k8s资源 deployment service ingress
func createWorkflowRes(data *WorkflowCreate) (err error) {
//创建deployment
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"
}
//创建service
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
}
//创建ingress
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
}
//workflow名字转换成service名字,添加-svc后缀
func getServiceName(workflowName string) (serviceName string) {
return workflowName + "-svc"
}
//workflow名字转换成ingress名字,添加-ing后缀
func getIngressName(workflowName string) (ingressName string) {
return workflowName + "-ing"
}
4. Workflow controller
controller/workflow.go
package controller
import (
"k8s-plantform/service"
"net/http"
"github.com/gin-gonic/gin"
"github.com/wonderivan/logger"
)
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,
})
}
// 查询workflow单条数据
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,
})
}
// 创建workflow
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,
})
}
// 删除workflow
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,
})
}
5. Workflow路由
controller/router.go
// workflows
GET("/api/k8s/workflows", Workflow.GetList).
GET("/api/k8s/workflow/detail", Workflow.GetById).
POST("/api/k8s/workflow/create", Workflow.Create).
DELETE("/api/k8s/workflow/del", Workflow.DelById)
6. 测试Workflow方法
5.1 获取Workflow
http://192.168.31.1:9091/api/k8s/workflows
5.2 获取Workflow详情
http://192.168.31.1:9091/api/k8s/workflow/detail?id=1
5.3 创建Workflow
http://192.168.31.1:9091/api/k8s/workflow/create
{
"name": "test-7",
"namespace": "default",
"replicas": 1,
"image": "nginx",
"resource": "0.5/1",
"health_check": false,
"health_path": "",
"label": {
"app": "test-7"
},
"container_port": 80,
"type": "Ingress",
"port": 80,
"node_port": null,
"host": "www.bbb.com",
"path": "/",
"path_type": "Prefix",
"cpu": "0.5",
"memory": "1Gi",
"hosts": {
"www.ccc.com": [
{
"path": "/",
"path_type":"Prefix",
"service_name": "test-7",
"service_port": 80
}
]
}
}
此时,对应资源也一并被创建
5.4 删除Workflow
/api/k8s/ingress/del
就删除刚才创建的test-7.根据id再获取一次
k8s下也能获取到相关资源
删除一下
资源被删除
数据库是软删除,所以可以看到deleted时间
至此workflow完成