Skip to content

PVC管理 原创

1、功能

446534d2e2ffd2c8cbc03644e443ae01 MD5

2、类型转换

2.1、实现类型转换

internal/pkg/k8s/pvc/common.go 文件中新增 PersistentVolumeClaimCell,实现和 corev1.PersistentVolumeClaim 进行类型转换,如下:

go
package pvc

import (
	corev1 "k8s.io/api/core/v1"
	"time"

	"github.com/joker-bai/hawkeye/internal/pkg/k8s/dataselect"
)

type PersistentVolumeClaimCell corev1.PersistentVolumeClaim

func (p PersistentVolumeClaimCell) GetCreation() time.Time {
	return p.CreationTimestamp.Time
}

func (p PersistentVolumeClaimCell) GetName() string {
	return p.Name
}

// toCells corev1.PersistentVolumeClaim 类型 转换成 DataCell 类型
// @description: PersistentVolumeClaim类型转换成DataCell
func toCells(sts []corev1.PersistentVolumeClaim) []dataselect.DataCell {
	cells := make([]dataselect.DataCell, len(sts))
	for i := range sts {
		cells[i] = PersistentVolumeClaimCell(sts[i])
	}
	return cells
}

// fromCells DataCell 类型转换成 batchv1.PersistentVolumeClaim 类型
// @description: DataCell类型转换成PersistentVolumeClaim
func fromCells(cells []dataselect.DataCell) []corev1.PersistentVolumeClaim {
	ds := make([]corev1.PersistentVolumeClaim, len(cells))
	for i := range cells {
		ds[i] = corev1.PersistentVolumeClaim(cells[i].(PersistentVolumeClaimCell))
	}
	return ds
}

2.2、实现增删改查

(1)创建 internal/pkg/k8s/pvc/create.go 文件,输入以下内容,用于创建 pvc

go
package pvc

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"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
	DescriptionAnnotationKey = "description"
)

func CreatePersistentVolumeClaim(ing *requests.K8sPersistentVolumeClaimCreateRequest) error {
	annotations := map[string]string{}
	if ing.Description != nil {
		annotations[DescriptionAnnotationKey] = *ing.Description
	}

	labels := common.GetLabelsMap(ing.Labels)

	metadata := metav1.ObjectMeta{
		Annotations: annotations,
		Labels:      labels,
		Name:        ing.Name,
	}

	serv := &corev1.PersistentVolumeClaim{
		ObjectMeta: metadata,
		Spec: corev1.PersistentVolumeClaimSpec{
			AccessModes: []corev1.PersistentVolumeAccessMode{corev1.PersistentVolumeAccessMode(ing.AccessMode)},
			Resources: corev1.ResourceRequirements{Requests: map[corev1.ResourceName]resource.Quantity{
				corev1.ResourceStorage: resource.MustParse(ing.Capacity),
			}},
			StorageClassName: ing.StorageClassName,
		},
	}

	if _, err := global.K8S.CoreV1().PersistentVolumeClaims(ing.Namespace).Create(context.TODO(), serv, metav1.CreateOptions{}); err != nil {
		return err
	}

	return nil
}

(2)在 internal/pkg/k8s/pvc/delete.go 中创建以下内容,用户删除 pvc

go
package pvc

