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

文章目录

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

简介

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

一.k8s的service列表

1.1.controllers控制器代码

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

func (this *SvcController) List() {clusterId := this.GetString("clusterId")    //集群名称nameSpace := this.GetString("nameSpace")    //命名空间serviceName := this.GetString("serviceName")    //服务名称labels := this.GetString("labels")  //标签:labels=key:valuelabelsKV := strings.Split(labels, ":")var labelsKey, labelsValue stringif len(labelsKV) == 2 {labelsKey = labelsKV[0]labelsValue = labelsKV[1]}//根据传递的参数进行过滤返回服务列表svcList, err := m.SvcList(clusterId, nameSpace, serviceName, labelsKey, labelsValue)msg := "success"code := 0count := len(svcList)if err != nil {log.Println(err)msg = err.Error()code = -1}this.Data["json"] = &map[string]interface{}{"code": code, "msg": msg, "count": count, "data": &svcList}this.ServeJSON()
}
1.2.models模型代码

先定义一个结构体Service,通过传递过来的参数,在svcList.Items中循环读取service的信息,然后赋值结构体Service,并追加到定义一个结构体数组var bbb = make([]Service, 0)

type Service struct {ServiceName string `json:"serviceName"` //服务名称NameSpace   string `json:"nameSpace"`   //命名空间Labels      string `json:"labels"`  //标签SvcType     string `json:"svcType"` //service的类型SvcIp       string `json:"svcIp"`   //服务IPSvcPort     string `json:"svcPort"`LanEndpoint string `json:"lanEndpoint"` //内部端点WanEndpoint string `json:"wanEndpoint"` //外部端点CreateTime  string `json:"createTime"`
}type ServicePort struct {PortName   string `json:"portName"`SvcPort    int32  `json:"svcPort"`TargetPort int32  `json:"targetPort"`
}func SvcList(kubeconfig, namespace, serviceName string, labelsKey, labelsValue string) ([]Service, error) {//获取链接clientset := common.ClientSet(kubeconfig)if namespace == "" {    //判断namespace是否为空,空则读取全部命名空间namespace = corev1.NamespaceAll}//定义标签过滤条件var listOptions = metav1.ListOptions{}if labelsKey != "" && labelsValue != "" {listOptions = metav1.ListOptions{LabelSelector: fmt.Sprintf("%s=%s", labelsKey, labelsValue)}}//读取service信息svcList, err := clientset.CoreV1().Services(namespace).List(context.TODO(), listOptions)if err != nil {log.Printf("list service error:%v\n", err)}//fmt.Println("svc count:", len(svcList.Items))//循环items并追加到结构体数组bbb上var bbb = make([]Service, 0)for _, svc := range svcList.Items {//搜索if serviceName != "" {if !strings.Contains(svc.Name, serviceName) {continue}}//提取标签var labelsStr stringfor kk, vv := range svc.ObjectMeta.Labels {labelsStr += fmt.Sprintf("%s:%s,", kk, vv)}if len(labelsStr) > 0 {labelsStr = labelsStr[0 : len(labelsStr)-1]}//提取所有映射的端口var wanEndPoint stringsvcType := fmt.Sprintf("%s", svc.Spec.Type)if svcType == "LoadBalancer" {wanEndPoint = fmt.Sprintf("%s:%d", svc.Status.LoadBalancer.Ingress[0].IP, svc.Spec.Ports[0].Port)}lanEndPoint := ""var svcPort stringif len(svc.Spec.Ports) > 0 {vsapp := svc.Spec.Selector["app"]if vsapp == "" {vsapp = svc.Spec.Selector["k8s-app"]}for _, vv := range svc.Spec.Ports {//lanEndPoint += fmt.Sprintf("%s:%s:%s\n", svc.Spec.Selector["app"], svc.Spec.Ports[0].TargetPort.StrVal, svc.Spec.Ports[0].Protocol)//svcPort += fmt.Sprintf("%s:", svc.Spec.Ports[0].Port)var vport stringif vv.TargetPort.Type == 0 {vport = fmt.Sprintf("%d", vv.TargetPort.IntVal)} else {vport = fmt.Sprintf("%s", vv.TargetPort.StrVal)}lanEndPoint += fmt.Sprintf("%s:%s:%s,", vsapp, vport, vv.Protocol)svcPort += fmt.Sprintf("%d,", vv.Port)}if len(svcPort) > 0 {svcPort = svcPort[0 : len(svcPort)-1]}lanEndPoint = lanEndPoint[0 : len(lanEndPoint)-1]}//赋值到结构体Items := &Service{ServiceName: svc.Name,NameSpace:   svc.Namespace,SvcType:     svcType,SvcIp:       svc.Spec.ClusterIP,Labels:      labelsStr,SvcPort:     svcPort,LanEndpoint: lanEndPoint,WanEndpoint: wanEndPoint,CreateTime:  svc.CreationTimestamp.Format("2006-01-02 15:04:05"),}//追加到数组bbbbbb = append(bbb, *Items)}return bbb, err
}

