【k8s多集群管理平台开发实践】九、client-go实现nginx-ingress读取列表、创建ingress、读取更新yaml配置

news2024/11/28 18:56:38

文章目录

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

简介

本章节主要讲解通过client-go实现ingress的列表读取和界面创建ingress,该部分的主要是实现nginx-ingress的功能,以及ingress的yaml配置文件读取和修改,通过layui实现界面操作,其中包含控制器这部分的代码,模型这部分代码,以及前端的html代码。

一.k8s的ingress列表

1.1.controllers控制器代码

通过传递集群id、命名空间、服务名称、ingress名称来进行过滤,并调用模型代码中ingressList函数来读取列表

func (this *IngressController) List() {
	clusterId := this.GetString("clusterId")    //集群名称
	nameSpace := this.GetString("nameSpace")    //命名空间
	ingressName := this.GetString("ingressName")    //ingress名称
	serviceName := this.GetString("serviceName")    //服务名称
	labels := this.GetString("labels")
	labelsKV := strings.Split(labels, ":")
	var labelsKey, labelsValue string
	if len(labelsKV) == 2 {
		labelsKey = labelsKV[0]
		labelsValue = labelsKV[1]
	}
	//调用IngList函数
	ingressList, err := m.IngList(clusterId, nameSpace, ingressName, serviceName, labelsKey, labelsValue)
	msg := "success"
	code := 0
	count := len(ingressList)
	if err != nil {
		log.Println(err)
		msg = err.Error()
		code = -1
	}

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

1.2.models模型代码

先定义一个结构体ingress,通过传递过来的参数,在clientset.NetworkingV1().Ingresses(namespace).List(context.TODO(), listOptions)中读取ingress的信息,然后定义一个结构体数组,并循环读取ingress.Items中的数据并赋值到结构体Ingress,然后再追加到数组中var bbb = make([]Ingress, 0)

type Ingress struct {
	IngressName  string `json:"ingressName"`
	NameSpace    string `json:"nameSpace"`
	Labels       string `json:"labels"`
	IngressClass string `json:"ingressClass"`
	Rules        string `json:"rules"`
	Endpoint     string `json:"endpoint"` //内部端点
	CreateTime   string `json:"createTime"`
}

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

func IngList(kubeconfig, namespace, ingressName, serviceName string, labelsKey, labelsValue string) ([]Ingress, error) {
	clientset := common.ClientSet(kubeconfig)
	if namespace == "" {
		//namespace = corev1.NamespaceDefault
		namespace = corev1.NamespaceAll
	}
    //定义标签过滤
	var listOptions = metav1.ListOptions{}
	if labelsKey != "" && labelsValue != "" {
		listOptions = metav1.ListOptions{LabelSelector: fmt.Sprintf("%s=%s", labelsKey, labelsValue)}
	}
    //通过api读取ingress信息
	ingList, err := clientset.NetworkingV1().Ingresses(namespace).List(context.TODO(), listOptions)
	if err != nil {
		log.Printf("list service error:%v\n", err)
	}

	var bbb = make([]Ingress, 0)
	for _, ing := range ingList.Items {
		//搜索
		if ingressName != "" {
			if !strings.Contains(ing.Name, ingressName) {
				continue
			}
		}
        //提取标签
		var labelsStr string
		for kk, vv := range ing.ObjectMeta.Labels {
			labelsStr += fmt.Sprintf("%s:%s,", kk, vv)
		}
		if len(labelsStr) > 0 {
			labelsStr = labelsStr[0 : len(labelsStr)-1]
		}
		//读取ingress中的path规则
		var rulestr string
		var isNeedService bool
		for _, v1 := range ing.Spec.Rules {
			for _, v2 := range v1.HTTP.Paths {
				var portstr string
				if v2.Backend.Service.Port.Number > 0 {
					portstr = fmt.Sprintf(":%d", v2.Backend.Service.Port.Number)
				}
				rulestr += fmt.Sprintf("%s%s-->%s%s,", v1.Host, v2.Path, v2.Backend.Service.Name, portstr)

				//用serviceName来搜索
				if serviceName != "" && v2.Backend.Service.Name == serviceName {
					isNeedService = true
				}
			}
		}
		//用serviceName来搜索
		if serviceName != "" && !isNeedService {
			continue
		}

		var endpointIp string
		if len(ing.Status.LoadBalancer.Ingress) > 0 {
			endpointIp = ing.Status.LoadBalancer.Ingress[0].IP
		}
        //赋值到结构体
		Items := &Ingress{
			IngressName:  ing.Name,
			NameSpace:    ing.Namespace,
			IngressClass: ing.ObjectMeta.Annotations["kubernetes.io/ingress.class"],
			Rules:        rulestr,
			Endpoint:     endpointIp,
			Labels:       labelsStr,
			CreateTime:   ing.CreationTimestamp.Format("2006-01-02 15:04:05"),
		}
		bbb = append(bbb, *Items)
	}
	return bbb, err
}

二.创建ingress

2.1.controllers控制器代码

将前端的form 表单提交的数据this.Ctx.Input.RequestBody传递到模型函数ingressCreate来进行创建

func (this *IngressController) Create() {
	clusterId := this.GetString("clusterId")
	msg := "success"
	code := 0
	err := m.IngCreate(clusterId, this.Ctx.Input.RequestBody)
	if err != nil {
		log.Println(err)
		msg = err.Error()
		code = -1
	}
	this.Data["json"] = &map[string]interface{}{"code": code, "msg": msg}
	this.ServeJSON()
}

2.2.models模分代码

将控制器Create函数中传递过来的body数据进行json解析,并将解析出来的数据赋值到结构体中,再调用clientset.NetworkingV1().Ingresses(nameSpace).Create(context.TODO(), newIngress, metav1.CreateOptions{})来进行创建.

func IngCreate(kubeconfig string, bodys []byte) error {
    //解析json
	gp := gjson.ParseBytes(bodys)
	clusterId := gp.Get("clusterId").String()
	if kubeconfig == "" {
		kubeconfig = clusterId
	}
	ingressName := gp.Get("ingressName").String()
	nameSpace := gp.Get("nameSpace").String()
	ingressHost := gp.Get("ingressHost").String()

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

	var annotationsMap = make(map[string]string)
	annotationsMap["kubernetes.io/ingress.class"] = "nginx"
    //提取path规则并赋值
	var ingresPaths []networkingv1.HTTPIngressPath
	paths := gp.Get("paths").Array()
	for _, vv := range paths {
		var pathType networkingv1.PathType
		switch vv.Get("pathType").Str {
		case "ImplementationSpecific":
			pathType = networkingv1.PathTypeImplementationSpecific
		case "Exact":
			pathType = networkingv1.PathTypeExact
		default:
			pathType = networkingv1.PathTypePrefix
		}

		ingresPaths = append(ingresPaths, *&networkingv1.HTTPIngressPath{
			Path:     vv.Get("path").Str,
			PathType: &pathType,
			Backend: networkingv1.IngressBackend{
				Service: &networkingv1.IngressServiceBackend{
					Name: vv.Get("serviceName").Str,
					Port: networkingv1.ServiceBackendPort{
						Number: int32(vv.Get("servicePort").Int()),
					},
				},
			},
		})
	}
    //定义的ingress实例结构体
	newIngress := &networkingv1.Ingress{
		ObjectMeta: metav1.ObjectMeta{
			Name:        ingressName,
			Namespace:   nameSpace,
			Labels:      labelsMap,
			Annotations: annotationsMap,
		},
		Spec: networkingv1.IngressSpec{
			Rules: []networkingv1.IngressRule{
				{
					Host: ingressHost,
					IngressRuleValue: networkingv1.IngressRuleValue{
						HTTP: &networkingv1.HTTPIngressRuleValue{
							Paths: ingresPaths,
						},
					},
				},
			},
		},
	}

	tlsCert := gp.Get("tlsCert").String()
	if tlsCert != "" {
		var ingTls = []networkingv1.IngressTLS{
			{
				Hosts:      []string{ingressHost},
				SecretName: tlsCert,
			},
		}
		newIngress.Spec.TLS = ingTls
	}

	// 创建ingress
	clientset := common.ClientSet(kubeconfig)
	_, err := clientset.NetworkingV1().Ingresses(nameSpace).Create(context.TODO(), newIngress, metav1.CreateOptions{})
	return err
}

三.读取和更新ingress的yaml配置

3.1.controllers控制器代码

通过传递集群id、命名空间、ingress名称来读取实例信息,并调用模型代码中ingressYmal函数来读取yaml配置

//读取yaml配置
func (this *IngressController) Yaml() {
	clusterId := this.GetString("clusterId")
	namespace := this.GetString("nameSpace")
	ingressName := this.GetString("ingressName")

	yamlStr, _ := m.GetIngYaml(clusterId, namespace, ingressName)
	this.Ctx.WriteString(yamlStr)
}

//更新yaml配置
func (this *IngressController) ModifyByYaml() {
	clusterId := this.GetString("clusterId")
	//nameSpace := gp.Get("nameSpace").String()
	//log.Println(string(this.Ctx.Input.RequestBody))
	//将提交的body替换掉%分号,否则导致解析出错
	bodyByte := []byte(strings.ReplaceAll(string(this.Ctx.Input.RequestBody), "%25", "%"))
	err := m.IngYamlModify(clusterId, bodyByte)
	if err != nil {
		log.Println(err)
	}
	this.Data["json"] = &map[string]interface{}{"code": 0, "msg": "ok"}
	this.ServeJSON()
}

3.2.models模型代码

先读取ingress实例信息,然后将实例信息解析成解构,然后解构的字节数据进行yaml序列化处理,并转成字符串进行返回

func GetIngYaml(kubeconfig, namespace, ingressName string) (string, error) {
	ingClient := common.ClientSet(kubeconfig).NetworkingV1().Ingresses(namespace)
	ingress, err := ingClient.Get(context.TODO(), ingressName, metav1.GetOptions{})
	ingresseUnstructured, err := runtime.DefaultUnstructuredConverter.ToUnstructured(ingress)
	if err != nil {
		return "", err
	}
	yamlBytes, err := yaml.Marshal(ingresseUnstructured)
	if err != nil {
		return "", err
	}
	return string(yamlBytes), nil
}

func IngYamlModify(kubeconfig string, yamlData []byte) error {
//转成json格式
	data, err := yamlutil.ToJSON(yamlData)
	if err != nil {
		return err
	}
	//反序列化到service结构体实例
	ingress := &networkingv1.Ingress{}
	err = json.Unmarshal(data, ingress)
	if err != nil {
		return err
	}
   //提取出名称和命名空间
	namespace := ingress.ObjectMeta.Namespace
	ingressName := ingress.ObjectMeta.Name
	clientset := common.ClientSet(kubeconfig)
	//调用更新api进行更新
	_, err = clientset.NetworkingV1().Ingresses(namespace).Update(context.TODO(), ingress, metav1.UpdateOptions{})
	if err != nil {
		return err
	}
	fmt.Println(namespace, ingressName)
	return err
}

四.路由设置

4.1.路由设置

将以下代码放到routers/route.go中

	beego.Router("/ing/v1/List", &controllers.IngressController{}, "*:List")
	beego.Router("/ing/v1/Create", &controllers.IngressController{}, "*:Create")
	beego.Router("/ing/v1/ModifyByYaml", &controllers.IngressController{}, "*:ModifyByYaml")
	beego.Router("/ing/v1/Yaml", &controllers.IngressController{}, "*:Yaml")

五.前端代码

5.1.列表部分html代码

5.1 ingressList.html,放到views/front/page/xkube下

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>ingress列表</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>创建Ingress</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="rulesTpl">
    {{# if (d.rules != "") { }}
            {{# layui.each(d.rules.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: '/ing/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: 'ingressName',title: '名称', sort: true},
                {field: 'nameSpace',title: '命名空间', sort: true},
                {field: 'ingressClass', title: '类型', sort: true},
                {field: 'rules', width: 400,title: '规则', sort: true,templet: '#rulesTpl'},
                {field: 'endpoint', title: '端点', sort: true},
                {field: 'labels', title: '标签', sort: true,templet: '#TagTpl',hide:true},
                {field: 'createTime', title: '创建时间',hide:true},
                {title: '操作', minWidth: 300,toolbar: '#currentTableBar', align: "center"}
            ]],
            //size:'lg',
            limits: [25, 50, 100],
            limit: 25,
            page: true
        });

        /**
         * toolbar监听事件
         */
        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/ingressCreate.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/ingressYaml.html?clusterId='+clusterId+'&nameSpace='+data.nameSpace+'&ingressName='+data.ingressName,
                });
                $(window).on("resize", function () {
                    layer.full(index);
                });
                return false;
            }
        });

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

