DKube/service/deployment.go
2022-09-15 20:49:49 +08:00

308 lines
9.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package service
import (
"context"
"encoding/json"
"errors"
"github.com/wonderivan/logger"
appsv1 "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"
"k8s.io/apimachinery/pkg/util/intstr"
"strconv"
"time"
)
var Deployment deployment
type deployment struct{}
//定义列表的返回内容Items是deployment元素列表Total为deployment元素数量
type DeploymentsResp struct {
Items []appsv1.Deployment `json:"items"`
Total int `json:"total"`
}
//定义DeployCreate结构体用于创建deployment需要的参数属性的定义
type DeployCreate 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"`
}
//定义DeploysNp类型用于返回namespace中deployment的数量
type DeploysNp struct {
Namespace string `json:"namespace"`
DeployNum int `json:"deployment_num"`
}
//获取deployment列表支持过滤、排序、分页
func (d *deployment) GetDeployments(filterName, namespace string, limit, page int) (deploymentsResp *DeploymentsResp, err error) {
//获取deploymentList类型的deployment列表
deploymentList, err := K8s.ClientSet.AppsV1().Deployments(namespace).List(context.TODO(), metav1.ListOptions{})
if err != nil {
logger.Error(errors.New("获取Deployment列表失败, " + err.Error()))
return nil, errors.New("获取Deployment列表失败, " + err.Error())
}
//将deploymentList中的deployment列表(Items)放进dataselector对象中进行排序
selectableData := &dataSelector{
GenericDataList: d.toCells(deploymentList.Items),
DataSelect: &DataSelectQuery{
Filter: &FilterQuery{Name: filterName},
Paginate: &PaginateQuery{
Limit: limit,
Page: page,
},
},
}
filtered := selectableData.Filter()
total := len(filtered.GenericDataList)
data := filtered.Sort().Paginate()
//将[]DataCell类型的deployment列表转为appsv1.deployment列表
deployments := d.fromCells(data.GenericDataList)
return &DeploymentsResp{
Items: deployments,
Total: total,
}, nil
}
//获取deployment详情
func (d *deployment) GetDeploymentDetail(deploymentName, namespace string) (deployment *appsv1.Deployment, err error) {
deployment, err = K8s.ClientSet.AppsV1().Deployments(namespace).Get(context.TODO(), deploymentName, metav1.GetOptions{})
if err != nil {
logger.Error(errors.New("获取Deployment详情失败, " + err.Error()))
return nil, errors.New("获取Deployment详情失败, " + err.Error())
}
return deployment, nil
}
//设置deployment副本数
func (d *deployment) ScaleDeployment(deploymentName, namespace string, scaleNum int) (replica int32, err error) {
//获取autoscalingv1.Scale类型的对象能点出当前的副本数
scale, err := K8s.ClientSet.AppsV1().Deployments(namespace).GetScale(context.TODO(), deploymentName, metav1.GetOptions{})
if err != nil {
logger.Error(errors.New("获取Deployment副本数信息失败, " + err.Error()))
return 0, errors.New("获取Deployment副本数信息失败, " + err.Error())
}
//修改副本数
scale.Spec.Replicas = int32(scaleNum)
//更新副本数传入scale对象
newScale, err := K8s.ClientSet.AppsV1().Deployments(namespace).UpdateScale(context.TODO(), deploymentName, scale, metav1.UpdateOptions{})
if err != nil {
logger.Error(errors.New("更新Deployment副本数信息失败, " + err.Error()))
return 0, errors.New("更新Deployment副本数信息失败, " + err.Error())
}
return newScale.Spec.Replicas, nil
}
//创建deployment接收DeployCreate对象
func (d *deployment) CreateDeployment(data *DeployCreate) (err error) {
//初始化appsv1.Deployment类型的对象并将入参的data数据放进去
deployment := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: data.Name,
Namespace: data.Namespace,
Labels: data.Label,
},
Spec: appsv1.DeploymentSpec{
Replicas: &data.Replicas,
Selector: &metav1.LabelSelector{
MatchLabels: data.Label,
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Name: data.Name,
Labels: data.Label,
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: data.Name,
Image: data.Image,
Ports: []corev1.ContainerPort{
{
Name: "http",
Protocol: corev1.ProtocolTCP,
ContainerPort: data.ContainerPort,
},
},
},
},
},
},
},
Status: appsv1.DeploymentStatus{},
}
//判断健康检查功能是否打开,若打开,则增加健康检查功能
if data.HealthCheck {
deployment.Spec.Template.Spec.Containers[0].ReadinessProbe = &corev1.Probe{
ProbeHandler: corev1.ProbeHandler{
HTTPGet: &corev1.HTTPGetAction{
Path: data.HealthPath,
Port: intstr.IntOrString{
Type: 0,
IntVal: data.ContainerPort,
},
},
},
InitialDelaySeconds: 5,
TimeoutSeconds: 5,
PeriodSeconds: 5,
}
deployment.Spec.Template.Spec.Containers[0].LivenessProbe = &corev1.Probe{
ProbeHandler: corev1.ProbeHandler{
HTTPGet: &corev1.HTTPGetAction{
Path: data.HealthPath,
Port: intstr.IntOrString{
Type: 0,
IntVal: data.ContainerPort,
},
},
},
InitialDelaySeconds: 15,
TimeoutSeconds: 5,
PeriodSeconds: 5,
}
}
//定义容器的limit和request资源
deployment.Spec.Template.Spec.Containers[0].Resources.Limits = map[corev1.ResourceName]resource.Quantity{
corev1.ResourceCPU: resource.MustParse(data.Cpu),
corev1.ResourceMemory: resource.MustParse(data.Memory),
}
deployment.Spec.Template.Spec.Containers[0].Resources.Requests = map[corev1.ResourceName]resource.Quantity{
corev1.ResourceCPU: resource.MustParse(data.Cpu),
corev1.ResourceMemory: resource.MustParse(data.Memory),
}
//调用sdk创建deployment
_, err = K8s.ClientSet.AppsV1().Deployments(data.Namespace).
Create(context.TODO(), deployment, metav1.CreateOptions{})
if err != nil {
logger.Error(errors.New("创建Deployment失败, " + err.Error()))
return errors.New("创建Deployment失败, " + err.Error())
}
return nil
}
//删除deployment
func (d *deployment) DeleteDeployment(deploymentName, namespace string) (err error) {
err = K8s.ClientSet.AppsV1().Deployments(namespace).Delete(context.TODO(), deploymentName, metav1.DeleteOptions{})
if err != nil {
logger.Error(errors.New("删除Deployment失败, " + err.Error()))
return errors.New("删除Deployment失败, " + err.Error())
}
return nil
}
//重启deployment
func (d *deployment) RestartDeployment(deploymentName, namespace string) (err error) {
//此功能等同于一下kubectl命令
//kubectl deployment ${service} -p \
//'{"spec":{"template":{"spec":{"containers":[{"name":"'"${service}"'","env":[{"name":"RESTART_","value":"'$(date +%s)'"}]}]}}}}'
//使用patchData Map组装数据
patchData := map[string]interface{}{
"spec": map[string]interface{}{
"template": map[string]interface{}{
"spec": map[string]interface{}{
"containers": []map[string]interface{}{
{"name": deploymentName,
"env": []map[string]string{{
"name": "RESTART_",
"value": strconv.FormatInt(time.Now().Unix(), 10),
}},
},
},
},
},
},
}
//序列化为字节因为patch方法只接收字节类型参数
patchByte, err := json.Marshal(patchData)
if err != nil {
logger.Error(errors.New("json序列化失败, " + err.Error()))
return errors.New("json序列化失败, " + err.Error())
}
//调用patch方法更新deployment
_, err = K8s.ClientSet.AppsV1().Deployments(namespace).Patch(context.TODO(), deploymentName, "application/strategic-merge-patch+json", patchByte, metav1.PatchOptions{})
if err != nil {
logger.Error(errors.New("重启Deployment失败, " + err.Error()))
return errors.New("重启Deployment失败, " + err.Error())
}
return nil
}
//更新deployment
func (d *deployment) UpdateDeployment(namespace, content string) (err error) {
var deploy = &appsv1.Deployment{}
err = json.Unmarshal([]byte(content), deploy)
if err != nil {
logger.Error(errors.New("反序列化失败, " + err.Error()))
return errors.New("反序列化失败, " + err.Error())
}
_, err = K8s.ClientSet.AppsV1().Deployments(namespace).Update(context.TODO(), deploy, metav1.UpdateOptions{})
if err != nil {
logger.Error(errors.New("更新Deployment失败, " + err.Error()))
return errors.New("更新Deployment失败, " + err.Error())
}
return nil
}
//获取每个namespace的deployment数量
func (d *deployment) GetDeployNumPerNp() (deploysNps []*DeploysNp, err error) {
namespaceList, err := K8s.ClientSet.CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{})
if err != nil {
return nil, err
}
for _, namespace := range namespaceList.Items {
deploymentList, err := K8s.ClientSet.AppsV1().Deployments(namespace.Name).List(context.TODO(), metav1.ListOptions{})
if err != nil {
return nil, err
}
deploysNp := &DeploysNp{
Namespace: namespace.Name,
DeployNum: len(deploymentList.Items),
}
deploysNps = append(deploysNps, deploysNp)
}
return deploysNps, nil
}
//类型转换
func (d *deployment) toCells(deployments []appsv1.Deployment) []DataCell {
cells := make([]DataCell, len(deployments))
for i := range deployments {
cells[i] = deploymentCell(deployments[i])
}
return cells
}
func (d *deployment) fromCells(cells []DataCell) []appsv1.Deployment {
deployments := make([]appsv1.Deployment, len(cells))
for i := range cells {
//cells[i].(podCell)是将DataCell类型转成podCell
deployments[i] = appsv1.Deployment(cells[i].(deploymentCell))
}
return deployments
}