二.创建service

2.1.controllers控制器代码

将前端的form 表单提交的json数据传递到模型函数serviceCreate来进行解析

func (this *SvcController) Create() {clusterId := this.GetString("clusterId") //集群IDcode := 0msg := "success"//log.Println(string(this.Ctx.Input.RequestBody))//将传递过来的json 二进制 body直接传递到模型中的函数SvcCreate处理err := m.SvcCreate(clusterId, this.Ctx.Input.RequestBody)if err != nil {code = -1msg = 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解析,创建一个service实例结构体svcInstance := &corev1.Service,并将解析json出来的数据赋值到结构体中,再调用clientset.CoreV1().Services(nameSpace).Create来进行创建.

func SvcCreate(kubeconfig string, bodys []byte) error {//解析json并提取数据gp := gjson.ParseBytes(bodys)clusterId := gp.Get("clusterId").String()if kubeconfig == "" {kubeconfig = clusterId}serviceName := gp.Get("serviceName").String()   //服务名称nameSpace := gp.Get("nameSpace").String()   //命名空间svcType := gp.Get("svcType").String()   //服务类型deployName := gp.Get("deployName").String() //关联的负载名称isHeadless := gp.Get("isHeadless").Str  //是否是无头服务var labelsMap = make(map[string]string) //提取标签labelsMap["app"] = serviceNamefor _, vv := range gp.Get("lables").Array() {labelsMap[vv.Get("key").Str] = vv.Get("value").Str}selectApp := serviceName    //默认将service的名称和deployment的名称一致,deployName不为空时,按照deployName的名称创建if deployName != "" {selectApp = deployName}var serviceType corev1.ServiceType  //赋值服务类型switch svcType {case "NodePort":serviceType = corev1.ServiceTypeNodePortcase "LoadBalancer":serviceType = corev1.ServiceTypeLoadBalancerdefault:serviceType = corev1.ServiceTypeClusterIP}if isHeadless == "on" { //判断是否是无头服务serviceType = corev1.ServiceTypeClusterIP}//将json解析的数据赋值到service实例结构体上svcInstance := &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name:      serviceName,Namespace: nameSpace,Labels:    labelsMap,},Spec: corev1.ServiceSpec{Selector: map[string]string{"app": selectApp,},Type: serviceType,},}if isHeadless == "on" {svcInstance.Spec.ClusterIP = corev1.ClusterIPNone}ports := gp.Get("ports").Array()var svcPorts = make([]corev1.ServicePort, 0)for _, vv := range ports {var svcProtocol corev1.Protocolif vv.Get("protocol").Str == "UDP" {svcProtocol = corev1.ProtocolUDP} else {svcProtocol = corev1.ProtocolTCP}svcPort := &corev1.ServicePort{Name:       vv.Get("portName").Str,Port:       int32(vv.Get("svcPort").Int()),Protocol:   svcProtocol,TargetPort: intstr.FromInt32(int32(vv.Get("targetPort").Int())),}svcPorts = append(svcPorts, *svcPort)}svcInstance.Spec.Ports = svcPorts//根据传递的集群ID进行创建serviceclientset := common.ClientSet(kubeconfig)_, err := clientset.CoreV1().Services(nameSpace).Create(context.TODO(), svcInstance, metav1.CreateOptions{})if err != nil {return err}return nil
}

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

3.1.controllers控制器代码

通过传递集群id、命名空间、服务名称来进行读取service,并调用模型代码中GetSvcYaml函数来读取yaml配置

//读取yaml配置
func (this *SvcController) Yaml() {clusterId := this.GetString("clusterId")nameSpace := this.GetString("nameSpace")serviceName := this.GetString("serviceName")yamlStr, _ := m.GetSvcYaml(clusterId, nameSpace, serviceName)this.Ctx.WriteString(yamlStr)
}
//更新yaml配置
func (this *SvcController) 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.SvcYamlModify(clusterId, bodyByte)if err != nil {log.Println(err)}this.Data["json"] = &map[string]interface{}{"code": 0, "msg": "ok"}this.ServeJSON()
}
3.2.models模型代码

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