import (
	"context"
	"github.com/joker-bai/hawkeye/global"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func DeletePersistentVolumeClaim(name, namespace string) error {
	return global.K8S.CoreV1().PersistentVolumeClaims(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{})
}

(3)在 internal/pkg/k8s/pvc/list.go 中创建以下内容,用于列出 pvc 列表

go
package pvc

import (
	"context"
	"github.com/joker-bai/hawkeye/global"
	"github.com/joker-bai/hawkeye/internal/pkg/k8s/dataselect"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func ListPersistentVolumeClaim(name, namespace string, page, limit int) ([]corev1.PersistentVolumeClaim, error) {
	global.Log.Info("获取ingress的列表")
	list, err := global.K8S.CoreV1().PersistentVolumeClaims(namespace).List(context.TODO(), metav1.ListOptions{})
	if err != nil {
		return nil, err
	}

	// 做排序
	selector := dataselect.DataSelector{
		GenericDataList: toCells(list.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/pvc/update.go 中创建以下内容,用于更新 pvc

go
package pvc

import (
	"context"
	"encoding/json"
	"github.com/joker-bai/hawkeye/global"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func UpdatePersistentVolumeClaim(namespace, content string) error {
	var sts corev1.PersistentVolumeClaim
	if err := json.Unmarshal([]byte(content), &sts); err != nil {
		return err
	}

	if _, err := global.K8S.CoreV1().PersistentVolumeClaims(namespace).Update(context.TODO(), &sts, metav1.UpdateOptions{}); err != nil {
		return err
	}

	return nil
}

(5)在 internal/pkg/k8s/pvc/detail.go 中创建以下内容,用于获取 pvc 详情

go
package pvc

import (
	"context"
	"github.com/joker-bai/hawkeye/global"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func GetPersistentVolumeClaimDetail(name, namespace string) (*corev1.PersistentVolumeClaim, error) {
	sts, err := global.K8S.CoreV1().PersistentVolumeClaims(namespace).Get(context.TODO(), name, metav1.GetOptions{})
	if err != nil {
		return nil, err
	}
	return sts, nil
}

3、实现 services 方法

3.1、请求参数校验

internal/app/requests 目录中新建 k8s_pvc.go 文件,写入以下内容以完成请求参数校验:

go
package requests

import (
	"github.com/gin-gonic/gin"
	"github.com/joker-bai/hawkeye/pkg/app"
	"github.com/thedevsaddam/govalidator"
)

type K8sPersistentVolumeClaimCreateRequest struct {
	Name             string  `json:"name" form:"name" valid:"name"` // PersistentVolumeClaim的名字
	Namespace        string  `json:"namespace" valid:"namespace"`
	Description      *string `json:"description" form:"description" valid:"description"` // 描述
	Labels           []Label `json:"labels" form:"labels" valid:"labels"`                // 关联标签
	AccessMode       string  `json:"access_mode" valid:"access_mode"`                    // 访问模式
	Capacity         string  `json:"capacity" valid:"capacity"`                          // 存储请求
	StorageClassName *string `json:"storage_class_name" valid:"storage_class_name"`      // 存储类
}

func ValidK8sPersistentVolumeClaimCreateRequest(data interface{}, ctx *gin.Context) map[string][]string {
	rules := govalidator.MapData{
		"name":        []string{"required"},
		"namespace":   []string{"required"},
		"access_mode": []string{"required"},
		"capacity":    []string{"required"},
	}

	messages := govalidator.MapData{
		"capacity": []string{
			"required: capacity不能为空",
		},
		"name": []string{
			"required: name不能为空",
		},
		"namespace": []string{
			"required: namespace不能为空",
		},
		"access_mode": []string{
			"required: access_mode不能为空",
		},
	}

	return app.ValidateOptions(data, rules, messages)
}

type K8sPersistentVolumeClaimUpdateRequest struct {
	Namespace string `json:"namespace" form:"namespace" valid:"namespace"`
	Content   string `json:"content" form:"content" valid:"content"`
}

func ValidK8sPersistentVolumeClaimUpdateRequest(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 K8sPersistentVolumeClaimListRequest struct {
	K8sCommonRequest
	Page  int `json:"page" form:"page" valid:"page"`    // 页数
	Limit int `json:"limit" form:"limit" valid:"limit"` // 每页条数
}

func ValidK8sPersistentVolumeClaimListRequest(data interface{}, ctx *gin.Context) map[string][]string {
	rules := govalidator.MapData{
		"page":  []string{"required"},
		"limit": []string{"required"},
	}
	messages := govalidator.MapData{
		"page": []string{
			"required: page不能为空",
		},
		"limit": []string{
			"required: limit不能为空",
		},
	}

	// 校验入参

	return app.ValidateOptions(data, rules, messages)
}

3.2、实现 services 方法

internal/app/services/k8s_pvc.go 文件中新增 PVC 操作的 services 方法,如下:

go
package services

import (
	"github.com/joker-bai/hawkeye/internal/app/requests"
	"github.com/joker-bai/hawkeye/internal/pkg/k8s/pvc"
	corev1 "k8s.io/api/core/v1"
)

// PersistentVolumeClaim

func (s *Services) K8sPersistentVolumeClaimList(param *requests.K8sPersistentVolumeClaimListRequest) ([]corev1.PersistentVolumeClaim, error) {
	return pvc.ListPersistentVolumeClaim(param.Name, param.Namespace, param.Page, param.Limit)
}

func (s *Services) K8sPersistentVolumeClaimDelete(param *requests.K8sCommonRequest) error {
	return pvc.DeletePersistentVolumeClaim(param.Name, param.Namespace)
}

func (s *Services) K8sPersistentVolumeClaimUpdate(param *requests.K8sPersistentVolumeClaimUpdateRequest) error {
	return pvc.UpdatePersistentVolumeClaim(param.Namespace, param.Content)
}

func (s *Services) K8sPersistentVolumeClaimCreate(param *requests.K8sPersistentVolumeClaimCreateRequest) error {
	return pvc.CreatePersistentVolumeClaim(param)
}

func (s *Services) K8sPersistentVolumeClaimDetail(param *requests.K8sCommonRequest) (*corev1.PersistentVolumeClaim, error) {
	return pvc.GetPersistentVolumeClaimDetail(param.Name, param.Namespace)
}

4、新增 controllers 方法

在 internal/app/controllers/api/v1/k8s 目录中新增 pvc.go 文件,实现如下方法:

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 PersistentVolumeClaimController struct{}

// List godoc
// @Summary 列出K8s PersistentVolumeClaim
// @Description 列出K8s PersistentVolumeClaim
// @Tags K8s PersistentVolumeClaim管理
// @Produce json
// @Param name query string false "PersistentVolumeClaim名" 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/pvc/list [get]
func (k *PersistentVolumeClaimController) List(ctx *gin.Context) {
	param := requests.K8sPersistentVolumeClaimListRequest{}
	response := app.NewResponse(ctx)

	if ok := app.Validate(ctx, &param, requests.ValidK8sPersistentVolumeClaimListRequest); !ok {
		return
	}

	svc := services.New(ctx)
	pvcs, err := svc.K8sPersistentVolumeClaimList(&param)
	if err != nil {
		global.Log.Error("获取PersistentVolumeClaim列表失败", zap.String("error", err.Error()))
		response.ToErrorResponse(errorcode.ErrorK8sPersistentVolumeClaimListFail)
		return
	}

	response.ToResponseList(pvcs, len(pvcs))
}

// Update godoc
// @Summary 更新PersistentVolumeClaim
// @Description 更新PersistentVolumeClaim
// @Tags K8s PersistentVolumeClaim管理
// @Produce json
// @Param body body requests.K8sPersistentVolumeClaimUpdateRequest true "body"
// @Success 200 {object} string "成功"
// @Failure 400 {object} errorcode.Error "请求错误"
// @Failure 500 {object} errorcode.Error "内部错误"
// @Router /api/v1/k8s/pvc/update [post]
func (k *PersistentVolumeClaimController) Update(ctx *gin.Context) {
	param := requests.K8sPersistentVolumeClaimUpdateRequest{}
	response := app.NewResponse(ctx)

	if ok := app.Validate(ctx, &param, requests.ValidK8sPersistentVolumeClaimUpdateRequest); !ok {
		return
	}

	svc := services.New(ctx)
	err := svc.K8sPersistentVolumeClaimUpdate(&param)
	if err != nil {
		global.Log.Error("更新PersistentVolumeClaim失败", zap.String("error", err.Error()))
		response.ToErrorResponse(errorcode.ErrorK8sPersistentVolumeClaimUpdateFail)
		return
	}

	response.ToResponse(gin.H{
		"msg": "PersistentVolumeClaim更新成功",
	})
}

// Delete godoc
// @Summary 删除PersistentVolumeClaim
// @Description 删除PersistentVolumeClaim
// @Tags K8s PersistentVolumeClaim管理
// @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/pvc/delete [post]
func (k *PersistentVolumeClaimController) Delete(ctx *gin.Context) {
	param := requests.K8sCommonRequest{}
	response := app.NewResponse(ctx)

	if ok := app.Validate(ctx, &param, requests.ValidK8sCommonRequest); !ok {
		return
	}

	svc := services.New(ctx)
	err := svc.K8sPersistentVolumeClaimDelete(&param)
	if err != nil {
		global.Log.Error("删除PersistentVolumeClaims失败", zap.String("error", err.Error()))
		response.ToErrorResponse(errorcode.ErrorK8sPersistentVolumeClaimDeleteFail)
		return
	}

	response.ToResponse(gin.H{
		"msg": "PersistentVolumeClaim删除成功",
	})
}

// Create godoc
// @Summary 创建PersistentVolumeClaim
// @Description 创建PersistentVolumeClaim
// @Tags K8s PersistentVolumeClaim管理
// @Produce json
// @Param body body requests.K8sPersistentVolumeClaimCreateRequest true "body"
// @Success 200 {object} string "成功"
// @Failure 400 {object} errorcode.Error "请求错误"
// @Failure 500 {object} errorcode.Error "内部错误"
// @Router /api/v1/k8s/pvc/create [post]
func (k *PersistentVolumeClaimController) Create(ctx *gin.Context) {
	param := requests.K8sPersistentVolumeClaimCreateRequest{}
	response := app.NewResponse(ctx)

	if ok := app.Validate(ctx, &param, requests.ValidK8sPersistentVolumeClaimCreateRequest); !ok {
		return
	}

	svc := services.New(ctx)
	err := svc.K8sPersistentVolumeClaimCreate(&param)
	if err != nil {
		global.Log.Error("创建PersistentVolumeClaim失败", zap.String("error", err.Error()))
		response.ToErrorResponse(errorcode.ErrorK8sPersistentVolumeClaimCreateFail)
		return
	}

	response.ToResponse(gin.H{
		"msg": "PersistentVolumeClaim创建成功",
	})
}

// Detail godoc
// @Summary 获取PersistentVolumeClaim的详情
// @Description 获取PersistentVolumeClaim的详情
// @Tags K8s PersistentVolumeClaim管理
// @Produce json
// @Param name query string false "PersistentVolumeClaim名" 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/pvc/detail [get]
func (k *PersistentVolumeClaimController) Detail(ctx *gin.Context) {
	param := requests.K8sCommonRequest{}
	response := app.NewResponse(ctx)

	if ok := app.Validate(ctx, &param, requests.ValidK8sCommonRequest); !ok {
		return
	}

	svc := services.New(ctx)
	pvc, err := svc.K8sPersistentVolumeClaimDetail(&param)
	if err != nil {
		global.Log.Error("获取获取PersistentVolumeClaim的详情失败", zap.String("error", err.Error()))
		response.ToErrorResponse(errorcode.ErrorK8sPersistentVolumeClaimDetailFail)
		return
	}

	response.ToResponse(gin.H{
		"data": pvc,
		"msg":  "获取PersistentVolumeClaim的详情成功",
	})
}

再到 pkg/errorcode/k8s.go 文件中新增如下错误代码:

go
package errorcode

var (
	......
	// K8s PersistentVolumeClaim 错误码

	ErrorK8sPersistentVolumeClaimUpdateFail = NewError(500131, "更新K8s PersistentVolumeClaim 失败")
	ErrorK8sPersistentVolumeClaimDeleteFail = NewError(500132, "删除K8s PersistentVolumeClaim 失败")
	ErrorK8sPersistentVolumeClaimListFail   = NewError(500133, "获取K8s PersistentVolumeClaim 列表失败")
	ErrorK8sPersistentVolumeClaimDetailFail = NewError(500134, "获取K8s PersistentVolumeClaim 详情失败")
	ErrorK8sPersistentVolumeClaimCreateFail = NewError(500135, "创建K8s PersistentVolumeClaim 失败")
)

5、新增路由

internal/app/routers/k8s.go 文件中新增 CronJob 操作的路由,如下:

go
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")
	{
		......

		// PersistentVolumeClaim 管理
		pvc := new(k8s.PersistentVolumeClaimController)
		ks.GET("/pvc/list", pvc.List)
		ks.POST("/pvc/create", pvc.Create)
		ks.POST("/pvc/update", pvc.Update)
		ks.POST("/pvc/delete", pvc.Delete)
		ks.GET("/pvc/detail", pvc.Detail)
	}
}

6、测试一下

PS:测试之前都需要先初始化集群,在 4.3.1 Pod 章节有介绍。

这里简单测试列出 PVC 接口,如下:

a87b2c9b24f07e316f4fb273aed363ce MD5

其他接口自行下去测试。

7、代码版本

本节开发完成后,记得生成 swag 和标记代码版本,如下:

go
$ swag init
$ git add .
$ git commit -m "新增k8s集群pvc操作"
最近更新