【k8s多集群管理平台开发实践】六、client-go实现k8s的cronjob的列表、创建cronjob、yaml配置更新

文章目录

简介

本章节主要讲解通过client-go实现cronjob的列表显示、界面创建cronjob,读取yaml配置并更改。功能主要有后端部分:控制器代码、模型部分代码、路由配置。前端部分:cronjobList.html的html代码,cronjobCreate.html,cronjobYaml.html这几部分代码组成。

一.cronjob的列表实现

1.1.controllers控制器代码

该列表支持传递集群ID、命名空间、cronjob名称来进行过滤查询

golang 复制代码
func (this *CronjobController) List() {
	clusterId := this.GetString("clusterId")
	nameSpace := this.GetString("nameSpace")
	cronjobName := this.GetString("cronjobName")
	labels := this.GetString("labels")
	labelsKV := strings.Split(labels, ":")
	var labelsKey, labelsValue string
	if len(labelsKV) == 2 {
		labelsKey = labelsKV[0]
		labelsValue = labelsKV[1]
	}

	dxList, err := m.CronjobList(clusterId, nameSpace, cronjobName, labelsKey, labelsValue)
	msg := "success"
	code := 0
	count := len(dxList)
	if err != nil {
		log.Printf("[ERROR] CronjobList error:%s \n", err)
		msg = err.Error()
		code = -1
	}

	this.Data["json"] = &map[string]interface{}{"code": code, "msg": msg, "count": count, "data": &dxList}
	//this.Data["json"] = &datas
	this.ServeJSON()
}
1.2.models模型代码

先定义一个结构体Cronjob将需要显示的信息整理出来,然后通过common.ClientSet(kubeconfig).BatchV1().CronJobs(namespace).List 读取出cronjob的列表,并将需要的信息赋值到结构体Cronjob,并追加到结构体列表var bbb = make([]Cronjob, 0),注意所使用的api版本,版本高于1.26的就用v1版本,否则用V1beta1.

golang 复制代码
type Cronjob struct {
	CronjobName      string `json:"cronjobName"`
	NameSpace        string `json:"nameSpace"`
	Labels           string `json:"labels"`
	Annotations      string `json:"annotations"`
	ImgUrl           string `json:"imgUrl"`
	Suspend          string `json:"suspend"`
	Schedule         string `json:"schedule"`
	Active           int    `json:"active"`
	CreateTime       string `json:"createTime"`
	LastScheduleTime string `json:"lastScheduleTime"`
}

func CronjobList(kubeconfig, namespace, cronjobName string, labelsKey, labelsValue string) ([]Cronjob, error) {
	if namespace == "" {
		//namespace = corev1.NamespaceDefault
		namespace = corev1.NamespaceAll
	}
	var bbb = make([]Cronjob, 0)

	//设置标签过滤的ListOptions
	var listOptions = metav1.ListOptions{}
	if labelsKey != "" && labelsValue != "" {
		listOptions = metav1.ListOptions{
			LabelSelector: fmt.Sprintf("%s=%s", labelsKey, labelsValue),
		}
	}

	//BatchV1 会提示the server could not find the requested resource,改成BatchV1beta1 后正常,注意k8s版本,大于1.26的就用v1版本
	//xList, err := common.ClientSet(kubeconfig).BatchV1beta1().CronJobs(namespace).List(context.TODO(), listOptions)
	xList, err := common.ClientSet(kubeconfig).BatchV1().CronJobs(namespace).List(context.TODO(), listOptions) 

	if err != nil {
		log.Printf("[ERROR] ListCronjobError err:%v", err)
		return bbb, err
	}

	for _, vv := range xList.Items {
		//根据名称过滤
		if cronjobName != "" {
			if !strings.Contains(vv.Name, cronjobName) {
				continue
			}
		}

		var labelsStr, imgUrlStr string
		for k1, v1 := range vv.ObjectMeta.Labels {
			labelsStr += fmt.Sprintf("%s:%s,", k1, v1)
		}
		if len(labelsStr) > 0 {
			labelsStr = labelsStr[0 : len(labelsStr)-1]
		}
		for _, v2 := range vv.Spec.JobTemplate.Spec.Template.Spec.Containers {
			imgUrlStr += fmt.Sprintf("%s,", v2.Image)
		}

		if len(imgUrlStr) > 0 {
			imgUrlStr = imgUrlStr[0 : len(imgUrlStr)-1]
		}

		var lastScheduleTime string
		if vv.Status.LastScheduleTime != nil {
			lastScheduleTime = vv.Status.LastScheduleTime.Format("2006-01-02 15:04:05")
		}

		xItems := &Cronjob{
			CronjobName: vv.Name,
			NameSpace:   vv.Namespace,
			Labels:      labelsStr,
			ImgUrl:      imgUrlStr,
			Suspend:     fmt.Sprintf("%v", *vv.Spec.Suspend),
			Schedule:    vv.Spec.Schedule,
			Active:      len(vv.Status.Active),
			CreateTime:  vv.CreationTimestamp.Format("2006-01-02 15:04:05"),
			//LastScheduleTime: vv.Status.LastScheduleTime.Format("2006-01-02 15:04:05"),
			LastScheduleTime: lastScheduleTime,
		}
		bbb = append(bbb, *xItems)
	}
	return bbb, err
}