//读取yaml配置
func GetSvcYaml(kubeconfig, namespace, serviceName string) (string, error) {//先通过传递的信息读取service实例servicesClient := common.ClientSet(kubeconfig).CoreV1().Services(namespace)service, err := servicesClient.Get(context.TODO(), serviceName, metav1.GetOptions{})//将读取的service进行解构serviceUnstructured, err := runtime.DefaultUnstructuredConverter.ToUnstructured(service)if err != nil {return "", err}//将结构的service数据进行序列号成yaml的bytesyamlBytes, err := yaml.Marshal(serviceUnstructured)if err != nil {return "", err}//以字符串的形式返回return string(yamlBytes), nil
}//更新yaml配置
func SvcYamlModify(kubeconfig string, yamlData []byte) error {//转成json格式data, err := yamlutil.ToJSON(yamlData)if err != nil {return err}//反序列化到service结构体实例service := &corev1.Service{}err = json.Unmarshal(data, service)if err != nil {return err}//提取出名称和命名空间namespace := service.ObjectMeta.NamespaceserviceName := service.ObjectMeta.Name//调用更新api进行更新clientset := common.ClientSet(kubeconfig)_, err = clientset.CoreV1().Services(namespace).Update(context.TODO(), service, metav1.UpdateOptions{})if err != nil {return err}fmt.Println(namespace, serviceName)return err
}

四.路由设置

4.1.路由设置

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

beego.Router("/svc/v1/List", &controllers.SvcController{}, "*:List")    //读取service列表beego.Router("/svc/v1/Create", &controllers.SvcController{}, "*:Create") //创建servicebeego.Router("/svc/v1/ModifyByYaml", &controllers.SvcController{}, "*:ModifyByYaml") //更新yaml配置beego.Router("/svc/v1/Yaml", &controllers.SvcController{}, "*:Yaml") //读取yaml配置

五.前端代码

5.1.列表部分html代码

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

<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>service列表</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:ellipsis;white-space: normal;}.layui-table-cell .layui-table-tool-panel li {word-break: break-word;}
</style>
</head>
<body>
<div class="layuimini-container"><div class="layuimini-main"><script type="text/html" id="toolbarDemo"><div class="layui-btn-container"><button class="layui-btn layui-btn-normal layui-btn-sm data-add-btn" lay-event="create"><i class="layui-icon">&#xe61f;</i>创建service</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="ipPortTpl"><span>{{ d.svcIp }}:{{ d.svcPort }}</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: '/svc/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: 'serviceName',title: '名称', sort: true},{field: 'nameSpace',title: '命名空间', sort: true},{field: 'svcType', title: '类型', sort: true},{field: 'svcIp', title: 'IP', sort: true,hide:true},{field: 'svcPort', title: '端口', sort: true,hide:true},{field: '', width:150,title: 'ip端口', sort: true,templet: '#ipPortTpl'},{field: 'lanEndpoint',title: '内部端点', sort: true},{field: 'wanEndpoint', title: '外部端点', sort: true,hide:true},{field: 'labels', title: '标签', sort: true,templet: '#TagTpl'},{field: 'createTime', title: '创建时间'},{title: '操作', minWidth: 250, 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/serviceCreate.html?v=1',//end: function(){//	window.parent.location.reload();//关闭open打开的页面时,刷新父页面//}});$(window).on("resize", function () {layer.full(index);});} });table.on('tool(currentTableFilter)', function (obj) {var data = obj.data;if (obj.event === 'viewYaml') {var index = layer.open({title: 'yaml',type: 2,shade: 0.2,maxmin:true,shadeClose: true,area: ['45%', '92%'],content: '/page/xkube/serviceYaml.html?clusterId='+clusterId+'&nameSpace='+data.nameSpace+'&serviceName='+data.serviceName,});$(window).on("resize", function () {layer.full(index);});return false;}});});
</script></body>
</html>
5.2.创建表单html代码

