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

时间:2024-05-09 09:33:21

文章目录

        • 简介
      • 一.configmap的列表实现
        • 1.1.controllers控制器代码
        • 1.2.models模型代码
      • 二.界面创建configmap
        • 2.1.controllers控制器代码
        • 2.2.models模分代码
      • 三.读取configmap的yaml配置并更新
        • 3.1.controllers控制器代码
        • 3.2.models模型代码
      • 四.路由设置
        • 4.1.路由设置
      • 五.前端代码
        • 5.1.列表部分html代码
        • 5.2.创建表单html代码
        • 5.3.显示yaml配置的html代码
      • 六.完整代码
        • 6.1.控制器完整代码
        • 6.2.模型完整代码
      • 七.效果图

简介

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

一.configmap的列表实现

1.1.controllers控制器代码

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

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)

//定义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函数处理。

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进行创建。

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()。

//读取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进行更新

//获取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中

	//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

<!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

<!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">