【k8s多集群管理平台开发实践】七、clint-go实现configmap的列表、创建configmap、yaml配置的编辑

文章目录

简介

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

一.configmap的列表实现

1.1.controllers控制器代码

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

golang 复制代码
func (this *ConfigMapController) List() {
	clusterId := this.GetString("clusterId")
	nameSpace := this.GetString("nameSpace")
	configmapName := this.GetString("configmapName")

	labels := this.GetString("labels")
	labelsKV := strings.Split(labels, ":")
	var labelsKey, labelsValue string
	if len(labelsKV) == 2 {
		labelsKey = labelsKV[0]
		labelsValue = labelsKV[1]
	}
	cmList, err := m.CmList(clusterId, nameSpace, configmapName, labelsKey, labelsValue)
	msg := "success"
	code := 0
	count := len(cmList)
	if err != nil {
		log.Println(err)
		msg = err.Error()
		code = -1
	}
	this.Data["json"] = &map[string]interface{}{"code": code, "msg": msg, "count": count, "data": &cmList}
	this.ServeJSON()
}
1.2.models模型代码

先定义configmap的实例信息结构体和存储数据的key-value的结构体,并通过传递的集群ID、命名空间、标签等信息过滤,调用clientset.CoreV1().ConfigMaps(namespace).List 函数读取列表,并赋值到Configmap结构体并追加到结构体数组var bbb = make([]Configmap, 0)

golang 复制代码
//定义configmap中信息的结构体
type Configmap struct {
	ConfigmapName string `json:"configmapName"`
	NameSpace     string `json:"nameSpace"`
	Labels        string `json:"labels"`
	CreateTime    string `json:"createTime"`
	Data          []Cmkv `json:"data"`
}
//定义configmap中存储的数据的key-value数据结构体
type Cmkv struct {
	Key   string `json:"key"`
	Value string `json:"value"`
}

func CmList(kubeconfig, namespace, configmapName string, labelsKey, labelsValue string) ([]Configmap, error) {
	clientset := common.ClientSet(kubeconfig)
	if namespace == "" {
		//namespace = corev1.NamespaceDefault
		namespace = corev1.NamespaceAll
	}

	//设置标签的ListOptions
	var listOptions = metav1.ListOptions{}
	if labelsKey != "" && labelsValue != "" {
		listOptions = metav1.ListOptions{
			LabelSelector: fmt.Sprintf("%s=%s", labelsKey, labelsValue),
		}
	}
    //读取api数据并返回configmap的信息
	cmList, err := clientset.CoreV1().ConfigMaps(namespace).List(context.TODO(), listOptions)
	if err != nil {
		log.Printf("list deployment error, err:%v\n", err)
	}
    //顶一个数组用来存放configmap的信息
	var bbb = make([]Configmap, 0)
	for _, cm := range cmList.Items {
		//根据configmap的名称来过滤
		if configmapName != "" {
			if !strings.Contains(cm.Name, configmapName) {
				continue
			}
		}
		var labelsStr string
		for kk, vv := range cm.ObjectMeta.Labels {
			labelsStr += fmt.Sprintf("%s:%s,", kk, vv)
		}
		if len(labelsStr) > 0 {
			labelsStr = labelsStr[0 : len(labelsStr)-1]
		}
		Items := &Configmap{
			ConfigmapName: cm.Name,
			NameSpace:     cm.Namespace,
			Labels:        labelsStr,
			CreateTime:    cm.CreationTimestamp.Format("2006-01-02 15:04:05"),
		}
		bbb = append(bbb, *Items)
	}
	return bbb, err
}

二.界面创建configmap

2.1.controllers控制器代码

接收html中表单传来的数据,并传递给CmCreate函数处理。