5.2 serviceCreate.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><label class="layui-form-label">命名空间</label><div class="layui-input-inline"><select name="nameSpace" lay-filter="name_Space" lay-search="" id="name_Space"></select></div></div></div><div class="layui-form-item"><label class="layui-form-label required">svc名称</label><div class="layui-input-inline"><input type="text" name="serviceName" placeholder="不能为空" value="" class="layui-input"></div><label class="layui-form-label">关联</label><div class="layui-input-inline"><input type="text" name="deployName" placeholder="关联的deploy或sts" value="" class="layui-input"></div></div><div class="layui-form-item"><label class="layui-form-label">svc类型</label><div class="layui-input-inline"><select name="svcType" lay-filter="svcType" lay-search="" id="svcType"><option value="ClusterIp" selected="">ClusterIp</option><option value="NodePort">NodePort</option><option value="LoadBalancer">LoadBalancer</option></select></div><div id="isHeadlessTpl"><div class="layui-input-inline"><input type="checkbox" name="isHeadless" lay-skin="primary" lay-filter="isHeadless" title="实例间服务发现(Headless Service)">   </div>             </div></div><div class="serviceTpl"><div class="layui-form-item"><label class="layui-form-label">svc端口</label><div class="layui-input-inline" style="width:120px">  <input type="text" id="svc_portsName0" name="svc_portsName[]" placeholder="名称" value="" class="layui-input"></div><div class="layui-input-inline" style="width:120px">  <input type="text" id="svc_port0" name="svc_port[]" placeholder="服务端口" value="" class="layui-input"></div><div class="layui-input-inline" style="width:120px">  <input type="text" id="svc_targetPort0" name="svc_targetPort[]" placeholder="容器端口" value="" class="layui-input"></div><div class="layui-input-inline" style="width:70px">  <select id="svc_protocol0" name="svc_protocol[]"><option value="TCP" selected="">TCP</option><option value="UDP">UDP</option></select></div><div class="layui-input-inline" style="width:80px">  <button class="layui-btn layui-btn-normal" id="svcaddbtn"><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>标签与注解</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><!--<div class="annotationTpl"><div class="layui-form-item"><label class="layui-form-label">注解</label><div class="layui-input-inline" style="width:240px">  <input type="text" id="labels_key0" name="labels_key[]" placeholder="key" value="" class="layui-input"></div><div class="layui-input-inline" style="width:240px">  <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="annotateaddbtn"><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();}//服务删除var sTplIndex = 0;function delsTpl(id) {sTplIndex--;$("#stpl-"+id).remove();}layui.use(['form'], function () {var form = layui.form,layer = layui.layer,$ = layui.$;$("#isHeadlessTpl").show();form.on('select(svcType)', function (data) { if (data.value == "ClusterIp") {$("#isHeadlessTpl").show();}else{$("#isHeadlessTpl").hide();}});//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;   });//service增加$('#svcaddbtn').on("click",function(){sTplIndex++;var addTpl ='<div class="layui-form-item" id="stpl-'+sTplIndex+'">' +'<label class="layui-form-label">svc端口</label>' +'<div class="layui-input-inline" style="width:120px">' +'<input type="text" name="svc_portsName[]" id="svc_portsName'+sTplIndex+'" placeholder="名称" value="" class="layui-input">' +'</div>' +    '<div class="layui-input-inline" style="width:120px">' +  '<input type="text" name="svc_port[]" id="svc_port'+sTplIndex+'" placeholder="服务端口" value="" class="layui-input">' +'</div>' +'<div class="layui-input-inline" style="width:120px">' +  '<input type="text" name="svc_targetPort[]" id="svc_targetPort'+sTplIndex+'" placeholder="容器端口" value="" class="layui-input">' +'</div>' +'<div class="layui-input-inline" style="width:70px">' +  '<select name="svc_protocol[]" id="svc_protocol'+sTplIndex+'" >' +'<option value="TCP" selected="">TCP</option>' +'<option value="UDP">UDP</option>' +'</select>' +'</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" value="&#xe616;" οnclick="delsTpl('+sTplIndex+');" />' +'</div>' +                '</div>'$('.serviceTpl').append(addTpl);   form.render(); return false;   });//监听提交form.on('submit(saveBtn)', function (data) {data.field.serviceName = data.field.serviceName.replace(/^\s*|\s*$/g,""); //替换空格if (data.field.svcType != "ClusterIp") {delete data.field.isHeadless;}         //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;}//service 处理var svcArry = [];for (var i=0;i<=sTplIndex;i++) {var kk = document.getElementById("svc_portsName"+i).value;var vv = document.getElementById("svc_port"+i).value; var ss = document.getElementById("svc_targetPort"+i).value; var tt = document.getElementById("svc_protocol"+i).value;if ( kk != "" && vv != "" && ss != "") {svcArry.push({portName:kk,svcPort:vv,targetPort:ss,protocol:tt,})}}if (svcArry.length > 0) {data.field.ports = svcArry;}console.log(data.field);layer.confirm('确定添加?', {icon: 3, title:'提示',yes: function(index){var index2 = layer.load(0, {shade: false});layer.msg('稍等片刻');$.ajax({url: "/svc/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>
</body>
</html>
5.3.显示yaml配置的html代码