5.2 ingressCreate.html,放到views/front/page/xkube下

<!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">命名空间</label>
                <div class="layui-input-inline">
                  <select name="nameSpace" lay-filter="name_Space" lay-search="" id="name_Space">
  		            </select>
                </div>
                <label class="layui-form-label required">ingress名称</label>
                <div class="layui-input-inline">
                    <input type="text" name="ingressName" 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:500px">  
                    <input type="text" name="ingressHost" 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="ingressTpl">
                <div class="layui-form-item">
                    <label class="layui-form-label">路径</label>
                    <div class="layui-input-inline" style="width:125px">  
                        <input type="text" id="ingress_path0" name="ingress_path[]" placeholder="路径" value="/" class="layui-input">
                    </div>
                    <div class="layui-input-inline" style="width:100px">  
                        <select name="pathType[]" id="pathType0" >
                    			<option value="" selected="">匹配规则</option>
                    			<option value="Prefix">Prefix</option>
                    			<option value="Exact">Exact</option>
                    			<option value="ImplementationSpecific">ImplementationSpecific</option>
        		            </select>
                    </div>
                    <div class="layui-input-inline" style="width:100px">  
                        <input type="text" id="ingress_svcName0" name="ingress_svcName[]" placeholder="服务" lay-verify="required" lay-reqtext="不能为空" value="" class="layui-input">
                    </div>
                    <div class="layui-input-inline" style="width:80px">  
                        <input type="text" id="ingress_svcPort0" name="ingress_svcPort[]" placeholder="端口" lay-verify="required" lay-reqtext="不能为空" value="" class="layui-input">
                    </div>   
                    <div class="layui-input-inline" style="width:80px">  
                        <button class="layui-btn layui-btn-normal" id="ingressaddbtn"><i class="layui-icon layui-icon-add-circle"></i></button>
                    </div>          
                </div> 
            </div> 
            <fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;">
                <legend>SSL配置</legend>
            </fieldset>
              <div class="layui-form-item">
                  <label class="layui-form-label required">证书配置</label>
                  <div class="layui-input-inline" style="width:550px">  
                      <input type="text" id="tlsCert" name="tlsCert" placeholder="保密字典[secret]" value="" class="layui-input">
                  </div>   
              </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" style="width:80px">  
                      <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) {
      TplIndex--;
      $("#tpl-"+id).remove();
    }

    //tls删除
    var tTplIndex = 0;
    function deltTpl(id) {
      $("#ttpl-"+id).remove();
      tTplIndex--;
    }

    //ingress删除
    var iTplIndex = 0;
    function deliTpl(id) {
      iTplIndex--;
      $("#itpl-"+id).remove();
    }

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

        //选择集群
        form.on('select(cluster_Id)', function(data){
          //console.log(data.elem); //得到select原始DOM对象
          console.log(data.value); //得到被选中的值
          setCookie('clusterId',data.value,30);
          window.location.reload();
          //console.log(data.othis); //得到美化后的DOM对象
        });

        //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" style="width:80px">' +  
                      '<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;   
        });

        //ingress增加
        $('#ingressaddbtn').on("click",function(){
          iTplIndex++;
          var addTpl =
                '<div class="layui-form-item" id="itpl-'+iTplIndex+'">' +
                    '<label class="layui-form-label">路径</label>' +  
                    '<div class="layui-input-inline" style="width:125px">' +  
                        '<input type="text" name="ingress_path[]" id="ingress_path'+iTplIndex+'" placeholder="路径" value="/" class="layui-input">' +
                    '</div>' +
                    '<div class="layui-input-inline" style="width:100px">' +  
                        '<select name="pathType[]" id="pathType'+iTplIndex+'">' +  
                    			'<option value="" selected="">匹配规则</option>' +  
                    			'<option value="Prefix">Prefix</option>' +  
                    			'<option value="Exact">Exact</option>' +  
                    			'<option value="ImplementationSpecific">ImplementationSpecific</option>' +  
        		            '</select>' +  
                    '</div>' +
                    '<div class="layui-input-inline" style="width:100px">' +  
                        '<input type="text" name="ingress_svcName[]" id="ingress_svcName'+iTplIndex+'" placeholder="服务" value="" class="layui-input">' +
                    '</div>' +
                    '<div class="layui-input-inline"  style="width:80px">' +  
                        '<input type="text" name="ingress_svcPort[]" id="ingress_svcPort'+iTplIndex+'" placeholder="端口" value="" class="layui-input">' +                        
                    '</div>' +   
                    '<div class="layui-input-inline" style="width:80px">' +  
                        '<input class="layui-btn layui-btn-normal layui-bg-orange layui-icon" style="width:60px" type="button" id="newDelbtn" value="&#xe616;" οnclick="deliTpl('+iTplIndex+');" />' +
                    '</div>' +              
                '</div>' 
            $('.ingressTpl').append(addTpl);   
          form.render(); 
          return false;   
        });

        //监听提交
        form.on('submit(saveBtn)', function (data) {
                data.field.ingressName = data.field.ingressName.replace(/^\s*|\s*$/g,""); //替换空格
                data.field.ingressHost = data.field.ingressHost.replace(/^\s*|\s*$/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;
                }

                //ingress 处理
                var ingressArry = [];
                for (var i=0;i<=iTplIndex;i++) {
                  var vv = document.getElementById("ingress_path"+i).value; 
                  var tt = document.getElementById("pathType"+i).value; 
                  var ss = document.getElementById("ingress_svcName"+i).value; 
                  var pp = document.getElementById("ingress_svcPort"+i).value; 
                  if ( kk != "" && vv != "" && tt != "" && ss != "" && pp != "") {
                    ingressArry.push(
                      {
                        path:vv,
                        pathType:tt,
                        serviceName:ss,
                        servicePort:pp,
                      }
                    )
                  }
                }
                if (ingressArry.length > 0) {
                  data.field.paths = ingressArry;
                }
                    
					      console.log(data.field);
			          layer.confirm('确定添加?', {icon: 3, title:'提示',yes: function(index){
                     var index2 = layer.load(0, {shade: false});
                     layer.msg('稍等片刻');
                     $.ajax({
                       url: "/ing/v1/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 ingressYaml.html,放到views/front/page/xkube下

<!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: "/ing/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: "/ing/v1/ModifyByYaml"+location.search,
                   type: "POST",
                   data: yamlBody,
                   dataType: "json",
                   success: function (resp) {
                      layer.msg(resp.msg);
                      console.log(resp);
                    }
                  });
                  //console.log(editor.getValue());
                  //layer.msg('ok');
              },
              cancel: function(index, layero){ 
                  layer.close(index);
              } 
            });
        });

        $('#DownLoadBtn').on("click",function(){
              var ingressName = getQueryString("ingressName");
              ExportRaw(ingressName+'.yaml',editor.getValue());
        });
    });
