StatefulSet管理 原创
1、功能
2、类型转换
2.1、实现类型转换
在 internal/pkg/k8s/statefulset/common.go
文件中新增 StatefulSetCell
,实现和 appv1.StatefulSetCell
进行类型转换,如下:
package statefulset
import (
appv1 "k8s.io/api/apps/v1"
"time"
"github.com/joker-bai/hawkeye/internal/pkg/k8s/dataselect"
)
type StatefulSetCell appv1.StatefulSet
func (p StatefulSetCell) GetCreation() time.Time {
return p.CreationTimestamp.Time
}
func (p StatefulSetCell) GetName() string {
return p.Name
}
// toCells corev1.Pod 类型 转换成 DataCell 类型
// @description: Pod类型转换成DataCell
func toCells(sts []appv1.StatefulSet) []dataselect.DataCell {
cells := make([]dataselect.DataCell, len(sts))
for i := range sts {
cells[i] = StatefulSetCell(sts[i])
}
return cells
}
// fromCells DataCell 类型转换成 corev1.Pod 类型
// @description: DataCell类型转换成Pod
func fromCells(cells []dataselect.DataCell) []appv1.StatefulSet {
sts := make([]appv1.StatefulSet, len(cells))
for i := range cells {
sts[i] = appv1.StatefulSet(cells[i].(StatefulSetCell))
}
return sts
}
2.2、实现增删改查
(1)创建 internal/pkg/k8s/statefulset/create.go 文件,输入以下内容,用于创建 statefulset
package statefulset
import (
"context"
"github.com/joker-bai/hawkeye/global"
"github.com/joker-bai/hawkeye/internal/app/requests"
"github.com/joker-bai/hawkeye/internal/pkg/k8s/common"
appv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"strings"
)
const (
DescriptionAnnotationKey = "description"
)
func CreateStatefulSet(st *requests.K8sStatefulSetCreateRequest) error {
annotations := map[string]string{}
if st.Description != nil {
annotations[DescriptionAnnotationKey] = *st.Description
}
labels := common.GetLabelsMap(st.Labels)
metadata := metav1.ObjectMeta{
Annotations: annotations,
Labels: labels,
Name: st.Name,
}
containerSpec := corev1.Container{
Name: st.Name,
Image: st.ContainerImage,
SecurityContext: &corev1.SecurityContext{
Privileged: &st.RunAsPrivileged,
},
Resources: corev1.ResourceRequirements{Requests: make(map[corev1.ResourceName]resource.Quantity)},
Env: common.ConvertEnvVarsSpec(st.Variables),
}
if st.ContainerCommand != nil {
containerSpec.Command = []string{*st.ContainerCommand}
}
if st.ContainerCommandArgs != nil {
containerSpec.Args = strings.Fields(*st.ContainerCommandArgs)
}
if st.MemoryRequirement != nil {
containerSpec.Resources.Requests[corev1.ResourceMemory] = resource.MustParse(*st.MemoryRequirement)
}
if st.CpuRequirement != nil {
containerSpec.Resources.Requests[corev1.ResourceCPU] = resource.MustParse(*st.CpuRequirement)
}
// 是否开启Readiness健康检测
if st.IsReadinessEnable {
probeHandler := common.GetContainerProbe(st.ReadinessProbe)
containerSpec.ReadinessProbe = &corev1.Probe{
ProbeHandler: probeHandler,
InitialDelaySeconds: st.ReadinessProbe.InitialDelaySeconds,
TimeoutSeconds: st.ReadinessProbe.TimeoutSeconds,
PeriodSeconds: st.ReadinessProbe.PeriodSeconds,
SuccessThreshold: st.ReadinessProbe.SuccessThreshold,
FailureThreshold: st.ReadinessProbe.FailureThreshold,
}
}
if st.IsLivenessEnable {
probehandler := common.GetContainerProbe(st.ReadinessProbe)
containerSpec.LivenessProbe = &corev1.Probe{
ProbeHandler: probehandler,
InitialDelaySeconds: st.LivenessProbe.InitialDelaySeconds,
TimeoutSeconds: st.LivenessProbe.TimeoutSeconds,
PeriodSeconds: st.LivenessProbe.PeriodSeconds,
SuccessThreshold: st.LivenessProbe.SuccessThreshold,
FailureThreshold: st.LivenessProbe.FailureThreshold,
}
}
podSpec := corev1.PodSpec{Containers: []corev1.Container{containerSpec}}
if st.ImagePullSecret != nil {
podSpec.ImagePullSecrets = []corev1.LocalObjectReference{{Name: *st.ImagePullSecret}}
}
podTemplate := corev1.PodTemplateSpec{
ObjectMeta: metadata,
Spec: podSpec,
}
stment := &appv1.StatefulSet{
ObjectMeta: metadata,
Spec: appv1.StatefulSetSpec{
Replicas: &st.Replicas,
Template: podTemplate,
Selector: &metav1.LabelSelector{
MatchLabels: labels,
},
},
}
if _, err := global.K8S.AppsV1().StatefulSets(st.Namespace).Create(context.TODO(), stment, metav1.CreateOptions{}); err != nil {
return err
}
// 如果需要创建Service,执行以下代码
if st.IsCreateService {
// TODO
}
return nil
}
(2)在 internal/pkg/k8s/statefulset/delete.go 中创建以下内容,用户删除 statefulset
package statefulset
import (
"context"
"github.com/joker-bai/hawkeye/global"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func DeleteStatefulSet(name, namespace string) error {
return global.K8S.AppsV1().StatefulSets(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{})
}
(3)在 internal/pkg/k8s/statefulset/list.go 中创建以下内容,用于列出 statefulset 列表
package statefulset
import (
"context"
"github.com/joker-bai/hawkeye/global"
"github.com/joker-bai/hawkeye/internal/pkg/k8s/dataselect"
appv1 "k8s.io/api/apps/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func ListStatefulSet(name, namespace string, page, limit int) ([]appv1.StatefulSet, error) {
sts, err := global.K8S.AppsV1().StatefulSets(namespace).List(context.TODO(), metav1.ListOptions{})
if err != nil {
return nil, err
}
// 做排序
selector := dataselect.DataSelector{
GenericDataList: toCells(sts.Items),
DataSelectQuery: &dataselect.DataSelectQuery{
Filter: &dataselect.FilterQuery{
Name: name,
},
Paginate: &dataselect.PaginateQuery{
Limit: limit,
Page: page,
},
},
}
filted := selector.Filter()
data := filted.Sort().Paginate()
return fromCells(data.GenericDataList), nil
}
(4)在 internal/pkg/k8s/statefulset/update.go 中创建以下内容,用于更新 statefulset
package statefulset
import (
"context"
"encoding/json"
"github.com/joker-bai/hawkeye/global"
appv1 "k8s.io/api/apps/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func UpdateStatefulSet(namespace, content string) error {
var sts appv1.StatefulSet
if err := json.Unmarshal([]byte(content), &sts); err != nil {
return err
}
if _, err := global.K8S.AppsV1().StatefulSets(namespace).Update(context.TODO(), &sts, metav1.UpdateOptions{}); err != nil {
return err
}
return nil
}
(5)在 internal/pkg/k8s/statefulset/detail.go 中创建以下内容,用于获取 statefulset 详情
package statefulset
import (
"context"
"github.com/joker-bai/hawkeye/global"
appv1 "k8s.io/api/apps/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func GetStatefulSetDetail(name, namespace string) (*appv1.StatefulSet, error) {
sts, err := global.K8S.AppsV1().StatefulSets(namespace).Get(context.TODO(), name, metav1.GetOptions{})
if err != nil {
return nil, err
}
return sts, nil
}
(6)在 internal/pkg/k8s/statefulset/restart.go 中创建以下内容,用于重启 statefulset
package statefulset
import (
"context"
"encoding/json"
"github.com/joker-bai/hawkeye/global"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"strconv"
"time"
)
func RestartStatefulSet(name, namespace string) error {
// 组装数据
patchData := map[string]interface{}{
"spec": map[string]interface{}{
"template": map[string]interface{}{
"containers": []map[string]interface{}{
{
"name": name,
"env": []map[string]string{
{
"name": "RESTART_",
"value": strconv.FormatInt(time.Now().Unix(), 10),
},
},
},
},
},
},
}
patchBytes, err := json.Marshal(patchData)
if err != nil {
return err
}
// 调用patch更新StatefulSet
_, err = global.K8S.AppsV1().StatefulSets(namespace).Patch(context.TODO(), name, types.MergePatchType, patchBytes, metav1.PatchOptions{})
if err != nil {
return err
}
return nil
}
(7)在 internal/pkg/k8s/statefulset/scale.go 中增加以下内容,用于伸缩 statefulset
package statefulset
import (
"context"
"github.com/joker-bai/hawkeye/global"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func ScaleStatefulSet(name, namespace string, scaleNum int32) error {
// 获取当前副本数
scale, err := global.K8S.AppsV1().StatefulSets(namespace).GetScale(context.TODO(), name, metav1.GetOptions{})
if err != nil {
return err
}
// 修改副本数
scale.Spec.Replicas = scaleNum
// 更新副本数
if _, err := global.K8S.AppsV1().StatefulSets(namespace).UpdateScale(context.TODO(), name, scale, metav1.UpdateOptions{}); err != nil {
return err
}
return nil
}
(8)在 internal/pkg/k8s/statefulset/pods.go 中新增以下内容,用户获取 statefulset 的 Pod
package statefulset
import (
"context"
"github.com/joker-bai/hawkeye/global"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func GetPods(name, namespace string) ([]corev1.Pod, error) {
d, err := global.K8S.AppsV1().StatefulSets(namespace).Get(context.TODO(), name, metav1.GetOptions{})
if err != nil {
return nil, err
}
// 先获取labelSelector
label := d.Spec.Selector.String()
var pods *corev1.PodList
// 根据label获取Pod
pods, err = global.K8S.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{LabelSelector: label})
if err != nil {
return nil, err
}
return pods.Items, nil
}
3、实现 services 方法
3.1、请求参数校验
在 internal/app/requests
目录中新建 k8s_statefulset.go
文件,写入以下内容以完成请求参数校验:
package requests
import (
"github.com/gin-gonic/gin"
"github.com/joker-bai/hawkeye/pkg/app"
"github.com/thedevsaddam/govalidator"
)
type K8sStatefulSetCreateRequest struct {
Name string `json:"name" valid:"name"` // StatefulSet的名字
ContainerImage string `json:"container_image" valid:"container_image"` // 容器镜像名
ImagePullSecret *string `json:"image_pull_secret" valid:"image_pull_secret"` // 拉取镜像的密钥
ContainerCommand *string `json:"container_command" valid:"container_command"` // 容器启动时执行的命令
ContainerCommandArgs *string `json:"container_command_args" valid:"container_command_args"` // 容器启动时执行命令的参数
Replicas int32 `json:"replicas" valid:"replicas"` // 副本数
PortMappings []PortMapping `json:"port_mappings" valid:"port_mappings"` // 容器端口
Variables []EnvironmentVariable `json:"variables" valid:"variables"` // 环境变量
IsCreateService bool `json:"is_create_service" valid:"is_create_service"` // 是否创建Service
Description *string `json:"description" valid:"description"` // 描述
Namespace string `json:"namespace" valid:"namespace"` // 名称空间
MemoryRequirement *string `json:"memory_requirement" valid:"memory_requirement"` // 内存
CpuRequirement *string `json:"cpu_requirement" valid:"cpu_requirement"` // CPU
Labels []Label `json:"labels" valid:"labels"` // 标签
RunAsPrivileged bool `json:"run_as_privileged" valid:"run_as_privileged"` // 是否特权用户运行容器
IsReadinessEnable bool `json:"is_readiness_enable" valid:"is_readiness_enable"` // 是否开启ReadinessProbe健康检查
ReadinessProbe HealthCheckDetail `json:"readiness_probe" valid:"readiness_probe"` // ReadinessProbe配置
IsLivenessEnable bool `json:"is_liveness_enable" valid:"is_liveness_enable"` // 是否开启LivenessProbe健康检查
LivenessProbe HealthCheckDetail `json:"liveness_probe" valid:"liveness_probe"` // LivenessProbe 主要配置
}
func ValidK8sStatefulSetCreateRequest(data interface{}, ctx *gin.Context) map[string][]string {
rules := govalidator.MapData{
"name": []string{"required"},
"namespace": []string{"required"},
"container_image": []string{"required"},
"replicas": []string{"required"},
}
messages := govalidator.MapData{
"namespace": []string{
"required: namespace不能为空",
},
"name": []string{
"required: name不能为空",
},
"container_image": []string{
"required: image不能为空",
},
"replicas": []string{
"required: replicas不能为空",
},
}
return app.ValidateOptions(data, rules, messages)
}
type K8sStatefulSetUpdateRequest struct {
Namespace string `json:"namespace" valid:"namespace"`
Content string `json:"content" valid:"content"`
}
func ValidK8sStatefulSetUpdateRequest(data interface{}, ctx *gin.Context) map[string][]string {
rules := govalidator.MapData{
"namespace": []string{"required"},
"content": []string{"required"},
}
messages := govalidator.MapData{
"namespace": []string{
"required: namespace 不能为空",
},
"content": []string{
"required: content 不能为空",
},
}
// 校验入参
return app.ValidateOptions(data, rules, messages)
}
type K8sStatefulSetListRequest struct {
K8sCommonRequest
Page int `json:"page" valid:"page" valid:"page"` // 页数
Limit int `json:"limit" valid:"limit" valid:"limit"` // 每页条数
}
func ValidK8sStatefulSetListRequest(data interface{}, ctx *gin.Context) map[string][]string {
rules := govalidator.MapData{
"namespace": []string{"required"},
"page": []string{"required"},
"limit": []string{"required"},
}
messages := govalidator.MapData{
"namespace": []string{
"required: namespace不能为空",
},
"page": []string{
"required: page不能为空",
},
"limit": []string{
"required: limit不能为空",
},
}
// 校验入参
return app.ValidateOptions(data, rules, messages)
}
type K8sStatefulSetScaleRequest struct {
K8sCommonRequest
ScaleNum int32 `json:"scale_num" valid:"scale_num"`
}
func ValidK8sStatefulSetScaleRequest(data interface{}, ctx *gin.Context) map[string][]string {
rules := govalidator.MapData{
"name": []string{"required"},
"namespace": []string{"required"},
"scale_num": []string{"required"},
}
messages := govalidator.MapData{
"name": []string{
"required: name不能为空",
},
"namespace": []string{
"required: namespace不能为空",
},
"scale_num": []string{
"required: scale_num不能为空",
},
}
// 校验入参
return app.ValidateOptions(data, rules, messages)
}
3.2、实现 services 方法
在 internal/app/services/k8s_statefuleset.go
文件中新增 StatefulSet
操作的 services 方法,如下:
package services
import (
"github.com/joker-bai/hawkeye/internal/app/requests"
"github.com/joker-bai/hawkeye/internal/pkg/k8s/statefulset"
appv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
)
// StatefulSet Service
func (s *Services) K8sStatefulSetList(param *requests.K8sStatefulSetListRequest) ([]appv1.StatefulSet, error) {
return statefulset.ListStatefulSet(param.Name, param.Namespace, param.Page, param.Limit)
}
func (s *Services) K8sStatefulSetDelete(param *requests.K8sCommonRequest) error {
return statefulset.DeleteStatefulSet(param.Name, param.Namespace)
}
func (s *Services) K8sStatefulSetUpdate(param *requests.K8sStatefulSetUpdateRequest) error {
return statefulset.UpdateStatefulSet(param.Namespace, param.Content)
}
func (s *Services) K8sStatefulSetCreate(param *requests.K8sStatefulSetCreateRequest) error {
return statefulset.CreateStatefulSet(param)
}
func (s *Services) K8sStatefulSetScale(param *requests.K8sStatefulSetScaleRequest) error {
return statefulset.ScaleStatefulSet(param.Name, param.Namespace, param.ScaleNum)
}
func (s *Services) K8sStatefulSetRestart(param *requests.K8sCommonRequest) error {
return statefulset.RestartStatefulSet(param.Name, param.Namespace)
}
func (s *Services) K8sStatefulSetGetPod(param *requests.K8sCommonRequest) ([]corev1.Pod, error) {
return statefulset.GetPods(param.Name, param.Namespace)
}
func (s *Services) K8sStatefulSetDetail(param *requests.K8sCommonRequest) (*appv1.StatefulSet, error) {
return statefulset.GetStatefulSetDetail(param.Name, param.Namespace)
}
4、新增 controllers 方法
在 internal/app/controllers/api/v1/k8s 目录中新增 statefulset.go 文件,实现如下方法:
package k8s
import (
"github.com/gin-gonic/gin"
"github.com/joker-bai/hawkeye/global"
"github.com/joker-bai/hawkeye/internal/app/requests"
"github.com/joker-bai/hawkeye/internal/app/services"
"github.com/joker-bai/hawkeye/pkg/app"
"github.com/joker-bai/hawkeye/pkg/errorcode"
"go.uber.org/zap"
)
type StatefulSetController struct{}
// List godoc
// @Summary 列出K8s StatefulSet
// @Description 列出K8s StatefulSet
// @Tags K8s StatefulSet管理
// @Produce json
// @Param name query string false "StatefulSet名" maxlength(100)
// @Param namespace query string false "命名空间" maxlength(100)
// @Param page query int true "页码"
// @Param limit query int true "每页数量"
// @Success 200 {object} string "成功"
// @Failure 400 {object} errorcode.Error "请求错误"
// @Failure 500 {object} errorcode.Error "内部错误"
// @Router /api/v1/k8s/statefulset/list [get]
func (k *StatefulSetController) List(ctx *gin.Context) {
param := requests.K8sStatefulSetListRequest{}
response := app.NewResponse(ctx)
if ok := app.Validate(ctx, ¶m, requests.ValidK8sStatefulSetListRequest); !ok {
return
}
svc := services.New(ctx)
statefulsets, err := svc.K8sStatefulSetList(¶m)
if err != nil {
global.Log.Error("获取StatefulSet列表失败", zap.String("error", err.Error()))
response.ToErrorResponse(errorcode.ErrorK8sStatefulSetListFail)
return
}
response.ToResponseList(statefulsets, len(statefulsets))
}
// Update godoc
// @Summary 更新StatefulSet
// @Description 更新StatefulSet
// @Tags K8s StatefulSet管理
// @Produce json
// @Param body body requests.K8sStatefulSetUpdateRequest true "body"
// @Success 200 {object} string "成功"
// @Failure 400 {object} errorcode.Error "请求错误"
// @Failure 500 {object} errorcode.Error "内部错误"
// @Router /api/v1/k8s/statefulset/update [post]
func (k *StatefulSetController) Update(ctx *gin.Context) {
param := requests.K8sStatefulSetUpdateRequest{}
response := app.NewResponse(ctx)
if ok := app.Validate(ctx, ¶m, requests.ValidK8sStatefulSetUpdateRequest); !ok {
return
}
svc := services.New(ctx)
err := svc.K8sStatefulSetUpdate(¶m)
if err != nil {
global.Log.Error("更新StatefulSet失败", zap.String("error", err.Error()))
response.ToErrorResponse(errorcode.ErrorK8sStatefulSetUpdateFail)
return
}
response.ToResponse(gin.H{
"msg": "StatefulSet更新成功",
})
}
// Delete godoc
// @Summary 删除StatefulSet
// @Description 删除StatefulSet
// @Tags K8s StatefulSet管理
// @Produce json
// @Param body body requests.K8sCommonRequest true "body"
// @Success 200 {object} string "成功"
// @Failure 400 {object} errorcode.Error "请求错误"
// @Failure 500 {object} errorcode.Error "内部错误"
// @Router /api/v1/k8s/statefulset/delete [post]
func (k *StatefulSetController) Delete(ctx *gin.Context) {
param := requests.K8sCommonRequest{}
response := app.NewResponse(ctx)
if ok := app.Validate(ctx, ¶m, requests.ValidK8sCommonRequest); !ok {
return
}
svc := services.New(ctx)
err := svc.K8sStatefulSetDelete(¶m)
if err != nil {
global.Log.Error("删除StatefulSets失败", zap.String("error", err.Error()))
response.ToErrorResponse(errorcode.ErrorK8sStatefulSetDeleteFail)
return
}
response.ToResponse(gin.H{
"msg": "StatefulSet删除成功",
})
}
// Create godoc
// @Summary 创建StatefulSet
// @Description 创建StatefulSet
// @Tags K8s StatefulSet管理
// @Produce json
// @Param body body requests.K8sStatefulSetCreateRequest true "body"
// @Success 200 {object} string "成功"
// @Failure 400 {object} errorcode.Error "请求错误"
// @Failure 500 {object} errorcode.Error "内部错误"
// @Router /api/v1/k8s/statefulset/create [post]
func (k *StatefulSetController) Create(ctx *gin.Context) {
param := requests.K8sStatefulSetCreateRequest{}
response := app.NewResponse(ctx)
if ok := app.Validate(ctx, ¶m, requests.ValidK8sStatefulSetCreateRequest); !ok {
return
}
svc := services.New(ctx)
err := svc.K8sStatefulSetCreate(¶m)
if err != nil {
global.Log.Error("创建StatefulSet失败", zap.String("error", err.Error()))
response.ToErrorResponse(errorcode.ErrorK8sStatefulSetCreateFail)
return
}
response.ToResponse(gin.H{
"msg": "StatefulSet创建成果",
})
}
// Restart godoc
// @Summary 重启StatefulSet
// @Description 重启StatefulSet
// @Tags K8s StatefulSet管理
// @Produce json
// @Param body body requests.K8sCommonRequest true "body"
// @Success 200 {object} string "成功"
// @Failure 400 {object} errorcode.Error "请求错误"
// @Failure 500 {object} errorcode.Error "内部错误"
// @Router /api/v1/k8s/statefulset/restart [post]
func (k *StatefulSetController) Restart(ctx *gin.Context) {
param := requests.K8sCommonRequest{}
response := app.NewResponse(ctx)
if ok := app.Validate(ctx, ¶m, requests.ValidK8sCommonRequest); !ok {
return
}
svc := services.New(ctx)
err := svc.K8sStatefulSetRestart(¶m)
if err != nil {
global.Log.Error("重启StatefulSet失败", zap.String("error", err.Error()))
response.ToErrorResponse(errorcode.ErrorK8sStatefulSetRestartFail)
return
}
response.ToResponse(gin.H{
"msg": "重启StatefulSet成功",
})
}
// Scale godoc
// @Summary 伸缩StatefulSet
// @Description 伸缩StatefulSet
// @Tags K8s StatefulSet管理
// @Produce json
// @Param body body requests.K8sStatefulSetScaleRequest true "body"
// @Success 200 {object} string "成功"
// @Failure 400 {object} errorcode.Error "请求错误"
// @Failure 500 {object} errorcode.Error "内部错误"
// @Router /api/v1/k8s/statefulset/scale [get]
func (k *StatefulSetController) Scale(ctx *gin.Context) {
param := requests.K8sStatefulSetScaleRequest{}
response := app.NewResponse(ctx)
if ok := app.Validate(ctx, ¶m, requests.ValidK8sStatefulSetScaleRequest); !ok {
return
}
svc := services.New(ctx)
err := svc.K8sStatefulSetScale(¶m)
if err != nil {
global.Log.Error("伸缩StatefulSet失败", zap.String("error", err.Error()))
response.ToErrorResponse(errorcode.ErrorK8sStatefulSetScaleFail)
return
}
response.ToResponse(gin.H{
"msg": "伸缩StatefulSet成功",
})
}
// GetPods godoc
// @Summary 获取StatefulSet的Pod
// @Description 获取StatefulSet的Pod
// @Tags K8s StatefulSet管理
// @Produce json
// @Param name query string false "StatefulSet名" maxlength(100)
// @Param namespace query string false "命名空间" maxlength(100)
// @Param container query string false "容器" maxlength(100)
// @Success 200 {object} string "成功"
// @Failure 400 {object} errorcode.Error "请求错误"
// @Failure 500 {object} errorcode.Error "内部错误"
// @Router /api/v1/k8s/statefulset/pods [get]
func (k *StatefulSetController) GetPods(ctx *gin.Context) {
param := requests.K8sCommonRequest{}
response := app.NewResponse(ctx)
if ok := app.Validate(ctx, ¶m, requests.ValidCommonIdRequest); !ok {
return
}
svc := services.New(ctx)
pods, err := svc.K8sStatefulSetGetPod(¶m)
if err != nil {
global.Log.Error("获取获取StatefulSet的Pod失败", zap.String("error", err.Error()))
response.ToErrorResponse(errorcode.ErrorK8sStatefulSetGetPodsFail)
return
}
response.ToResponse(gin.H{
"data": pods,
"msg": "获取StatefulSet的Pod成功",
})
}
// Detail godoc
// @Summary 获取StatefulSet的详情
// @Description 获取StatefulSet的详情
// @Tags K8s StatefulSet管理
// @Produce json
// @Param name query string false "Job名" maxlength(100)
// @Param namespace query string false "命名空间" maxlength(100)
// @Success 200 {object} string "成功"
// @Failure 400 {object} errorcode.Error "请求错误"
// @Failure 500 {object} errorcode.Error "内部错误"
// @Router /api/v1/k8s/statefulset/detail [get]
func (k *StatefulSetController) Detail(ctx *gin.Context) {
param := requests.K8sCommonRequest{}
response := app.NewResponse(ctx)
if ok := app.Validate(ctx, ¶m, requests.ValidK8sCommonRequest); !ok {
return
}
svc := services.New(ctx)
st, err := svc.K8sStatefulSetDetail(¶m)
if err != nil {
global.Log.Error("获取获取StatefulSet的详情失败", zap.String("error", err.Error()))
response.ToErrorResponse(errorcode.ErrorK8sStatefulSetDetailFail)
return
}
response.ToResponse(gin.H{
"data": st,
"msg": "获取StatefulSet的详情成功",
})
}
再到 pkg/errorcode/k8s.go
文件中新增如下错误代码:
package errorcode
var (
......
// K8s StatefulSet 错误码
ErrorK8sStatefulSetUpdateFail = NewError(500031, "更新K8s StatefulSet失败")
ErrorK8sStatefulSetDeleteFail = NewError(500032, "删除K8s StatefulSet失败")
ErrorK8sStatefulSetListFail = NewError(500033, "获取K8s StatefulSet列表失败")
ErrorK8sStatefulSetDetailFail = NewError(500034, "获取K8s StatefulSet详情失败")
ErrorK8sStatefulSetRestartFail = NewError(500035, "重启K8s StatefulSet失败")
ErrorK8sStatefulSetGetPodsFail = NewError(500036, "获取K8s StatefulSet的Pod失败")
ErrorK8sStatefulSetScaleFail = NewError(500037, "伸缩K8s StatefulSet失败")
ErrorK8sStatefulSetCreateFail = NewError(500038, "创建K8s StatefulSet失败")
)
5、新增路由
在 internal/app/routers/k8s.go
文件中新增 StatefulSet
操作的路由,如下:
package routers
import (
"github.com/gin-gonic/gin"
v1 "github.com/joker-bai/kubemana/internal/app/controllers/api/v1"
)
type K8sRouter struct{}
func (r *K8sRouter) Inject(router *gin.RouterGroup) {
k8s := router.Group("/k8s")
{
......
// StatefulSet管理
sc := new(k8s.StatefulSetController)
ks.GET("/statefulset/list", sc.List)
ks.POST("/statefulset/create", sc.Create)
ks.POST("/statefulset/update", sc.Update)
ks.POST("/statefulset/delete", sc.Delete)
ks.POST("/statefulset/scale", sc.Scale)
ks.GET("/statefulset/detail", sc.Detail)
ks.POST("/statefulset/restart", sc.Restart)
ks.GET("/statefulset/pods", sc.GetPods)
}
}
6、测试一下
PS:测试之前都需要先初始化集群,在 4.3.1 Pod 章节有介绍。
这里简单测试列出 StatefulSet 接口,如下:
其他接口自行下去测试。
7、代码版本
本节开发完成后,记得生成 swag 和标记代码版本,如下:
$ swag init
$ git add .
$ git commit -m "新增k8s集群deployment操作"