5.3 serviceYaml.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"><linkrel="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: "/svc/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: "/svc/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 serviceName = getQueryString("serviceName");ExportRaw(serviceName+'.yaml',editor.getValue());});});
</script></body>
</html>

六.完整代码

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

6.1 service.go,放到contrillers下

// service.go
package controllersimport (//"encoding/json"//"fmt""log"m "myk8s/models""strings"beego "github.com/beego/beego/v2/server/web"
)type SvcController struct {beego.Controller
}func (this *SvcController) List() {clusterId := this.GetString("clusterId")nameSpace := this.GetString("nameSpace")serviceName := this.GetString("serviceName")labels := this.GetString("labels")labelsKV := strings.Split(labels, ":")var labelsKey, labelsValue stringif len(labelsKV) == 2 {labelsKey = labelsKV[0]labelsValue = labelsKV[1]}svcList, err := m.SvcList(clusterId, nameSpace, serviceName, labelsKey, labelsValue)msg := "success"code := 0count := len(svcList)if err != nil {log.Println(err)msg = err.Error()code = -1}this.Data["json"] = &map[string]interface{}{"code": code, "msg": msg, "count": count, "data": &svcList}this.ServeJSON()
}func (this *SvcController) Create() {clusterId := this.GetString("clusterId")code := 0msg := "success"//log.Println(string(this.Ctx.Input.RequestBody))err := m.SvcCreate(clusterId, this.Ctx.Input.RequestBody)if err != nil {code = -1msg = 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 *SvcController) 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.SvcYamlModify(clusterId, bodyByte)if err != nil {log.Println(err)}this.Data["json"] = &map[string]interface{}{"code": 0, "msg": "ok"}this.ServeJSON()
}func (this *SvcController) Yaml() {clusterId := this.GetString("clusterId")nameSpace := this.GetString("nameSpace")serviceName := this.GetString("serviceName")yamlStr, _ := m.GetSvcYaml(clusterId, nameSpace, serviceName)this.Ctx.WriteString(yamlStr)
}
6.2.模型serviceModel.go的完整代码

6.2 serviceModel.go,放到models下