</script>

</body>
</html>

六.完整代码

6.1.控制器ingress.go的完整代码

6.1 ingress.go,放到contrillers下

// ingress.go
package controllers

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

	"github.com/tidwall/gjson"

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

type IngressController struct {
	beego.Controller
}

func (this *IngressController) List() {
	clusterId := this.GetString("clusterId")
	nameSpace := this.GetString("nameSpace")
	ingressName := this.GetString("ingressName")
	serviceName := this.GetString("serviceName")
	labels := this.GetString("labels")
	if this.Ctx.Input.Method() == "POST" {
		gp := gjson.ParseBytes(this.Ctx.Input.RequestBody)
		ingressName = gp.Get("ingressName").String()
		nameSpace = gp.Get("nameSpace").String()
	}
	labelsKV := strings.Split(labels, ":")
	var labelsKey, labelsValue string
	if len(labelsKV) == 2 {
		labelsKey = labelsKV[0]
		labelsValue = labelsKV[1]
	}
	ingressList, err := m.IngList(clusterId, nameSpace, ingressName, serviceName, labelsKey, labelsValue)
	msg := "success"
	code := 0
	count := len(ingressList)
	if err != nil {
		log.Println(err)
		msg = err.Error()
		code = -1
	}

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

func (this *IngressController) Create() {
	clusterId := this.GetString("clusterId")
	msg := "success"
	code := 0
	err := m.IngCreate(clusterId, this.Ctx.Input.RequestBody)
	if err != nil {
		log.Println(err)
		msg = err.Error()
		code = -1
	}
	this.Data["json"] = &map[string]interface{}{"code": code, "msg": msg}
	this.ServeJSON()
}

func (this *IngressController) ModifyByYaml() {
	clusterId := this.GetString("clusterId")
	//nameSpace := gp.Get("nameSpace").String()
	log.Println(string(this.Ctx.Input.RequestBody))
	bodyByte := []byte(strings.ReplaceAll(string(this.Ctx.Input.RequestBody), "%25", "%"))
	err := m.IngYamlModify(clusterId, bodyByte)
	if err != nil {
		log.Println(err)
	}
	this.Data["json"] = &map[string]interface{}{"code": 0, "msg": "ok"}
	this.ServeJSON()
}

func (this *IngressController) Yaml() {
	clusterId := this.GetString("clusterId")
	namespace := this.GetString("nameSpace")
	ingressName := this.GetString("ingressName")

	yamlStr, _ := m.GetIngYaml(clusterId, namespace, ingressName)
	this.Ctx.WriteString(yamlStr)
}

6.2.模型ingressModel.go的完整代码

6.2 ingressModel.go,放到models下

// ingressModel.go
package models

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

	"github.com/tidwall/gjson"

	corev1 "k8s.io/api/core/v1"
	networkingv1 "k8s.io/api/networking/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 Ingress struct {
	IngressName  string `json:"ingressName"`
	NameSpace    string `json:"nameSpace"`
	Labels       string `json:"labels"`
	IngressClass string `json:"ingressClass"`
	Rules        string `json:"rules"`
	Endpoint     string `json:"endpoint"` //内部端点
	CreateTime   string `json:"createTime"`
}

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

