【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
}

七.效果图



相关推荐
yuguo.im2 小时前
Docker 两大基石:Namespace 和 Cgroups
运维·docker·容器
会飞的土拨鼠呀2 小时前
docker部署 outline(栗子云笔记)
笔记·docker·容器
没有bug.的程序员3 小时前
高频IO服务优化实战指南
java·jvm·spring·容器
lisanmengmeng4 小时前
docker 方式安装部署禅道zentao(五)
运维·docker·容器
露临霜6 小时前
Docker安装nginx
nginx·docker·容器
半桶水专家6 小时前
GORM 结构体字段标签(Struct Tags)详解
golang·go·gorm
GokuCode6 小时前
【GO高级编程】05.类的扩展与复用
golang·embedding·xcode
Tony Bai7 小时前
Jepsen 报告震动 Go 社区:NATS JetStream 会丢失已确认写入
开发语言·后端·golang
bing.shao7 小时前
Golang 之 defer 延迟函数
开发语言·后端·golang
penngo8 小时前
Golang使用Fyne开发桌面应用
开发语言·后端·golang