// serviceModel.go
package modelsimport ("context""fmt""log""myk8s/common""strings""encoding/json""github.com/tidwall/gjson"//"k8s.io/apimachinery/pkg/api/errors"corev1 "k8s.io/api/core/v1"metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"yamlutil "k8s.io/apimachinery/pkg/util/yaml""k8s.io/apimachinery/pkg/runtime""k8s.io/apimachinery/pkg/util/intstr""sigs.k8s.io/yaml"
)type Service struct {ServiceName string `json:"serviceName"` //服务名称NameSpace   string `json:"nameSpace"`Labels      string `json:"labels"`SvcType     string `json:"svcType"`SvcIp       string `json:"svcIp"`SvcPort     string `json:"svcPort"`LanEndpoint string `json:"lanEndpoint"` //内部端点WanEndpoint string `json:"wanEndpoint"` //外部端点CreateTime  string `json:"createTime"`
}type ServicePort struct {PortName   string `json:"portName"`SvcPort    int32  `json:"svcPort"`TargetPort int32  `json:"targetPort"`
}func SvcList(kubeconfig, namespace, serviceName string, labelsKey, labelsValue string) ([]Service, error) {clientset := common.ClientSet(kubeconfig)if namespace == "" {//namespace = corev1.NamespaceDefaultnamespace = corev1.NamespaceAll}//var selector labels.Selectorvar listOptions = metav1.ListOptions{}if labelsKey != "" && labelsValue != "" {listOptions = metav1.ListOptions{LabelSelector: fmt.Sprintf("%s=%s", labelsKey, labelsValue)}}svcList, err := clientset.CoreV1().Services(namespace).List(context.TODO(), listOptions)if err != nil {log.Printf("list service error:%v\n", err)}//fmt.Println("svc count:", len(svcList.Items))var bbb = make([]Service, 0)for _, svc := range svcList.Items {//搜索if serviceName != "" {if !strings.Contains(svc.Name, serviceName) {continue}}//fmt.Printf("name: %s\n", svc.Name)var labelsStr stringfor kk, vv := range svc.ObjectMeta.Labels {labelsStr += fmt.Sprintf("%s:%s,", kk, vv)}if len(labelsStr) > 0 {labelsStr = labelsStr[0 : len(labelsStr)-1]}var wanEndPoint stringsvcType := fmt.Sprintf("%s", svc.Spec.Type)if svcType == "LoadBalancer" {wanEndPoint = fmt.Sprintf("%s:%d", svc.Status.LoadBalancer.Ingress[0].IP, svc.Spec.Ports[0].Port)}lanEndPoint := ""var svcPort stringif len(svc.Spec.Ports) > 0 {vsapp := svc.Spec.Selector["app"]if vsapp == "" {vsapp = svc.Spec.Selector["k8s-app"]}for _, vv := range svc.Spec.Ports {//lanEndPoint += fmt.Sprintf("%s:%s:%s\n", svc.Spec.Selector["app"], svc.Spec.Ports[0].TargetPort.StrVal, svc.Spec.Ports[0].Protocol)//svcPort += fmt.Sprintf("%s:", svc.Spec.Ports[0].Port)var vport stringif vv.TargetPort.Type == 0 {vport = fmt.Sprintf("%d", vv.TargetPort.IntVal)} else {vport = fmt.Sprintf("%s", vv.TargetPort.StrVal)}lanEndPoint += fmt.Sprintf("%s:%s:%s,", vsapp, vport, vv.Protocol)svcPort += fmt.Sprintf("%d,", vv.Port)}if len(svcPort) > 0 {svcPort = svcPort[0 : len(svcPort)-1]}lanEndPoint = lanEndPoint[0 : len(lanEndPoint)-1]}Items := &Service{ServiceName: svc.Name,NameSpace:   svc.Namespace,SvcType:     svcType,SvcIp:       svc.Spec.ClusterIP,Labels:      labelsStr,SvcPort:     svcPort,LanEndpoint: lanEndPoint,WanEndpoint: wanEndPoint,CreateTime:  svc.CreationTimestamp.Format("2006-01-02 15:04:05"),}bbb = append(bbb, *Items)}return bbb, err
}func SvcCreate(kubeconfig string, bodys []byte) error {gp := gjson.ParseBytes(bodys)clusterId := gp.Get("clusterId").String()if kubeconfig == "" {kubeconfig = clusterId}serviceName := gp.Get("serviceName").String()nameSpace := gp.Get("nameSpace").String()svcType := gp.Get("svcType").String()deployName := gp.Get("deployName").String()isHeadless := gp.Get("isHeadless").Strvar labelsMap = make(map[string]string)labelsMap["app"] = serviceNamefor _, vv := range gp.Get("lables").Array() {labelsMap[vv.Get("key").Str] = vv.Get("value").Str}selectApp := serviceNameif deployName != "" {selectApp = deployName}var serviceType corev1.ServiceTypeswitch svcType {case "NodePort":serviceType = corev1.ServiceTypeNodePortcase "LoadBalancer":serviceType = corev1.ServiceTypeLoadBalancerdefault:serviceType = corev1.ServiceTypeClusterIP}if isHeadless == "on" {serviceType = corev1.ServiceTypeClusterIP}svcInstance := &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name:      serviceName,Namespace: nameSpace,Labels:    labelsMap,},Spec: corev1.ServiceSpec{Selector: map[string]string{"app": selectApp,},Type: serviceType,},}if isHeadless == "on" {svcInstance.Spec.ClusterIP = corev1.ClusterIPNone}ports := gp.Get("ports").Array()var svcPorts = make([]corev1.ServicePort, 0)for _, vv := range ports {var svcProtocol corev1.Protocolif vv.Get("protocol").Str == "UDP" {svcProtocol = corev1.ProtocolUDP} else {svcProtocol = corev1.ProtocolTCP}svcPort := &corev1.ServicePort{Name:       vv.Get("portName").Str,Port:       int32(vv.Get("svcPort").Int()),Protocol:   svcProtocol,TargetPort: intstr.FromInt32(int32(vv.Get("targetPort").Int())),}svcPorts = append(svcPorts, *svcPort)}svcInstance.Spec.Ports = svcPortsclientset := common.ClientSet(kubeconfig)_, err := clientset.CoreV1().Services(nameSpace).Create(context.TODO(), svcInstance, metav1.CreateOptions{})if err != nil {return err}return nil
}func SvcYamlModify(kubeconfig string, yamlData []byte) error {data, err := yamlutil.ToJSON(yamlData)if err != nil {return err}service := &corev1.Service{}err = json.Unmarshal(data, service)if err != nil {return err}namespace := service.ObjectMeta.NamespaceserviceName := service.ObjectMeta.Nameclientset := common.ClientSet(kubeconfig)_, err = clientset.CoreV1().Services(namespace).Update(context.TODO(), service, metav1.UpdateOptions{})if err != nil {return err}fmt.Println(namespace, serviceName)return err
}func GetSvcYaml(kubeconfig, namespace, serviceName string) (string, error) {servicesClient := common.ClientSet(kubeconfig).CoreV1().Services(namespace)service, err := servicesClient.Get(context.TODO(), serviceName, metav1.GetOptions{})serviceUnstructured, err := runtime.DefaultUnstructuredConverter.ToUnstructured(service)if err != nil {return "", err}yamlBytes, err := yaml.Marshal(serviceUnstructured)if err != nil {return "", err}return string(yamlBytes), nil
}