二.界面创建cronjob

2.1.controllers控制器代码

控制器函数接收前端html的表单传递过来的json 请求头,并传递给模型中的函数CronjobCreate来处理。

golang 复制代码
func (this *CronjobController) Create() {
	clusterId := this.GetString("clusterId")
	code := 0
	msg := "success"
	log.Println(string(this.Ctx.Input.RequestBody))
	err := m.CronjobCreate(clusterId, this.Ctx.Input.RequestBody)
	if err != nil {
		code = -1
		msg = err.Error()
		log.Printf("[ERROR] configmap Create Fail:%s\n", err)
	}
	this.Data["json"] = &map[string]interface{}{"code": code, "msg": msg}
	this.ServeJSON()
}
2.2.models模分代码

通过控制器部分传递过来的body内容,进行json解析。首先创建一个 cronjob := &batchv1.CronJob{cronjob的结构体,然后将解析json的值赋值到结构体,并调用Create(context.TODO(), cronjob, metav1.CreateOptions{}) 函数进行创建。

golang 复制代码
func CronjobCreate(kubeconfig string, bodys []byte) error {
    //解析json并取出相关值
	gp := gjson.ParseBytes(bodys)

	clusterId := gp.Get("clusterId").String()
	if kubeconfig == "" {
		kubeconfig = clusterId
	}
	cronjobName := gp.Get("cronjobName").String()
	nameSpace := gp.Get("nameSpace").String()
	var pullPolicy corev1.PullPolicy
	imagePullPolicy := gp.Get("imagePullPolicy").String()
	switch imagePullPolicy {
	case "Never":
		pullPolicy = corev1.PullNever
	case "IfNotPresent":
		pullPolicy = corev1.PullIfNotPresent
	default:
		pullPolicy = corev1.PullAlways
	}
	imageUrl := gp.Get("imageUrl").String()

	resourceLimitCheck := gp.Get("resourceLimitCheck").String()
	periodCheck := gp.Get("periodCheck").String()
	taskSetCheck := gp.Get("taskSetCheck").String()

	schedule := gp.Get("schedule").String()
	if schedule == "" {
		schedule = "* * * * *"
	}

	labelsMap := map[string]string{
		"app": cronjobName,
	}
	for _, vv := range gp.Get("lables").Array() {
		labelsMap[vv.Get("key").String()] = vv.Get("value").String()
	}
    //定义一个cronjob的实例,然后进行赋值,
	cronjob := &batchv1.CronJob{
		ObjectMeta: metav1.ObjectMeta{
			Name:      cronjobName,
			Namespace: nameSpace,
			Labels:    labelsMap,
		},
		Spec: batchv1.CronJobSpec{
			Schedule: schedule, // 定时任务表达式
			JobTemplate: batchv1.JobTemplateSpec{
				Spec: batchv1.JobSpec{ //v1版本有JobSpec,beta1版本没有
					Template: corev1.PodTemplateSpec{
						Spec: corev1.PodSpec{
							Containers: []corev1.Container{
								{
									Name:            cronjobName,
									Image:           imageUrl,
									ImagePullPolicy: pullPolicy,
								},
							},
							RestartPolicy: corev1.RestartPolicyNever,
						},
					},
				},
			},
		},
	}

	commandStr := gp.Get("command").Str
	if commandStr != "" {
		commandArry := strings.Split(commandStr, ",")
		cronjob.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Command = commandArry
	}
	argsStr := gp.Get("args").Str
	if argsStr != "" {
		argsArry := strings.Split(argsStr, ",")
		cronjob.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Args = argsArry
	}
    //将定义的cronjob实例进行创建
	jobClient := common.ClientSet(kubeconfig).BatchV1().CronJobs(nameSpace)
	_, err := jobClient.Create(context.TODO(), cronjob, metav1.CreateOptions{})
	if err != nil {
		return err
	}
	return nil
}

三.读取cronjob的yaml配置并更新

3.1.controllers控制器代码

通过传递的集群名称、命名空间、cronjob的名称通过GetCronjobYaml函数处理

golang 复制代码
//读取yaml配置
func (this *CronjobController) Yaml() {
	clusterId := this.GetString("clusterId")
	namespace := this.GetString("nameSpace")
	cronjobName := this.GetString("cronjobName")

	yamlStr, _ := m.GetCronjobYaml(clusterId, namespace, cronjobName)
	this.Ctx.WriteString(yamlStr)
}

//更新yaml配置
func (this *CronjobController) ModifyByYaml() {
	clusterId := this.GetString("clusterId")
	code := 0
	msg := "success"
	bodyByte := []byte(strings.ReplaceAll(string(this.Ctx.Input.RequestBody), "%25", "%"))
	err := m.CronjobYamlModify(clusterId, bodyByte)
	if err != nil {
		code = -1
		msg = err.Error()
		log.Printf("[ERROR] cronjob ModifyByYaml Fail:%s\n", err)
	}
	this.Data["json"] = &map[string]interface{}{"code": code, "msg": msg}
	this.ServeJSON()
}
3.2.models模型代码

该列表可以通过传递

golang 复制代码
//读取cronjob实例并转化成yaml配置文件
func GetCronjobYaml(kubeconfig, namespace, cronjobName string) (string, error) {
	jobClient := common.ClientSet(kubeconfig).BatchV1().CronJobs(namespace)
	jobInstance, err := jobClient.Get(context.TODO(), cronjobName, metav1.GetOptions{})
    //将cronjob实例进行解构
	jobUnstructured, err := runtime.DefaultUnstructuredConverter.ToUnstructured(jobInstance)
	if err != nil {
		return "", err
	}
	//并将解构后的数据序列化成yaml格式
	yamlBytes, err := yaml.Marshal(jobUnstructured)
	if err != nil {
		return "", err
	}
	//fmt.Println(string(yamlBytes))
	return string(yamlBytes), nil
}

//更新yaml配置
func CronjobYamlModify(kubeconfig string, yamlData []byte) error {
	data, err := yamlutil.ToJSON(yamlData)
	if err != nil {
		return err
	}
	cronjob := &batchv1.CronJob{}
	//将读取的yaml配置反序列化成cronjob实例
	err = json.Unmarshal(data, cronjob)
	if err != nil {
		return err
	}
    //调用Update函数更新
	namespace := cronjob.ObjectMeta.Namespace
	//cronjobName := cronjob.ObjectMeta.Name
	jobClient := common.ClientSet(kubeconfig).BatchV1().CronJobs(namespace)
	_, err = jobClient.Update(context.TODO(), cronjob, metav1.UpdateOptions{})
	return err
}

四.路由设置

4.1.路由设置

将这段代码添加到routers/route.go中

golang 复制代码
	//cronjob
	beego.Router("/xkube/cronjob/v1/List", &controllers.CronjobController{}, "*:List")
	beego.Router("/xkube/cronjob/v1/Create", &controllers.CronjobController{}, "*:Create")
	beego.Router("/xkube/cronjob/v1/ModifyByYaml", &controllers.CronjobController{}, "*:ModifyByYaml")
	beego.Router("/xkube/cronjob/v1/Yaml", &controllers.CronjobController{}, "*:Yaml")

五.前端代码

5.1.列表部分html代码

5.1 cronjobList.html

html 复制代码
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>cronjob列表</title>
    <meta name="renderer" content="webkit">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <link rel="stylesheet" href="/lib/layui-v2.6.3/css/layui.css" media="all">
    <link rel="stylesheet" href="/css/public.css" media="all">
    <script type="text/javascript" src="/lib/jquery-3.4.1/jquery-3.4.1.min.js"></script>
    <script src="/lib/layui-v2.6.3/layui.js" charset="utf-8"></script>
    <script src="/js/xkube.js?v=1" charset="utf-8"></script>
    <script src="/js/lay-config.js?v=1.0.4" charset="utf-8"></script>
<style type="text/css">
  .layui-table-cell {
    height: auto;
    line-height: 15px !important;
    text-overflow: inherit;
    overflow: ellipsis;
    white-space: normal;
  }
  .layui-table-cell .layui-table-tool-panel li {
    word-break: break-word;
  }
</style>
</head>
<body>
<div class="layuimini-container">
    <div class="layuimini-main">
        <script type="text/html" id="toolbarDemo">
            <div class="layui-btn-container">
                <button class="layui-btn layui-btn-normal layui-btn-sm data-add-btn" lay-event="createCronjob"><i class="layui-icon">&#xe61f;</i>创建cronjob</button>
            </div>
        </script>

        <table class="layui-table" id="currentTableId" lay-filter="currentTableFilter"></table>

        <script type="text/html" id="currentTableBar">
            <a class="layui-btn layui-btn-normal layui-btn-sm" lay-event="viewYaml">yaml编辑</a>
        </script>
    </div>
</div>

</body>

<script type="text/html" id="TagTpl">
    {{# if (d.labels != "") { }}
            {{# layui.each(d.labels.split(','), function(index, item){ }}
                {{# if(index == 0) { }}
                        <span>{{ item }}</span>
                {{# }else{ }}
                        <br><span>{{ item }}</span>
                {{# } }}  
            {{# });  }}
    {{# }else{  }}
            <span></span>
    {{# } }}
</script>	
<script type="text/html" id="ImgTpl">
    {{# if (d.imgUrl != "") { }}
            {{# layui.each(d.imgUrl.split(','), function(index, item){ }}
                {{# if(index == 0) { }}
                        <span>{{ item }}</span>
                {{# }else{ }}
                        <br><span>{{ item }}</span>
                {{# } }}  
            {{# });  }}
    {{# }else{  }}
            <span></span>
    {{# } }}
</script>	
<script type="text/html" id="suspendTpl">
  {{# if ( d.suspend == 'false' ) { }}
     <span style="color:#218868">{{ d.suspend}}</span>
	{{# } else { }}
    <span style="color:#FF5722">{{ d.suspend}}</span>
{{# } }}
</script>
<script type="text/html" id="sactiveTpl">
  {{# if ( d.active > 0 ) { }}
     <span style="color:#218868">d.active</span>
	{{# } else { }}
    <span style="color:#FF5722">{{ d.active}}</span>
{{# } }}
</script>
<script>

var clusterId = getQueryString("clusterId");
if (clusterId == null) {
	clusterId = getCookie("clusterId")
}

var cronjobApi = "v1";
    layui.use(['form', 'table','miniTab'], function () {
        var $ = layui.jquery,
            form = layui.form,
            table = layui.table;
            miniTab = layui.miniTab,
            miniTab.listen();

            form.render();

        table.render({
            elem: '#currentTableId',
            initSort: {field:'createTime', type:'desc'},
            url: '/cronjob/'+cronjobApi+'/List?clusterId='+clusterId,
            toolbar: '#toolbarDemo',
            defaultToolbar: ['filter', 'exports', 'print', {
                title: '提示',
                layEvent: 'LAYTABLE_TIPS',
                icon: 'layui-icon-tips'
            }],
            parseData: function(res) { //实现加载全部数据后再分页
            	if(this.page.curr) {
            		result=res.data.slice(this.limit*(this.page.curr-1),this.limit*this.page.curr);
            	}else{
            	  result=res.data.slice(0,this.limit);
              }
              return {
              	"code": res.code,
              	"msg":'',
              	"count":res.count,
              	"data":result
              };
            },
            cols: [[
                //{type: "checkbox", width: 50},
                {field: 'cronjobName', title: '名称'},
                {field: 'nameSpace', title: '命名空间', sort: true},
                {field: 'labes', title: '标签', sort: true,templet: '#TagTpl',hide:true},
                {field: 'imgUrl', title: '镜像地址', sort: true,templet: '#ImgTpl'},
                {field: 'suspend',  title: '暂停', sort: true,templet: '#suspendTpl'},
                {field: 'schedule', minWidth: 200, title: '计划', sort: true},
                {field: 'active',title: '活跃',width:120, sort: true,templet: '#activeTpl'},
                {field: 'createTime', title: '创建时间',hide:true},
                {field: 'lastScheduleTime', width:180, title: '最近调度'},
                {title: '操作', minWidth: 360, toolbar: '#currentTableBar', align: "center"}
            ]],
            //size:'lg',
            limits: [25, 50, 100],
            limit: 25,
            page: true
        });

        /**
         * toolbar监听事件
         */
        table.on('toolbar(currentTableFilter)', function (obj) {
            if (obj.event === 'createCronjob') {  // 监听添加操作
                var index = layer.open({
                    title: '创建',
                    type: 2,
                    shade: 0.2,
                    maxmin:true,
                    shadeClose: true,
                    area: ['60%', '90%'],
                    content: '/page/xkube/cronjobCreate.html?v=1'
                    //end: function(){
                    //	window.parent.location.reload();//关闭open打开的页面时,刷新父页面
                    //}
                });
                $(window).on("resize", function () {
                    layer.full(index);
                });
            }
        });

        table.on('tool(currentTableFilter)', function (obj) {
            var data = obj.data;
            if (obj.event === 'viewYaml') {
                var index = layer.open({
                    title: 'yaml',
                    type: 2,
                    shade: 0.2,
                    maxmin:true,
                    shadeClose: true,
                    area: ['45%', '92%'],
                    content: '/page/xkube/cronjobYaml.html?clusterId='+clusterId+'&nameSpace='+data.nameSpace+'&cronjobName='+data.cronjobName,
                });
                $(window).on("resize", function () {
                    layer.full(index);
                });
                return false;
            }
        });

    });
</script>
</html>
5.2.创建表单html代码

5.2 cronjobCreate.html

html 复制代码
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>创建</title>
    <meta name="renderer" content="webkit">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <link rel="stylesheet" href="/lib/layui-v2.6.3/css/layui.css" media="all">
    <link rel="stylesheet" href="/css/public.css" media="all">
    <script type="text/javascript" src="/lib/jquery-3.4.1/jquery-3.4.1.min.js"></script>
    <script src="/lib/layui-v2.6.3/layui.js" charset="utf-8"></script>
    <script src="/js/xkube.js?v=1" charset="utf-8"></script>
    <style>
        body {
            background-color: #ffffff;
        }
    </style>
</head>
<body>
<div class="layuimini-container">
    <div class="layuimini-main">
        <blockquote class="layui-elem-quote layui-text">
            帮助中心:<a href="#" target="_blank">添加说明文档</a>
        </blockquote>
        <form class="layui-form layui-form-pane" action="">

            <div class="layui-form-item">
                <div class="layui-inline">
                    <label class="layui-form-label">当前集群</label>
                    <div class="layui-input-inline">
                        <select name="clusterId" lay-filter="cluster_Id" lay-search="" id="cluster_Id">
                          <option value="">请选择集群</option>
                        </select>
                    </div>
                </div>
            </div>

            <div class="layui-form-item">
                <label class="layui-form-label required">名称</label>
                <div class="layui-input-inline">
                    <input type="text" name="cronjobName"  lay-verify="required" lay-reqtext="不能为空"  placeholder="不能为空" value="" class="layui-input">
                </div>

                <label class="layui-form-label">命名空间</label>
                <div class="layui-input-inline">
                  <select name="nameSpace" lay-filter="name_Space" lay-search="" id="name_Space">
  		            </select>
                </div>
                <div class="layui-form-mid layui-word-aux"><span style="color:red">*</span></div>
            </div>
            <div class="layui-form-item">
                <label class="layui-form-label">镜像策略</label>
                  <div class="layui-input-block">
                      <input type="radio" name="imagePullPolicy" value="Always" title="Always" checked="">
                      <input type="radio" name="imagePullPolicy" value="IfNotPresent" title="IfNotPresent">
                      <input type="radio" name="imagePullPolicy" value="Never" title="Never">
                </div>
            </div>
            <div class="layui-form-item">
                <label class="layui-form-label required">镜像地址</label>
                <div class="layui-input-inline" style="width:380px">
                    <input type="text" name="imageUrl"  lay-verify="required" lay-reqtext="不能为空"  placeholder="不能为空" value="" class="layui-input">
                </div>
                <div class="layui-form-mid layui-word-aux"><span style="color:red">*</span></div>
            </div>

            <div class="layui-form-item">
                <label class="layui-form-label">定时规则</label>
                <div class="layui-input-inline" style="width:380px">  
                    <input type="text" name="schedule" placeholder="* * * * *" lay-verify="required" lay-reqtext="不能为空" value="" class="layui-input">
                </div>
                <div class="layui-form-mid layui-word-aux"><span style="color:red">*</span></div>              
            </div>
            <div class="layui-form-item">
                <label class="layui-form-label required">启动执行</label>
                <div class="layui-input-inline">
                    <input type="text" name="command" placeholder="命令" value="" class="layui-input">
                </div>
                <div class="layui-input-inline">
                    <input type="text" name="args" placeholder="参数" value="" class="layui-input">
                </div>
            </div>

            <div class="labelsTpl">
              <div class="layui-form-item">
                  <label class="layui-form-label">标签</label>
                  <div class="layui-input-inline">  
                      <input type="text" id="labels_key0" name="labels_key[]" placeholder="key" value="" class="layui-input">
                  </div>
                  <div class="layui-input-inline">  
                      <input type="text" id="labels_value0" name="labels_value[]" placeholder="value" value="" class="layui-input">
                  </div>
                  <div class="layui-input-inline">  
                      <button class="layui-btn layui-btn-normal" style="width:100px" id="newaddbtn"><i class="layui-icon layui-icon-add-circle"></i></button>
                  </div>                
              </div> 
            </div>
  <br>
            <div class="layui-form-item">
                <div class="layui-input-block">
                    <button class="layui-btn layui-btn-normal" lay-submit lay-filter="saveBtn">确认保存</button>
                </div>
            </div>
        </form>
    </div>
</div>
</body>
<script>

    //标签删除
    var TplIndex = 0;
    function delTpl(id) {
      $("#tpl-"+id).remove();
      TplIndex--;
    }

var clusterId = getQueryString("clusterId");
if (clusterId == null) {
	clusterId = getCookie("clusterId")
}

var cronjobApi = "v1";

    layui.use(['form'], function () {
        var form = layui.form,
            layer = layui.layer,
            $ = layui.$;

        //labes add
        $('#newaddbtn').on("click",function(){
          TplIndex++;
          var addTpl =
              '<div class="layui-form-item" id="tpl-'+TplIndex+'">' +
                  '<label class="layui-form-label">标签</label>' +
                  '<div class="layui-input-inline">' +
        	              '<input type="text" name="labels_key[]" id="labels_key'+TplIndex+'" placeholder="key" value="" class="layui-input">' +
                  '</div>' +    
                  '<div class="layui-input-inline">' +  
                      '<input type="text" name="labels_value[]" id="labels_value'+TplIndex+'" placeholder="value" value="" class="layui-input">' +
                  '</div>' +
                  '<div class="layui-input-inline">' +  
                      '<input class="layui-btn layui-btn-normal layui-bg-orange layui-icon" style="width:100px" type="button" id="newDelbtn" value="&#xe616;" οnclick="delTpl('+TplIndex+');" />' +
                  '</div>' +                
              '</div>'
            $('.labelsTpl').append(addTpl);   
          form.render(); 
          return false;   
        });

        //监听提交
        form.on('submit(saveBtn)', function (data) {
                data.field.cronjobName = data.field.cronjobName.replace(/^\s*|\s*$/g,""); //替换空格
                data.field.imageUrl = data.field.imageUrl.replace(/^\s*|\s*$/g,""); //替换空格
                data.field.command = data.field.command.replace(/,|\s|;|\r|\n/g,",");//将空格等符号替换成逗号
                data.field.args = data.field.args.replace(/,|\s|;|\r|\n/g,",");//将空格等符号替换成逗号
                
                //lables 处理
                var labelsArry = [];
                for (var i=0;i<=TplIndex;i++) {
                  //delete data.field.lables_key[i];                  
                  //delete data.field.labels_value[i];
                  var kk = document.getElementById("labels_key"+i).value;
                  var vv = document.getElementById("labels_value"+i).value; 
                  if ( kk != "" && vv != "") {
                    labelsArry.push(
                      {
                        key:kk,
                        value:vv,
                      }
                    )
                  }
                }
                if (labelsArry.length > 0) {
                  data.field.lables = labelsArry;
                }
					      //console.log(data.field);
			          layer.confirm('确定添加?', {icon: 3, title:'提示',yes: function(index){
                     var index2 = layer.load(0, {shade: false});
                     layer.msg('稍等片刻');
                     $.ajax({
                       url: '/cronjob/'+cronjobApi+'/Create',
                       type: "post",
                       data: JSON.stringify(data.field),
                       dataType: "json",
                       success: function (resp) {
                                layer.close(index2);
                                 if(resp.code == 0){
                                    layer.msg('添加成功', {icon: 1});
									                  //window.location.reload();
                                 }else{
                                    layer.msg(resp.msg,{icon:2});
                                 }
                        }
                      });		  	  
                  },
                  cancel: function(index, layero){ 
                    layer.close(index);
                    layer.close(index2);
		                console.log("不操作");
                  } 
                });
              return false;
        });

    });
</script>
</html>
5.3.显示yaml配置的html代码

5.3 cronjobYaml.html

html 复制代码
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>yaml编辑</title>
    <meta name="renderer" content="webkit">
    <link rel="stylesheet" href="/lib/layui-v2.6.3/css/layui.css" media="all">
    <link rel="stylesheet" href="/css/public.css" media="all">
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
		<link
			rel="stylesheet"
			data-name="vs/editor/editor.main"
			href="/monaco-editor/min/vs/editor/editor.main.css"
		/>
    <script src="/js/xkube.js?v=1" charset="utf-8"></script>
    <script type="text/javascript" src="/lib/jquery-3.4.1/jquery-3.4.1.min.js"></script>
</head>
<body>
<div class="layuimini-container">
    <div class="layuimini-main">
        <div id="container" style="width: 100%; height:460px; border: 1px solid grey"></div>
        <br>
        <fieldset class="table-search-fieldset">
            <legend></legend>
            <button class="layui-btn layui-btn-sm" id="SaveBtn" >保存更新</button>
            <button class="layui-btn layui-btn-normal layui-btn-sm">下载</button>
        </fieldset>   
    </div>
</div>
<script src="/lib/layui-v2.6.3/layui.js" charset="utf-8"></script>
<script>
	var require = { paths: { vs: '/monaco-editor/min/vs' } };
</script>
<script src="/monaco-editor/min/vs/loader.js"></script>
<script src="/monaco-editor/min/vs/editor/editor.main.nls.js"></script>
<script src="/monaco-editor/min/vs/editor/editor.main.js"></script>
<script>

var clusterId = getQueryString("clusterId");
if (clusterId == null) {
	clusterId = getCookie("clusterId")
}

var cronjobApi = "v1";

    layui.use(['form', 'table','code'], function () {
        var $ = layui.jquery;
        var form = layui.form,
            layer = layui.layer;
        
    		var editor = monaco.editor.create(document.getElementById('container'), {
    			value: '',
    			language: 'yaml',
    			//automaticLayout: true,
    			minimap: {enabled: false},
    			wordWrap: 'on',
    			theme: 'vs-dark'
    		});

    		$.ajax({
    		    url: '/cronjob/'+cronjobApi+'/Yaml'+location.search,
    		    type: "GET",
    		    success: function (resp) {
    			  //codeyaml = resp;
    			  editor.setValue(resp);
    			}
    		});	

        $('#SaveBtn').on("click",function(){
              var yamlBody = editor.getValue();
              yamlBody = yamlBody.replace(/%/g,"%25");
              layer.confirm('确定修改?', {icon: 3, title:'提示',yes: function(index){
                  $.ajax({
                   url: '/cronjob/'+cronjobApi+'/ModifyByYaml'+location.search,
                   type: "POST",
                   data: yamlBody,
                   dataType: "json",
                   success: function (resp) {
                      layer.msg(resp.msg);
                    }
                  });
              },
              cancel: function(index, layero){ 
                  layer.close(index);
              } 
            });
        });
    });
</script>

</body>
</html>

六.完整代码

6.1.控制器完整代码

6.1 cronjob.go,放到controllers下

golang 复制代码
// cronjob.go
package controllers

import (
	"log"
	m "myk8s/models"
	"strings"

	beego "github.com/beego/beego/v2/server/web"
	//"github.com/tidwall/gjson"
)

type CronjobController struct {
	beego.Controller
}

func (this *CronjobController) List() {
	clusterId := this.GetString("clusterId")
	nameSpace := this.GetString("nameSpace")
	cronjobName := this.GetString("cronjobName")
	labels := this.GetString("labels")
	labelsKV := strings.Split(labels, ":")
	var labelsKey, labelsValue string
	if len(labelsKV) == 2 {
		labelsKey = labelsKV[0]
		labelsValue = labelsKV[1]
	}

	dxList, err := m.CronjobList(clusterId, nameSpace, cronjobName, labelsKey, labelsValue)
	msg := "success"
	code := 0
	count := len(dxList)
	if err != nil {
		log.Printf("[ERROR] CronjobList error:%s \n", err)
		msg = err.Error()
		code = -1
	}

	this.Data["json"] = &map[string]interface{}{"code": code, "msg": msg, "count": count, "data": &dxList}
	//this.Data["json"] = &datas
	this.ServeJSON()
}

func (this *CronjobController) Create() {
	clusterId := this.GetString("clusterId")
	code := 0
	msg := "success"
	log.Println(string(this.Ctx.Input.RequestBody))
	err := m.CronjobCreate(clusterId, this.Ctx.Input.RequestBody)
	if err != nil {
		code = -1
		msg = err.Error()
		log.Printf("[ERROR] configmap Create Fail:%s\n", err)
	}
	this.Data["json"] = &map[string]interface{}{"code": code, "msg": msg}
	this.ServeJSON()
}

func (this *CronjobController) ModifyByYaml() {
	clusterId := this.GetString("clusterId")
	code := 0
	msg := "success"
	bodyByte := []byte(strings.ReplaceAll(string(this.Ctx.Input.RequestBody), "%25", "%"))
	err := m.CronjobYamlModify(clusterId, bodyByte)
	if err != nil {
		code = -1
		msg = err.Error()
		log.Printf("[ERROR] cronjob ModifyByYaml Fail:%s\n", err)
	}
	this.Data["json"] = &map[string]interface{}{"code": code, "msg": msg}
	this.ServeJSON()
}

func (this *CronjobController) Yaml() {
	clusterId := this.GetString("clusterId")
	namespace := this.GetString("nameSpace")
	cronjobName := this.GetString("cronjobName")

	yamlStr, _ := m.GetCronjobYaml(clusterId, namespace, cronjobName)
	this.Ctx.WriteString(yamlStr)
}
6.2.模型完整代码

6.2 cronjobModel.go放到models下

golang 复制代码
// cronjobModel.go
package models

import (
	"context"
	"encoding/json"
	"fmt"
	"log"
	"myk8s/common"
	"strings"

	"github.com/tidwall/gjson"
	"sigs.k8s.io/yaml"

	yamlutil "k8s.io/apimachinery/pkg/util/yaml"

	batchv1 "k8s.io/api/batch/v1" //注意有的地方要用beta1版本才行
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
)

type Cronjob struct {
	CronjobName      string `json:"cronjobName"`
	NameSpace        string `json:"nameSpace"`
	Labels           string `json:"labels"`
	Annotations      string `json:"annotations"`
	ImgUrl           string `json:"imgUrl"`
	Suspend          string `json:"suspend"`
	Schedule         string `json:"schedule"`
	Active           int    `json:"active"`
	CreateTime       string `json:"createTime"`
	LastScheduleTime string `json:"lastScheduleTime"`
}

func CronjobList(kubeconfig, namespace, cronjobName string, labelsKey, labelsValue string) ([]Cronjob, error) {
	if namespace == "" {
		//namespace = corev1.NamespaceDefault
		namespace = corev1.NamespaceAll
	}
	var bbb = make([]Cronjob, 0)

	//设置ListOptions
	var listOptions = metav1.ListOptions{}
	if labelsKey != "" && labelsValue != "" {
		listOptions = metav1.ListOptions{
			LabelSelector: fmt.Sprintf("%s=%s", labelsKey, labelsValue),
		}
	}

	//BatchV1 会提示the server could not find the requested resource,改成BatchV1beta1 后正常
	//xList, err := common.ClientSet(kubeconfig).BatchV1beta1().CronJobs(namespace).List(context.TODO(), listOptions) //zx-pcauto ok,yt-pcauto异常:the server could not find the requested resource
	xList, err := common.ClientSet(kubeconfig).BatchV1().CronJobs(namespace).List(context.TODO(), listOptions) //yt-pcauto ok,zx-pcauto 异常:the server could not find the requested resource

	if err != nil {
		log.Printf("[ERROR] ListCronjobError err:%v", err)
		return bbb, err
	}

	for _, vv := range xList.Items {
		//搜索
		if cronjobName != "" {
			if !strings.Contains(vv.Name, cronjobName) {
				continue
			}
		}

		var labelsStr, imgUrlStr string
		for k1, v1 := range vv.ObjectMeta.Labels {
			labelsStr += fmt.Sprintf("%s:%s,", k1, v1)
		}
		if len(labelsStr) > 0 {
			labelsStr = labelsStr[0 : len(labelsStr)-1]
		}
		for _, v2 := range vv.Spec.JobTemplate.Spec.Template.Spec.Containers {
			imgUrlStr += fmt.Sprintf("%s,", v2.Image)
		}

		if len(imgUrlStr) > 0 {
			imgUrlStr = imgUrlStr[0 : len(imgUrlStr)-1]
		}

		var lastScheduleTime string
		if vv.Status.LastScheduleTime != nil {
			lastScheduleTime = vv.Status.LastScheduleTime.Format("2006-01-02 15:04:05")
		}

		xItems := &Cronjob{
			CronjobName: vv.Name,
			NameSpace:   vv.Namespace,
			Labels:      labelsStr,
			ImgUrl:      imgUrlStr,
			Suspend:     fmt.Sprintf("%v", *vv.Spec.Suspend),
			Schedule:    vv.Spec.Schedule,
			Active:      len(vv.Status.Active),
			CreateTime:  vv.CreationTimestamp.Format("2006-01-02 15:04:05"),
			//LastScheduleTime: vv.Status.LastScheduleTime.Format("2006-01-02 15:04:05"),
			LastScheduleTime: lastScheduleTime,
		}
		bbb = append(bbb, *xItems)
	}
	return bbb, err
}

func CronjobCreate(kubeconfig string, bodys []byte) error {
	gp := gjson.ParseBytes(bodys)

	clusterId := gp.Get("clusterId").String()
	if kubeconfig == "" {
		kubeconfig = clusterId
	}
	cronjobName := gp.Get("cronjobName").String()
	nameSpace := gp.Get("nameSpace").String()
	var pullPolicy corev1.PullPolicy
	imagePullPolicy := gp.Get("imagePullPolicy").String()
	switch imagePullPolicy {
	case "Never":
		pullPolicy = corev1.PullNever
	case "IfNotPresent":
		pullPolicy = corev1.PullIfNotPresent
	default:
		pullPolicy = corev1.PullAlways
	}
	imageUrl := gp.Get("imageUrl").String()

	resourceLimitCheck := gp.Get("resourceLimitCheck").String()
	periodCheck := gp.Get("periodCheck").String()
	taskSetCheck := gp.Get("taskSetCheck").String()

	schedule := gp.Get("schedule").String()
	if schedule == "" {
		schedule = "* * * * *"
	}

	labelsMap := map[string]string{
		"app": cronjobName,
	}
	for _, vv := range gp.Get("lables").Array() {
		labelsMap[vv.Get("key").String()] = vv.Get("value").String()
	}

	cronjob := &batchv1.CronJob{
		ObjectMeta: metav1.ObjectMeta{
			Name:      cronjobName,
			Namespace: nameSpace,
			Labels:    labelsMap,
		},
		Spec: batchv1.CronJobSpec{
			Schedule: schedule, // 定时任务表达式
			JobTemplate: batchv1.JobTemplateSpec{
				Spec: batchv1.JobSpec{ //v1版本有JobSpec,beta1版本没有
					Template: corev1.PodTemplateSpec{
						Spec: corev1.PodSpec{
							Containers: []corev1.Container{
								{
									Name:            cronjobName,
									Image:           imageUrl,
									ImagePullPolicy: pullPolicy,
								},
							},
							RestartPolicy: corev1.RestartPolicyNever,
						},
					},
				},
			},
		},
	}

	commandStr := gp.Get("command").Str
	if commandStr != "" {
		commandArry := strings.Split(commandStr, ",")
		cronjob.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Command = commandArry
	}
	argsStr := gp.Get("args").Str
	if argsStr != "" {
		argsArry := strings.Split(argsStr, ",")
		cronjob.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Args = argsArry
	}

	jobClient := common.ClientSet(kubeconfig).BatchV1().CronJobs(nameSpace)
	_, err := jobClient.Create(context.TODO(), cronjob, metav1.CreateOptions{})
	if err != nil {
		return err
	}
	return nil
}

func CronjobYamlModify(kubeconfig string, yamlData []byte) error {
	data, err := yamlutil.ToJSON(yamlData)
	if err != nil {
		return err
	}
	cronjob := &batchv1.CronJob{}
	err = json.Unmarshal(data, cronjob)
	if err != nil {
		return err
	}

	namespace := cronjob.ObjectMeta.Namespace
	//cronjobName := cronjob.ObjectMeta.Name
	jobClient := common.ClientSet(kubeconfig).BatchV1().CronJobs(namespace)
	_, err = jobClient.Update(context.TODO(), cronjob, metav1.UpdateOptions{})
	return err
}

func GetCronjobYaml(kubeconfig, namespace, cronjobName string) (string, error) {
	jobClient := common.ClientSet(kubeconfig).BatchV1().CronJobs(namespace)
	jobInstance, err := jobClient.Get(context.TODO(), cronjobName, metav1.GetOptions{})

	jobUnstructured, err := runtime.DefaultUnstructuredConverter.ToUnstructured(jobInstance)
	if err != nil {
		return "", err
	}
	yamlBytes, err := yaml.Marshal(jobUnstructured)
	if err != nil {
		return "", err
	}
	//fmt.Println(string(yamlBytes))
	return string(yamlBytes), nil
}

七.效果图



相关推荐
Yeats_Liao1 天前
Go Web 编程快速入门 10 - 数据库集成与ORM:连接池、查询优化与事务管理
前端·数据库·后端·golang
KubeSphere 云原生1 天前
云原生周刊:在 Kubernetes 上运行机器学习
云原生·容器·kubernetes
码界奇点1 天前
通往Docker之路从单机到容器编排的架构演进全景
docker·容器·架构
阿Y加油吧1 天前
Docker从入门到实战——含容器部署、docker基础、项目部署
运维·docker·容器
victory04311 天前
progen2 docker镜像打包命令文档
运维·docker·容器
算是难了1 天前
Docker基础总结
运维·docker·容器
ityangs1 天前
GitLab 私服(基于 Docker)搭建方案
git·docker·容器·gitlab
Tony Bai1 天前
【Go模块构建与依赖管理】01 前世今生:从 GOPATH 的“混乱”到 Go Modules 的“秩序”
开发语言·后端·golang
gopyer1 天前
Go语言2D游戏开发入门004:零基础打造射击游戏《太空大战》3
golang·go·游戏开发