func IngList(kubeconfig, namespace, ingressName, serviceName string, labelsKey, labelsValue string) ([]Ingress, error) {
	clientset := common.ClientSet(kubeconfig)
	if namespace == "" {
		//namespace = corev1.NamespaceDefault
		namespace = corev1.NamespaceAll
	}

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

	ingList, err := clientset.NetworkingV1().Ingresses(namespace).List(context.TODO(), listOptions)
	if err != nil {
		log.Printf("list service error:%v\n", err)
	}

	var bbb = make([]Ingress, 0)
	for _, ing := range ingList.Items {
		//搜索
		if ingressName != "" {
			if !strings.Contains(ing.Name, ingressName) {
				continue
			}
		}

		var labelsStr string
		for kk, vv := range ing.ObjectMeta.Labels {
			labelsStr += fmt.Sprintf("%s:%s,", kk, vv)
		}
		if len(labelsStr) > 0 {
			labelsStr = labelsStr[0 : len(labelsStr)-1]
		}
		var rulestr string
		var isNeedService bool
		for _, v1 := range ing.Spec.Rules {
			for _, v2 := range v1.HTTP.Paths {
				var portstr string
				if v2.Backend.Service.Port.Number > 0 {
					portstr = fmt.Sprintf(":%d", v2.Backend.Service.Port.Number)
				}
				rulestr += fmt.Sprintf("%s%s-->%s%s,", v1.Host, v2.Path, v2.Backend.Service.Name, portstr)

				//用serviceName来搜索
				if serviceName != "" && v2.Backend.Service.Name == serviceName {
					isNeedService = true
				}
			}
		}
		//用serviceName来搜索
		if serviceName != "" && !isNeedService {
			continue
		}

		var endpointIp string
		if len(ing.Status.LoadBalancer.Ingress) > 0 {
			endpointIp = ing.Status.LoadBalancer.Ingress[0].IP
		}

		Items := &Ingress{
			IngressName:  ing.Name,
			NameSpace:    ing.Namespace,
			IngressClass: ing.ObjectMeta.Annotations["kubernetes.io/ingress.class"],
			Rules:        rulestr,
			Endpoint:     endpointIp,
			Labels:       labelsStr,
			CreateTime:   ing.CreationTimestamp.Format("2006-01-02 15:04:05"),
		}
		bbb = append(bbb, *Items)
	}
	return bbb, err
}

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

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

	var annotationsMap = make(map[string]string)
	annotationsMap["kubernetes.io/ingress.class"] = "nginx"

	var ingresPaths []networkingv1.HTTPIngressPath
	paths := gp.Get("paths").Array()
	for _, vv := range paths {
		var pathType networkingv1.PathType
		switch vv.Get("pathType").Str {
		case "ImplementationSpecific":
			pathType = networkingv1.PathTypeImplementationSpecific
		case "Exact":
			pathType = networkingv1.PathTypeExact
		default:
			pathType = networkingv1.PathTypePrefix
		}

		ingresPaths = append(ingresPaths, *&networkingv1.HTTPIngressPath{
			Path:     vv.Get("path").Str,
			PathType: &pathType,
			Backend: networkingv1.IngressBackend{
				Service: &networkingv1.IngressServiceBackend{
					Name: vv.Get("serviceName").Str,
					Port: networkingv1.ServiceBackendPort{
						Number: int32(vv.Get("servicePort").Int()),
					},
				},
			},
		})
	}

	newIngress := &networkingv1.Ingress{
		ObjectMeta: metav1.ObjectMeta{
			Name:        ingressName,
			Namespace:   nameSpace,
			Labels:      labelsMap,
			Annotations: annotationsMap,
		},
		Spec: networkingv1.IngressSpec{
			Rules: []networkingv1.IngressRule{
				{
					Host: ingressHost,
					IngressRuleValue: networkingv1.IngressRuleValue{
						HTTP: &networkingv1.HTTPIngressRuleValue{
							Paths: ingresPaths,
						},
					},
				},
			},
		},
	}

	tlsCert := gp.Get("tlsCert").String()
	if tlsCert != "" {
		var ingTls = []networkingv1.IngressTLS{
			{
				Hosts:      []string{ingressHost},
				SecretName: tlsCert,
			},
		}
		newIngress.Spec.TLS = ingTls
	}

	// 创建Service
	clientset := common.ClientSet(kubeconfig)
	_, err := clientset.NetworkingV1().Ingresses(nameSpace).Create(context.TODO(), newIngress, metav1.CreateOptions{})
	return err
}