七.效果图

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

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

向各位请教一个问题

这是菜鸟上的一道题目&#xff0c;单单拿出来问问大家&#xff0c;看看能不能解惑 &#xff0c;谢谢各位&#xff01; 题目25&#xff1a;求12!3!...20!的和 解题思路&#xff1a;这个题不知道为什么我用DEV C 5.11显示出来为0.000000&#xff0c;可能版本有问题&#xff1f;&a…

人工智能|机器学习——强大的 Scikit-learn 可视化让模型说话

一、显示 API 简介 使用 utils.discovery.all_displays 查找可用的 API。 Sklearn 的utils.discovery.all_displays可以让你看到哪些类可以使用。 from sklearn.utils.discovery import all_displays displays all_displays() displays Scikit-learn (sklearn) 总是会在新版本…

小滴大课训练营-微服务架构-海量数据商用短链平台项目大课

小滴大课训练营-微服务架构-海量数据商用短链平台项目大课【2023最新升级版】 一、海量数据大课架构课程简介 海量数据项目大课&#xff0c;是小滴课堂准备耗时近1年的综合性实战项目&#xff0c;从0~1开发商用短链平台&#xff1b; 从产品需求文档定制出发&#xff0c;掌握核…

orbslam2基础

目录 一、 内容概要二、 orbslam2基础介绍三 、 orbslam2安装3.1 安装依赖3.2 安装orbslam23.3 下载Kitee数据集 四、 进行ORBSLAM2仿真五、 心得体会六、 参考链接 一、 内容概要 orbslam2基础介绍orbslam2安装orbslam2使用案例&#xff1a;orbslam2kitti数据集序列图像 二、…