golang 复制代码
func (this *ConfigMapController) Create() {
	clusterId := this.GetString("clusterId")
	code := 0
	msg := "success"
	err := m.CmCreate(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模分代码

接收控制器中Create函数传递过来的body,并进行json解析,先创建一个configmap实例&corev1.ConfigMap,然后将解析出来的值赋值到实例中,然后通过调用clientset.CoreV1().ConfigMaps(nameSpace).Create进行创建。

golang 复制代码
func CmCreate(kubeconfig string, bodys []byte) error {
	gp := gjson.ParseBytes(bodys)
	clusterId := gp.Get("clusterId").String()
	if kubeconfig == "" {
		kubeconfig = clusterId
	}
	configmapName := gp.Get("configmapName").String()
	nameSpace := gp.Get("nameSpace").String()

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

	var dataMap = make(map[string]string)
	for _, vv := range gp.Get("configmaps").Array() {
		dataMap[vv.Get("key").Str] = vv.Get("value").Str
	}

	cm := &corev1.ConfigMap{
		ObjectMeta: metav1.ObjectMeta{
			Name:      configmapName,
			Namespace: nameSpace,
			Labels:    labelsMap,
		},
		Data: dataMap,
	}

	clientset := common.ClientSet(kubeconfig)
	_, err := clientset.CoreV1().ConfigMaps(nameSpace).Create(context.TODO(), cm, metav1.CreateOptions{})
	if err != nil {
		return err
	}
	return nil
}

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

3.1.controllers控制器代码

两个功能:一个是读取yaml配置Yaml(),一个是更新yaml配置ModifyByYaml()。

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

	yamlStr, _ := m.GetCmYaml(clusterId, namespace, configmapName)
	this.Ctx.WriteString(yamlStr)
}

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

GetCmYaml通过集群ID、命名空间、configmap的名称获取实例,并进行解构然后进行序列化成yaml配置,CmYamlModify将提交的yaml配置进行反序列化成configmap实例,然后通过调用clientset.CoreV1().ConfigMaps(namespace).Update进行更新

golang 复制代码
//获取configmapyaml配置
func GetCmYaml(kubeconfig, namespace, configmapName string) (string, error) {
	cmClient := common.ClientSet(kubeconfig).CoreV1().ConfigMaps(namespace)
	//读取configmap实例配置
	configmap, err := cmClient.Get(context.TODO(), configmapName, metav1.GetOptions{})
	//将配置进行解构
	cmUnstructured, err := runtime.DefaultUnstructuredConverter.ToUnstructured(configmap)
	if err != nil {
		return "", err
	}
	//将解构的数据进行序列化成yaml配置
	yamlBytes, err := yaml.Marshal(cmUnstructured)
	if err != nil {
		return "", err
	}
	return string(yamlBytes), nil
}

//更新configmap的yaml配置
func CmYamlModify(kubeconfig string, yamlData []byte) error {
    //将yaml配置解析成json
	data, err := yamlutil.ToJSON(yamlData)
	if err != nil {
		return err
	}
	//定义configmap实例
	configmap := &corev1.ConfigMap{}
	
	//将json数据转成结构体configmap的实例上
	err = json.Unmarshal(data, configmap)
	if err != nil {
		return err
	}

	namespace := configmap.ObjectMeta.Namespace
	confignameName := configmap.ObjectMeta.Name
	//调用Update函数进行更新
	clientset := common.ClientSet(kubeconfig)
	_, err = clientset.CoreV1().ConfigMaps(namespace).Update(context.TODO(), configmap, metav1.UpdateOptions{})
	if err != nil {
		return err
	}
	//fmt.Println(namespace, confignameName)
	return err
}

四.路由设置

4.1.路由设置

将以下路由配置添加到routers/route.go中

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

五.前端代码

5.1.列表部分html代码

5.1 configmapList.html

html 复制代码
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>configmap列表</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: 22px !important;
    text-overflow: inherit;
    overflow: visible;
    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="create"><i class="layui-icon">&#xe61f;</i>创建configmap</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>
            <a class="layui-btn layui-btn-normal layui-btn-sm layui-btn-danger" lay-event="delete">删除</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>
var clusterId = getQueryString("clusterId");
if (clusterId == null) {
	clusterId = getCookie("clusterId")
}
    layui.use(['form', 'table','miniTab'], function () {
        var $ = layui.jquery,
            form = layui.form,
            table = layui.table;
            miniTab = layui.miniTab,
            miniTab.listen();
        table.render({
            elem: '#currentTableId',
            url: '/cm/v1/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: 'configmapName', width: 250,title: '名称', sort: true},
                {field: 'nameSpace', width: 150,title: '命名空间', sort: true},
                {field: 'labels', title: '标签', sort: true,templet: '#TagTpl'},
                {field: 'createTime', width:180, title: '创建时间'},
                {title: '操作', toolbar: '#currentTableBar', align: "center"}
            ]],
            //size:'lg',
            limits: [25, 50, 100],
            limit: 25,
            page: true
        });

        table.on('toolbar(currentTableFilter)', function (obj) {
            if (obj.event === 'create') {  // 监听添加操作
                var index = layer.open({
                    title: '创建',
                    type: 2,
                    shade: 0.2,
                    maxmin:true,
                    shadeClose: true,
                    area: ['60%', '90%'],
                    content: '/page/xkube/configmapCreate.html',
                    //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/configmapYaml.html?clusterId='+clusterId+'&nameSpace='+data.nameSpace+'&configmapName='+data.configmapName,
                });
                $(window).on("resize", function () {
                    layer.full(index);
                });
                return false;
            }
        });

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

5.2 configmapCreate.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">
        <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">cm名称</label>
                <div class="layui-input-inline" style="width:400px">
                    <input type="text" name="configmapName" 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">命名空间</label>
                <div class="layui-input-inline" style="width:400px">
                  <select name="nameSpace" lay-filter="name_Space" lay-search="" id="name_Space">
  		            </select>
                </div>
            </div>

            <div class="configmapTpl">
              <blockquote class="layui-elem-quote">
                <div class="layui-form-item">
                    <label class="layui-form-label required">key名称</label>
                    <div class="layui-input-inline" style="width:250px">
                        <input type="text" id="configmapKey0" name="configmapKey[]" lay-verify="required" lay-reqtext="不能为空" placeholder="key" value="" class="layui-input">
                    </div>
                    <div class="layui-input-inline">  
                        <button class="layui-btn layui-btn-normal" id="cmaddbtn"><i class="layui-icon layui-icon-add-circle"></i></button>
                    </div>
                </div>
                <div class="layui-form-item">
                  <label class="layui-form-label">值</label>
                  <div class="layui-input-block" style="width:400px">
                    <textarea id="configmapValue0" name="configmapValue[]" lay-verify="required" lay-reqtext="不能为空" placeholder="请输入内容" class="layui-textarea"></textarea>
                  </div>
                </div>
              </blockquote>
            </div> 
            <fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;">
                <legend>标签</legend>
            </fieldset>
            <div class="labelsTpl">
              <div class="layui-form-item">
                  <label class="layui-form-label">标签</label>
                  <div class="layui-input-inline" style="width:150px">  
                      <input type="text" id="labels_key0" name="labels_key[]" placeholder="key" value="" class="layui-input">
                  </div>
                  <div class="layui-input-inline" style="width:150px">  
                      <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" 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) {
      //console.log(id);
      //var TplIndex = id;
      TplIndex--;
      $("#tpl-"+id).remove();
    }

    //configmap删除
    var cTplIndex = 0;
    function delcTpl(id) {
      //console.log(id);
      //var cTplIndex = id;
      cTplIndex--;
      $("#ctpl-"+id).remove();
    }

    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" style="width:150px">' +
        	              '<input type="text" name="labels_key[]" id="labels_key'+TplIndex+'" placeholder="key" value="" class="layui-input">' +
                  '</div>' +    
                  '<div class="layui-input-inline" style="width:150px">' +  
                      '<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:60px" type="button" id="newDelbtn" value="&#xe616;" οnclick="delTpl('+TplIndex+');" />' +
                  '</div>' +                
              '</div>'
            $('.labelsTpl').append(addTpl);   
          form.render(); 
          return false;   
        });
        
        //configmap增加
        $('#cmaddbtn').on("click",function(){
          cTplIndex++;
          var addTpl =
              '<blockquote class="layui-elem-quote"id="ctpl-'+cTplIndex+'">' +
                  '<div class="layui-form-item">' +
                    '<label class="layui-form-label required">名称</label>' +
                    '<div class="layui-input-inline" style="width:250px">' +
          	              '<input type="text" name="configmapKey[]" id="configmapKey'+cTplIndex+'" placeholder="key" value="" class="layui-input">' +
                    '</div>' +   
                    '<div class="layui-input-inline">' +  
                        '<input class="layui-btn layui-btn-normal layui-bg-orange layui-icon" style="width:60px" type="button" value="&#xe616;" οnclick="delcTpl('+cTplIndex+');" />' +
                    '</div>' + 
                  '</div>' +   
                  '<div class="layui-form-item">' + 
                  '<label class="layui-form-label">值</label>' +  
                  '<div class="layui-input-block" style="width:400px">' +  
                      '<textarea placeholder="请输入内容" class="layui-textarea" id="configmapValue'+cTplIndex+'"></textarea>' +
                  '</div>' +
              '</div>'
            $('.configmapTpl').append(addTpl);   
          form.render(); 
          return false;   
        });

        //监听提交
        form.on('submit(saveBtn)', function (data) {
                //data.field.appname = data.field.appname.replace(/^\s*|\s*$/g,""); //替换空格
                //labels
                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.replace(/^\s*|\s*$/g,""),
                        value:vv.replace(/^\s*|\s*$/g,""),
                      }
                    )
                  }
                }
                if (labelsArry.length > 0) {
                  data.field.lables = labelsArry;
                }

                //cm 处理
                var cmArry = [];
                console.log(cTplIndex);
                for (var i=0;i<=cTplIndex;i++) {
                  var kk = document.getElementById("configmapKey"+i).value;
                  var vv = document.getElementById("configmapValue"+i).value; 
                  console.log(i);
                  if ( kk != null && vv != null) {
                    console.log(kk);
                    console.log(vv);
                    cmArry.push(
                      {
                        key:kk.replace(/^\s*|\s*$/g,""),
                        value:vv,
                      }
                    )
                  }
                }
                if (cmArry.length > 0) {
                  data.field.configmaps = cmArry;
                }
                    
					      console.log(data.field);
			          layer.confirm('确定添加?', {icon: 3, title:'提示',yes: function(index){
                     var index2 = layer.load(0, {shade: false});
                     layer.msg('稍等片刻');
                     $.ajax({
                       url: "/cm/v1/Create?clusterId="+data.field.clusterId,
                       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 configmapYaml.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" id="DownLoadBtn">下载</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>
    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: "/cm/v1/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: "/cm/v1/ModifyByYaml"+location.search,
                   type: "POST",
                   data: yamlBody,
                   dataType: "json",
                   success: function (resp) {
                      layer.msg(resp.msg);
                      console.log(resp);
                    }
                  });
              },
              cancel: function(index, layero){ 
                  layer.close(index);
              } 
            });
        });

        $('#DownLoadBtn').on("click",function(){
              var configmapName = getQueryString("configmapName");
              ExportRaw(configmapName+'.yaml',editor.getValue());
        });

    });