func IngYamlModify(kubeconfig string, yamlData []byte) error {
	data, err := yamlutil.ToJSON(yamlData)
	if err != nil {
		return err
	}
	ingress := &networkingv1.Ingress{}
	err = json.Unmarshal(data, ingress)
	if err != nil {
		return err
	}

	namespace := ingress.ObjectMeta.Namespace
	ingressName := ingress.ObjectMeta.Name
	clientset := common.ClientSet(kubeconfig)
	_, err = clientset.NetworkingV1().Ingresses(namespace).Update(context.TODO(), ingress, metav1.UpdateOptions{})
	if err != nil {
		return err
	}
	fmt.Println(namespace, ingressName)
	return err
}

func GetIngYaml(kubeconfig, namespace, ingressName string) (string, error) {
	ingClient := common.ClientSet(kubeconfig).NetworkingV1().Ingresses(namespace)
	ingress, err := ingClient.Get(context.TODO(), ingressName, metav1.GetOptions{})
	ingresseUnstructured, err := runtime.DefaultUnstructuredConverter.ToUnstructured(ingress)
	if err != nil {
		return "", err
	}
	yamlBytes, err := yaml.Marshal(ingresseUnstructured)
	if err != nil {
		return "", err
	}
	return string(yamlBytes), nil
}