ESP8266固件烧写

概述 因为手上有块闲置的ESP8266开发板&#xff0c;想着拿来倒腾一下WIFI探针&#xff0c;倒腾了一阵测试成功&#xff0c;博文记录用以备忘 硬件 ESP8266 NodeMCU 环境 Windows 11 步骤 1.下载esp32_win32_msys2_environment_and_toolchain-20181001.zip 2.下载xtensa…

scikit-learn实现单因子线性回归模型

1.是什么&#xff1a; 针对机器学习提供了数据预处理&#xff0c;分类&#xff0c;回归等常见算法的框架 2.基于scikit-learn求解线性回归的问题&#xff1a; 2.1.求解a&#xff0c;b对新数据进行预测&#xff1a; 2.2评估模型表现&#xff08;y和y’的方差MSE&#xff09;…

为什么会查询不到DNS信息?怎么排查?

DNS&#xff08;域名系统&#xff09;是将域名转换为相应 IP 地址的关键系统。查询 DNS 信息具有重要作用&#xff0c;通过查询 DNS 信息&#xff0c;我们可以知道域名对应的 IP 地址&#xff0c;这是最主要的信息&#xff0c;使设备能与目标服务器进行通信&#xff1b;其次是域…

Innodb实现的索引

概念 一种用于提高数据库查询性能的有序的数据结构。通过使用索引&#xff0c;数据库引擎可以快速定位到存储表中的特定数据&#xff0c;而不必逐行遍历整个表。在处理大量数据的时候可以显著加快数据检索的速度。 通过索引列队数据进行排序&#xff0c;降低数据排序的成本&a…

商家制作微信小程序有什么好处?微信小程序的制作有哪些步骤和流程

微信小程序全面指南 微信小程序是微信生态系统中一项革命性的功能&#xff0c;为希望与庞大的微信用户群体互动的企业提供了独特的融合便捷性和功能性的体验。本全面指南深入探讨了微信小程序的世界&#xff0c;强调了其重要性、工作原理以及实际用例&#xff0c;特别是针对企…

Llama 3 模型上下文长度扩展至1048K

AI苏妲己&#xff1a; Gradient AI 近日宣布&#xff0c;通过其创新的渐进式训练方法&#xff0c;成功将 Llama 3 系列模型的上下文长度扩展至超过100万令牌。 Llama 3 模型最初设计用于处理8000个令牌的上下文长度&#xff0c;这大约相当于6000字或10页文档。为了适应更长的…

LLMs之GPT4ALL:GPT4ALL的简介、安装和使用方法、案例应用之详细攻略

LLMs之GPT4ALL&#xff1a;GPT4ALL的简介、安装和使用方法、案例应用之详细攻略 目录 GPT4ALL的简介 0、新功能 1、特点 2、功能 3、技术报告 GPT4ALL的安装和使用方法 1、安装 2、使用方法 GPT4ALL的案例应用 LLMs之LLaMA3&#xff1a;基于GPT4ALL框架对LLaMA-3实现…

python turtle

名字动画 #SquareSpiral1.py import turtle t turtle.Pen() turtle.bgcolor("black")my_nameturtle.textinput("输入你的姓名","你的名字&#xff1f;") colors["red","yellow","purple","blue"] for…