</script>

</body>
</html>

六.完整代码

6.1.控制器完整代码

6.1 configmap.go,放到controllers下

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

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

	//"github.com/tidwall/gjson"

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

type ConfigMapController struct {
	beego.Controller
}

func (this *ConfigMapController) List() {
	clusterId := this.GetString("clusterId")
	nameSpace := this.GetString("nameSpace")
	configmapName := this.GetString("configmapName")

	labels := this.GetString("labels")
	labelsKV := strings.Split(labels, ":")
	var labelsKey, labelsValue string
	if len(labelsKV) == 2 {
		labelsKey = labelsKV[0]
		labelsValue = labelsKV[1]
	}
	cmList, err := m.CmList(clusterId, nameSpace, configmapName, labelsKey, labelsValue)
	msg := "success"
	code := 0
	count := len(cmList)
	if err != nil {
		log.Println(err)
		msg = err.Error()
		code = -1
	}
	this.Data["json"] = &map[string]interface{}{"code": code, "msg": msg, "count": count, "data": &cmList}
	this.ServeJSON()
}

func (this *ConfigMapController) Create() {
	clusterId := this.GetString("clusterId")
	code := 0
	msg := "success"
	err := m.CmCreate(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 *ConfigMapController) ModifyByYaml() {
	clusterId := this.GetString("clusterId")
	code := 0
	msg := "success"
	bodyByte := []byte(strings.ReplaceAll(string(this.Ctx.Input.RequestBody), "%25", "%"))
	err := m.CmYamlModify(clusterId, bodyByte)
	if err != nil {
		code = -1
		msg = err.Error()
		log.Printf("[ERROR] configmap ModifyByYaml Fail:%s\n", err)
	}
	this.Data["json"] = &map[string]interface{}{"code": code, "msg": msg}
	this.ServeJSON()
}

func (this *ConfigMapController) Yaml() {
	clusterId := this.GetString("clusterId")
	namespace := this.GetString("nameSpace")
	configmapName := this.GetString("configmapName")

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

6.2 configmapModel.go,放到models下

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

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

	"github.com/tidwall/gjson"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	yamlutil "k8s.io/apimachinery/pkg/util/yaml"
	"sigs.k8s.io/yaml"
)

type Configmap struct {
	ConfigmapName string `json:"configmapName"`
	NameSpace     string `json:"nameSpace"`
	Labels        string `json:"labels"`
	CreateTime    string `json:"createTime"`
	Data          []Cmkv `json:"data"`
}

type Cmkv struct {
	Key   string `json:"key"`
	Value string `json:"value"`
}

func CmList(kubeconfig, namespace, configmapName string, labelsKey, labelsValue string) ([]Configmap, error) {
	clientset := common.ClientSet(kubeconfig)
	if namespace == "" {
		//namespace = corev1.NamespaceDefault
		namespace = corev1.NamespaceAll
	}

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

	cmList, err := clientset.CoreV1().ConfigMaps(namespace).List(context.TODO(), listOptions)
	if err != nil {
		log.Printf("list deployment error, err:%v\n", err)
	}

	var bbb = make([]Configmap, 0)
	for _, cm := range cmList.Items {
		//搜索
		if configmapName != "" {
			if !strings.Contains(cm.Name, configmapName) {
				continue
			}
		}
		var labelsStr string
		for kk, vv := range cm.ObjectMeta.Labels {
			labelsStr += fmt.Sprintf("%s:%s,", kk, vv)
		}
		if len(labelsStr) > 0 {
			labelsStr = labelsStr[0 : len(labelsStr)-1]
		}
		Items := &Configmap{
			ConfigmapName: cm.Name,
			NameSpace:     cm.Namespace,
			Labels:        labelsStr,
			CreateTime:    cm.CreationTimestamp.Format("2006-01-02 15:04:05"),
		}
		bbb = append(bbb, *Items)
	}
	return bbb, err
}

func CmCreate(kubeconfig string, bodys []byte) error {
	gp := gjson.ParseBytes(bodys)
	clusterId := gp.Get("clusterId").String()
	if kubeconfig == "" {
		kubeconfig = clusterId
	}
	configmapName := gp.Get("configmapName").String()
	nameSpace := gp.Get("nameSpace").String()

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

	var dataMap = make(map[string]string)
	for _, vv := range gp.Get("configmaps").Array() {
		dataMap[vv.Get("key").Str] = vv.Get("value").Str
	}

	cm := &corev1.ConfigMap{
		ObjectMeta: metav1.ObjectMeta{
			Name:      configmapName,
			Namespace: nameSpace,
			Labels:    labelsMap,
		},
		Data: dataMap,
	}

	clientset := common.ClientSet(kubeconfig)
	_, err := clientset.CoreV1().ConfigMaps(nameSpace).Create(context.TODO(), cm, metav1.CreateOptions{})
	if err != nil {
		return err
	}
	return nil
}

func CmYamlModify(kubeconfig string, yamlData []byte) error {
	data, err := yamlutil.ToJSON(yamlData)
	if err != nil {
		return err
	}
	configmap := &corev1.ConfigMap{}
	err = json.Unmarshal(data, configmap)
	if err != nil {
		return err
	}

	namespace := configmap.ObjectMeta.Namespace
	confignameName := configmap.ObjectMeta.Name
	clientset := common.ClientSet(kubeconfig)
	_, err = clientset.CoreV1().ConfigMaps(namespace).Update(context.TODO(), configmap, metav1.UpdateOptions{})
	if err != nil {
		return err
	}
	fmt.Println(namespace, confignameName)
	return err
}

func GetCmYaml(kubeconfig, namespace, configmapName string) (string, error) {
	cmClient := common.ClientSet(kubeconfig).CoreV1().ConfigMaps(namespace)
	configmap, err := cmClient.Get(context.TODO(), configmapName, metav1.GetOptions{})
	cmUnstructured, err := runtime.DefaultUnstructuredConverter.ToUnstructured(configmap)
	if err != nil {
		return "", err
	}
	yamlBytes, err := yaml.Marshal(cmUnstructured)
	if err != nil {
		return "", err
	}
	return string(yamlBytes), nil
}

七.效果图



相关推荐
闲猫1 小时前
go orm GORM
开发语言·后端·golang
丁卯4041 小时前
Go语言中使用viper绑定结构体和yaml文件信息时,标签的使用
服务器·后端·golang
Bright16683 小时前
centos9安装k8s集群
云原生·容器·kubernetes
!!!5254 小时前
华为云镜像加速器
docker·容器·华为云
xidianjiapei0015 小时前
Kubernetes的Ingress 资源是什么?
云原生·容器·kubernetes
sszdzq7 小时前
Docker
运维·docker·容器
dmy7 小时前
docker 快速构建开发环境
后端·docker·容器
土豆沒加9 小时前
K8S的Dashboard登录及验证
云原生·容器·kubernetes
终端行者10 小时前
kubernetes1.28部署mysql5.7主从同步,使用Nfs制作持久卷存储,适用于centos7/9操作系统,
数据库·容器·kubernetes
伪装成塔的小兵15 小时前
Windows使用docker部署fastgpt出现的一些问题
windows·docker·容器·oneapi·fastgpt