七.效果图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1670671.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

低血压怎么办?低血压患者应该如何调理?

点击文末领取揿针的视频教程跟直播讲解 低血压在生活中也是一种常见的问题&#xff0c;低血压的朋友常有头晕眼黑、冒冷汗等症状&#xff0c;对工作学习产生了一定的影响。 什么是低血压呢&#xff1f; 低血压是指体循环动脉压力低于正常的状态。即血压低于正常水平。 ​一般…

LearnOpenGL(十四)之模型加载

Model类的结构&#xff1a; class Model {public:/* 函数 */Model(char *path){loadModel(path);}void Draw(Shader shader); private:/* 模型数据 */vector<Mesh> meshes;string directory;/* 函数 */void loadModel(string path);void processNode(aiNode …

初识指针(5)<C语言>

前言 在前几篇文章中&#xff0c;已经介绍了指针一些基本概念、用途和一些不同类型的指针&#xff0c;下文将介绍某些指针类型的运用。本文主要介绍函数指针数组、转移表&#xff08;函数指针的用途&#xff09;、回调函数、qsort使用举例等。 函数指针数组 函数指针数组即每个…

京东h5st4.7逆向分析

声明 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;不提供完整代码&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 本文章未…

信息量、熵、KL散度、交叉熵概念理解

信息量、熵、KL散度、交叉熵概念理解 (1) 信息量 信息量是对事件的不确定性的度量。 假设我们听到了两件事&#xff0c;分别如下&#xff1a;事件A&#xff1a;巴西队进入了世界杯决赛圈。 事件B&#xff1a;中国队进入了世界杯决赛圈。仅凭直觉来说&#xff0c;显而易见事件…

SpringAMQP-消息转换器

这边发送消息接收消息默认是jdk的序列化方式&#xff0c;发送到服务器是以字节码的形式&#xff0c;我们看不懂也很占内存&#xff0c;所以我们要手动设置一下 我这边设置成json的序列化方式&#xff0c;注意发送方和接收方的序列化方式要保持一致 不然回报错。 引入依赖&#…

STM32_HAL_TIM_1介绍

1.F1的定时器类型&#xff08;高的拥有低级的全部功能&#xff09; 高级定时器&#xff08;TIM1和TIM8&#xff09;&#xff1a; 16位自动重装载计数器。支持多种工作模式&#xff0c;包括中心对齐模式、边沿对齐模式等。可以产生7个独立的通道&#xff0c;用于PWM、输出比较、…

Cosmo Bunny Girl

可爱的宇宙兔女郎的3D模型。用额外的骨骼装配到Humanoid上,Apple混合了形状。完全模块化,包括不带衣服的身体。 技术细节 内置,包括URP和HDRP PDF。还包括关于如何启用URP和HDRP的说明。 LOD 0:面:40076,tris 76694,verts 44783 装配了Humanoid。添加到Humanoid中的其他…

测试用例编写规范

1.1目的 统一测试用例编写的规范&#xff0c;为测试设计人员提供测试用例编写的指导&#xff0c;提高编写的测试用例的可读性&#xff0c;可执行性、合理性。为测试执行人员更好执行测试&#xff0c;提高测试效率&#xff0c;最终提高公司整个产品的质量。 1.2使用范围 适用…

数字人实训室助推元宇宙人才培养

如今&#xff0c;全身动作捕捉设备已经大量应用在影视、动画、游戏领域&#xff0c;在热门的元宇宙内容领域中&#xff0c;全身动作捕捉设备逐步发挥着重要的作用&#xff0c;在包括体育训练、数字娱乐虚拟偶像、虚拟主持人、非物质文化遗产保护等等场景&#xff0c;数字人实训…

第5章 处理GET请求参数

1 什么是GET请求参数 表单GET请求参数是指在HTML表单中通过GET方法提交表单数据时所附带的参数信息。在HTML表单中&#xff0c;可以通过表单元素的name属性来指定表单字段的名称&#xff0c;通过表单元素的value属性来指定表单字段的值。当用户提交表单时&#xff0c;浏览器会将…

【数据结构】有关栈和队列相互转换问题

文章目录 用队列实现栈思路实现 用栈实现队列思路实现 用队列实现栈 Leetcode-225 用队列实现栈 思路 建立队列的基本结构并实现队列的基本操作 这部分这里就不多说了&#xff0c;需要的可以看笔者的另一篇博客 【数据结构】队列详解(Queue) 就简单带过一下需要实现的功能 …

金融业开源软件应用 评估规范

金融业开源软件应用 评估规范 1 范围 本文件规定了金融机构在应用开源软件时的评估要求&#xff0c;对开源软件的引入、维护和退出提出了实现 要求、评估方法和判定准则。 本文件适用于金融机构对应用的开源软件进行评估。 2 规范性引用文件 下列文件中的内容通过文中的规范…

数据科学:使用Optuna进行特征选择

大家好&#xff0c;特征选择是机器学习流程中的关键步骤&#xff0c;在实践中通常有大量的变量可用作模型的预测变量&#xff0c;但其中只有少数与目标相关。特征选择包括找到这些特征的子集&#xff0c;主要用于改善泛化能力、助力推断预测、提高训练效率。有许多技术可用于执…

【kettle012】kettle访问FTP服务器文件并处理数据至PostgreSQL(已更新)

1.一直以来想写下基于kettle的系列文章,作为较火的数据ETL工具,也是日常项目开发中常用的一款工具,最近刚好挤时间梳理、总结下这块儿的知识体系。 2.熟悉、梳理、总结下FTP服务器相关知识体系 3.欢迎批评指正,跪谢一键三连! kettle访问FTP服务器文件并处理数据至PostgreS…

【Unity UI系统介绍】

Unity UI系统介绍 想了解更多游戏开发知识,可以扫描下方二维码,免费领取游戏开发4天训练营课程 Unity UI 是 Unity 引擎中的一套用户界面&#xff08;UI&#xff09;系统&#xff0c;允许开发者创建和管理游戏的用户界面。 Canvas&#xff1a;Canvas 是 Unity UI 的核心组件…

如何3分钟快速训练一个属于自己的(暴躁老哥、猫娘)AI大模型?(弱智吧经典问题为例)

首先我们进入谷歌Gemini&#xff1a;Gemini - Google DeepMind 然后我们进入官网看见左边的几个选项 选择Create new prompt创建新的提示 选择结构化提示 点击action可以增加列&#xff0c;也就是设置更多回答或者选项 那么那个温度&#xff08;Temperature&#xff09;是什么…

二、安装、使用Grafana

目录 一、安装Grafana 二、使用grafana 一、安装Grafana 官网&#xff1a;https://grafana.com/ 账号&#xff1a;admin 密码&#xff1a;xxxxxx [rootrabbitmq_2 prometheus]# [rootrabbitmq_2 prometheus]# wget https://dl.grafana.com/enterprise/release/grafana-enter…

redis安装与群集

项目需求&#xff1a; 1.安装redis 2.测试redis性能&#xff0c;100个并发连接&#xff0c;100000个请求测试 3.在当前数据库下创建键值对 a11,a22,a33,a44&#xff0c;a55 4.查看键值对 5.将a1改名为a11,将a2删除 5.将a3移动到1号数据库 6搭建redis集群(可选) 测试环境…

第六十节 Java设计模式 - 过滤器/标准模式

Java设计模式 - 过滤器/标准模式 过滤器模式使用不同的条件过滤对象。 这些标准可以通过逻辑操作链接在一起。 过滤器模式是一种结构型模式。 例子 import java.util.List; import java.util.ArrayList;class Employee {private String name;private String gender;private…