Develop PaaS platform based on Go 2

Go develops the core functions of the PaaS platform

Code warehouse address GitHub - yunixiangfeng/gopaas

Chapter 7 Cloud native Go PaaS platform routing management function development, external domain name mapping, dynamic domain name setting

The domain name enables our services to provide access to the external network, allowing the public network to access resources within the cluster, which is the entrance for our open business. It will explain the core principles of Ingress and the traffic conversion process, and master how to use the services in K8s to map to the public network through the domain name to provide access to the external network.

7-1 Detailed explanation of routing ingress architecture

Go PaaS platform service management development

Main content
Explanation of routing function
Routing principle and architecture description
Developing routing management function
Why use Ingress?
ClusterIP can only be accessed within the cluster.
NodePort is difficult to manage when there are tens or hundreds of services running in the cluster.
The LoadBalance method is limited by the cloud platform.

Introduction to routing Ingress
Ingress: a common resource object used to indicate specific routing rules
Ingress-controller: to execute forwarding rules

Routing Ingress Architecture Description

How does routing Ingress-controller (nginx) work? 

7-2 Routing model and repository development adjustment

PS C:\Users\Administrator\Desktop\gopaas> .\yu-tool\yu-tool.exe  newService github.com/yunixiangfeng/gopaas/route

cd route

go mod tidy

// versions:
// 	protoc-gen-go v1.27.1
// 	protoc        v3.15.7
// source: proto/svc/svc.proto
protoc --proto_path=. --micro_out=. --go_out=:. ./proto/route/route.proto

C:\Users\Administrator\Desktop\gopaas\route\domain\model\route.go

package model

type Route struct {
	ID             int64       `gorm:"primary_key;not_null;auto_increment"`
	RouteName      string      `json:"route_name"`
	RouteNamespace string      `json:"route_namespace"`
	RouteHost      string      `json:"route_host"`
	RoutePath      []RoutePath `gorm:"ForeignKey:RouteID" json:"route_path"`
}

C:\Users\Administrator\Desktop\gopaas\route\domain\model\route_path.go

package model

type RoutePath struct {
	ID                      int64  `gorm:"primary_key;not_null;auto_increment"`
	RouteID                 int64  `json:"route_id"`
	RoutePathName           string `json:"route_path_name"`
	RouteBackendService     string `json:"route_backend_service"`
	RouteBackendServicePort int32  `json:"route_backend_service_port"`
}

C:\Users\Administrator\Desktop\gopaas\route\domain\repository\route_repository.go

package repository

import (
	"github.com/jinzhu/gorm"
	"github.com/yunixiangfeng/gopaas/common"
	"github.com/yunixiangfeng/gopaas/route/domain/model"
)

//创建需要实现的接口
type IRouteRepository interface {
	//初始化表
	InitTable() error
	//根据ID查处找数据
	FindRouteByID(int64) (*model.Route, error)
	//创建一条 route 数据
	CreateRoute(*model.Route) (int64, error)
	//根据ID删除一条 route 数据
	DeleteRouteByID(int64) error
	//修改更新数据
	UpdateRoute(*model.Route) error
	//查找route所有数据
	FindAll() ([]model.Route, error)
}

//创建routeRepository
func NewRouteRepository(db *gorm.DB) IRouteRepository {
	return &RouteRepository{mysqlDb: db}
}

type RouteRepository struct {
	mysqlDb *gorm.DB
}

//初始化表
func (u *RouteRepository) InitTable() error {
	return u.mysqlDb.CreateTable(&model.Route{}, &model.RoutePath{}).Error
}

//根据ID查找Route信息
func (u *RouteRepository) FindRouteByID(routeID int64) (route *model.Route, err error) {
	route = &model.Route{}
	return route, u.mysqlDb.Preload("RoutePath").First(route, routeID).Error
}

//创建Route信息
func (u *RouteRepository) CreateRoute(route *model.Route) (int64, error) {
	return route.ID, u.mysqlDb.Create(route).Error
}

//根据ID删除Route信息
func (u *RouteRepository) DeleteRouteByID(routeID int64) error {
	tx := u.mysqlDb.Begin()
	//遇到问题回滚
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
		}
	}()
	if tx.Error != nil {
		common.Error(tx.Error)
		return tx.Error
	}
	//开始删除
	if err := u.mysqlDb.Where("id = ?", routeID).Delete(&model.Route{}).Error; err != nil {
		tx.Rollback()
		common.Error(err)
		return err
	}
	//删除关联表
	if err := u.mysqlDb.Where("route_id = ?", routeID).Delete(&model.RoutePath{}).Error; err != nil {
		tx.Rollback()
		common.Error(err)
		return err
	}
	return tx.Commit().Error
}

//更新Route信息
func (u *RouteRepository) UpdateRoute(route *model.Route) error {
	return u.mysqlDb.Model(route).Update(route).Error
}

//获取结果集
func (u *RouteRepository) FindAll() (routeAll []model.Route, err error) {
	return routeAll, u.mysqlDb.Preload("RoutePath").Find(&routeAll).Error
}

7-3 Routing service development 

C:\Users\Administrator\Desktop\gopaas\route\domain\service\route_data_service.go 

package service

import (
	"context"
	"errors"
	"strconv"

	"github.com/yunixiangfeng/gopaas/common"
	"github.com/yunixiangfeng/gopaas/route/domain/model"
	"github.com/yunixiangfeng/gopaas/route/domain/repository"
	"github.com/yunixiangfeng/gopaas/route/proto/route"
	v1 "k8s.io/api/apps/v1"
	v12 "k8s.io/api/networking/v1"
	v14 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
)

//这里是接口类型
type IRouteDataService interface {
	AddRoute(*model.Route) (int64, error)
	DeleteRoute(int64) error
	UpdateRoute(*model.Route) error
	FindRouteByID(int64) (*model.Route, error)
	FindAllRoute() ([]model.Route, error)

	CreateRouteToK8s(*route.RouteInfo) error
	DeleteRouteFromK8s(*model.Route) error
	UpdateRouteToK8s(*route.RouteInfo) error
}

//创建
//注意:返回值 IRouteDataService 接口类型
func NewRouteDataService(routeRepository repository.IRouteRepository, clientSet *kubernetes.Clientset) IRouteDataService {
	return &RouteDataService{RouteRepository: routeRepository, K8sClientSet: clientSet, deployment: &v1.Deployment{}}
}

type RouteDataService struct {
	//注意:这里是 IRouteRepository 类型
	RouteRepository repository.IRouteRepository
	K8sClientSet    *kubernetes.Clientset
	deployment      *v1.Deployment
}

//创建k8s(把proto 属性补全)
func (u *RouteDataService) CreateRouteToK8s(info *route.RouteInfo) (err error) {
	ingress := u.setIngress(info)
	//查找是否存在
	if _, err = u.K8sClientSet.NetworkingV1().Ingresses(info.RouteNamespace).Get(context.TODO(), info.RouteName, v14.GetOptions{}); err != nil {
		if _, err = u.K8sClientSet.NetworkingV1().Ingresses(info.RouteNamespace).Create(context.TODO(), ingress, v14.CreateOptions{}); err != nil {
			//创建不成功记录错误
			common.Error(err)
			return err
		}
		return nil
	} else {
		common.Error("路由 " + info.RouteName + " 已经存在")
		return errors.New("路由 " + info.RouteName + " 已经存在")
	}
}

func (u *RouteDataService) setIngress(info *route.RouteInfo) *v12.Ingress {
	route := &v12.Ingress{}
	//设置路由
	route.TypeMeta = v14.TypeMeta{
		Kind:       "Ingress",
		APIVersion: "v1",
	}
	//设置路由基础信息
	route.ObjectMeta = v14.ObjectMeta{
		Name:      info.RouteName,
		Namespace: info.RouteNamespace,
		Labels: map[string]string{
			"app-name": info.RouteName,
			"author":   "wu123",
		},
		Annotations: map[string]string{
			"k8s/generated-by-wu": "由代码创建",
		},
	}
	//使用 ingress-nginx
	className := "nginx"
	//设置路由 spec 信息
	route.Spec = v12.IngressSpec{
		IngressClassName: &className,
		//默认访问服务
		DefaultBackend: nil,
		//如果开启https这里要设置
		TLS:   nil,
		Rules: u.getIngressPath(info),
	}
	return route
}

//根据info信息获取path路径
func (u *RouteDataService) getIngressPath(info *route.RouteInfo) (path []v12.IngressRule) {
	//1.设置host
	pathRule := v12.IngressRule{Host: info.RouteHost}
	//2.设置Path
	ingressPath := []v12.HTTPIngressPath{}
	for _, v := range info.RoutePath {
		pathType := v12.PathTypePrefix
		ingressPath = append(ingressPath, v12.HTTPIngressPath{
			Path:     v.RoutePathName,
			PathType: &pathType,
			Backend: v12.IngressBackend{
				Service: &v12.IngressServiceBackend{
					Name: v.RouteBackendService,
					Port: v12.ServiceBackendPort{
						Number: v.RouteBackendServicePort,
					},
				},
			},
		})
	}

	//3.赋值 Path
	pathRule.IngressRuleValue = v12.IngressRuleValue{HTTP: &v12.HTTPIngressRuleValue{Paths: ingressPath}}
	path = append(path, pathRule)
	return
}

//更新route
func (u *RouteDataService) UpdateRouteToK8s(info *route.RouteInfo) (err error) {
	ingress := u.setIngress(info)
	if _, err = u.K8sClientSet.NetworkingV1().Ingresses(info.RouteNamespace).Update(context.TODO(), ingress, v14.UpdateOptions{}); err != nil {
		common.Error(err)
		return err
	}
	return nil
}

//删除route
func (u *RouteDataService) DeleteRouteFromK8s(route2 *model.Route) (err error) {
	//删除Ingress
	if err = u.K8sClientSet.NetworkingV1().Ingresses(route2.RouteNamespace).Delete(context.TODO(), route2.RouteName, v14.DeleteOptions{}); err != nil {
		//如果删除失败记录下
		common.Error(err)
		return err
	} else {
		if err := u.DeleteRoute(route2.ID); err != nil {
			common.Error(err)
			return err
		}
		common.Info("删除 ingress ID:" + strconv.FormatInt(route2.ID, 10) + " 成功!")
	}
	return
}

//插入
func (u *RouteDataService) AddRoute(route *model.Route) (int64, error) {
	return u.RouteRepository.CreateRoute(route)
}

//删除
func (u *RouteDataService) DeleteRoute(routeID int64) error {
	return u.RouteRepository.DeleteRouteByID(routeID)
}

//更新
func (u *RouteDataService) UpdateRoute(route *model.Route) error {
	return u.RouteRepository.UpdateRoute(route)
}

//查找
func (u *RouteDataService) FindRouteByID(routeID int64) (*model.Route, error) {
	return u.RouteRepository.FindRouteByID(routeID)
}

//查找
func (u *RouteDataService) FindAllRoute() ([]model.Route, error) {
	return u.RouteRepository.FindAll()
}

C:\Users\Administrator\Desktop\gopaas\route\proto\route\route.proto

syntax = "proto3";

package route;

option go_package = "./proto/route;route";

service Route {
	//对外提供添加服务
	rpc AddRoute(RouteInfo) returns (Response) {}
	rpc DeleteRoute(RouteId) returns (Response) {}
	rpc UpdateRoute(RouteInfo) returns (Response) {}
	rpc FindRouteByID(RouteId) returns (RouteInfo) {}
	rpc FindAllRoute(FindAll) returns (AllRoute) {}
}
message RouteInfo {
	int64 id = 1;
	string route_name = 2;
	string route_namespace =3;
	string route_host =4;
	repeated RoutePath route_path=5;
}

message RoutePath {
	int64 id = 1;
	int64 route_id =2;
	string route_path_name=3;
	string route_backend_service=4;
	int32 route_backend_service_port=5;
}

message RouteId {
	int64 id = 1;
}

message FindAll {

}

message Response {
	string msg =1 ;
}

message AllRoute {
	repeated RouteInfo route_info = 1;
}


7-4 Routing handler logic development and precautions

Create a handler to expose services externally

C:\Users\Administrator\Desktop\gopaas\route\handler\routeHandler.go

package handler

import (
	"context"
	"strconv"

	log "github.com/asim/go-micro/v3/logger"
	"github.com/yunixiangfeng/gopaas/common"
	"github.com/yunixiangfeng/gopaas/route/domain/model"
	"github.com/yunixiangfeng/gopaas/route/domain/service"
	route "github.com/yunixiangfeng/gopaas/route/proto/route"
)

type RouteHandler struct {
	//注意这里的类型是 IRouteDataService 接口类型
	RouteDataService service.IRouteDataService
}

// 添加路由
func (e *RouteHandler) AddRoute(ctx context.Context, info *route.RouteInfo, rsp *route.Response) error {
	log.Info("Received *route.AddRoute request")
	route := &model.Route{}
	if err := common.SwapTo(info, route); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	//创建route到k8s
	if err := e.RouteDataService.CreateRouteToK8s(info); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	} else {
		//写入数据库
		routeID, err := e.RouteDataService.AddRoute(route)
		if err != nil {
			common.Error(err)
			rsp.Msg = err.Error()
			return err
		}
		common.Info("Route 添加成功 ID 号为:" + strconv.FormatInt(routeID, 10))
		rsp.Msg = "Route 添加成功 ID 号为:" + strconv.FormatInt(routeID, 10)
	}
	return nil
}

//删除route
func (e *RouteHandler) DeleteRoute(ctx context.Context, req *route.RouteId, rsp *route.Response) error {
	log.Info("Received *route.DeleteRoute request")
	routeModel, err := e.RouteDataService.FindRouteByID(req.Id)
	if err != nil {
		common.Error(err)
		return err
	}
	//从k8s中删除,并且删除数据库中数据
	if err := e.RouteDataService.DeleteRouteFromK8s(routeModel); err != nil {
		common.Error(err)
		return err
	}
	return nil
}

//更新route
func (e *RouteHandler) UpdateRoute(ctx context.Context, req *route.RouteInfo, rsp *route.Response) error {
	log.Info("Received *route.UpdateRoute request")
	if err := e.RouteDataService.UpdateRouteToK8s(req); err != nil {
		common.Error(err)
		return err
	}
	//查询数据库的信息
	routeModel, err := e.RouteDataService.FindRouteByID(req.Id)
	if err != nil {
		common.Error(err)
		return err
	}
	//数据更新
	if err := common.SwapTo(req, routeModel); err != nil {
		common.Error(err)
		return err
	}
	return e.RouteDataService.UpdateRoute(routeModel)
}

//根据ID查询route信息
func (e *RouteHandler) FindRouteByID(ctx context.Context, req *route.RouteId, rsp *route.RouteInfo) error {
	log.Info("Received *route.FindRouteByID request")
	routeModel, err := e.RouteDataService.FindRouteByID(req.Id)
	if err != nil {
		common.Error(err)
		return err
	}
	//数据转化
	if err := common.SwapTo(routeModel, rsp); err != nil {
		common.Error(err)
		return err
	}
	return nil
}

func (e *RouteHandler) FindAllRoute(ctx context.Context, req *route.FindAll, rsp *route.AllRoute) error {
	log.Info("Received *route.FindAllRoute request")
	allRoute, err := e.RouteDataService.FindAllRoute()
	if err != nil {
		common.Error(err)
		return err
	}
	//整理下格式
	for _, v := range allRoute {
		//创建实例
		routeInfo := &route.RouteInfo{}
		//把查询出来的数据进行转化
		if err := common.SwapTo(v, routeInfo); err != nil {
			common.Error(err)
			return err
		}
		//数据合并
		rsp.RouteInfo = append(rsp.RouteInfo, routeInfo)
	}
	return nil
}

C:\Users\Administrator\Desktop\gopaas\route\main.go

package main

import (
	"flag"
	"fmt"
	"path/filepath"

	"github.com/yunixiangfeng/gopaas/common"
	"github.com/yunixiangfeng/gopaas/route/domain/repository"

	//"github.com/afex/hystrix-go/hystrix"
	"github.com/asim/go-micro/plugins/registry/consul/v3"
	ratelimit "github.com/asim/go-micro/plugins/wrapper/ratelimiter/uber/v3"
	opentracing2 "github.com/asim/go-micro/plugins/wrapper/trace/opentracing/v3"
	"github.com/asim/go-micro/v3"
	"github.com/asim/go-micro/v3/registry"
	"github.com/asim/go-micro/v3/server"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
	"github.com/opentracing/opentracing-go"
	service2 "github.com/yunixiangfeng/gopaas/route/domain/service"
	"github.com/yunixiangfeng/gopaas/route/handler"

	//hystrix2 "github.com/yunixiangfeng/gopaas/route/plugin/hystrix"
	"strconv"

	route "github.com/yunixiangfeng/gopaas/route/proto/route"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/client-go/util/homedir"
)

var (
	//服务地址
	hostIp = "192.168.204.130"
	//服务地址
	serviceHost = hostIp
	//服务端口
	servicePort = "8085"

	//注册中心配置
	consulHost       = hostIp
	consulPort int64 = 8500
	//链路追踪
	tracerHost = hostIp
	tracerPort = 6831
	//熔断端口,每个服务不能重复
	//hystrixPort = 9095
	//监控端口,每个服务不能重复
	prometheusPort = 9195
)

func main() {
	//需要本地启动,mysql,consul中间件服务
	//1.注册中心
	consul := consul.NewRegistry(func(options *registry.Options) {
		options.Addrs = []string{
			consulHost + ":" + strconv.FormatInt(consulPort, 10),
		}
	})
	//2.配置中心,存放经常变动的变量
	consulConfig, err := common.GetConsulConfig(consulHost, consulPort, "/micro/config")
	if err != nil {
		common.Error(err)
	}
	//3.使用配置中心连接 mysql
	mysqlInfo := common.GetMysqlFromConsul(consulConfig, "mysql")
	//初始化数据库
	db, err := gorm.Open("mysql", mysqlInfo.User+":"+mysqlInfo.Pwd+"@("+mysqlInfo.Host+":3306)/"+mysqlInfo.Database+"?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		//命令行输出下,方便查看错误
		fmt.Println(err)
		common.Fatal(err)
	}
	defer db.Close()
	//禁止复表
	db.SingularTable(true)

	//4.添加链路追踪
	t, io, err := common.NewTracer("go.micro.service.route", tracerHost+":"+strconv.Itoa(tracerPort))
	if err != nil {
		common.Error(err)
	}
	defer io.Close()
	opentracing.SetGlobalTracer(t)

	//添加熔断器,作为客户端需要启用
	//hystrixStreamHandler := hystrix.NewStreamHandler()
	//hystrixStreamHandler.Start()

	//添加日志中心
	//1)需要程序日志打入到日志文件中
	//2)在程序中添加filebeat.yml 文件
	//3) 启动filebeat,启动命令 ./filebeat -e -c filebeat.yml
	fmt.Println("日志统一记录在根目录 micro.log 文件中,请点击查看日志!")

	//启动监听程序
	//go func() {
	//	//http://192.168.204.130:9092/turbine/turbine.stream
	//	//看板访问地址 http://127.0.0.1:9002/hystrix,url后面一定要带 /hystrix
	//	err = http.ListenAndServe(net.JoinHostPort("0.0.0.0",strconv.Itoa(hystrixPort)),hystrixStreamHandler)
	//	if err !=nil {
	//		common.Error(err)
	//	}
	//}()

	//5.添加监控
	common.PrometheusBoot(prometheusPort)

	//下载kubectl:https://kubernetes.io/docs/tasks/tools/#tabset-2
	//macos:
	// 1.curl -LO "https://dl.k8s.io/release/v1.21.0/bin/darwin/amd64/kubectl"
	// 2.chmod +x ./kubectl
	// 3.sudo mv ./kubectl /usr/local/bin/kubectl
	//   sudo chown root: /usr/local/bin/kubectl
	// 5.kubectl version --client
	// 6.集群模式下直接拷贝服务端~/.kube/config 文件到本机 ~/.kube/confg 中
	//   注意:- config中的域名要能解析正确
	//        - 生产环境可以创建另一个证书
	// 7.kubectl get ns 查看是否正常
	//
	//6.创建k8s连接
	//在集群外面连接
	var kubeconfig *string
	if home := homedir.HomeDir(); home != "" {
		kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
	} else {
		kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
	}
	flag.Parse()
	config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
	if err != nil {
		common.Fatal(err.Error())
	}

	//在集群中外的配置
	//config, err := rest.InClusterConfig()
	//if err != nil {
	//	panic(err.Error())
	//}

	// create the clientset
	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		common.Fatal(err.Error())
	}

	//7.创建服务
	service := micro.NewService(
		//自定义服务地址,且必须写在其它参数前面
		micro.Server(server.NewServer(func(options *server.Options) {
			options.Advertise = serviceHost + ":" + servicePort
		})),
		micro.Name("go.micro.service.route"),
		micro.Version("latest"),
		//指定服务端口
		micro.Address(":"+servicePort),
		//添加注册中心
		micro.Registry(consul),
		//添加链路追踪
		micro.WrapHandler(opentracing2.NewHandlerWrapper(opentracing.GlobalTracer())),
		micro.WrapClient(opentracing2.NewClientWrapper(opentracing.GlobalTracer())),
		//只作为客户端的时候起作用,如果存在调用别人的情况,原则上不去主动调用
		//micro.WrapClient(hystrix2.NewClientHystrixWrapper()),
		//添加限流
		micro.WrapHandler(ratelimit.NewHandlerWrapper(1000)),
	)

	service.Init()

	//只能执行一遍
	err = repository.NewRouteRepository(db).InitTable()
	if err != nil {
		common.Fatal(err)
	}

	// 注册句柄,可以快速操作已开发的服务
	routeDataService := service2.NewRouteDataService(repository.NewRouteRepository(db), clientset)
	route.RegisterRouteHandler(service.Server(), &handler.RouteHandler{RouteDataService: routeDataService})

	// 启动服务
	if err := service.Run(); err != nil {
		//输出启动失败信息
		common.Fatal(err)
	}
}

7-5 route External API development

PS C:\Users\Administrator\Desktop\gopaas> .\yu-tool\yu-tool.exe createApi github.com/yunixiangfeng/gopaas/routeApi

yu-v3 --proto_path=. --micro_out=. --go_out=:. ./proto/routeApi/routeApi.proto

go mod tidy

C:\Users\Administrator\Desktop\gopaas\routeapi\main.go

package main

import (
	"fmt"

	"github.com/afex/hystrix-go/hystrix"
	"github.com/asim/go-micro/plugins/registry/consul/v3"
	ratelimit "github.com/asim/go-micro/plugins/wrapper/ratelimiter/uber/v3"
	"github.com/asim/go-micro/plugins/wrapper/select/roundrobin/v3"
	opentracing2 "github.com/asim/go-micro/plugins/wrapper/trace/opentracing/v3"
	"github.com/asim/go-micro/v3"
	"github.com/asim/go-micro/v3/registry"
	"github.com/asim/go-micro/v3/server"
	"github.com/yunixiangfeng/gopaas/common"
	go_micro_service_route "github.com/yunixiangfeng/gopaas/route/proto/route"

	"net"
	"net/http"
	"strconv"

	_ "github.com/jinzhu/gorm/dialects/mysql"
	"github.com/opentracing/opentracing-go"
	"github.com/yunixiangfeng/gopaas/routeApi/handler"
	hystrix2 "github.com/yunixiangfeng/gopaas/routeApi/plugin/hystrix"

	routeApi "github.com/yunixiangfeng/gopaas/routeApi/proto/routeApi"
)

var (
	//服务地址
	hostIp = "192.168.204.130"
	//服务地址
	serviceHost = hostIp
	//服务端口
	servicePort = "8086"
	//注册中心配置
	consulHost       = hostIp
	consulPort int64 = 8500
	//链路追踪
	tracerHost = hostIp
	tracerPort = 6831
	//熔断端口,每个服务不能重复
	hystrixPort = 9096
	//监控端口,每个服务不能重复
	prometheusPort = 9196
)

func main() {
	//需要本地启动,mysql,consul中间件服务
	//1.注册中心
	consul := consul.NewRegistry(func(options *registry.Options) {
		options.Addrs = []string{
			consulHost + ":" + strconv.FormatInt(consulPort, 10),
		}
	})

	//2.添加链路追踪
	t, io, err := common.NewTracer("go.micro.api.routeApi", tracerHost+":"+strconv.Itoa(tracerPort))
	if err != nil {
		common.Error(err)
	}
	defer io.Close()
	opentracing.SetGlobalTracer(t)

	//3.添加熔断器
	hystrixStreamHandler := hystrix.NewStreamHandler()
	hystrixStreamHandler.Start()

	//添加日志中心
	//1)需要程序日志打入到日志文件中
	//2)在程序中添加filebeat.yml 文件
	//3) 启动filebeat,启动命令 ./filebeat -e -c filebeat.yml
	fmt.Println("日志统一记录在根目录 micro.log 文件中,请点击查看日志!")

	//启动监听程序
	go func() {
		//http://192.168.204.130:9092/turbine/turbine.stream
		//看板访问地址 http://127.0.0.1:9002/hystrix,url后面一定要带 /hystrix
		err = http.ListenAndServe(net.JoinHostPort("0.0.0.0", strconv.Itoa(hystrixPort)), hystrixStreamHandler)
		if err != nil {
			common.Error(err)
		}
	}()

	//4.添加监控
	common.PrometheusBoot(prometheusPort)

	//5.创建服务
	service := micro.NewService(
		//自定义服务地址,且必须写在其它参数前面
		micro.Server(server.NewServer(func(opts *server.Options) {
			opts.Advertise = serviceHost + ":" + servicePort

		})),
		micro.Name("go.micro.api.routeApi"),
		micro.Version("latest"),
		//指定服务端口
		micro.Address(":"+servicePort),
		//添加注册中心
		micro.Registry(consul),
		//添加链路追踪
		micro.WrapHandler(opentracing2.NewHandlerWrapper(opentracing.GlobalTracer())),
		micro.WrapClient(opentracing2.NewClientWrapper(opentracing.GlobalTracer())),
		//只作为客户端的时候起作用
		micro.WrapClient(hystrix2.NewClientHystrixWrapper()),
		//添加限流
		micro.WrapHandler(ratelimit.NewHandlerWrapper(1000)),
		//增加负载均衡
		micro.WrapClient(roundrobin.NewClientWrapper()),
	)

	service.Init()

	// 指定需要访问的服务,可以快速操作已开发的服务,
	// 默认API服务名称带有"Api",程序会自动替换
	// 如果不带有特定字符会使用默认"XXX" 请自行替换
	routeService := go_micro_service_route.NewRouteService("go.micro.service.route", service.Client())
	// 注册控制器
	if err := routeApi.RegisterRouteApiHandler(service.Server(), &handler.RouteApi{RouteService: routeService}); err != nil {
		common.Error(err)
	}

	// 启动服务
	if err := service.Run(); err != nil {
		//输出启动失败信息
		common.Fatal(err)
	}
}

C:\Users\Administrator\Desktop\gopaas\routeapi\handler\routeApiHandler.go

package handler

import (
	"context"
	"encoding/json"
	"errors"
	"strconv"

	log "github.com/asim/go-micro/v3/logger"
	"github.com/yunixiangfeng/gopaas/common"
	route "github.com/yunixiangfeng/gopaas/route/proto/route"
	routeApi "github.com/yunixiangfeng/gopaas/routeApi/proto/routeApi"
)

type RouteApi struct {
	RouteService route.RouteService
}

// routeApi.FindRouteById 通过API向外暴露为/routeApi/findRouteById,接收http请求
// 即:/routeApi/FindRouteById 请求会调用go.micro.api.routeApi 服务的routeApi.FindRouteById 方法
func (e *RouteApi) FindRouteById(ctx context.Context, req *routeApi.Request, rsp *routeApi.Response) error {
	log.Info("Received routeApi.FindRouteById request")
	if _, ok := req.Get["route_id"]; !ok {
		rsp.StatusCode = 500
		return errors.New("参数异常")
	}
	//获取 route id
	routeIdString := req.Get["route_id"].Values[0]
	routeId, err := strconv.ParseInt(routeIdString, 10, 64)
	if err != nil {
		common.Error(err)
		return err
	}
	//获取route信息
	routeInfo, err := e.RouteService.FindRouteByID(ctx, &route.RouteId{
		Id: routeId,
	})
	if err != nil {
		common.Error(err)
		return err
	}
	//返回route结果
	rsp.StatusCode = 200
	b, _ := json.Marshal(routeInfo)
	rsp.Body = string(b)
	return nil
}

// routeApi.AddRoute 通过API向外暴露为/routeApi/AddRoute,接收http请求
// 即:/routeApi/AddRoute 请求会调用go.micro.api.routeApi 服务的routeApi.AddRoute 方法
func (e *RouteApi) AddRoute(ctx context.Context, req *routeApi.Request, rsp *routeApi.Response) error {
	log.Info("Received routeApi.AddRoute request")
	addRouteInfo := &route.RouteInfo{}
	routePathName, ok := req.Post["route_path_name"]
	if ok && len(routePathName.Values) > 0 {
		port, err := strconv.ParseInt(req.Post["route_backend_service_port"].Values[0], 10, 32)
		if err != nil {
			common.Error(err)
			return err
		}
		//这里如果有多个Path需要处理多个
		routePath := &route.RoutePath{
			RoutePathName:           req.Post["route_path_name"].Values[0],
			RouteBackendService:     req.Post["route_backend_service"].Values[0],
			RouteBackendServicePort: int32(port),
		}
		//合并
		addRouteInfo.RoutePath = append(addRouteInfo.RoutePath, routePath)
	}
	form.FormToSvcStruct(req.Post, addRouteInfo)
	response, err := e.RouteService.AddRoute(ctx, addRouteInfo)
	if err != nil {
		common.Error(err)
		return err
	}
	rsp.StatusCode = 200
	b, _ := json.Marshal(response)
	rsp.Body = string(b)
	return nil
}

// routeApi.DeleteRouteById 通过API向外暴露为/routeApi/DeleteRouteById,接收http请求
// 即:/routeApi/DeleteRouteById 请求会调用go.micro.api.routeApi 服务的 routeApi.DeleteRouteById 方法
func (e *RouteApi) DeleteRouteById(ctx context.Context, req *routeApi.Request, rsp *routeApi.Response) error {
	log.Info("Received routeApi.DeleteRouteById request")
	if _, ok := req.Get["route_id"]; !ok {
		rsp.StatusCode = 500
		return errors.New("参数异常")
	}
	//获取 route id
	routeIdString := req.Get["route_id"].Values[0]
	routeId, err := strconv.ParseInt(routeIdString, 10, 64)
	if err != nil {
		common.Error(err)
		return err
	}
	//调用route 删除服务
	response, err := e.RouteService.DeleteRoute(ctx, &route.RouteId{
		Id: routeId,
	})
	if err != nil {
		common.Error(err)
		return err
	}
	rsp.StatusCode = 200
	b, _ := json.Marshal(response)
	rsp.Body = string(b)
	return nil
}

// routeApi.UpdateRoute 通过API向外暴露为/routeApi/UpdateRoute,接收http请求
// 即:/routeApi/UpdateRoute 请求会调用go.micro.api.routeApi 服务的routeApi.UpdateRoute 方法
func (e *RouteApi) UpdateRoute(ctx context.Context, req *routeApi.Request, rsp *routeApi.Response) error {
	log.Info("Received routeApi.UpdateRoute request")
	rsp.StatusCode = 200
	b, _ := json.Marshal("{success:'成功访问/routeApi/UpdateRoute'}")
	rsp.Body = string(b)
	return nil
}

// 默认的方法routeApi.Call 通过API向外暴露为/routeApi/call,接收http请求
// 即:/routeApi/call或/routeApi/ 请求会调用go.micro.api.routeApi 服务的routeApi.FindRouteById 方法
func (e *RouteApi) Call(ctx context.Context, req *routeApi.Request, rsp *routeApi.Response) error {
	log.Info("Received routeApi.Call request")
	allRoute, err := e.RouteService.FindAllRoute(ctx, &route.FindAll{})
	if err != nil {
		common.Error(err)
		return err
	}
	rsp.StatusCode = 200
	b, _ := json.Marshal(allRoute)
	rsp.Body = string(b)
	return nil
}

C:\Users\Administrator\Desktop\gopaas\routeapi\plugin\form\form.go

package form

import (
	"errors"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/yunixiangfeng/gopaas/common"
	"github.com/yunixiangfeng/gopaas/routeApi/proto/routeApi"
)

//根据结构体中name标签映射数据到结构体中并且转换类型
func FormToSvcStruct(data map[string]*routeApi.Pair, obj interface{}) {
	objValue := reflect.ValueOf(obj).Elem()
	for i := 0; i < objValue.NumField(); i++ {
		//获取sql对应的值
		dataTag := strings.Replace(objValue.Type().Field(i).Tag.Get("json"), ",omitempty", "", -1)
		dataSlice, ok := data[dataTag]
		if !ok {
			continue
		}
		valueSlice := dataSlice.Values
		if len(valueSlice) <= 0 {
			continue
		}
		//排除port和env
		if dataTag == "route_path" {
			continue
		}
		value := valueSlice[0]
		//端口,环境变量的单独处理
		//获取对应字段的名称
		name := objValue.Type().Field(i).Name
		//获取对应字段类型
		structFieldType := objValue.Field(i).Type()
		//获取变量类型,也可以直接写"string类型"
		val := reflect.ValueOf(value)
		var err error
		if structFieldType != val.Type() {
			//类型转换
			val, err = TypeConversion(value, structFieldType.Name()) //类型转换
			if err != nil {
				common.Error(err)
			}
		}
		//设置类型值
		objValue.FieldByName(name).Set(val)
	}
}

//类型转换
func TypeConversion(value string, ntype string) (reflect.Value, error) {
	if ntype == "string" {
		return reflect.ValueOf(value), nil
	} else if ntype == "time.Time" {
		t, err := time.ParseInLocation("2006-01-02 15:04:05", value, time.Local)
		return reflect.ValueOf(t), err
	} else if ntype == "Time" {
		t, err := time.ParseInLocation("2006-01-02 15:04:05", value, time.Local)
		return reflect.ValueOf(t), err
	} else if ntype == "int" {
		i, err := strconv.Atoi(value)
		return reflect.ValueOf(i), err
	} else if ntype == "int32" {
		i, err := strconv.ParseInt(value, 10, 32)
		if err != nil {
			return reflect.ValueOf(int32(i)), err
		}
		return reflect.ValueOf(int32(i)), err
	} else if ntype == "int64" {
		i, err := strconv.ParseInt(value, 10, 64)
		return reflect.ValueOf(i), err
	} else if ntype == "float32" {
		i, err := strconv.ParseFloat(value, 64)
		return reflect.ValueOf(float32(i)), err
	} else if ntype == "float64" {
		i, err := strconv.ParseFloat(value, 64)
		return reflect.ValueOf(i), err
	}

	//else if .......增加其他一些类型的转换

	return reflect.ValueOf(value), errors.New("未知的类型:" + ntype)
}

7-6 Create nginx-controller resource object in k8s

C:\Users\Administrator\Desktop\gopaas\ingress\deploy.yml

apiVersion: v1
kind: Namespace
metadata:
  labels:
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
  name: default
---
apiVersion: v1
automountServiceAccountToken: true
kind: ServiceAccount
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.2.0
  name: ingress-nginx
  namespace: default
---
apiVersion: v1
kind: ServiceAccount
metadata:
  labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.2.0
  name: ingress-nginx-admission
  namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.2.0
  name: ingress-nginx
  namespace: default
rules:
- apiGroups:
  - ""
  resources:
  - namespaces
  verbs:
  - get
- apiGroups:
  - ""
  resources:
  - configmaps
  - pods
  - secrets
  - endpoints
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - ""
  resources:
  - services
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - networking.k8s.io
  resources:
  - ingresses
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - networking.k8s.io
  resources:
  - ingresses/status
  verbs:
  - update
- apiGroups:
  - networking.k8s.io
  resources:
  - ingressclasses
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - ""
  resourceNames:
  - ingress-controller-leader
  resources:
  - configmaps
  verbs:
  - get
  - update
- apiGroups:
  - ""
  resources:
  - configmaps
  verbs:
  - create
- apiGroups:
  - ""
  resources:
  - events
  verbs:
  - create
  - patch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.2.0
  name: ingress-nginx-admission
  namespace: default
rules:
- apiGroups:
  - ""
  resources:
  - secrets
  verbs:
  - get
  - create
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  labels:
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.2.0
  name: ingress-nginx
rules:
- apiGroups:
  - ""
  resources:
  - configmaps
  - endpoints
  - nodes
  - pods
  - secrets
  - namespaces
  verbs:
  - list
  - watch
- apiGroups:
  - ""
  resources:
  - nodes
  verbs:
  - get
- apiGroups:
  - ""
  resources:
  - services
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - networking.k8s.io
  resources:
  - ingresses
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - ""
  resources:
  - events
  verbs:
  - create
  - patch
- apiGroups:
  - networking.k8s.io
  resources:
  - ingresses/status
  verbs:
  - update
- apiGroups:
  - networking.k8s.io
  resources:
  - ingressclasses
  verbs:
  - get
  - list
  - watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.2.0
  name: ingress-nginx-admission
rules:
- apiGroups:
  - admissionregistration.k8s.io
  resources:
  - validatingwebhookconfigurations
  verbs:
  - get
  - update
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.2.0
  name: ingress-nginx
  namespace: default
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: ingress-nginx
subjects:
- kind: ServiceAccount
  name: ingress-nginx
  namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.2.0
  name: ingress-nginx-admission
  namespace: default
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: ingress-nginx-admission
subjects:
- kind: ServiceAccount
  name: ingress-nginx-admission
  namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  labels:
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.2.0
  name: ingress-nginx
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: ingress-nginx
subjects:
- kind: ServiceAccount
  name: ingress-nginx
  namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.2.0
  name: ingress-nginx-admission
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: ingress-nginx-admission
subjects:
- kind: ServiceAccount
  name: ingress-nginx-admission
  namespace: default
---
apiVersion: v1
data:
  allow-snippet-annotations: "true"
kind: ConfigMap
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.2.0
  name: ingress-nginx-controller
  namespace: default
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.2.0
  name: ingress-nginx-controller
  namespace: default
spec:
  #externalTrafficPolicy: Local
  ports:
  - appProtocol: http
    name: http
    port: 80
    protocol: TCP
    targetPort: http
  - appProtocol: https
    name: https
    port: 443
    protocol: TCP
    targetPort: https
  selector:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
#  type: LoadBalancer
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.2.0
  name: ingress-nginx-controller-admission
  namespace: default
spec:
  ports:
  - appProtocol: https
    name: https-webhook
    port: 443
    targetPort: webhook
  selector:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
  type: ClusterIP
---
apiVersion: apps/v1
#kind: Deployment
kind: DaemonSet
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.2.0
  name: ingress-nginx-controller
  namespace: default
spec:
  minReadySeconds: 0
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app.kubernetes.io/component: controller
      app.kubernetes.io/instance: ingress-nginx
      app.kubernetes.io/name: ingress-nginx
  template:
    metadata:
      labels:
        app.kubernetes.io/component: controller
        app.kubernetes.io/instance: ingress-nginx
        app.kubernetes.io/name: ingress-nginx
    spec:
      containers:
      - args:
        - /nginx-ingress-controller
        - --publish-service=$(POD_NAMESPACE)/ingress-nginx-controller
        - --election-id=ingress-controller-leader
        - --controller-class=k8s.io/ingress-nginx
        - --ingress-class=nginx
        - --configmap=$(POD_NAMESPACE)/ingress-nginx-controller
        - --validating-webhook=:8443
        - --validating-webhook-certificate=/usr/local/certificates/cert
        - --validating-webhook-key=/usr/local/certificates/key
        env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: LD_PRELOAD
          value: /usr/local/lib/libmimalloc.so
        image: k8s.gcr.io/ingress-nginx/controller:v1.2.0@sha256:d8196e3bc1e72547c5dec66d6556c0ff92a23f6d0919b206be170bc90d5f9185
        imagePullPolicy: IfNotPresent
        lifecycle:
          preStop:
            exec:
              command:
              - /wait-shutdown
        livenessProbe:
          failureThreshold: 5
          httpGet:
            path: /healthz
            port: 10254
            scheme: HTTP
          initialDelaySeconds: 10
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 1
        name: controller
        ports:
        - containerPort: 80
          name: http
          protocol: TCP
        - containerPort: 443
          name: https
          protocol: TCP
        - containerPort: 8443
          name: webhook
          protocol: TCP
        readinessProbe:
          failureThreshold: 3
          httpGet:
            path: /healthz
            port: 10254
            scheme: HTTP
          initialDelaySeconds: 10
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 1
        resources:
          requests:
            cpu: 100m
            memory: 90Mi
        securityContext:
          allowPrivilegeEscalation: true
          capabilities:
            add:
            - NET_BIND_SERVICE
            drop:
            - ALL
          runAsUser: 101
        volumeMounts:
        - mountPath: /usr/local/certificates/
          name: webhook-cert
          readOnly: true
#      dnsPolicy: ClusterFirst
      dnsPolicy: ClusterFirstWithHostNet
      hostNetwork: true
      nodeSelector:
        kubernetes.io/os: linux
      serviceAccountName: ingress-nginx
      terminationGracePeriodSeconds: 300
      volumes:
      - name: webhook-cert
        secret:
          secretName: ingress-nginx-admission
---
apiVersion: batch/v1
kind: Job
metadata:
  labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.2.0
  name: ingress-nginx-admission-create
  namespace: default
spec:
  template:
    metadata:
      labels:
        app.kubernetes.io/component: admission-webhook
        app.kubernetes.io/instance: ingress-nginx
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/part-of: ingress-nginx
        app.kubernetes.io/version: 1.2.0
      name: ingress-nginx-admission-create
    spec:
      containers:
      - args:
        - create
        - --host=ingress-nginx-controller-admission,ingress-nginx-controller-admission.$(POD_NAMESPACE).svc
        - --namespace=$(POD_NAMESPACE)
        - --secret-name=ingress-nginx-admission
        env:
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        image: k8s.gcr.io/ingress-nginx/kube-webhook-certgen:v1.1.1@sha256:64d8c73dca984af206adf9d6d7e46aa550362b1d7a01f3a0a91b20cc67868660
        imagePullPolicy: IfNotPresent
        name: create
        securityContext:
          allowPrivilegeEscalation: false
      nodeSelector:
        kubernetes.io/os: linux
      restartPolicy: OnFailure
      securityContext:
        fsGroup: 2000
        runAsNonRoot: true
        runAsUser: 2000
      serviceAccountName: ingress-nginx-admission
---
apiVersion: batch/v1
kind: Job
metadata:
  labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.2.0
  name: ingress-nginx-admission-patch
  namespace: default
spec:
  template:
    metadata:
      labels:
        app.kubernetes.io/component: admission-webhook
        app.kubernetes.io/instance: ingress-nginx
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/part-of: ingress-nginx
        app.kubernetes.io/version: 1.2.0
      name: ingress-nginx-admission-patch
    spec:
      containers:
      - args:
        - patch
        - --webhook-name=ingress-nginx-admission
        - --namespace=$(POD_NAMESPACE)
        - --patch-mutating=false
        - --secret-name=ingress-nginx-admission
        - --patch-failure-policy=Fail
        env:
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        image: k8s.gcr.io/ingress-nginx/kube-webhook-certgen:v1.1.1@sha256:64d8c73dca984af206adf9d6d7e46aa550362b1d7a01f3a0a91b20cc67868660
        imagePullPolicy: IfNotPresent
        name: patch
        securityContext:
          allowPrivilegeEscalation: false
      nodeSelector:
        kubernetes.io/os: linux
      restartPolicy: OnFailure
      securityContext:
        fsGroup: 2000
        runAsNonRoot: true
        runAsUser: 2000
      serviceAccountName: ingress-nginx-admission
---
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.2.0
  name: nginx
spec:
  controller: k8s.io/ingress-nginx
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
  labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.2.0
  name: ingress-nginx-admission
webhooks:
- admissionReviewVersions:
  - v1
  clientConfig:
    service:
      name: ingress-nginx-controller-admission
      namespace: default
      path: /networking/v1/ingresses
  failurePolicy: Fail
  matchPolicy: Equivalent
  name: validate.nginx.ingress.kubernetes.io
  rules:
  - apiGroups:
    - networking.k8s.io
    apiVersions:
    - v1
    operations:
    - CREATE
    - UPDATE
    resources:
    - ingresses
  sideEffects: None
#### 在master节点执行命令
kubectl apply -f deploy.yml

[root@k8s-master01 k8s]# kubectl apply -f ingress-deploy.yml
[root@k8s-master01 k8s]# kubectl get svc
NAME                                 TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
go-test                              ClusterIP   10.100.201.177   <none>        9099/TCP         13h
ingress-nginx-controller             ClusterIP   10.106.55.124    <none>        80/TCP,443/TCP   21s
ingress-nginx-controller-admission   ClusterIP   10.111.49.109    <none>        443/TCP          21s
kubernetes                           ClusterIP   10.96.0.1        <none>        443/TCP          94d
[root@k8s-master01 k8s]# kubectl get daemonset
NAME                       DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR            AGE
ingress-nginx-controller   2         2         0       2            0           kubernetes.io/os=linux   2m51s

7-7 route front-end management page and effect demonstration

C:\Users\Administrator\Desktop\gopaas\go-paas-front\route-create.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="description" content="">
    <meta name="author" content="">
    <link rel="shortcut icon" href="assets/img/logo-fav.png">
    <title>CPaaS</title>
    <link rel="stylesheet" type="text/css" href="assets/lib/perfect-scrollbar/css/perfect-scrollbar.min.css"/>
    <link rel="stylesheet" type="text/css" href="assets/lib/material-design-icons/css/material-design-iconic-font.min.css"/><!--[if lt IE 9]>
    <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
    <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
    <link rel="stylesheet" href="assets/css/style.css" type="text/css"/>
  </head>
  <body>
    <div class="be-wrapper">
      <nav class="navbar navbar-default navbar-fixed-top be-top-header">
        <div class="container-fluid">
          <div class="navbar-header"><a href="index.html" class="navbar-brand"></a>
          </div>
          <div class="be-right-navbar">
            <ul class="nav navbar-nav navbar-right be-user-nav">
              <li class="dropdown"><a href="#" data-toggle="dropdown" role="button" aria-expanded="false" class="dropdown-toggle"><img src="assets/img/avatar.png" alt="Avatar"><span class="user-name">Túpac Amaru</span></a>
                <ul role="menu" class="dropdown-menu">
                  <li>
                    <div class="user-info">
                      <div class="user-name">Túpac Amaru</div>
                      <div class="user-position online">Available</div>
                    </div>
                  </li>
                  <li><a href="#"><span class="icon mdi mdi-face"></span> Account</a></li>
                  <li><a href="#"><span class="icon mdi mdi-settings"></span> Settings</a></li>
                  <li><a href="#"><span class="icon mdi mdi-power"></span> Logout</a></li>
                </ul>
              </li>
            </ul>
            <div class="page-title"><span>Form Validation</span></div>
            <ul class="nav navbar-nav navbar-right be-icons-nav">
              <li class="dropdown"><a href="#" role="button" aria-expanded="false" class="be-toggle-right-sidebar"><span class="icon mdi mdi-settings"></span></a></li>
              <li class="dropdown"><a href="#" data-toggle="dropdown" role="button" aria-expanded="false" class="dropdown-toggle"><span class="icon mdi mdi-notifications"></span><span class="indicator"></span></a>
                <ul class="dropdown-menu be-notifications">
                  <li>
                    <div class="title">Notifications<span class="badge">3</span></div>
                    <div class="list">
                      <div class="be-scroller">
                        <div class="content">
                          <ul>
                            <li class="notification notification-unread"><a href="#">
                                <div class="image"><img src="assets/img/avatar2.png" alt="Avatar"></div>
                                <div class="notification-info">
                                  <div class="text"><span class="user-name">Jessica Caruso</span> accepted your invitation to join the team.</div><span class="date">2 min ago</span>
                                </div></a></li>
                            <li class="notification"><a href="#">
                                <div class="image"><img src="assets/img/avatar3.png" alt="Avatar"></div>
                                <div class="notification-info">
                                  <div class="text"><span class="user-name">Joel King</span> is now following you</div><span class="date">2 days ago</span>
                                </div></a></li>
                            <li class="notification"><a href="#">
                                <div class="image"><img src="assets/img/avatar4.png" alt="Avatar"></div>
                                <div class="notification-info">
                                  <div class="text"><span class="user-name">John Doe</span> is watching your main repository</div><span class="date">2 days ago</span>
                                </div></a></li>
                            <li class="notification"><a href="#">
                                <div class="image"><img src="assets/img/avatar5.png" alt="Avatar"></div>
                                <div class="notification-info"><span class="text"><span class="user-name">Emily Carter</span> is now following you</span><span class="date">5 days ago</span></div></a></li>
                          </ul>
                        </div>
                      </div>
                    </div>
                    <div class="footer"> <a href="#">View all notifications</a></div>
                  </li>
                </ul>
              </li>
              <li class="dropdown"><a href="#" data-toggle="dropdown" role="button" aria-expanded="false" class="dropdown-toggle"><span class="icon mdi mdi-apps"></span></a>
                <ul class="dropdown-menu be-connections">
                  <li>
                    <div class="list">
                      <div class="content">
                        <div class="row">
                          <div class="col-xs-4"><a href="#" class="connection-item"><img src="assets/img/github.png" alt="Github"><span>GitHub</span></a></div>
                          <div class="col-xs-4"><a href="#" class="connection-item"><img src="assets/img/bitbucket.png" alt="Bitbucket"><span>Bitbucket</span></a></div>
                          <div class="col-xs-4"><a href="#" class="connection-item"><img src="assets/img/slack.png" alt="Slack"><span>Slack</span></a></div>
                        </div>
                        <div class="row">
                          <div class="col-xs-4"><a href="#" class="connection-item"><img src="assets/img/dribbble.png" alt="Dribbble"><span>Dribbble</span></a></div>
                          <div class="col-xs-4"><a href="#" class="connection-item"><img src="assets/img/mail_chimp.png" alt="Mail Chimp"><span>Mail Chimp</span></a></div>
                          <div class="col-xs-4"><a href="#" class="connection-item"><img src="assets/img/dropbox.png" alt="Dropbox"><span>Dropbox</span></a></div>
                        </div>
                      </div>
                    </div>
                    <div class="footer"> <a href="#">More</a></div>
                  </li>
                </ul>
              </li>
            </ul>
          </div>
        </div>
      </nav>
<!--
  侧边栏-开始
-->
      <div class="be-left-sidebar">
        <div class="left-sidebar-wrapper"><a href="#" class="left-sidebar-toggle">看版</a>
          <div class="left-sidebar-spacer">
            <div class="left-sidebar-scroll">
              <div class="left-sidebar-content">
                <ul class="sidebar-elements">
                  <li class="divider">菜单</li>
                  <li class="parent"><a href="#"><i class="icon mdi mdi-home"></i><span>控制台</span></a>
                  </li>
                  <li class="parent"><a href="#"><i class="icon mdi mdi-face"></i><span>应用管理</span></a>
                    <ul class="sub-menu">
                      <li ><a href="pod-index.html">添加应用</a>
                      </li>
                    </ul>
                  </li>
                  <li class="parent"><a href="charts.html"><i class="icon mdi mdi-chart-donut"></i><span>服务管理</span></a>
                    <ul class="sub-menu">
                      <li ><a href="svc-index.html">添加服务</a>
                      </li>
                    </ul>
                  </li>
                  <li class="parent"><a href="#"><i class="icon mdi mdi-dot-circle"></i><span>域名管理</span></a>
                    <ul class="sub-menu">
                      <li class="active"><a href="route-index.html">添加路由</a>
                      </li>
                    </ul>
                  </li>
                  <li class="parent"><a href="#"><i class="icon mdi mdi-border-all"></i><span>中间件</span></a>
                    <ul class="sub-menu">
                      <li><a href="tables-general.html">General</a>
                      </li>
                      <li><a href="tables-datatables.html">Data Tables</a>
                      </li>
                      <li><a href="tables-filters.html"><span class="label label-primary pull-right">New</span>Table Filters</a>
                      </li>
                    </ul>
                  </li>
                  <li class="parent"><a href="#"><i class="icon mdi mdi-layers"></i><span>应用市场</span></a>
                    <ul class="sub-menu">
                      <li><a href="pages-blank.html">Blank Page</a>
                      </li>
                      <li><a href="pages-blank-header.html">Blank Page Header</a>
                      </li>
                      <li><a href="pages-login.html">Login</a>
                      </li>
                      <li><a href="pages-login2.html">Login v2</a>
                      </li>
                      <li><a href="pages-404.html">404 Page</a>
                      </li>
                      <li><a href="pages-sign-up.html">Sign Up</a>
                      </li>
                      <li><a href="pages-forgot-password.html">Forgot Password</a>
                      </li>
                      <li><a href="pages-profile.html">Profile</a>
                      </li>
                      <li><a href="pages-pricing-tables.html">Pricing Tables</a>
                      </li>
                      <li><a href="pages-pricing-tables2.html">Pricing Tables v2</a>
                      </li>
                      <li><a href="pages-timeline.html">Timeline</a>
                      </li>
                      <li><a href="pages-timeline2.html">Timeline v2</a>
                      </li>
                      <li><a href="pages-invoice.html"><span class="label label-primary pull-right">New</span>Invoice</a>
                      </li>
                      <li><a href="pages-calendar.html">Calendar</a>
                      </li>
                      <li><a href="pages-gallery.html">Gallery</a>
                      </li>
                      <li><a href="pages-code-editor.html"><span class="label label-primary pull-right">New    </span>Code Editor</a>
                      </li>
                      <li><a href="pages-booking.html"><span class="label label-primary pull-right">New</span>Booking</a>
                      </li>
                      <li><a href="pages-loaders.html"><span class="label label-primary pull-right">New</span>Loaders</a>
                      </li>
                    </ul>
                  </li>

                </ul>
              </div>
            </div>
          </div>
          <div class="progress-widget">
            <div class="progress-data"><span class="progress-value">60%</span><span class="name">Current Project</span></div>
            <div class="progress">
              <div style="width: 60%;" class="progress-bar progress-bar-primary"></div>
            </div>
          </div>
        </div>
      </div>
<!--
侧边栏-结束
-->
      <div class="be-content">
        <div class="main-content container-fluid">
          <div class="row">
            <div class="col-md-12">
              <div class="panel panel-default panel-border-color panel-border-color-primary">
                <div class="panel-heading panel-heading-divider">添加路由<span class="panel-subtitle"></span></div>
                <div class="panel-body">
                  <form action="http://localhost:8080/routeApi/addRoute" class="form-horizontal group-border-dashed" method="post" >
                    <div class="form-group">
                      <label class="col-sm-3 control-label">路由名称:</label>
                      <div class="col-sm-6">
                        <input type="text" required=""   class="form-control" id="route_name"  name="route_name">
                      </div>
                    </div>
                    <div class="form-group">
                      <label class="col-sm-3 control-label">命名空间:</label>
                      <div class="col-sm-6">
                        <input type="text" required=""   class="form-control" id="route_namespace"  name="route_namespace" value="default">
                      </div>
                    </div>
                    <div class="form-group">
                      <label class="col-sm-3 control-label">设置域名:</label>
                      <div class="col-sm-6">
                        <input type="text" required=""   class="form-control" id="route_host"  name="route_host"  >
                      </div>
                    </div>
                    <div class="form-group">
                      <label class="col-sm-3 control-label">设置路径:</label>
                      <div class="col-sm-6">
                        <input type="text" required=""   class="form-control"  name="route_path_name" id="route_path_name">
                      </div>
                    </div>
                    <div class="form-group">
                      <label class="col-sm-3 control-label">后端服务:</label>
                      <div class="col-sm-6">
                        <input type="text" required=""   class="form-control" id=""  name="route_backend_service" id="route_backend_service">
                      </div>
                    </div>

                    <div class="form-group">
                      <label class="col-sm-3 control-label">后端服务端口:</label>
                      <div class="col-sm-6">
                        <input type="text" required=""   class="form-control"  name="route_backend_service_port" id="route_backend_service_port">
                      </div>
                    </div>

                    <div class="form-group">
                      <div class="col-sm-2 col-sm-10">
                        <button type="submit" class="btn btn-space btn-primary">添加路由</button>
                        <button class="btn btn-space btn-default">Cancel</button>
                      </div>
                    </div>
                  </form>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
      
    </div>
    <script src="assets/lib/jquery/jquery.min.js" type="text/javascript"></script>
    <script src="assets/lib/perfect-scrollbar/js/perfect-scrollbar.jquery.min.js" type="text/javascript"></script>
    <script src="assets/js/main.js" type="text/javascript"></script>
    <script src="assets/lib/bootstrap/dist/js/bootstrap.min.js" type="text/javascript"></script>
    <script src="assets/lib/parsley/parsley.min.js" type="text/javascript"></script>
 
        <script type="text/javascript">
          $(document).ready(function(){
          App.init();
          });
        
    //获取url中的参数
    function getUrlParam(name) {
     var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)"); //构造一个含有目标参数的正则表达式对象
     var r = window.location.search.substr(1).match(reg); //匹配目标参数
     if (r != null) return unescape(r[2]); return null; //返回参数值
    }
     
        </script>
    
  </body>
</html>

C:\Users\Administrator\Desktop\gopaas\go-paas-front\route-detail.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="description" content="">
    <meta name="author" content="">
    <link rel="shortcut icon" href="assets/img/logo-fav.png">
    <title>CPaaS</title>
    <link rel="stylesheet" type="text/css" href="assets/lib/perfect-scrollbar/css/perfect-scrollbar.min.css"/>
    <link rel="stylesheet" type="text/css" href="assets/lib/material-design-icons/css/material-design-iconic-font.min.css"/><!--[if lt IE 9]>
    <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
    <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
    <link rel="stylesheet" href="assets/css/style.css" type="text/css"/>
  </head>
  <body>
    <div class="be-wrapper">
      <nav class="navbar navbar-default navbar-fixed-top be-top-header">
        <div class="container-fluid">
          <div class="navbar-header"><a href="index.html" class="navbar-brand"></a>
          </div>
          <div class="be-right-navbar">
            <ul class="nav navbar-nav navbar-right be-user-nav">
              <li class="dropdown"><a href="#" data-toggle="dropdown" role="button" aria-expanded="false" class="dropdown-toggle"><img src="assets/img/avatar.png" alt="Avatar"><span class="user-name">Túpac Amaru</span></a>
                <ul role="menu" class="dropdown-menu">
                  <li>
                    <div class="user-info">
                      <div class="user-name">Túpac Amaru</div>
                      <div class="user-position online">Available</div>
                    </div>
                  </li>
                  <li><a href="#"><span class="icon mdi mdi-face"></span> Account</a></li>
                  <li><a href="#"><span class="icon mdi mdi-settings"></span> Settings</a></li>
                  <li><a href="#"><span class="icon mdi mdi-power"></span> Logout</a></li>
                </ul>
              </li>
            </ul>
            <div class="page-title"><span>Form Validation</span></div>
            <ul class="nav navbar-nav navbar-right be-icons-nav">
              <li class="dropdown"><a href="#" role="button" aria-expanded="false" class="be-toggle-right-sidebar"><span class="icon mdi mdi-settings"></span></a></li>
              <li class="dropdown"><a href="#" data-toggle="dropdown" role="button" aria-expanded="false" class="dropdown-toggle"><span class="icon mdi mdi-notifications"></span><span class="indicator"></span></a>
                <ul class="dropdown-menu be-notifications">
                  <li>
                    <div class="title">Notifications<span class="badge">3</span></div>
                    <div class="list">
                      <div class="be-scroller">
                        <div class="content">
                          <ul>
                            <li class="notification notification-unread"><a href="#">
                                <div class="image"><img src="assets/img/avatar2.png" alt="Avatar"></div>
                                <div class="notification-info">
                                  <div class="text"><span class="user-name">Jessica Caruso</span> accepted your invitation to join the team.</div><span class="date">2 min ago</span>
                                </div></a></li>
                            <li class="notification"><a href="#">
                                <div class="image"><img src="assets/img/avatar3.png" alt="Avatar"></div>
                                <div class="notification-info">
                                  <div class="text"><span class="user-name">Joel King</span> is now following you</div><span class="date">2 days ago</span>
                                </div></a></li>
                            <li class="notification"><a href="#">
                                <div class="image"><img src="assets/img/avatar4.png" alt="Avatar"></div>
                                <div class="notification-info">
                                  <div class="text"><span class="user-name">John Doe</span> is watching your main repository</div><span class="date">2 days ago</span>
                                </div></a></li>
                            <li class="notification"><a href="#">
                                <div class="image"><img src="assets/img/avatar5.png" alt="Avatar"></div>
                                <div class="notification-info"><span class="text"><span class="user-name">Emily Carter</span> is now following you</span><span class="date">5 days ago</span></div></a></li>
                          </ul>
                        </div>
                      </div>
                    </div>
                    <div class="footer"> <a href="#">View all notifications</a></div>
                  </li>
                </ul>
              </li>
              <li class="dropdown"><a href="#" data-toggle="dropdown" role="button" aria-expanded="false" class="dropdown-toggle"><span class="icon mdi mdi-apps"></span></a>
                <ul class="dropdown-menu be-connections">
                  <li>
                    <div class="list">
                      <div class="content">
                        <div class="row">
                          <div class="col-xs-4"><a href="#" class="connection-item"><img src="assets/img/github.png" alt="Github"><span>GitHub</span></a></div>
                          <div class="col-xs-4"><a href="#" class="connection-item"><img src="assets/img/bitbucket.png" alt="Bitbucket"><span>Bitbucket</span></a></div>
                          <div class="col-xs-4"><a href="#" class="connection-item"><img src="assets/img/slack.png" alt="Slack"><span>Slack</span></a></div>
                        </div>
                        <div class="row">
                          <div class="col-xs-4"><a href="#" class="connection-item"><img src="assets/img/dribbble.png" alt="Dribbble"><span>Dribbble</span></a></div>
                          <div class="col-xs-4"><a href="#" class="connection-item"><img src="assets/img/mail_chimp.png" alt="Mail Chimp"><span>Mail Chimp</span></a></div>
                          <div class="col-xs-4"><a href="#" class="connection-item"><img src="assets/img/dropbox.png" alt="Dropbox"><span>Dropbox</span></a></div>
                        </div>
                      </div>
                    </div>
                    <div class="footer"> <a href="#">More</a></div>
                  </li>
                </ul>
              </li>
            </ul>
          </div>
        </div>
      </nav>
<!--
  侧边栏-开始
-->
      <div class="be-left-sidebar">
        <div class="left-sidebar-wrapper"><a href="#" class="left-sidebar-toggle">看版</a>
          <div class="left-sidebar-spacer">
            <div class="left-sidebar-scroll">
              <div class="left-sidebar-content">
                <ul class="sidebar-elements">
                  <li class="divider">菜单</li>
                  <li class="parent"><a href="#"><i class="icon mdi mdi-home"></i><span>控制台</span></a>
                  </li>
                  <li class="parent"><a href="#"><i class="icon mdi mdi-face"></i><span>应用管理</span></a>
                    <ul class="sub-menu">
                      <li ><a href="pod-index.html">添加应用</a>
                      </li>
                    </ul>
                  </li>
                  <li class="parent"><a href="charts.html"><i class="icon mdi mdi-chart-donut"></i><span>服务管理</span></a>
                    <ul class="sub-menu">
                      <li ><a href="svc-index.html">添加服务</a>
                      </li>
                    </ul>
                  </li>
                  <li class="parent"><a href="#"><i class="icon mdi mdi-dot-circle"></i><span>域名管理</span></a>
                    <ul class="sub-menu">
                      <li class="active"><a href="route-index.html">添加路由</a>
                      </li>
                    </ul>
                  </li>
                  <li class="parent"><a href="#"><i class="icon mdi mdi-border-all"></i><span>中间件</span></a>
                    <ul class="sub-menu">
                      <li><a href="tables-general.html">General</a>
                      </li>
                      <li><a href="tables-datatables.html">Data Tables</a>
                      </li>
                      <li><a href="tables-filters.html"><span class="label label-primary pull-right">New</span>Table Filters</a>
                      </li>
                    </ul>
                  </li>
                  <li class="parent"><a href="#"><i class="icon mdi mdi-layers"></i><span>应用市场</span></a>
                    <ul class="sub-menu">
                      <li><a href="pages-blank.html">Blank Page</a>
                      </li>
                      <li><a href="pages-blank-header.html">Blank Page Header</a>
                      </li>
                      <li><a href="pages-login.html">Login</a>
                      </li>
                      <li><a href="pages-login2.html">Login v2</a>
                      </li>
                      <li><a href="pages-404.html">404 Page</a>
                      </li>
                      <li><a href="pages-sign-up.html">Sign Up</a>
                      </li>
                      <li><a href="pages-forgot-password.html">Forgot Password</a>
                      </li>
                      <li><a href="pages-profile.html">Profile</a>
                      </li>
                      <li><a href="pages-pricing-tables.html">Pricing Tables</a>
                      </li>
                      <li><a href="pages-pricing-tables2.html">Pricing Tables v2</a>
                      </li>
                      <li><a href="pages-timeline.html">Timeline</a>
                      </li>
                      <li><a href="pages-timeline2.html">Timeline v2</a>
                      </li>
                      <li><a href="pages-invoice.html"><span class="label label-primary pull-right">New</span>Invoice</a>
                      </li>
                      <li><a href="pages-calendar.html">Calendar</a>
                      </li>
                      <li><a href="pages-gallery.html">Gallery</a>
                      </li>
                      <li><a href="pages-code-editor.html"><span class="label label-primary pull-right">New    </span>Code Editor</a>
                      </li>
                      <li><a href="pages-booking.html"><span class="label label-primary pull-right">New</span>Booking</a>
                      </li>
                      <li><a href="pages-loaders.html"><span class="label label-primary pull-right">New</span>Loaders</a>
                      </li>
                    </ul>
                  </li>

                </ul>
              </div>
            </div>
          </div>
          <div class="progress-widget">
            <div class="progress-data"><span class="progress-value">60%</span><span class="name">Current Project</span></div>
            <div class="progress">
              <div style="width: 60%;" class="progress-bar progress-bar-primary"></div>
            </div>
          </div>
        </div>
      </div>
<!--
侧边栏-结束
-->
      <div class="be-content">
        <div class="main-content container-fluid">
          <div class="row">
            <div class="col-md-12">
              <div class="panel panel-default panel-border-color panel-border-color-primary">
                <div class="panel-heading panel-heading-divider">路由详情<span class="panel-subtitle">查看路由详情信息</span></div>
                <div class="panel-body">
                  <form action="#" class="form-horizontal group-border-dashed">
                    <div class="form-group">
                      <label class="col-sm-3 control-label">路由ID:</label>
                      <div class="col-sm-6">
                        <input type="text" required="" readonly  class="form-control" id="route_id" name="route_id">
                      </div>
                    </div>
                    <div class="form-group">
                      <label class="col-sm-3 control-label">路由名称:</label>
                      <div class="col-sm-6">
                        <input type="text" required="" readonly  class="form-control" id="route_name"  name="route_name">
                      </div>
                    </div>
                    <div class="form-group">
                      <label class="col-sm-3 control-label">命名空间:</label>
                      <div class="col-sm-6">
                        <input type="text" required="" readonly  class="form-control" id="route_namespace"  name="route_namespace">
                      </div>
                    </div>
                    <div class="form-group">
                      <label class="col-sm-3 control-label">设置域名:</label>
                      <div class="col-sm-6">
                        <input type="text" required=""   class="form-control" id="route_host"  name="route_host"  >
                      </div>
                    </div>
                    <div class="form-group">
                      <label class="col-sm-3 control-label">设置路径:</label>
                      <div class="col-sm-6">
                        <input type="text" required=""   class="form-control"  name="route_path_name" id="route_path_name">
                      </div>
                    </div>
                    <div class="form-group">
                      <label class="col-sm-3 control-label">后端服务:</label>
                      <div class="col-sm-6">
                        <input type="text" required=""   class="form-control"   name="route_backend_service" id="route_backend_service">
                      </div>
                    </div>

                    <div class="form-group">
                      <label class="col-sm-3 control-label">后端服务端口:</label>
                      <div class="col-sm-6">
                        <input type="text" required=""   class="form-control"  name="route_backend_service_port" id="route_backend_service_port">
                      </div>
                    </div>



                  </form>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
      
    </div>
    <script src="assets/lib/jquery/jquery.min.js" type="text/javascript"></script>
    <script src="assets/lib/perfect-scrollbar/js/perfect-scrollbar.jquery.min.js" type="text/javascript"></script>
    <script src="assets/js/main.js" type="text/javascript"></script>
    <script src="assets/lib/bootstrap/dist/js/bootstrap.min.js" type="text/javascript"></script>
    <script src="assets/lib/parsley/parsley.min.js" type="text/javascript"></script>
        <script type="text/javascript">
          $(document).ready(function(){
            App.init();
      	   $('form').parsley();
          $.ajax({
              type:"get",
              url:"http://127.0.0.1:8080/routeApi/findRouteById?route_id="+getUrlParam('route_id'),
              success: function(data){
                console.log(data);
                if(data.id != "" || data.id != null || data.id != undefined){
                  $('#route_id').val(data.id);
                  $("#route_name").val(data.route_name);
                  $('#route_namespace').val(data.route_namespace);
                  $('#route_host').val(data.route_host);
                  //这里有多个要循环
                  $('#route_path_name').val(data.route_path[0]['route_path_name']);
                  $('#route_backend_service').val(data.route_path[0]['route_backend_service']);
                  $('#route_backend_service_port').val(data.route_path[0]['route_backend_service_port']);

                }else{
                  console.log(data);
                }
              },
              error: function(result){
                console.log(result);
              }
            });
          });
        
    //获取url中的参数
    function getUrlParam(name) {
     var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)"); //构造一个含有目标参数的正则表达式对象
     var r = window.location.search.substr(1).match(reg); //匹配目标参数
     if (r != null) return unescape(r[2]); return null; //返回参数值
    }
     
        </script>
    
  </body>
</html>

C:\Users\Administrator\Desktop\gopaas\go-paas-front\route-index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="description" content="">
    <meta name="author" content="">
    <link rel="shortcut icon" href="assets/img/logo-fav.png">
    <title>CPaaS</title>
    <link rel="stylesheet" type="text/css" href="assets/lib/perfect-scrollbar/css/perfect-scrollbar.min.css"/>
    <link rel="stylesheet" type="text/css" href="assets/lib/material-design-icons/css/material-design-iconic-font.min.css"/><!--[if lt IE 9]>
    <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
    <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
    <link rel="stylesheet" type="text/css" href="assets/lib/datatables/css/dataTables.bootstrap.min.css"/>
    <link rel="stylesheet" href="assets/css/style.css" type="text/css"/>
  </head>
  <body>
    <div class="be-wrapper">
      <nav class="navbar navbar-default navbar-fixed-top be-top-header">
        <div class="container-fluid">
          <div class="navbar-header"><a href="index.html" class="navbar-brand"></a>
          </div>
          <div class="be-right-navbar">
            <ul class="nav navbar-nav navbar-right be-user-nav">
              <li class="dropdown"><a href="#" data-toggle="dropdown" role="button" aria-expanded="false" class="dropdown-toggle"><img src="assets/img/avatar.png" alt="Avatar"><span class="user-name">Túpac Amaru</span></a>
                <ul role="menu" class="dropdown-menu">
                  <li>
                    <div class="user-info">
                      <div class="user-name">wu123</div>
                      <div class="user-position online">在线</div>
                    </div>
                  </li>
                  <li><a href="#"><span class="icon mdi mdi-face"></span> 账户</a></li>
                  <li><a href="#"><span class="icon mdi mdi-settings"></span> 设置</a></li>
                  <li><a href="#"><span class="icon mdi mdi-power"></span> 推出登录</a></li>
                </ul>
              </li>
            </ul>
            <div class="page-title"></div>
            <ul class="nav navbar-nav navbar-right be-icons-nav">
              <li class="dropdown"><a href="#" role="button" aria-expanded="false" class="be-toggle-right-sidebar"><span class="icon mdi mdi-settings"></span></a></li>
              <li class="dropdown"><a href="#" data-toggle="dropdown" role="button" aria-expanded="false" class="dropdown-toggle"><span class="icon mdi mdi-notifications"></span><span class="indicator"></span></a>
                <ul class="dropdown-menu be-notifications">
                  <li>
                    <div class="title">消息提醒<span class="badge">3</span></div>
                    <div class="list">
                      <div class="be-scroller">
                        <div class="content">
                          <ul>
                            <li class="notification notification-unread"><a href="#">
                                <div class="image"><img src="assets/img/avatar2.png" alt="Avatar"></div>
                                <div class="notification-info">
                                  <div class="text"><span class="user-name">Jessica Caruso</span> accepted your invitation to join the team.</div><span class="date">2 min ago</span>
                                </div></a></li>
                            <li class="notification"><a href="#">
                                <div class="image"><img src="assets/img/avatar3.png" alt="Avatar"></div>
                                <div class="notification-info">
                                  <div class="text"><span class="user-name">Joel King</span> is now following you</div><span class="date">2 days ago</span>
                                </div></a></li>
                            <li class="notification"><a href="#">
                                <div class="image"><img src="assets/img/avatar4.png" alt="Avatar"></div>
                                <div class="notification-info">
                                  <div class="text"><span class="user-name">John Doe</span> is watching your main repository</div><span class="date">2 days ago</span>
                                </div></a></li>
                            <li class="notification"><a href="#">
                                <div class="image"><img src="assets/img/avatar5.png" alt="Avatar"></div>
                                <div class="notification-info"><span class="text"><span class="user-name">Emily Carter</span> is now following you</span><span class="date">5 days ago</span></div></a></li>
                          </ul>
                        </div>
                      </div>
                    </div>
                    <div class="footer"> <a href="#">View all notifications</a></div>
                  </li>
                </ul>
              </li>
              <li class="dropdown"><a href="#" data-toggle="dropdown" role="button" aria-expanded="false" class="dropdown-toggle"><span class="icon mdi mdi-apps"></span></a>
                <ul class="dropdown-menu be-connections">
                  <li>
                    <div class="list">
                      <div class="content">
                        <div class="row">
                          <div class="col-xs-4"><a href="#" class="connection-item"><img src="assets/img/github.png" alt="Github"><span>GitHub</span></a></div>
                          <div class="col-xs-4"><a href="#" class="connection-item"><img src="assets/img/bitbucket.png" alt="Bitbucket"><span>Bitbucket</span></a></div>
                          <div class="col-xs-4"><a href="#" class="connection-item"><img src="assets/img/slack.png" alt="Slack"><span>Slack</span></a></div>
                        </div>
                        <div class="row">
                          <div class="col-xs-4"><a href="#" class="connection-item"><img src="assets/img/dribbble.png" alt="Dribbble"><span>Dribbble</span></a></div>
                          <div class="col-xs-4"><a href="#" class="connection-item"><img src="assets/img/mail_chimp.png" alt="Mail Chimp"><span>Mail Chimp</span></a></div>
                          <div class="col-xs-4"><a href="#" class="connection-item"><img src="assets/img/dropbox.png" alt="Dropbox"><span>Dropbox</span></a></div>
                        </div>
                      </div>
                    </div>
                    <div class="footer"> <a href="#">More</a></div>
                  </li>
                </ul>
              </li>
            </ul>
          </div>
        </div>
      </nav>
<!--
  侧边栏-开始
-->
<div class="be-left-sidebar">
  <div class="left-sidebar-wrapper"><a href="#" class="left-sidebar-toggle">看版</a>
    <div class="left-sidebar-spacer">
      <div class="left-sidebar-scroll">
        <div class="left-sidebar-content">
          <ul class="sidebar-elements">
            <li class="divider">菜单</li>
            <li class="parent"><a href="#"><i class="icon mdi mdi-home"></i><span>控制台</span></a>
            </li>
            <li class="parent"><a href="#"><i class="icon mdi mdi-face"></i><span>应用管理</span></a>
              <ul class="sub-menu">
                <li ><a href="pod-index.html">添加应用</a>
                </li>
              </ul>
            </li>
            <li class="parent"><a href="charts.html"><i class="icon mdi mdi-chart-donut"></i><span>服务管理</span></a>
              <ul class="sub-menu">
                <li ><a href="svc-index.html">添加服务</a>
                </li>
              </ul>
            </li>
            <li class="parent"><a href="#"><i class="icon mdi mdi-dot-circle"></i><span>域名管理</span></a>
              <ul class="sub-menu">
                <li class="active"><a href="route-index.html">添加路由</a>
                </li>
              </ul>
            </li>
            <li class="parent"><a href="#"><i class="icon mdi mdi-border-all"></i><span>中间件</span></a>
              <ul class="sub-menu">
                <li><a href="tables-general.html">General</a>
                </li>
                <li><a href="tables-datatables.html">Data Tables</a>
                </li>
                <li><a href="tables-filters.html"><span class="label label-primary pull-right">New</span>Table Filters</a>
                </li>
              </ul>
            </li>
            <li class="parent"><a href="#"><i class="icon mdi mdi-layers"></i><span>应用市场</span></a>
              <ul class="sub-menu">
                <li><a href="pages-blank.html">Blank Page</a>
                </li>
                <li><a href="pages-blank-header.html">Blank Page Header</a>
                </li>
                <li><a href="pages-login.html">Login</a>
                </li>
                <li><a href="pages-login2.html">Login v2</a>
                </li>
                <li><a href="pages-404.html">404 Page</a>
                </li>
                <li><a href="pages-sign-up.html">Sign Up</a>
                </li>
                <li><a href="pages-forgot-password.html">Forgot Password</a>
                </li>
                <li><a href="pages-profile.html">Profile</a>
                </li>
                <li><a href="pages-pricing-tables.html">Pricing Tables</a>
                </li>
                <li><a href="pages-pricing-tables2.html">Pricing Tables v2</a>
                </li>
                <li><a href="pages-timeline.html">Timeline</a>
                </li>
                <li><a href="pages-timeline2.html">Timeline v2</a>
                </li>
                <li><a href="pages-invoice.html"><span class="label label-primary pull-right">New</span>Invoice</a>
                </li>
                <li><a href="pages-calendar.html">Calendar</a>
                </li>
                <li><a href="pages-gallery.html">Gallery</a>
                </li>
                <li><a href="pages-code-editor.html"><span class="label label-primary pull-right">New    </span>Code Editor</a>
                </li>
                <li><a href="pages-booking.html"><span class="label label-primary pull-right">New</span>Booking</a>
                </li>
                <li><a href="pages-loaders.html"><span class="label label-primary pull-right">New</span>Loaders</a>
                </li>
              </ul>
            </li>
         
          </ul>
        </div>
      </div>
    </div>
    <div class="progress-widget">
      <div class="progress-data"><span class="progress-value">60%</span><span class="name">Current Project</span></div>
      <div class="progress">
        <div style="width: 60%;" class="progress-bar progress-bar-primary"></div>
      </div>
    </div>
  </div>
</div>
<!--
侧边栏-结束
-->
      <div class="be-content">
        <div class="page-head">
          <h2 class="page-head-title">路由管理</h2>
          <ol class="breadcrumb page-head-nav">
            <li><a href="#">控制台</a></li>
            <li><a href="#">路由管理</a></li>
            <li class="active">路由列表</li>
          </ol>
        </div>
        <div class="main-content container-fluid">
          <div class="row">
            <div class="col-sm-12">
              <div class="panel panel-default panel-table">
                <div class="panel-heading">
                  <a href="route-create.html"><button class="btn btn-space btn-primary">添加路由</button></a>
                  <div class="tools dropdown"><span class="icon mdi mdi-download"></span><a href="#" type="button" data-toggle="dropdown" class="dropdown-toggle"><span class="icon mdi mdi-more-vert"></span></a>
                    <ul role="menu" class="dropdown-menu pull-right">
                      <li><a href="#">Action</a></li>
                      <li><a href="#">Another action</a></li>
                      <li><a href="#">Something else here</a></li>
                      <li class="divider"></li>
                      <li><a href="#">Separated link</a></li>
                    </ul>
                  </div>
                </div>
                <div class="panel-body">
                  <table id="table1" class="table table-striped table-hover table-fw-widget">
                    <thead>
                      <tr>
                        <th>ID</th>
                        <th>域名</th>
                        <th>命名空间</th>
                        <th>路径</th>
                        <th>操作</th>
                      </tr>
                    </thead>
                    <tbody id="table-data">
                    </tbody>
                  </table>
                </div>
              </div>
            </div>
          </div>
       
        </div>
      </div>
      <nav class="be-right-sidebar">
        <div class="sb-content">
          <div class="tab-navigation">
            <ul role="tablist" class="nav nav-tabs nav-justified">
              <li role="presentation" class="active"><a href="#tab1" aria-controls="tab1" role="tab" data-toggle="tab">Chat</a></li>
              <li role="presentation"><a href="#tab2" aria-controls="tab2" role="tab" data-toggle="tab">Todo</a></li>
              <li role="presentation"><a href="#tab3" aria-controls="tab3" role="tab" data-toggle="tab">Settings</a></li>
            </ul>
          </div>
          <div class="tab-panel">
            <div class="tab-content">
              <div id="tab1" role="tabpanel" class="tab-pane tab-chat active">
                <div class="chat-contacts">
                  <div class="chat-sections">
                    <div class="be-scroller">
                      <div class="content">
                        <h2>Recent</h2>
                        <div class="contact-list contact-list-recent">
                          <div class="user"><a href="#"><img src="assets/img/avatar1.png" alt="Avatar">
                              <div class="user-data"><span class="status away"></span><span class="name">Claire Sassu</span><span class="message">Can you share the...</span></div></a></div>
                          <div class="user"><a href="#"><img src="assets/img/avatar2.png" alt="Avatar">
                              <div class="user-data"><span class="status"></span><span class="name">Maggie jackson</span><span class="message">I confirmed the info.</span></div></a></div>
                          <div class="user"><a href="#"><img src="assets/img/avatar3.png" alt="Avatar">
                              <div class="user-data"><span class="status offline"></span><span class="name">Joel King		</span><span class="message">Ready for the meeti...</span></div></a></div>
                        </div>
                        <h2>Contacts</h2>
                        <div class="contact-list">
                          <div class="user"><a href="#"><img src="assets/img/avatar4.png" alt="Avatar">
                              <div class="user-data2"><span class="status"></span><span class="name">Mike Bolthort</span></div></a></div>
                          <div class="user"><a href="#"><img src="assets/img/avatar5.png" alt="Avatar">
                              <div class="user-data2"><span class="status"></span><span class="name">Maggie jackson</span></div></a></div>
                          <div class="user"><a href="#"><img src="assets/img/avatar6.png" alt="Avatar">
                              <div class="user-data2"><span class="status offline"></span><span class="name">Jhon Voltemar</span></div></a></div>
                        </div>
                      </div>
                    </div>
                  </div>
                  <div class="bottom-input">
                    <input type="text" placeholder="Search..." name="q"><span class="mdi mdi-search"></span>
                  </div>
                </div>
                <div class="chat-window">
                  <div class="title">
                    <div class="user"><img src="assets/img/avatar2.png" alt="Avatar">
                      <h2>Maggie jackson</h2><span>Active 1h ago</span>
                    </div><span class="icon return mdi mdi-chevron-left"></span>
                  </div>
                  <div class="chat-messages">
                    <div class="be-scroller">
                      <div class="content">
                        <ul>
                          <li class="friend">
                            <div class="msg">Hello</div>
                          </li>
                          <li class="self">
                            <div class="msg">Hi, how are you?</div>
                          </li>
                          <li class="friend">
                            <div class="msg">Good, I'll need support with my pc</div>
                          </li>
                          <li class="self">
                            <div class="msg">Sure, just tell me what is going on with your computer?</div>
                          </li>
                          <li class="friend">
                            <div class="msg">I don't know it just turns off suddenly</div>
                          </li>
                        </ul>
                      </div>
                    </div>
                  </div>
                  <div class="chat-input">
                    <div class="input-wrapper"><span class="photo mdi mdi-camera"></span>
                      <input type="text" placeholder="Message..." name="q" autocomplete="off"><span class="send-msg mdi mdi-mail-send"></span>
                    </div>
                  </div>
                </div>
              </div>
              <div id="tab2" role="tabpanel" class="tab-pane tab-todo">
                <div class="todo-container">
                  <div class="todo-wrapper">
                    <div class="be-scroller">
                      <div class="todo-content"><span class="category-title">Today</span>
                        <ul class="todo-list">
                          <li>
                            <div class="be-checkbox be-checkbox-sm"><span class="delete mdi mdi-delete"></span>
                              <input id="todo1" type="checkbox" checked="">
                              <label for="todo1">Initialize the project</label>
                            </div>
                          </li>
                          <li>
                            <div class="be-checkbox be-checkbox-sm"><span class="delete mdi mdi-delete"></span>
                              <input id="todo2" type="checkbox">
                              <label for="todo2">Create the main structure</label>
                            </div>
                          </li>
                          <li>
                            <div class="be-checkbox be-checkbox-sm"><span class="delete mdi mdi-delete"></span>
                              <input id="todo3" type="checkbox">
                              <label for="todo3">Updates changes to GitHub</label>
                            </div>
                          </li>
                        </ul><span class="category-title">Tomorrow</span>
                        <ul class="todo-list">
                          <li>
                            <div class="be-checkbox be-checkbox-sm"><span class="delete mdi mdi-delete"></span>
                              <input id="todo4" type="checkbox">
                              <label for="todo4">Initialize the project</label>
                            </div>
                          </li>
                          <li>
                            <div class="be-checkbox be-checkbox-sm"><span class="delete mdi mdi-delete"></span>
                              <input id="todo5" type="checkbox">
                              <label for="todo5">Create the main structure</label>
                            </div>
                          </li>
                          <li>
                            <div class="be-checkbox be-checkbox-sm"><span class="delete mdi mdi-delete"></span>
                              <input id="todo6" type="checkbox">
                              <label for="todo6">Updates changes to GitHub</label>
                            </div>
                          </li>
                          <li>
                            <div class="be-checkbox be-checkbox-sm"><span class="delete mdi mdi-delete"></span>
                              <input id="todo7" type="checkbox">
                              <label for="todo7" title="This task is too long to be displayed in a normal space!">This task is too long to be displayed in a normal space!</label>
                            </div>
                          </li>
                        </ul>
                      </div>
                    </div>
                  </div>
                  <div class="bottom-input">
                    <input type="text" placeholder="Create new task..." name="q"><span class="mdi mdi-plus"></span>
                  </div>
                </div>
              </div>
              <div id="tab3" role="tabpanel" class="tab-pane tab-settings">
                <div class="settings-wrapper">
                  <div class="be-scroller"><span class="category-title">General</span>
                    <ul class="settings-list">
                      <li>
                        <div class="switch-button switch-button-sm">
                          <input type="checkbox" checked="" name="st1" id="st1"><span>
                            <label for="st1"></label></span>
                        </div><span class="name">Available</span>
                      </li>
                      <li>
                        <div class="switch-button switch-button-sm">
                          <input type="checkbox" checked="" name="st2" id="st2"><span>
                            <label for="st2"></label></span>
                        </div><span class="name">Enable notifications</span>
                      </li>
                      <li>
                        <div class="switch-button switch-button-sm">
                          <input type="checkbox" checked="" name="st3" id="st3"><span>
                            <label for="st3"></label></span>
                        </div><span class="name">Login with Facebook</span>
                      </li>
                    </ul><span class="category-title">Notifications</span>
                    <ul class="settings-list">
                      <li>
                        <div class="switch-button switch-button-sm">
                          <input type="checkbox" name="st4" id="st4"><span>
                            <label for="st4"></label></span>
                        </div><span class="name">Email notifications</span>
                      </li>
                      <li>
                        <div class="switch-button switch-button-sm">
                          <input type="checkbox" checked="" name="st5" id="st5"><span>
                            <label for="st5"></label></span>
                        </div><span class="name">Project updates</span>
                      </li>
                      <li>
                        <div class="switch-button switch-button-sm">
                          <input type="checkbox" checked="" name="st6" id="st6"><span>
                            <label for="st6"></label></span>
                        </div><span class="name">New comments</span>
                      </li>
                      <li>
                        <div class="switch-button switch-button-sm">
                          <input type="checkbox" name="st7" id="st7"><span>
                            <label for="st7"></label></span>
                        </div><span class="name">Chat messages</span>
                      </li>
                    </ul><span class="category-title">Workflow</span>
                    <ul class="settings-list">
                      <li>
                        <div class="switch-button switch-button-sm">
                          <input type="checkbox" name="st8" id="st8"><span>
                            <label for="st8"></label></span>
                        </div><span class="name">Deploy on commit</span>
                      </li>
                    </ul>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </nav>
    </div>
    <script src="assets/lib/jquery/jquery.min.js" type="text/javascript"></script>
    <script src="assets/lib/perfect-scrollbar/js/perfect-scrollbar.jquery.min.js" type="text/javascript"></script>
    <script src="assets/js/main.js" type="text/javascript"></script>
    <script src="assets/lib/bootstrap/dist/js/bootstrap.min.js" type="text/javascript"></script>
    <script src="assets/lib/datatables/js/jquery.dataTables.min.js" type="text/javascript"></script>
    <script src="assets/lib/datatables/js/dataTables.bootstrap.min.js" type="text/javascript"></script>
    <script src="assets/lib/datatables/plugins/buttons/js/dataTables.buttons.js" type="text/javascript"></script>
    <script src="assets/lib/datatables/plugins/buttons/js/buttons.html5.js" type="text/javascript"></script>
    <script src="assets/lib/datatables/plugins/buttons/js/buttons.flash.js" type="text/javascript"></script>
    <script src="assets/lib/datatables/plugins/buttons/js/buttons.print.js" type="text/javascript"></script>
    <script src="assets/lib/datatables/plugins/buttons/js/buttons.colVis.js" type="text/javascript"></script>
    <script src="assets/lib/datatables/plugins/buttons/js/buttons.bootstrap.js" type="text/javascript"></script>
    <script src="assets/js/app-tables-datatables.js" type="text/javascript"></script>
 
    <script type="text/javascript">
      $(document).ready(function(){
        
      	//initialize the javascript
      	App.init();
  
      	App.dataTables();
         
      $.ajax({
          type:"get",
          url:"http://127.0.0.1:8080/routeApi/",
          success: function(data){
            console.log(data);
            $("#table-data").html("");
            $.each(data['route_info'],function (i,item) {
              $("#table-data").append('                      <tr class="gradeA">\
                        <td>'+item.id+'</td>\
                        <td>'+item.route_name+'</td>\
                        <td>'+item.route_namespace+'</td>\
                        <td class="center">'+item.route_host+'</td>\
                        <td class="center"><a href="route-detail.html?route_id='+item.id+'">详情</a> <a href="http://localhost:8080/routeApi/deleteRouteById?route_id='+item.id+'" style="color:red;" >删除</a></td>\
                      </tr>'
              );
            })
          },
          error: function(result){
            console.log(result);
          }
        });
      });
    

 
    </script>

  </body>
</html>

7-8 Summary & Thinking 

Main content
Introduces the function of ingress and ingress-controller
Explains the operating principle of ingress-controller
Code function development of ingress-controller (nginx)

According to experience,
the type of Ingress controller generally adopts the daemonset mode,
and the routing management needs to be reviewed, otherwise the management is very complicated.

What are the usages of other modes of Route-controller?
What are the commonly used types other than nginx?

7-9 [Extended reading] Kubernetes uses ingress to configure https clusters

Chapter 8 Cloud native Go PaaS platform background monitoring capability building, overview of cluster resource usage

The state of the cluster requires a strong global view, and a monitoring system has emerged as the times require. Through a powerful monitoring system, you can spy on the running status of the entire PaaS, implement feedback cluster information, and easily grasp the monitoring capabilities of the entire PaaS platform applications and components using Promethus. Adapt to the changing business needs of the enterprise by monitoring and combining different business logics.

8-1 Go PaaS platform monitoring system Prometheus architecture introduction

GO PaaS platform monitoring system established

Main content
Mainstream monitoring Promethus monitoring explanation
Installing monitoring Promethus in K8s
Monitoring dimension effect display

Introduction to Promethus architecture
What aspects of information can be monitored by
monitoring Core components of
monitoring Core architecture of monitoring

Go PaaS platform monitoring module establishment

Goals of monitoring
To detect existing problems
To prevent impending problems

What content to monitor
System basic indicators (memory, CPU, IO, Disk, Network, etc.) service basic information (survival, occupied system resources, etc.) service personalization
(interface, fixed return value, etc.);
log content (get error from the log information);

Monitoring content in K8s
Node
Service
components of K8s itself

How to monitor?
Data collection: What content to collect first?
Data storage: After collecting data, a database is required to store it. Define alarm rules: set the alarm red line, and alarm if the conditions are met. Configuration alarm
method: set SMS, email, WeChat, etc. alarm :

(Prometheus) monitoring Prometheus
is a set of open source monitoring & alarm & time series database combination
Multidimensional data model identified by metric name and kv
Supports pull and push to add data

Prometheus (Prometheus) architecture

(Prometheus) data source rometheus

Server Metric Source
Container Metric Source
Component Metric Source 

8-2 Go PaaS platform Prometheus monitoring installation

C:\Users\Administrator\Desktop\gopaas\promethues\README.md

### Prometheus 安装说明

#### 1.解压 zip 包
```cassandraql
unzip v0.9.0.zip
```

#### 2.进入目录 
```cassandraql
cd kube-prometheus-0.9.0
```

#### 3.执行安装命令
```cassandraql
//创建命名空间和CRD
kubectl create -f manifests/setup

//等待创建结束
until kubectl get servicemonitors --all-namespaces ; do date; sleep 1; echo ""; done

//创建监控组件
kubectl create -f manifests/

//查看 monitoring 内的 pod
kubectl get pods -n monitoring
```

#### 4.添加路由外网访问
通过前一章开发的 route 功能添加 monitoring 命名空间下
grafana.wu123.com 域名
第一次登录默认账号:admin 密码:admin
[root@k8s-worker01 kube-prometheus-0.9.0]# kubectl get pods -n monitoring
NAME                                   READY   STATUS              RESTARTS   AGE
alertmanager-main-0                    2/2     Running             0          4m52s
alertmanager-main-1                    2/2     Running             0          4m41s
blackbox-exporter-589d9b456-zs74d      3/3     Running             0          4m52s
grafana-6767dc8755-sl48b               1/1     Running             0          4m55s
kube-state-metrics-8fc46689b-hgh9w     2/3     Running             0          4m52s
node-exporter-5h2vw                    2/2     Running             0          4m52s
node-exporter-6gp27                    2/2     Running             0          4m52s
node-exporter-j6phd                    2/2     Running             0          4m52s
prometheus-adapter-58cd975bff-9rvj8    1/1     Running             0          4m52s
prometheus-adapter-58cd975bff-lwjhl    1/1     Running             0          4m52s
prometheus-k8s-0                       2/2     Running             0          4m52s
prometheus-k8s-1                       2/2     Running             0          4m52s
prometheus-operator-6cdd8db9f7-7mp6q   2/2     Running             0          9m6s

8-3 Go PaaS Platform Monitoring Grafana Icon Instructions

8-4 Summary & Thinking

The main content
introduces the basic components and architecture of Prometheus, installs Promethues in k8s and monitors the Kanban
grafana function display

Talking from experience
Monitoring is an indispensable capability of the GOPaaS platform, and the data needs to be further preserved.
The monitoring expansion of the PaaS platform is based on Prometheus
monitoring. More business attributes and decision-making goals should be considered

What other decisions need the support of monitoring data
? What scenarios can monitoring bring to PaaS operations?

8-5 [Extended reading] Prometheus Operator installation and configuration detailed documentation

Chapter 9 Cloud-native Go PaaS Platform Distributed Storage Management Function Development, Provide Data Storage Solution

Data is a very valuable asset, and data generated during business operation is an intangible asset. The distributed storage system can provide various data storage solutions. In-depth interpretation of the principles, architecture, core components and other knowledge of the Ceph distributed system

9-1 Go PaaS Platform Distributed Storage Ceph Architecture Introduction

GO PaaS platform distributed storage development

Main content
Ceph basic concept introduction
Ceph architecture description and installation
Service back-end function development

Ceph Cluster BasicsCeph FeaturesCeph
Storage
TypeCeph
Core Components

Ceph features
High performance: supports thousands of nodes, and supports data from TP to PB.
High availability: Separation of fault domains, strong consistency, and automatic repair.

High scalability: decentralization, flexible expansion.

Types supported by Ceph storage
Block storage: analogy to traditional storage hard drives, disk arrays
File storage: analogy to FTP services
Object storage: analogy to large-capacity hard disks with file services

Ceph storage architecture and core components

The full name of Ceph basic storage system RADOS
: Reliable Autonomic Distributed Object Store
RADOS is the basis of ceph storage cluster
All data is stored in the form of objects

Ceph base library LIBRADOS
The function of the LIBRADOS layer is to abstract and encapsulate RADOS
Provide APIs to the upper layer for direct application development based on RADOS
Support multiple programming languages, such as GO, C, C++, Python, etc.

Upper interface RADOSGW, RBD and CEPHFS
RADOSGW: based on RESTFUL protocol, compatible with S3 and Swift
RBD provides distributed block storage device interface, mounted like a disk using
CephFS is a POSIX compatible distributed file system

9-2 Introduction to Ceph Core Components of Go PaaS Platform 

Introduction to Ceph Core Components OSD (ceph-osd)
object storage daemon is used to store all data and objects in the cluster.
Responsible for processing cluster data replication, recovery, backfill, and rebalancing.
The Ceph cluster manages physical hard disks by managing OSDs

Introduction to Ceph Core Components Manager (ceph-mgr)
collects Ceph cluster status, and
provides ceph dashboard (ceph ui) and resetful api in addition to operating indicators (storage utilization, system load, etc.).
When High Availability is enabled for the Manager component, at least two should be used to achieve high availability.

Introduction to Ceph core components MDSceph-mds):
Metadata server, metadata service
Provides metadata calculation, caching and synchronization services for the Ceph file system
MDS is similar to a proxy cache server for metadata

Introduction to Ceph core components Monitor (ceph-mon)
maintains the status of the cluster Cluster Map, maintains
the Cluster MAP binary table of the cluster, and ensures the consistency of the cluster data Monitor component information, manger component information, osd component information, mds component information, crush algorithm information

9-3 Go PaaS Platform Ceph Stored Procedures and Introduction to Core Concepts

System logic structure of RADOS

RADOS stored procedure

Logical concept description
File: the file that the user needs to store or access
Objects: the basic storage unit of RADOS, that is, the object.
PG (Placement Group): The role of PG here is to organize and map the storage of objects 

Logical concept description
OSD (object storage device 
oid: each object will have a unique OID, generated by ino and ono
pgid: use a static hash function to hash the OID to remove the signature

The mapping relationship between the various levels in the stored procedure
file -> object
object -> PG
PG -> OSD

Why introduce the concept of PG?
Because the size of the Object object is very small, it will not be directly stored in the OSD.
If multiple objects are traversed and addressed, the speed is very slow. They
are directly mapped to the OSD. When a single OSD is damaged, the data cannot be migrated.

9-4 Notes on purchasing Ceph resources on the Go PaaS platform

The Ceph cluster is installed with
3 2C 4G 20G system disks and 20G data disks.
The operating system is Centos 8.3
and Cephadm is used to install ceph 16 version

9-5 Go PaaS platform Ceph security machine initialization

### 基于 Centos8 系统通过 Cephadm 快速部署  Ceph16(pacific)版本

#### 前期准备

##### 1. 安装依赖
lvm因为系统自带的都有,所以就不用单独安装了,
```cassandraql
dnf install epel-release -y
dnf install python3 -y
dnf install podman -y
dnf install -y chrony
systemctl start chronyd && systemctl enable chronyd
```
chrony时间服务为必须安装,具体有2点原因:1为不安装在添加主机的时候会报错,2为即使安装成功ceph -s会也提示时间不同步!
##### 2.关闭防火墙和selinux (每台都执行)
```cassandraql
systemctl disable firewalld && systemctl stop firewalld
setenforce 0
sed -i "s/SELINUX=enforcing/SELINUX=disabled/g" /etc/selinux/config
```

##### 3.分别在三个节点设置主机名
```cassandraql
hostnamectl set-hostname ceph01
hostnamectl set-hostname ceph02
hostnamectl set-hostname ceph03
```
重启机器 reboot
cephadm需要主机名为短主机名,不能为FQDN,否者在添加主机会报错!

##### 4.添加hosts文件中的主机名和IP关系,主机名需要和上面一致
172.31.96.70 ceph01  2Cpu 4G内存  20G系统盘,20G数据盘
172.31.96.71 ceph02  2Cpu 4G内存  20G系统盘,20G数据盘
172.31.96.72 ceph03  2Cpu 4G内存  20G系统盘,20G数据盘
```cassandraql
cat >> /etc/hosts <<EOF
172.31.96.70 ceph01 
172.31.96.71 ceph02 
172.31.96.72 ceph03 
EOF
```

9-6 Go PaaS platform cephadm installs the basic cluster (Part 1)

C:\Users\Administrator\Desktop\gopaas\ceph\1.ceph install.md

#### 安装 cephadm
cephadm 命令可以
1. 引导新集群
2. 使用有效的Ceph CLI启动容器化的Shell
3. 帮助调试容器化的Ceph守护进程。
以下操作只在一台节点执行就可以
##### 1.使用curl获取独立脚本的最新版本。网络不好的话可直接去GitHub复制

```cassandraql
curl --silent --remote-name --location https://github.com/ceph/ceph/raw/pacific/src/cephadm/cephadm
chmod +x cephadm
./cephadm add-repo --release pacific
./cephadm install
./cephadm install  ceph-common
```
官方文档中还提到了另一种安装cephadm方式,就是通过dnf install -y cephadm安装,实践证明最好不要使用这种方式,这种方式安装的cephadm可能不是最新版本的,但cephadm去拉的容器版本又是最新的,会导致两个版本不一致!


 

#### 引导新群集
##### 1.先创建一个目录:/etc/ceph
```cassandraql
mkdir -p /etc/ceph
```

##### 2.运行该命令:ceph bootstrap
```cassandraql
cephadm bootstrap --mon-ip 172.31.96.70
```
此命令将会进行以下操作:
- 为本地主机上的新群集创建monitor和manager守护程序。
- 为 Ceph 群集生成新的 SSH 密钥,并将其添加到root用户的文件/root/.ssh/authorized_keys
- 将与新群集通信所需的最小配置文件保存到 /etc/ceph/ceph.conf
- 将client.admin管理(特权!)密钥的副本写入/etc/ceph/ceph.client.admin.keyring
- 将公钥的副本写入/etc/ceph/ceph.pub

安装日志如下为成功
```cassandraql
[root@ceph01 ~]# cephadm bootstrap --mon-ip 172.31.96.70
Verifying podman|docker is present...
Verifying lvm2 is present...
Verifying time synchronization is in place...
Unit chronyd.service is enabled and running
Repeating the final host check...
podman (/usr/bin/podman) version 3.3.1 is present
systemctl is present
lvcreate is present
Unit chronyd.service is enabled and running
Host looks OK
Cluster fsid: ba25aef4-19f6-11ed-867d-00163e005933
Verifying IP 172.31.96.71 port 3300 ...
ERROR: [Errno 99] Cannot assign requested address
[root@ceph01 ~]# cephadm bootstrap --mon-ip 172.31.96.70
Verifying podman|docker is present...
Verifying lvm2 is present...
Verifying time synchronization is in place...
Unit chronyd.service is enabled and running
Repeating the final host check...
podman (/usr/bin/podman) version 3.3.1 is present
systemctl is present
lvcreate is present
Unit chronyd.service is enabled and running
Host looks OK
Cluster fsid: c32ff766-19f6-11ed-aa17-00163e005933
Verifying IP 172.31.96.70 port 3300 ...
Verifying IP 172.31.96.70 port 6789 ...
Mon IP `172.31.96.70` is in CIDR network `172.31.96.0/20`
- internal network (--cluster-network) has not been provided, OSD replication will default to the public_network
Pulling container image quay.io/ceph/ceph:v16...
Ceph version: ceph version 16.2.10 (45fa1a083152e41a408d15505f594ec5f1b4fe17) pacific (stable)
Extracting ceph user uid/gid from container image...
Creating initial keys...
Creating initial monmap...
Creating mon...
Waiting for mon to start...
Waiting for mon...
mon is available
Assimilating anything we can from ceph.conf...
Generating new minimal ceph.conf...
Restarting the monitor...
Setting mon public_network to 172.31.96.0/20
Wrote config to /etc/ceph/ceph.conf
Wrote keyring to /etc/ceph/ceph.client.admin.keyring
Creating mgr...
Verifying port 9283 ...
Waiting for mgr to start...
Waiting for mgr...
mgr not available, waiting (1/15)...
mgr not available, waiting (2/15)...
mgr not available, waiting (3/15)...
mgr not available, waiting (4/15)...
mgr is available
Enabling cephadm module...
Waiting for the mgr to restart...
Waiting for mgr epoch 5...
mgr epoch 5 is available
Setting orchestrator backend to cephadm...
Generating ssh key...
Wrote public SSH key to /etc/ceph/ceph.pub
Adding key to root@localhost authorized_keys...
Adding host ceph01...
Deploying mon service with default placement...
Deploying mgr service with default placement...
Deploying crash service with default placement...
Deploying prometheus service with default placement...
Deploying grafana service with default placement...
Deploying node-exporter service with default placement...
Deploying alertmanager service with default placement...
Enabling the dashboard module...
Waiting for the mgr to restart...
Waiting for mgr epoch 9...
mgr epoch 9 is available
Generating a dashboard self-signed certificate...
Creating initial admin user...
Fetching dashboard port number...
Ceph Dashboard is now available at:

             URL: https://ceph01:8443/
            User: admin
        Password: 9ro6qdzyel

Enabling client.admin keyring and conf on hosts with "admin" label
Enabling autotune for osd_memory_target
You can access the Ceph CLI as following in case of multi-cluster or non-default config:

        sudo /usr/sbin/cephadm shell --fsid c32ff766-19f6-11ed-aa17-00163e005933 -c /etc/ceph/ceph.conf -k /etc/ceph/ceph.client.admin.keyring

Or, if you are only running a single cluster on this host:

        sudo /usr/sbin/cephcadm shell 

Please consider enabling telemetry to help improve Ceph:

        ceph telemetry on

For more information see:

        https://docs.ceph.com/en/pacific/mgr/telemetry/

Bootstrap complete.
```

完成后记录以上了IP以及用户和密码,打开Ceph Dashboard并根据提示修改密码,打开后提示要激活统计模块。
如果错过使用命令
```cassandraql
如果Ceph Dashboard中错过了启用,也可以使用命令启用,命令是“ceph telemetry on --license sharing-1-0”。
```

如果忘记记录密码可以通过以下方法重置密码(将密码写入password文件中,通过命令导入密码)
```cassandraql
ceph dashboard ac-user-set-password admin -i password 
{"username": "admin", "password": "$2b$12$6oFrEpssXCzLnKTWQy5fM.YZwlHjn8CuQRdeSSJR9hBGgVuwGCxoa", "roles": ["administrator"], "name": null, "email": null, "lastUpdate": 1620495653, "enabled": true, "pwdExpirationDate": null, "pwdUpdateRequired": false}
```

9-7 Go PaaS platform cephadm installs the basic cluster (below)

##### 3.添加主机
在引导成功单节点Ceph群集后会引导程序会将public key的副本写入/etc/ceph/ceph.pub,在添加主机节点前需要讲该key分发到要加入群集的主机上

拷贝到ceph02
```cassandraql
ssh-copy-id -f -i /etc/ceph/ceph.pub root@ceph02
```

拷贝到ceph03
```cassandraql
ssh-copy-id -f -i /etc/ceph/ceph.pub root@ceph03
```

添加ceph 02 节点
```cassandraql
ceph orch host add ceph02 172.31.96.71
```
添加ceph 03 节点
```cassandraql
ceph orch host add ceph03 172.31.96.72
```

查看节点
```cassandraql
[root@ceph01 ~]# ceph orch host ls
HOST    ADDR          LABELS  STATUS  
ceph01  172.31.96.70  _admin          
ceph02  172.31.96.71                  
ceph03  172.31.96.72                  
3 hosts in cluster
```

##### 4.添加OSD
添加OSD需求满足以下所有条件:
- 设备必须没有分区。
- 设备不得具有任何LVM状态。
- 不得安装设备。
- 该设备不得包含文件系统。
- 该设备不得包含Ceph BlueStore OSD。
- 设备必须大于 5 GB。
添加OSD有2种方式,
1.为自动添加所有满足条件的OSD。
```
ceph orch apply osd --all-available-devices
```
2.为通过手工指定的方式添加OSD。
```cassandraql
ceph orch daemon add osd ceph1:/dev/sdb
```
本次使用第一种自动部署的方式,部署完成后查看设备列表,显示为NO就完成了。
```cassandraql
[root@ceph01 ~]# ceph orch device ls
HOST    PATH      TYPE  DEVICE ID              SIZE  AVAILABLE  REFRESHED  REJECT REASONS                                                 
ceph01  /dev/vdb  hdd   j6c3xd1a6qug5beqk71y  21.4G             9s ago     Insufficient space (<10 extents) on vgs, LVM detected, locked  
ceph02  /dev/vdb  hdd   j6c3xd1a6qug5beqk720  21.4G             14s ago    Insufficient space (<10 extents) on vgs, LVM detected, locked  
ceph03  /dev/vdb  hdd   j6c3xd1a6qug5beqk71z  21.4G             14s ago    Insufficient space (<10 extents) on vgs, LVM detected, locked
```

#### 5.查看Ceph部署服务
```cassandraql
[root@ceph01 ~]# ceph -s
  cluster:
    id:     c32ff766-19f6-11ed-aa17-00163e005933
    health: HEALTH_OK
 
  services:
    mon: 3 daemons, quorum ceph01,ceph02,ceph03 (age 4m)
    mgr: ceph01.sdapbz(active, since 30m), standbys: ceph02.njhkgr
    osd: 3 osds: 3 up (since 96s), 3 in (since 114s)
 
  data:
    pools:   1 pools, 1 pgs
    objects: 0 objects, 0 B
    usage:   15 MiB used, 60 GiB / 60 GiB avail
    pgs:     1 active+clean
 
```

打开 dashboard 看监控数据

9-8 Go ​​PaaS platform ceph core component installation

#### 6.部署RGW
使用指定数量匹配模式部署。
```
ceph orch apply rgw rgw --placement=3
```
通过Service查看命令ceph orch ls查看该服务状态。
```cassandraql
[root@ceph01 ~]# ceph orch ls
NAME                       PORTS        RUNNING  REFRESHED  AGE  PLACEMENT  
alertmanager               ?:9093,9094      1/1  10s ago    40m  count:1    
crash                                       3/3  15s ago    40m  *          
grafana                    ?:3000           1/1  10s ago    40m  count:1    
mgr                                         2/2  14s ago    40m  count:2    
mon                                         3/5  15s ago    40m  count:5    
node-exporter              ?:9100           3/3  15s ago    40m  *          
osd.all-available-devices                     3  15s ago    9m   *          
prometheus                 ?:9095           1/1  10s ago    40m  count:1    
rgw.rgw                    ?:80             3/3  15s ago    30s  count:3
```
通过Deamon查看命令ceph orch ps查看该进程状态。
```cassandraql
[root@ceph01 ~]# ceph orch ps
NAME                   HOST    PORTS        STATUS         REFRESHED  AGE  MEM USE  MEM LIM  VERSION  IMAGE ID      CONTAINER ID  
alertmanager.ceph01    ceph01  *:9093,9094  running (12m)    41s ago  39m    27.9M        -           ba2b418f427c  b6fbf7382136  
crash.ceph01           ceph01               running (39m)    41s ago  39m    7717k        -  16.2.10  0d668911f040  9660dd933bd2  
crash.ceph02           ceph02               running (14m)    44s ago  14m    9713k        -  16.2.10  0d668911f040  35cac443b75c  
crash.ceph03           ceph03               running (13m)    45s ago  13m    8632k        -  16.2.10  0d668911f040  3135f2560d98  
grafana.ceph01         ceph01  *:3000       running (38m)    41s ago  39m    67.9M        -  8.3.5    dad864ee21e9  7f47d88ad676  
mgr.ceph01.sdapbz      ceph01  *:9283       running (41m)    41s ago  41m     499M        -  16.2.10  0d668911f040  9e001eab3a53  
mgr.ceph02.njhkgr      ceph02  *:8443,9283  running (14m)    44s ago  14m     409M        -  16.2.10  0d668911f040  146058cf2aea  
mon.ceph01             ceph01               running (41m)    41s ago  41m     147M    2048M  16.2.10  0d668911f040  5896f4b2a014  
mon.ceph02             ceph02               running (14m)    44s ago  14m    85.3M    2048M  16.2.10  0d668911f040  7fd0c2c36613  
mon.ceph03             ceph03               running (13m)    45s ago  13m    82.1M    2048M  16.2.10  0d668911f040  db5a5b5039d5  
node-exporter.ceph01   ceph01  *:9100       running (38m)    41s ago  38m    16.5M        -           1dbe0e931976  2d7d68e3a0da  
node-exporter.ceph02   ceph02  *:9100       running (14m)    44s ago  14m    18.2M        -           1dbe0e931976  083f26d39f07  
node-exporter.ceph03   ceph03  *:9100       running (12m)    45s ago  12m    14.4M        -           1dbe0e931976  c3b21c79b2f4  
osd.0                  ceph03               running (9m)     45s ago   9m    40.4M    4096M  16.2.10  0d668911f040  2210df27e314  
osd.1                  ceph02               running (9m)     44s ago   9m    44.7M    4096M  16.2.10  0d668911f040  9ab8967c65b9  
osd.2                  ceph01               running (9m)     41s ago   9m    47.6M    4096M  16.2.10  0d668911f040  78a2000670f7  
prometheus.ceph01      ceph01  *:9095       running (12m)    41s ago  38m    69.2M        -           514e6a882f6e  06de63766e30  
rgw.rgw.ceph01.bvcnos  ceph01  *:80         running (58s)    41s ago  57s    43.7M        -  16.2.10  0d668911f040  96c4d8963d47  
rgw.rgw.ceph02.awygjp  ceph02  *:80         running (54s)    44s ago  53s    46.2M        -  16.2.10  0d668911f040  9d0bd0b0b060  
rgw.rgw.ceph03.iiixeq  ceph03  *:80         running (50s)    45s ago  49s    21.2M        -  16.2.10  0d668911f040  a5d6e8a8983a 
```
集成到dashboard
```cassandraql
[root@ceph01 ~]# radosgw-admin user create --uid=rgw --display-name=rgw --system

以下是输出结果

"keys": [
        {
            "user": "rgw",
            "access_key": "M0XRR80H4AGGE4PP0A5B",
            "secret_key": "Tbln48sfIceDGNill5muCrX0oMCHrQcl2oC9OURe"
        }
    ],{
    "user_id": "rgw",
    "display_name": "rgw",
    "email": "",
    "suspended": 0,
    "max_buckets": 1000,
    "subusers": [],
    "keys": [
        {
            "user": "rgw",
            "access_key": "43DTMZ3EY02B10QNTJSS",
            "secret_key": "4d3qzY1A7uqVk3LWVMsGJY0negbcxg95d8QJYuzi"
        }
    ],
    "swift_keys": [],
    "caps": [],
    "op_mask": "read, write, delete",
    "system": "true",
    "default_placement": "",
    "default_storage_class": "",
    "placement_tags": [],
    "bucket_quota": {
        "enabled": false,
        "check_on_raw": false,
        "max_size": -1,
        "max_size_kb": 0,
        "max_objects": -1
    },
    "user_quota": {
        "enabled": false,
        "check_on_raw": false,
        "max_size": -1,
        "max_size_kb": 0,
        "max_objects": -1
    },
    "temp_url_keys": [],
    "type": "rgw",
    "mfa_ids": []
}

```
查看 Dashboard 是否集成成功

#### 7.部署Cephfs
部署cephfs服务并创建cepfs,创建cephfs有两种方式,一种是使用的是ceph fs命令该命令会自动创建相应的池,另一种手工创建池并创建Service,下面方法任选一种。
方法一:
```cassandraql
ceph fs volume create cephfs --placement=3
```
方法二:
```cassandraql
#ceph osd pool create cephfs_data 32
#ceph osd pool create cephfs_metadata 32
#ceph fs new cephfs cephfs_metadata cephfs_data
#ceph orch apply mds cephfs --placement=3
```
我们采用方法一
查看Service状态。
```cassandraql
[root@ceph01 ~]# ceph fs volume create cephfs --placement=3
[root@ceph01 ~]# ceph orch ls
NAME                       PORTS        RUNNING  REFRESHED  AGE  PLACEMENT  
alertmanager               ?:9093,9094      1/1  9s ago     48m  count:1    
crash                                       3/3  13s ago    48m  *          
grafana                    ?:3000           1/1  9s ago     48m  count:1    
mds.cephfs                                  3/3  13s ago    29s  count:3    
mgr                                         2/2  13s ago    48m  count:2    
mon                                         3/5  13s ago    48m  count:5    
node-exporter              ?:9100           3/3  13s ago    48m  *          
osd.all-available-devices                     3  13s ago    17m  *          
prometheus                 ?:9095           1/1  9s ago     48m  count:1    
rgw.rgw                    ?:80             3/3  13s ago    8m   count:3   
```
查看Deamon状态。
```cassandraql
[root@ceph01 ~]# ceph orch ps
NAME                      HOST    PORTS        STATUS         REFRESHED  AGE  MEM USE  MEM LIM  VERSION  IMAGE ID      CONTAINER ID  
alertmanager.ceph01       ceph01  *:9093,9094  running (20m)    59s ago  48m    29.5M        -           ba2b418f427c  b6fbf7382136  
crash.ceph01              ceph01               running (48m)    59s ago  48m    7587k        -  16.2.10  0d668911f040  9660dd933bd2  
crash.ceph02              ceph02               running (22m)    63s ago  22m    9667k        -  16.2.10  0d668911f040  35cac443b75c  
crash.ceph03              ceph03               running (21m)    63s ago  21m    8606k        -  16.2.10  0d668911f040  3135f2560d98  
grafana.ceph01            ceph01  *:3000       running (46m)    59s ago  47m    77.2M        -  8.3.5    dad864ee21e9  7f47d88ad676  
mds.cephfs.ceph01.uwclzo  ceph01               running (74s)    59s ago  74s    28.2M        -  16.2.10  0d668911f040  e7ead0c130a6  
mds.cephfs.ceph02.zqmpyt  ceph02               running (71s)    63s ago  70s    23.7M        -  16.2.10  0d668911f040  06ed8fd3bc1a  
mds.cephfs.ceph03.glufpl  ceph03               running (67s)    63s ago  67s    22.9M        -  16.2.10  0d668911f040  a3e497cb8a85  
mgr.ceph01.sdapbz         ceph01  *:9283       running (49m)    59s ago  49m     508M        -  16.2.10  0d668911f040  9e001eab3a53  
mgr.ceph02.njhkgr         ceph02  *:8443,9283  running (22m)    63s ago  22m     410M        -  16.2.10  0d668911f040  146058cf2aea  
mon.ceph01                ceph01               running (50m)    59s ago  50m     177M    2048M  16.2.10  0d668911f040  5896f4b2a014  
mon.ceph02                ceph02               running (22m)    63s ago  22m     115M    2048M  16.2.10  0d668911f040  7fd0c2c36613  
mon.ceph03                ceph03               running (21m)    63s ago  21m     111M    2048M  16.2.10  0d668911f040  db5a5b5039d5  
node-exporter.ceph01      ceph01  *:9100       running (47m)    59s ago  47m    17.4M        -           1dbe0e931976  2d7d68e3a0da  
node-exporter.ceph02      ceph02  *:9100       running (22m)    63s ago  22m    18.0M        -           1dbe0e931976  083f26d39f07  
node-exporter.ceph03      ceph03  *:9100       running (21m)    63s ago  21m    14.7M        -           1dbe0e931976  c3b21c79b2f4  
osd.0                     ceph03               running (17m)    63s ago  17m    69.7M    4096M  16.2.10  0d668911f040  2210df27e314  
osd.1                     ceph02               running (17m)    63s ago  17m    72.6M    4096M  16.2.10  0d668911f040  9ab8967c65b9  
osd.2                     ceph01               running (17m)    59s ago  17m    72.4M    4096M  16.2.10  0d668911f040  78a2000670f7  
prometheus.ceph01         ceph01  *:9095       running (20m)    59s ago  46m    73.6M        -           514e6a882f6e  06de63766e30  
rgw.rgw.ceph01.bvcnos     ceph01  *:80         running (9m)     59s ago   9m    73.3M        -  16.2.10  0d668911f040  96c4d8963d47  
rgw.rgw.ceph02.awygjp     ceph02  *:80         running (9m)     63s ago   9m    80.8M        -  16.2.10  0d668911f040  9d0bd0b0b060  
rgw.rgw.ceph03.iiixeq     ceph03  *:80         running (9m)     63s ago   8m    57.4M        -  16.2.10  0d668911f040  a5d6e8a8983a  
```

查看 Dashboard

#### 8.部署NFS
先创建nfs所需求的池。
```cassandraql
#ceph osd pool create ganesha_data 32
#ceph osd pool application enable ganesha_data nfs
```
部署nfs Service。
```cassandraql
#ceph orch apply nfs nfs ganesha_data --placement=3
```
查看Service状态。
```cassandraql
[root@ceph01 ~]# ceph orch ls
NAME                       PORTS        RUNNING  REFRESHED  AGE  PLACEMENT  
alertmanager               ?:9093,9094      1/1  8s ago     52m  count:1    
crash                                       3/3  13s ago    52m  *          
grafana                    ?:3000           1/1  8s ago     52m  count:1    
mds.cephfs                                  3/3  13s ago    4m   count:3    
mgr                                         2/2  13s ago    52m  count:2    
mon                                         3/5  13s ago    52m  count:5    
nfs.nfs                                     3/3  13s ago    36s  count:3    
node-exporter              ?:9100           3/3  13s ago    52m  *          
osd.all-available-devices                     3  13s ago    21m  *          
prometheus                 ?:9095           1/1  8s ago     52m  count:1    
rgw.rgw                    ?:80             3/3  13s ago    12m  count:3  
```

查看Deamon状态。
```cassandraql
[root@ceph01 ~]# ceph orch ls
NAME                       PORTS        RUNNING  REFRESHED  AGE  PLACEMENT  
alertmanager               ?:9093,9094      1/1  8s ago     52m  count:1    
crash                                       3/3  13s ago    52m  *          
grafana                    ?:3000           1/1  8s ago     52m  count:1    
mds.cephfs                                  3/3  13s ago    4m   count:3    
mgr                                         2/2  13s ago    52m  count:2    
mon                                         3/5  13s ago    52m  count:5    
nfs.nfs                                     3/3  13s ago    36s  count:3    
node-exporter              ?:9100           3/3  13s ago    52m  *          
osd.all-available-devices                     3  13s ago    21m  *          
prometheus                 ?:9095           1/1  8s ago     52m  count:1    
rgw.rgw                    ?:80             3/3  13s ago    12m  count:3    

```

#### 9.部署iSCSi
创建iscsi所需求的池。
```cassandraql
#ceph osd pool create  iscsi_pool 16 16
#ceph osd pool application enable iscsi_pool iscsi
```

部署iscsi我们换YAM方式
```cassandraql
#vi iscsi.yaml
service_type: iscsi
service_id: gw
placement:
  hosts:
    - ceph01
    - ceph02
    - ceph03
spec:
  pool: iscsi_pool
  trusted_ip_list: "172.31.96.70,172.31.96.71,172.31.96.72"
  api_user: admin
  api_password: admin
  api_secure: false

```
通过apply命令部署,cephadm也是声明式的,所以如果想修改配置参数只需要直接修改YAML文件。

```cassandraql
[root@ceph01 ~]# ceph orch apply -i iscsi.yaml
Scheduled iscsi.gw update...
```
查看Service状态。
```cassandraql
[root@ceph01 ~]# ceph orch ls
NAME                       PORTS        RUNNING  REFRESHED  AGE  PLACEMENT             
alertmanager               ?:9093,9094      1/1  9s ago     67m  count:1               
crash                                       3/3  21s ago    67m  *                     
grafana                    ?:3000           1/1  9s ago     67m  count:1               
iscsi.gw                                    3/3  21s ago    40s  ceph01;ceph02;ceph03  
mds.cephfs                                  3/3  21s ago    19m  count:3               
mgr                                         2/2  20s ago    67m  count:2               
mon                                         3/5  21s ago    67m  count:5               
nfs.nfs                                     3/3  21s ago    16m  count:3               
node-exporter              ?:9100           3/3  21s ago    67m  *                     
osd.all-available-devices                     3  21s ago    36m  *                     
prometheus                 ?:9095           1/1  9s ago     67m  count:1               
rgw.rgw                    ?:80             3/3  21s ago    27m  count:3   
```
查看Deamon状态。
```cassandraql
[root@ceph01 ~]# ceph orch ps
NAME                       HOST    PORTS        STATUS         REFRESHED  AGE  MEM USE  MEM LIM  VERSION  IMAGE ID      CONTAINER ID  
alertmanager.ceph01        ceph01  *:9093,9094  running (39m)    33s ago  67m    35.2M        -           ba2b418f427c  b6fbf7382136  
crash.ceph01               ceph01               running (67m)    33s ago  67m    7386k        -  16.2.10  0d668911f040  9660dd933bd2  
crash.ceph02               ceph02               running (41m)    44s ago  41m    10.5M        -  16.2.10  0d668911f040  35cac443b75c  
crash.ceph03               ceph03               running (40m)    45s ago  40m    10.8M        -  16.2.10  0d668911f040  3135f2560d98  
grafana.ceph01             ceph01  *:3000       running (65m)    33s ago  66m    80.2M        -  8.3.5    dad864ee21e9  7f47d88ad676  
iscsi.gw.ceph01.mqjxqu     ceph01               running (60s)    33s ago  59s    76.1M        -  3.5      0d668911f040  e712bdbb2d80  
iscsi.gw.ceph02.xeggjo     ceph02               running (56s)    44s ago  56s    52.9M        -  3.5      0d668911f040  04c7f5263155  
iscsi.gw.ceph03.xykglu     ceph03               running (52s)    45s ago  52s    76.9M        -  3.5      0d668911f040  59cb4c7bbfd5  
mds.cephfs.ceph01.uwclzo   ceph01               running (20m)    33s ago  20m    27.6M        -  16.2.10  0d668911f040  e7ead0c130a6  
mds.cephfs.ceph02.zqmpyt   ceph02               running (20m)    44s ago  20m    26.8M        -  16.2.10  0d668911f040  06ed8fd3bc1a  
mds.cephfs.ceph03.glufpl   ceph03               running (20m)    45s ago  20m    26.5M        -  16.2.10  0d668911f040  a3e497cb8a85  
mgr.ceph01.sdapbz          ceph01  *:9283       running (68m)    33s ago  68m     495M        -  16.2.10  0d668911f040  9e001eab3a53  
mgr.ceph02.njhkgr          ceph02  *:8443,9283  running (41m)    44s ago  41m     411M        -  16.2.10  0d668911f040  146058cf2aea  
mon.ceph01                 ceph01               running (68m)    33s ago  69m     237M    2048M  16.2.10  0d668911f040  5896f4b2a014  
mon.ceph02                 ceph02               running (41m)    44s ago  41m     164M    2048M  16.2.10  0d668911f040  7fd0c2c36613  
mon.ceph03                 ceph03               running (40m)    45s ago  40m     146M    2048M  16.2.10  0d668911f040  db5a5b5039d5  
nfs.nfs.0.0.ceph01.ijxnwe  ceph01  *:2049       running (16m)    33s ago  16m    72.4M        -  3.5      0d668911f040  f9b0609cbe7b  
nfs.nfs.1.0.ceph02.pedadk  ceph02  *:2049       running (16m)    44s ago  16m    75.8M        -  3.5      0d668911f040  54d58b785ed6  
nfs.nfs.2.0.ceph03.daqdkw  ceph03  *:2049       running (16m)    45s ago  16m    73.0M        -  3.5      0d668911f040  c1bebe7782d4  
node-exporter.ceph01       ceph01  *:9100       running (66m)    33s ago  66m    17.7M        -           1dbe0e931976  2d7d68e3a0da  
node-exporter.ceph02       ceph02  *:9100       running (41m)    44s ago  41m    18.3M        -           1dbe0e931976  083f26d39f07  
node-exporter.ceph03       ceph03  *:9100       running (40m)    45s ago  40m    15.5M        -           1dbe0e931976  c3b21c79b2f4  
osd.0                      ceph03               running (36m)    45s ago  36m    77.8M    4096M  16.2.10  0d668911f040  2210df27e314  
osd.1                      ceph02               running (36m)    44s ago  36m    80.1M    4096M  16.2.10  0d668911f040  9ab8967c65b9  
osd.2                      ceph01               running (36m)    33s ago  36m    79.3M    4096M  16.2.10  0d668911f040  78a2000670f7  
prometheus.ceph01          ceph01  *:9095       running (39m)    33s ago  65m    97.3M        -           514e6a882f6e  06de63766e30  
rgw.rgw.ceph01.bvcnos      ceph01  *:80         running (28m)    33s ago  28m    61.8M        -  16.2.10  0d668911f040  96c4d8963d47  
rgw.rgw.ceph02.awygjp      ceph02  *:80         running (28m)    44s ago  28m    81.6M        -  16.2.10  0d668911f040  9d0bd0b0b060  
rgw.rgw.ceph03.iiixeq      ceph03  *:80         running (27m)    45s ago  27m    60.0M        -  16.2.10  0d668911f040  a5d6e8a8983a  
```

9-9 Go PaaS platform k8s adds an external Ceph system through CSI (Part 1)

C:\Users\Administrator\Desktop\gopaas\ceph\2.k8s Use CSI to add ceph as storage.md

### k8s 1.21.5 CSI 模式添加 ceph 16 为为外部存储(动态存储卷)

#### 1.动态持久卷
不需要存储管理员干预,使k8s使用的存储image创建自动化,即根据使用需要可以动态申请存储空间并自动创建。需要先定义一个或者多个StorageClass,每个StorageClass都必须配置一个provisioner,用来决定使用哪个卷插件分配PV。然后,StorageClass资源指定持久卷声明请求StorageClass时使用哪个provisioner来在对应存储创建持久卷。

#### 2.创建一个普通用户来给k8s做rdb的映射
在ceph集群中创建一个k8s专用的pool和用户:
```cassandraql
ceph osd pool create kubernetes 16 16
ceph auth get-or-create client.kubernetes mon 'profile rbd' osd 'profile rbd pool=kubernetes' mgr 'profile rbd pool=kubernetes'

初始化
rbd pool init kubernetes

查看 pool
ceph osd pool ls 
rados lspools
```
得到
```cassandraql
[client.kubernetes]
        key = AQC2Q/ZiecM/MBAA2nwfDPKgfReHxz/o4kQV3A==
```
后面的配置需要用到这里的 key,如果忘了可以通过以下命令来获取:
```cassandraql
ceph auth get client.kubernetes
```
得到
```cassandraql
[client.kubernetes]
        key = AQC2Q/ZiecM/MBAA2nwfDPKgfReHxz/o4kQV3A==
        caps mgr = "profile rbd pool=kubernetes"
        caps mon = "profile rbd"
        caps osd = "profile rbd pool=kubernetes"
exported keyring for client.kubernetes
```
#### 3. 部署 ceph-csi 在k8s master 集群上
拉取 ceph-csi 的 最新 release 分支(v3.6.2)
```cassandraql
git clone --depth 1 --branch v3.6.2 https://github.com/ceph/ceph-csi
```
##### 修改 Configmap
获取 Ceph 集群的信息:
```cassandraql
[root@ceph01 ~]# ceph mon dump
epoch 3
fsid c32ff766-19f6-11ed-aa17-00163e005933
last_changed 2022-08-12T04:55:22.875422+0000
created 2022-08-12T04:26:27.658334+0000
min_mon_release 16 (pacific)
election_strategy: 1
0: [v2:172.31.96.70:3300/0,v1:172.31.96.70:6789/0] mon.ceph01
1: [v2:172.31.96.71:3300/0,v1:172.31.96.71:6789/0] mon.ceph02
2: [v2:172.31.96.72:3300/0,v1:172.31.96.72:6789/0] mon.ceph03
dumped monmap epoch 3
```

这里需要用到两个信息:
- fsid : 这个是 Ceph 的集群 ID。
- 监控节点信息。目前 ceph-csi 只支持 v1 版本的协议,所以监控节点那里我们只能用 v1 的那个 IP 和端口号(例如,172.31.96.70:6789)。

进入 ceph-csi 的 deploy/rbd/kubernetes 目录:
```cassandraql
[root@master ~]# cd ceph-csi/deploy/rbd/kubernetes
[root@master kubernetes]# ls -l ./
total 40
-rw-r--r-- 1 root root  309 Aug 12 20:18 csi-config-map.yaml
-rw-r--r-- 1 root root  435 Aug 12 20:18 csidriver.yaml
-rw-r--r-- 1 root root 1776 Aug 12 20:18 csi-nodeplugin-psp.yaml
-rw-r--r-- 1 root root 1110 Aug 12 20:18 csi-nodeplugin-rbac.yaml
-rw-r--r-- 1 root root 1199 Aug 12 20:18 csi-provisioner-psp.yaml
-rw-r--r-- 1 root root 3264 Aug 12 20:18 csi-provisioner-rbac.yaml
-rw-r--r-- 1 root root 8021 Aug 12 20:18 csi-rbdplugin-provisioner.yaml
-rw-r--r-- 1 root root 7242 Aug 12 20:18 csi-rbdplugin.yaml
```
将以上获取的信息写入 csi-config-map.yaml:
vi csi-config-map.yaml
```cassandraql
apiVersion: v1
kind: ConfigMap
data:
  config.json: |-
    [
      {
        "clusterID": "c32ff766-19f6-11ed-aa17-00163e005933",
        "monitors": [
          "172.31.96.70:6789",
          "172.31.96.71:6789",
          "172.31.96.72:6789"
        ]
      }
    ]    
metadata:
  name: ceph-csi-config
```
将此 Configmap 存储到 Kubernetes 集群中:
```cassandraql
kubectl  apply -f csi-config-map.yaml
```

创建ceph-config
```cassandraql
cat <<EOF > ceph-config-map.yaml
---
apiVersion: v1
kind: ConfigMap
data:
  ceph.conf: |
    [global]
    auth_cluster_required = cephx
    auth_service_required = cephx
    auth_client_required = cephx
  # keyring is a required key and its value should be empty
  keyring: |
metadata:
  name: ceph-config
EOF
```
创建
```cassandraql
kubectl apply -f ceph-config-map.yaml
```

创建 ceph-csi-encryption-kms-config
```cassandraql
cat <<EOF > csi-kms-config-map.yaml
---
apiVersion: v1
kind: ConfigMap
data:
  config.json: |-
    {}
metadata:
  name: ceph-csi-encryption-kms-config
EOF
```
启用
```cassandraql
kubectl apply -f csi-kms-config-map.yaml
```

9-10 Go PaaS platform k8s adds external Ceph system through CSI (Part 2)

##### 新建 Secret
使用创建的 kubernetes 用户 ID 和 cephx 密钥生成 Secret:
```cassandraql
cat <<EOF > csi-rbd-secret.yaml
   apiVersion: v1
   kind: Secret
   metadata:
     name: csi-rbd-secret
     namespace: default
   stringData:
     userID: kubernetes
     userKey: AQC2Q/ZiecM/MBAA2nwfDPKgfReHxz/o4kQV3A==
EOF
```
部署 Secret:
```cassandraql
kubectl apply -f csi-rbd-secret.yaml
```
##### RBAC 授权
创建必须的 ServiceAccount 和 RBAC ClusterRole/ClusterRoleBinding 资源对象:
```cassandraql
kubectl create -f csi-provisioner-rbac.yaml
kubectl create -f csi-nodeplugin-rbac.yaml
```

创建 PodSecurityPolicy:
```cassandraql
kubectl create -f csi-provisioner-psp.yaml
kubectl create -f csi-nodeplugin-psp.yaml
```

##### 部署 CSI sidecar
将 csi-rbdplugin-provisioner.yaml 和 csi-rbdplugin.yaml 中的 kms 部分配置注释掉:
```cassandraql
           # - name: ceph-csi-encryption-kms-config
           #   mountPath: /etc/ceph-csi-encryption-kms-config/

.... 
        #- name: ceph-csi-encryption-kms-config
        #  configMap:
        #    name: ceph-csi-encryption-kms-config
          
```
注释掉pod亲和性
```cassandraql
#      affinity:
#        podAntiAffinity:
#          requiredDuringSchedulingIgnoredDuringExecution:
#            - labelSelector:
#                matchExpressions:
#                  - key: app
#                    operator: In
#                    values:
#                      - csi-rbdplugin-provisioner
#              topologyKey: "kubernetes.io/hostname"
```

改为一个副本
```cassandraql
spec:
  replicas: 1
```

部署 csi-rbdplugin-provisioner:
```
kubectl create -f csi-rbdplugin-provisioner.yaml 
```
这里面包含了 6 个 Sidecar 容器,包括 external-provisioner、external-attacher、csi-resizer 和 csi-rbdplugin。

##### 部署 RBD CSI driver
最后部署 RBD CSI Driver:
``` 
kubectl create -f csi-rbdplugin.yaml
```
Pod 中包含两个容器:CSI node-driver-registrar 和 CSI RBD driver。
##### 创建 Storageclass
```cassandraql
cat <<EOF > storageclass.yaml
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
   name: csi-rbd-sc
provisioner: rbd.csi.ceph.com
parameters:
   clusterID: c32ff766-19f6-11ed-aa17-00163e005933
   pool: kubernetes
   imageFeatures: layering
   csi.storage.k8s.io/provisioner-secret-name: csi-rbd-secret
   csi.storage.k8s.io/provisioner-secret-namespace: default
   csi.storage.k8s.io/controller-expand-secret-name: csi-rbd-secret
   csi.storage.k8s.io/controller-expand-secret-namespace: default
   csi.storage.k8s.io/node-stage-secret-name: csi-rbd-secret
   csi.storage.k8s.io/node-stage-secret-namespace: default
   csi.storage.k8s.io/fstype: ext4
reclaimPolicy: Delete
allowVolumeExpansion: true
mountOptions:
   - discard
EOF
```
创建 storageclass 
```cassandraql
 kubectl apply -f storageclass.yaml 
```

- 这里的 clusterID 对应之前步骤中的 fsid。
- imageFeatures 用来确定创建的 image 特征,如果不指定,就会使用 RBD 内核中的特征列表,但 Linux 不一定支持所有特征,所以这里需要限制一下。

#### 4.试用 ceph-csi
Kubernetes 通过 PersistentVolume 子系统为用户和管理员提供了一组 API,将存储如何供应的细节从其如何被使用中抽象出来,其中 PV(PersistentVolume) 是实际的存储,PVC(PersistentVolumeClaim) 是用户对存储的请求。

下面通过官方仓库的示例来演示如何使用 ceph-csi。

先进入 ceph-csi 项目的 example/rbd 目录,然后直接创建 PVC:
```cassandraql
kubectl apply -f pvc.yaml
```
查看 PVC 和申请成功的 PV:
```cassandraql
$ kubectl get pvc
NAME      STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
rbd-pvc   Bound    pvc-44b89f0e-4efd-4396-9316-10a04d289d7f   1Gi        RWO            csi-rbd-sc     8m21s

$ kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                STORAGECLASS   REASON   AGE
pvc-44b89f0e-4efd-4396-9316-10a04d289d7f   1Gi        RWO            Delete           Bound    default/rbd-pvc      csi-rbd-sc              8m18s
```
再创建示例 Pod:
```
kubectl apply -f pod.yaml
```

进入 Pod 里面测试读写数据:
```cassandraql
kubectl apply -f pod.yaml
```
进入 Pod 里面测试读写数据:
```cassandraql

kubectl exec -it csi-rbd-demo-pod bash
root@csi-rbd-demo-pod:/# cd /var/lib/www/
root@csi-rbd-demo-pod:/var/lib/www# ls -l
total 4
drwxrwxrwx 3 root root 4096 Sep 14 09:09 html
root@csi-rbd-demo-pod:/var/lib/www# echo "你好!" > wu123.txt
wu123
```
列出 kubernetes pool 中的 rbd images:
```cassandraql
rbd ls -p kubernetes
csi-vol-fe40eb16-1a4e-11ed-bb7c-0eb2f382cefd
```

查看该 image 的特征:

```cassandraql
[root@ceph01 ceph]# rbd info csi-vol-fe40eb16-1a4e-11ed-bb7c-0eb2f382cefd -p kubernetes
rbd image 'csi-vol-fe40eb16-1a4e-11ed-bb7c-0eb2f382cefd':
        size 1 GiB in 256 objects
        order 22 (4 MiB objects)
        snapshot_count: 0
        id: d50f18182e6b
        block_name_prefix: rbd_data.d50f18182e6b
        format: 2
        features: layering
        op_features: 
        flags: 
        create_timestamp: Fri Aug 12 22:56:49 2022
        access_timestamp: Fri Aug 12 22:56:49 2022
        modify_timestamp: Fri Aug 12 22:56:49 2022
```

到此,k8s 使用外部 ceph 存储配置结束

9-11 PVC model development for Go PaaS platform 

yu-tool.exe  newService github.com/yunixiangfeng/gopaas/volume

yu-v3 --proto_path=. --micro_out=. --go_out=:. ./proto/volume/volume.proto
go mod tidy

C:\Users\Administrator\Desktop\gopaas\volume\domain\model\volume.go

package model

type Volume struct{
	ID int64 `gorm:"primary_key;not_null;auto_increment"`
	//存储的名称
	VolumeName string `json:"volume_name"`
	//存储的所属的命名空间
	VolumeNamespace string `json:"volume_namespace"`
	//存储的访问模式,RWO,ROX,RWX
	VolumeAccessMode string `json:"volume_access_mode"`
	//sc 的 class name
	VolumeStorageClassName string `json:"volume_storage_class_name"`
	//请求资源的大小
	VolumeRequest float32 `json:"volume_request"`
	//存储类型 Block,filesystem
	VolumePersistentVolumeMode string `json:"volume_persistent_volume_mode"`
}

 C:\Users\Administrator\Desktop\gopaas\volume\proto\volume\volume.proto

syntax = "proto3";

package volume;

option go_package = "./proto/volume;volume";

service Volume {
	//对外提供添加服务
	rpc AddVolume(VolumeInfo) returns (Response) {}
	rpc DeleteVolume(VolumeId) returns (Response) {}
	rpc UpdateVolume(VolumeInfo) returns (Response) {}
	rpc FindVolumeByID(VolumeId) returns (VolumeInfo) {}
	rpc FindAllVolume(FindAll) returns (AllVolume) {}
}
message VolumeInfo {
	int64 id = 1;
	string volume_name=2;
	string volume_namespace=3;
	string volume_access_mode=4;
	string volume_storage_class_name=5;
	float volume_request=6;
	string volume_persistent_volume_mode=7;
}

message VolumeId {
	int64 id = 1;
}

message FindAll {

}

message Response {
	string msg =1 ;
}

message AllVolume {
	repeated VolumeInfo volume_info = 1;
}


9-12 Go PaaS Platform Service Development (Part 1) 

C:\Users\Administrator\Desktop\gopaas\volume\domain\service\volume_data_service.go

package service

import (
	"context"
	"errors"
	"strconv"

	"github.com/yunixiangfeng/gopaas/common"
	"github.com/yunixiangfeng/gopaas/volume/domain/model"
	"github.com/yunixiangfeng/gopaas/volume/domain/repository"
	"github.com/yunixiangfeng/gopaas/volume/proto/volume"
	"k8s.io/api/apps/v1"
	v12 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	v13 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
)

//这里是接口类型
type IVolumeDataService interface {
	AddVolume(*model.Volume) (int64 , error)
	DeleteVolume(int64) error
	UpdateVolume(*model.Volume) error
	FindVolumeByID(int64) (*model.Volume, error)
	FindAllVolume() ([]model.Volume, error)

	CreateVolumeToK8s(*volume.VolumeInfo) error
	DeleteVolumeFromK8s(*model.Volume) error
}


//创建
//注意:返回值 IVolumeDataService 接口类型
func NewVolumeDataService(volumeRepository repository.IVolumeRepository,clientSet *kubernetes.Clientset) IVolumeDataService{
	return &VolumeDataService{ VolumeRepository:volumeRepository, K8sClientSet: clientSet,deployment:&v1.Deployment{}}
}

type VolumeDataService struct {
    //注意:这里是 IVolumeRepository 类型
	VolumeRepository repository.IVolumeRepository
	K8sClientSet  *kubernetes.Clientset
	deployment  *v1.Deployment
}
//从 k8s 删除一个 pvc
func (u *VolumeDataService) DeleteVolumeFromK8s(volume *model.Volume) (err error) {
	//先从K8s 中删除
	if err = u.K8sClientSet.CoreV1().PersistentVolumeClaims(volume.VolumeNamespace).Delete(context.TODO(),volume.VolumeName, v13.DeleteOptions{});err!=nil {
		common.Error(err)
		return err
	} else {
		//从数据表中删除
		if err := u.DeleteVolume(volume.ID);err !=nil {
			common.Error(err)
			return err
		}
		common.Info("删除存储ID"+ strconv.FormatInt(volume.ID,10)+" 成功!")
	}
	return
}

//创建存储到 k8s 中
func (u *VolumeDataService) CreateVolumeToK8s(info *volume.VolumeInfo)(err error) {
	volume := u.setVolume(info)
	if _,err = u.K8sClientSet.CoreV1().PersistentVolumeClaims(info.VolumeNamespace).Get(context.TODO(),info.VolumeName,v13.GetOptions{});err !=nil {
		//如果存储不存在
		if _,err = u.K8sClientSet.CoreV1().PersistentVolumeClaims(info.VolumeNamespace).Create(context.TODO(),volume,v13.CreateOptions{});err !=nil{
			common.Error(err)
			return err
		}
		common.Info("存储创建成功")
		return nil
	}else {
		common.Error("存储空间"+ info.VolumeName +" 已经存在")
		return errors.New("存储空间"+ info.VolumeName +" 已经存在")
	}
}
//设置 pvc 的详情信息
func (u *VolumeDataService) setVolume(info *volume.VolumeInfo) *v12.PersistentVolumeClaim {
	pvc := &v12.PersistentVolumeClaim{}
	//设置接口类型
	pvc.TypeMeta = v13.TypeMeta{
		Kind:       "PersistentVolumeClaim",
		APIVersion: "v1",
	}
	//设置存储基础信息
	pvc.ObjectMeta = v13.ObjectMeta{
		Name:                       info.VolumeName,
		Namespace:                  info.VolumeNamespace,
		Annotations: map[string]string{
			"pv.kubernetes.io/bound-by-controller":"yes",
			"volume.beta.kubernetes.io/storage-provisioner":"rbd.csi.ceph.com",
			"wu":"wu123",
		},
	}
	//设置存储动态信息
	pvc.Spec = v12.PersistentVolumeClaimSpec{
		AccessModes:      u.getAccessModes(info),
		Resources:        u.getResource(info),
		StorageClassName: &info.VolumeStorageClassName,
		VolumeMode:       u.getVolumeMode(info),
	}
	return pvc

}

//获取存储类型
func (u *VolumeDataService) getVolumeMode(info *volume.VolumeInfo) *v12.PersistentVolumeMode {
	var pvm v12.PersistentVolumeMode
	switch info.VolumePersistentVolumeMode {
	case "Block":
		pvm = v12.PersistentVolumeBlock
	case "Filesystem":
		pvm = v12.PersistentVolumeFilesystem
	default:
		pvm = v12.PersistentVolumeFilesystem
	}
	return &pvm
}

//插入
func (u *VolumeDataService) AddVolume(volume *model.Volume) (int64 ,error) {
	 return u.VolumeRepository.CreateVolume(volume)
}

//删除
func (u *VolumeDataService) DeleteVolume(volumeID int64) error {
	return u.VolumeRepository.DeleteVolumeByID(volumeID)
}

//更新
func (u *VolumeDataService) UpdateVolume(volume *model.Volume) error {
	return u.VolumeRepository.UpdateVolume(volume)
}

//查找
func (u *VolumeDataService) FindVolumeByID(volumeID int64) (*model.Volume, error) {
	return u.VolumeRepository.FindVolumeByID(volumeID)
}

//查找
func (u *VolumeDataService) FindAllVolume() ([]model.Volume, error) {
	return u.VolumeRepository.FindAll()
}

9-13 Go PaaS Platform Service Development (Part 2) 

C:\Users\Administrator\Desktop\gopaas\volume\domain\service\volume_data_service.go

//获取资源配置
func (u *VolumeDataService) getResource (info *volume.VolumeInfo)(source v12.ResourceRequirements)  {
	source.Requests = v12.ResourceList{
		"storage": resource.MustParse(strconv.FormatFloat(float64(info.VolumeRequest),'f',6,64)+"Gi"),
	}
	return
}

//获取访问模式
func (u *VolumeDataService) getAccessModes(info *volume.VolumeInfo)(pvam []v12.PersistentVolumeAccessMode)  {
	var pm v12.PersistentVolumeAccessMode
	switch info.VolumeAccessMode {
	case "ReadWriteOnce":
		pm = v12.ReadWriteOnce
	case "ReadOnlyMany":
		pm = v12.ReadOnlyMany
	case "ReadWriteMany":
		pm = v12.ReadWriteMany
	case "ReadWriteOncePod":
		pm = v12.ReadWriteOncePod
	default:
		pm = v12.ReadWriteOnce
	}
	pvam = append(pvam,pm)
	return pvam
	
}

9-14 The relationship and principle between PV and PVC on Go PaaS platform 

The relationship between PV and PVC
PV (Persistent Volume): The meaning of persistent volume is an abstraction of the underlying shared storage
PVC (Persistent Volume Claim): User storage request

PV access mode (accessModes
ReadWriteOnce(RWO): readable and writable, single mount
ReadOnlyMany(ROX): read-only, multiple mounts
ReadWriteMany(RWX): readable and writable, multiple mounts

PV's recovery policy (persistentVolumeReclaimPolicy)
Retain: Do not clean up, keep the Volume (requires manual cleanup
Recycle: delete data, similar to rm -rf /xxx/*
Delete: delete storage resources

PV status
Available: Available
Bound: Already assigned to PVC
Released: PVC unbound but has not yet executed the recycling policy
Failed: Storage exception

Key parameters of PVC
access mode (accessModes): indicates the access right selector (selector): label selects and filters binding PV
storage class (storageClassName): set to automatically create PV
request resources (Resources): indicates the required storage size

The life cycle of PV and PVC: Provisioning configuration stage
Static: The administrator creates multiple PVs, the attributes are determined, and real storage devices are bound.
Dynamic: Through StorageClass, k8s will create a dynamic PV for PVC to benefit when resources can be used. The creation mode is automatic.

Life cycle of PV and PVC: Binding
is dynamically configured. After the user creates a PVC,
the process of binding the PVC to the PV is indicated
. If there is no PV that meets the PVC request, the PVC cannot be created.
If a PVC is bound to a Pod, the pod will not be created

Life cycle of PV and PVC
Using: After PVC is bound to PV, Pod uses the storage space normally
Releasing: When Pod is deleted or PV resources are used up
Reclaiming: Reclaiming strategy of PV Recycling
: PV Allocated after being erased 

9-15 Go PaaS Platform Service Handler Development 

C:\Users\Administrator\Desktop\gopaas\volume\handler\volumeHandler.go

package handler

import (
	"context"
	"strconv"

	log "github.com/asim/go-micro/v3/logger"
	"github.com/yunixiangfeng/gopaas/common"
	"github.com/yunixiangfeng/gopaas/volume/domain/model"
	"github.com/yunixiangfeng/gopaas/volume/domain/service"
	volume "github.com/yunixiangfeng/gopaas/volume/proto/volume"
)

type VolumeHandler struct {
	//注意这里的类型是 IVolumeDataService 接口类型
	VolumeDataService service.IVolumeDataService
}

// Call is a single request handler called via client.Call or the generated client code
func (e *VolumeHandler) AddVolume(ctx context.Context, info *volume.VolumeInfo, rsp *volume.Response) error {
	log.Info("Received *volume.AddVolume request")
	volume := &model.Volume{}
	if err := common.SwapTo(info, volume); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	//创建volume
	if err := e.VolumeDataService.CreateVolumeToK8s(info); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	} else {
		//写入数据库
		volumeID, err := e.VolumeDataService.AddVolume(volume)
		if err != nil {
			common.Error(err)
			rsp.Msg = err.Error()
			return err
		}
		rsp.Msg = "volume 添加成功 ID 号为:" + strconv.FormatInt(volumeID, 10)
	}
	return nil
}

//删除
func (e *VolumeHandler) DeleteVolume(ctx context.Context, req *volume.VolumeId, rsp *volume.Response) error {
	log.Info("Received *volume.DeleteVolume request")
	volumModel, err := e.VolumeDataService.FindVolumeByID(req.Id)
	if err != nil {
		common.Error(err)
		return err
	}
	//从k8s中删除,并且删除数据库
	if err := e.VolumeDataService.DeleteVolumeFromK8s(volumModel); err != nil {
		common.Error(err)
		return err
	}
	return nil
}

func (e *VolumeHandler) UpdateVolume(ctx context.Context, req *volume.VolumeInfo, rsp *volume.Response) error {
	log.Info("Received *volume.UpdateVolume request")
	return nil
}

//根据 ID 查找 volume
func (e *VolumeHandler) FindVolumeByID(ctx context.Context, req *volume.VolumeId, rsp *volume.VolumeInfo) error {
	log.Info("Received *volume.FindVolumeByID request")
	volumeModel, err := e.VolumeDataService.FindVolumeByID(req.Id)
	if err != nil {
		common.Error(err)
		return err
	}
	//数据转化
	if err := common.SwapTo(volumeModel, rsp); err != nil {
		common.Error(err)
		return err
	}
	return nil
}

func (e *VolumeHandler) FindAllVolume(ctx context.Context, req *volume.FindAll, rsp *volume.AllVolume) error {
	log.Info("Received *volume.FindAllVolume request")
	allVolume, err := e.VolumeDataService.FindAllVolume()
	if err != nil {
		common.Error(err)
		return err
	}
	//整理格式
	for _, v := range allVolume {
		//创建实例
		volumeInfo := &volume.VolumeInfo{}
		//数据转化
		if err := common.SwapTo(v, volumeInfo); err != nil {
			common.Error(err)
			return err
		}
		//数据合并
		rsp.VolumeInfo = append(rsp.VolumeInfo, volumeInfo)
	}
	return nil
}

C:\Users\Administrator\Desktop\gopaas\volume\main.go

package main

import (
	"flag"
	"fmt"
	"path/filepath"

	"github.com/yunixiangfeng/gopaas/common"
	"github.com/yunixiangfeng/gopaas/volume/domain/repository"

	//"github.com/afex/hystrix-go/hystrix"
	"github.com/asim/go-micro/plugins/registry/consul/v3"
	ratelimit "github.com/asim/go-micro/plugins/wrapper/ratelimiter/uber/v3"
	opentracing2 "github.com/asim/go-micro/plugins/wrapper/trace/opentracing/v3"
	"github.com/asim/go-micro/v3"
	"github.com/asim/go-micro/v3/registry"
	"github.com/asim/go-micro/v3/server"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
	"github.com/opentracing/opentracing-go"
	service2 "github.com/yunixiangfeng/gopaas/volume/domain/service"
	"github.com/yunixiangfeng/gopaas/volume/handler"

	//hystrix2 "github.com/yunixiangfeng/gopaas/volume/plugin/hystrix"
	"strconv"

	volume "github.com/yunixiangfeng/gopaas/volume/proto/volume"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/client-go/util/homedir"
)

var (
	//服务地址
	hostIp = "192.168.204.130"
	//服务地址
	serviceHost = hostIp
	//服务端口
	servicePort = "8087"

	//注册中心配置
	consulHost       = hostIp
	consulPort int64 = 8500
	//链路追踪
	tracerHost = hostIp
	tracerPort = 6831
	//熔断端口,每个服务不能重复
	//hystrixPort = 9097
	//监控端口,每个服务不能重复
	prometheusPort = 9197
)

func main() {
	//需要本地启动,mysql,consul中间件服务
	//1.注册中心
	consul := consul.NewRegistry(func(options *registry.Options) {
		options.Addrs = []string{
			consulHost + ":" + strconv.FormatInt(consulPort, 10),
		}
	})
	//2.配置中心,存放经常变动的变量
	consulConfig, err := common.GetConsulConfig(consulHost, consulPort, "/micro/config")
	if err != nil {
		common.Error(err)
	}
	//3.使用配置中心连接 mysql
	mysqlInfo := common.GetMysqlFromConsul(consulConfig, "mysql")
	//初始化数据库
	db, err := gorm.Open("mysql", mysqlInfo.User+":"+mysqlInfo.Pwd+"@("+mysqlInfo.Host+":3306)/"+mysqlInfo.Database+"?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		//命令行输出下,方便查看错误
		fmt.Println(err)
		common.Fatal(err)
	}
	defer db.Close()
	//禁止复表
	db.SingularTable(true)

	//4.添加链路追踪
	t, io, err := common.NewTracer("go.micro.service.volume", tracerHost+":"+strconv.Itoa(tracerPort))
	if err != nil {
		common.Error(err)
	}
	defer io.Close()
	opentracing.SetGlobalTracer(t)

	//添加熔断器,作为客户端需要启用
	//hystrixStreamHandler := hystrix.NewStreamHandler()
	//hystrixStreamHandler.Start()

	//添加日志中心
	//1)需要程序日志打入到日志文件中
	//2)在程序中添加filebeat.yml 文件
	//3) 启动filebeat,启动命令 ./filebeat -e -c filebeat.yml
	fmt.Println("日志统一记录在根目录 micro.log 文件中,请点击查看日志!")

	//启动监听程序
	//go func() {
	//	//http://192.168.204.130:9092/turbine/turbine.stream
	//	//看板访问地址 http://127.0.0.1:9002/hystrix,url后面一定要带 /hystrix
	//	err = http.ListenAndServe(net.JoinHostPort("0.0.0.0",strconv.Itoa(hystrixPort)),hystrixStreamHandler)
	//	if err !=nil {
	//		common.Error(err)
	//	}
	//}()

	//5.添加监控
	common.PrometheusBoot(prometheusPort)

	//下载kubectl:https://kubernetes.io/docs/tasks/tools/#tabset-2
	//macos:
	// 1.curl -LO "https://dl.k8s.io/release/v1.21.0/bin/darwin/amd64/kubectl"
	// 2.chmod +x ./kubectl
	// 3.sudo mv ./kubectl /usr/local/bin/kubectl
	//   sudo chown root: /usr/local/bin/kubectl
	// 5.kubectl version --client
	// 6.集群模式下直接拷贝服务端~/.kube/config 文件到本机 ~/.kube/confg 中
	//   注意:- config中的域名要能解析正确
	//        - 生产环境可以创建另一个证书
	// 7.kubectl get ns 查看是否正常
	//
	//6.创建k8s连接
	//在集群外面连接
	var kubeconfig *string
	if home := homedir.HomeDir(); home != "" {
		kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
	} else {
		kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
	}
	flag.Parse()
	config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
	if err != nil {
		common.Fatal(err.Error())
	}

	//在集群中外的配置
	//config, err := rest.InClusterConfig()
	//if err != nil {
	//	panic(err.Error())
	//}

	// create the clientset
	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		common.Fatal(err.Error())
	}

	//7.创建服务
	service := micro.NewService(
		//自定义服务地址,且必须写在其它参数前面
		micro.Server(server.NewServer(func(options *server.Options) {
			options.Advertise = serviceHost + ":" + servicePort
		})),
		micro.Name("go.micro.service.volume"),
		micro.Version("latest"),
		//指定服务端口
		micro.Address(":"+servicePort),
		//添加注册中心
		micro.Registry(consul),
		//添加链路追踪
		micro.WrapHandler(opentracing2.NewHandlerWrapper(opentracing.GlobalTracer())),
		micro.WrapClient(opentracing2.NewClientWrapper(opentracing.GlobalTracer())),
		//只作为客户端的时候起作用,如果存在调用别人的情况,原则上不去主动调用
		//micro.WrapClient(hystrix2.NewClientHystrixWrapper()),
		//添加限流
		micro.WrapHandler(ratelimit.NewHandlerWrapper(1000)),
	)

	service.Init()

	//只能执行一遍
	//err = repository.NewVolumeRepository(db).InitTable()
	//if err != nil {
	//	common.Fatal(err)
	//}

	// 注册句柄,可以快速操作已开发的服务
	volumeDataService := service2.NewVolumeDataService(repository.NewVolumeRepository(db), clientset)
	volume.RegisterVolumeHandler(service.Server(), &handler.VolumeHandler{VolumeDataService: volumeDataService})

	// 启动服务
	if err := service.Run(); err != nil {
		//输出启动失败信息
		common.Fatal(err)
	}
}

9-16 Go PaaS platform volume api development 

PS C:\Users\Administrator\Desktop\gopaas> .\yu-tool\yu-tool.exe  createApi github.com/yunixiangfeng/gopaas/volumeApi

yu-v3 --proto_path=. --micro_out=. --go_out=:. ./proto/volumeApi/volumeApi.proto

go mod tidy

C:\Users\Administrator\Desktop\gopaas\volumeapi\handler\volumeApiHandler.go

package handler

import (
	"context"
	"encoding/json"
	"errors"
	"strconv"

	log "github.com/asim/go-micro/v3/logger"
	"github.com/yunixiangfeng/gopaas/common"
	volume "github.com/yunixiangfeng/gopaas/volume/proto/volume"
	"github.com/yunixiangfeng/gopaas/volumeApi/plugin/form"
	volumeApi "github.com/yunixiangfeng/gopaas/volumeApi/proto/volumeApi"
)

type VolumeApi struct {
	VolumeService volume.VolumeService
}

// volumeApi.FindVolumeById 通过API向外暴露为/volumeApi/findVolumeById,接收http请求
// 即:/volumeApi/FindVolumeById 请求会调用go.micro.api.volumeApi 服务的volumeApi.FindVolumeById 方法
func (e *VolumeApi) FindVolumeById(ctx context.Context, req *volumeApi.Request, rsp *volumeApi.Response) error {
	log.Info("Received volumeApi.FindVolumeById request")
	if _, ok := req.Get["volume_id"]; !ok {
		rsp.StatusCode = 500
		return errors.New("参数有异常")
	}
	//获取 volume_id
	volumeIdString := req.Get["volume_id"].Values[0]
	volumeId, err := strconv.ParseInt(volumeIdString, 10, 64)
	if err != nil {
		common.Error(err)
		return err
	}
	//获取 volume 信息
	volumeInfo, err := e.VolumeService.FindVolumeByID(ctx, &volume.VolumeId{
		Id: volumeId,
	})
	if err != nil {
		common.Error(err)
		return err
	}
	rsp.StatusCode = 200
	b, _ := json.Marshal(volumeInfo)
	rsp.Body = string(b)
	return nil
}

// volumeApi.AddVolume 通过API向外暴露为/volumeApi/AddVolume,接收http请求
// 即:/volumeApi/AddVolume 请求会调用go.micro.api.volumeApi 服务的volumeApi.AddVolume 方法
func (e *VolumeApi) AddVolume(ctx context.Context, req *volumeApi.Request, rsp *volumeApi.Response) error {
	log.Info("Received volumeApi.AddVolume request")
	addVolumeInfo := &volume.VolumeInfo{}
	form.FormToSvcStruct(req.Post, addVolumeInfo)
	response, err := e.VolumeService.AddVolume(ctx, addVolumeInfo)
	if err != nil {
		common.Error(err)
		return err
	}
	rsp.StatusCode = 200
	b, _ := json.Marshal(response)
	rsp.Body = string(b)
	return nil
}

// volumeApi.DeleteVolumeById 通过API向外暴露为/volumeApi/DeleteVolumeById,接收http请求
// 即:/volumeApi/DeleteVolumeById 请求会调用go.micro.api.volumeApi 服务的 volumeApi.DeleteVolumeById 方法
func (e *VolumeApi) DeleteVolumeById(ctx context.Context, req *volumeApi.Request, rsp *volumeApi.Response) error {
	log.Info("Received volumeApi.DeleteVolumeById request")
	if _, ok := req.Get["volume_id"]; !ok {
		rsp.StatusCode = 500
		return errors.New("参数异常")
	}
	//获取 volume_id
	volumeIdString := req.Get["volume_id"].Values[0]
	volumeId, err := strconv.ParseInt(volumeIdString, 10, 64)
	if err != nil {
		common.Error(err)
		return err
	}
	//调用 volume 删除服务
	response, err := e.VolumeService.DeleteVolume(ctx, &volume.VolumeId{
		Id: volumeId,
	})
	if err != nil {
		common.Error(err)
		return err
	}
	rsp.StatusCode = 200
	b, _ := json.Marshal(response)
	rsp.Body = string(b)
	return nil
}

// volumeApi.UpdateVolume 通过API向外暴露为/volumeApi/UpdateVolume,接收http请求
// 即:/volumeApi/UpdateVolume 请求会调用go.micro.api.volumeApi 服务的volumeApi.UpdateVolume 方法
func (e *VolumeApi) UpdateVolume(ctx context.Context, req *volumeApi.Request, rsp *volumeApi.Response) error {
	log.Info("Received volumeApi.UpdateVolume request")
	rsp.StatusCode = 200
	b, _ := json.Marshal("{success:'成功访问/volumeApi/UpdateVolume'}")
	rsp.Body = string(b)
	return nil
}

// 默认的方法volumeApi.Call 通过API向外暴露为/volumeApi/call,接收http请求
// 即:/volumeApi/call或/volumeApi/ 请求会调用go.micro.api.volumeApi 服务的volumeApi.FindVolumeById 方法
func (e *VolumeApi) Call(ctx context.Context, req *volumeApi.Request, rsp *volumeApi.Response) error {
	log.Info("Received volumeApi.Call request")
	allVolume, err := e.VolumeService.FindAllVolume(ctx, &volume.FindAll{})
	if err != nil {
		common.Error(err)
		return err
	}
	rsp.StatusCode = 200
	b, _ := json.Marshal(allVolume)
	rsp.Body = string(b)
	return nil
}

C:\Users\Administrator\Desktop\gopaas\volumeapi\plugin\form\form.go

package form

import (
	"errors"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/yunixiangfeng/gopaas/common"
	"github.com/yunixiangfeng/gopaas/volumeApi/proto/volumeApi"
)

//根据结构体中name标签映射数据到结构体中并且转换类型
func FormToSvcStruct(data map[string]*volumeApi.Pair, obj interface{}) {
	objValue := reflect.ValueOf(obj).Elem()
	for i := 0; i < objValue.NumField(); i++ {
		//获取sql对应的值
		dataTag := strings.Replace(objValue.Type().Field(i).Tag.Get("json"), ",omitempty", "", -1)
		dataSlice, ok := data[dataTag]
		if !ok {
			continue
		}
		valueSlice := dataSlice.Values
		if len(valueSlice) <= 0 {
			continue
		}
		//排除port和env
		if dataTag == "route_path" {
			continue
		}
		value := valueSlice[0]
		//端口,环境变量的单独处理
		//获取对应字段的名称
		name := objValue.Type().Field(i).Name
		//获取对应字段类型
		structFieldType := objValue.Field(i).Type()
		//获取变量类型,也可以直接写"string类型"
		val := reflect.ValueOf(value)
		var err error
		if structFieldType != val.Type() {
			//类型转换
			val, err = TypeConversion(value, structFieldType.Name()) //类型转换
			if err != nil {
				common.Error(err)
			}
		}
		//设置类型值
		objValue.FieldByName(name).Set(val)
	}
}

//类型转换
func TypeConversion(value string, ntype string) (reflect.Value, error) {
	if ntype == "string" {
		return reflect.ValueOf(value), nil
	} else if ntype == "time.Time" {
		t, err := time.ParseInLocation("2006-01-02 15:04:05", value, time.Local)
		return reflect.ValueOf(t), err
	} else if ntype == "Time" {
		t, err := time.ParseInLocation("2006-01-02 15:04:05", value, time.Local)
		return reflect.ValueOf(t), err
	} else if ntype == "int" {
		i, err := strconv.Atoi(value)
		return reflect.ValueOf(i), err
	} else if ntype == "int32" {
		i, err := strconv.ParseInt(value, 10, 32)
		if err != nil {
			return reflect.ValueOf(int32(i)), err
		}
		return reflect.ValueOf(int32(i)), err
	} else if ntype == "int64" {
		i, err := strconv.ParseInt(value, 10, 64)
		return reflect.ValueOf(i), err
	} else if ntype == "float32" {
		i, err := strconv.ParseFloat(value, 64)
		return reflect.ValueOf(float32(i)), err
	} else if ntype == "float64" {
		i, err := strconv.ParseFloat(value, 64)
		return reflect.ValueOf(i), err
	}

	//else if .......增加其他一些类型的转换

	return reflect.ValueOf(value), errors.New("未知的类型:" + ntype)
}

 C:\Users\Administrator\Desktop\gopaas\volumeapi\main.go

package main

import (
	"fmt"

	"github.com/afex/hystrix-go/hystrix"
	"github.com/asim/go-micro/plugins/registry/consul/v3"
	ratelimit "github.com/asim/go-micro/plugins/wrapper/ratelimiter/uber/v3"
	"github.com/asim/go-micro/plugins/wrapper/select/roundrobin/v3"
	opentracing2 "github.com/asim/go-micro/plugins/wrapper/trace/opentracing/v3"
	"github.com/asim/go-micro/v3"
	"github.com/asim/go-micro/v3/registry"
	"github.com/asim/go-micro/v3/server"
	"github.com/yunixiangfeng/gopaas/common"
	go_micro_service_volume "github.com/yunixiangfeng/gopaas/volume/proto/volume"

	"net"
	"net/http"
	"strconv"

	_ "github.com/jinzhu/gorm/dialects/mysql"
	"github.com/opentracing/opentracing-go"
	"github.com/yunixiangfeng/gopaas/volumeApi/handler"
	hystrix2 "github.com/yunixiangfeng/gopaas/volumeApi/plugin/hystrix"

	volumeApi "github.com/yunixiangfeng/gopaas/volumeApi/proto/volumeApi"
)

var (
	//服务地址
	hostIp = "192.168.204.130"
	//服务地址
	serviceHost = hostIp
	//服务端口
	servicePort = "8087"
	//注册中心配置
	consulHost       = hostIp
	consulPort int64 = 8500
	//链路追踪
	tracerHost = hostIp
	tracerPort = 6831
	//熔断端口,每个服务不能重复
	hystrixPort = 9097
	//监控端口,每个服务不能重复
	prometheusPort = 9197
)

func main() {
	//需要本地启动,mysql,consul中间件服务
	//1.注册中心
	consul := consul.NewRegistry(func(options *registry.Options) {
		options.Addrs = []string{
			consulHost + ":" + strconv.FormatInt(consulPort, 10),
		}
	})

	//2.添加链路追踪
	t, io, err := common.NewTracer("go.micro.api.volumeApi", tracerHost+":"+strconv.Itoa(tracerPort))
	if err != nil {
		common.Error(err)
	}
	defer io.Close()
	opentracing.SetGlobalTracer(t)

	//3.添加熔断器
	hystrixStreamHandler := hystrix.NewStreamHandler()
	hystrixStreamHandler.Start()

	//添加日志中心
	//1)需要程序日志打入到日志文件中
	//2)在程序中添加filebeat.yml 文件
	//3) 启动filebeat,启动命令 ./filebeat -e -c filebeat.yml
	fmt.Println("日志统一记录在根目录 micro.log 文件中,请点击查看日志!")

	//启动监听程序
	go func() {
		//http://192.168.204.130:9092/turbine/turbine.stream
		//看板访问地址 http://127.0.0.1:9002/hystrix,url后面一定要带 /hystrix
		err = http.ListenAndServe(net.JoinHostPort("0.0.0.0", strconv.Itoa(hystrixPort)), hystrixStreamHandler)
		if err != nil {
			common.Error(err)
		}
	}()

	//4.添加监控
	common.PrometheusBoot(prometheusPort)

	//5.创建服务
	service := micro.NewService(
		//自定义服务地址,且必须写在其它参数前面
		micro.Server(server.NewServer(func(opts *server.Options) {
			opts.Advertise = serviceHost + ":" + servicePort

		})),
		micro.Name("go.micro.api.volumeApi"),
		micro.Version("latest"),
		//指定服务端口
		micro.Address(":"+servicePort),
		//添加注册中心
		micro.Registry(consul),
		//添加链路追踪
		micro.WrapHandler(opentracing2.NewHandlerWrapper(opentracing.GlobalTracer())),
		micro.WrapClient(opentracing2.NewClientWrapper(opentracing.GlobalTracer())),
		//只作为客户端的时候起作用
		micro.WrapClient(hystrix2.NewClientHystrixWrapper()),
		//添加限流
		micro.WrapHandler(ratelimit.NewHandlerWrapper(1000)),
		//增加负载均衡
		micro.WrapClient(roundrobin.NewClientWrapper()),
	)

	service.Init()

	// 指定需要访问的服务,可以快速操作已开发的服务,
	// 默认API服务名称带有"Api",程序会自动替换
	// 如果不带有特定字符会使用默认"XXX" 请自行替换
	volumeService := go_micro_service_volume.NewVolumeService("go.micro.service.volume", service.Client())
	// 注册控制器
	if err := volumeApi.RegisterVolumeApiHandler(service.Server(), &handler.VolumeApi{VolumeService: volumeService}); err != nil {
		common.Error(err)
	}

	// 启动服务
	if err := service.Run(); err != nil {
		//输出启动失败信息
		common.Fatal(err)
	}
}

file:///home/gopath/src/gopaas/go-paas-front/volume-create.html 

9-17 Summary & Thinking

Main content
Ceph basic concept introduction
Ceph architecture description and installation
Service back-end function development

Talking from experience,
Ceph needs to plan the number of PGs when creating expenditures.
Distributed storage recommends keeping a minimum of 2 copies for data loss and high availability.

Take snapshots before important data operations during operation

What is the data recovery process of a single Ceph node downtime?
What dimensions of Ceph monitoring should we care about?

9-18 [Extended reading] Using Rook to deploy Ceph based on kubernetes-1.21.5

Chapter 10 Cloud Native Go PaaS Platform Middleware Backend Management Service, Dynamically Create Middleware

During the research and development process, there is usually a need to create different middleware. It is a very efficient way to provide out-of-the-box middleware resources. Learn the creation and management of mysql middleware, and mount distributed Storage to meet the demand for data storage.

10-1 Go cloud native PaaS platform middleware model–middleware creation

GO PaaS platform middleware creation and management

PS C:\Users\Administrator\Desktop\gopaas> .\yu-tool\yu-tool.exe  newService github.com/yunixiangfeng/gopaas/middleware

yu-v3 --proto_path=. --micro_out=. --go_out=:. ./proto/middleware/middleware.proto

go mod tidy

 middleware\domain\model\middleware.go

package model

type Middleware struct {
	ID int64 `gorm:"primary_key;not_null;auto_increment"`
	//中间件的名称
	MiddleName string `json:"middle_name"`
	//中间件创建的命名空间
	MiddleNamespace string `json:"middle_namespace"`
	//中间件的类型
	MiddleTypeID int64 `json:"middle_type_id"`
	//中间件的版本
	MiddleVersionID int64 `json:"middle_version_id"`
	//中间件的端口
	MiddlePort []MiddlePort `gorm:"ForeignKey:MiddleID" json:"middle_port"`
	//默认生成的账号密码
	MiddleConfig MiddleConfig `gorm:"ForeignKey:MiddleID" json:"middle_config"`
	//环境变量
	MiddleEnv []MiddleEnv `gorm:"ForeignKey:MiddleID" json:"middle_env"`
	//中间件的CPU
	MiddleCpu float32 `json:"middle_cpu"`
	//中间件内存
	MiddleMemory float32 `json:"middle_memory"`
	//中间件存储
	MiddleStorage []MiddleStorage `gorm:"ForeignKey:MiddleID" json:"middle_storage"`
	//中间件副本
	MiddleReplicas int32 `json:"middle_replicas"`
}

10-2 Go PAAS platform middleware model-middle_port creation 

C:\Users\Administrator\Desktop\gopaas\middleware\domain\model\middle_port.go

package model

type MiddlePort struct {
	ID int64 `gorm:"primary_key;not_null;auto_increment" json:"id"`
	//主要用来关联中间件的ID
	MiddleID int64 `json:"middle_id"`
	//中间件开放的端口
	MiddlePort int32 `json:"middle_port"`
	//中间件开放的端口协议
	MiddleProtocol string `json:"middle_protocol"`
}

10-3 Middleware model-middle_env creation

C:\Users\Administrator\Desktop\gopaas\middleware\domain\model\middle_env.go

package model

//中间件的变量
type MiddleEnv struct {
	ID int64 `gorm:"primary_key;not_null;auto_increment" json:"id"`
	//关联的环境变量ID
	MiddleID int64 `json:"middle_id"`
	//环境变量key
	EnvKey string `json:"env_key"`
	//环境变量Value
	EnvValue string `json:"env_value"`
}

10-4 Middleware model-MiddleConfig creation

C:\Users\Administrator\Desktop\gopaas\middleware\domain\model\middle_config.go

package model

//中间件配置的结构体
type MiddleConfig struct {
	ID int64 `gorm:"primary_key;not_null;auto_increment" json:"id"`
	//关联的中间件ID
	MiddleID int64 `json:"middle_id"`
	//可能存在的root 用户
	MiddleConfigRootUser string `json:"middle_config_root_user"`
	//可能存在的root 密码
	MiddleConfigRootPwd string `json:"middle_config_root_pwd"`
	//可能存在的普通用户
	MiddleConfigUser string `json:"middle_config_user"`
	//普通用户的密码
	MiddleConfigPwd string `json:"middle_config_pwd"`
	//预置数据库名称
	MiddleConfigDataBase string `json:"middle_config_data_base"`
	//其它设置
}

10-5 Middleware model-MiddleStorage creation 

C:\Users\Administrator\Desktop\gopaas\middleware\domain\model\middle_storage.go

package model

//中间件的存储盘
type MiddleStorage struct {
	ID int64 `gorm:"primary_key;not_null;auto_increment" json:"id"`
	//关联的中间件ID
	MiddleID int64 `json:"middle_id"`
	//存储名称
	MiddleStorageName string `json:"middle_storage_name"`
	//存储的大小
	MiddleStorageSize float32 `json:"middle_storage_size"`
	//存储需要挂载的目录
	MiddleStoragePath string `json:"middle_storage_path"`
	//存储创建的类型
	MiddleStorageClass string `json:"middle_storage_class"`
	//存储的权限
	MiddleStorageAccessMode string `json:"middle_storage_access_mode"`
}

10-6 Middleware type type and version creation

C:\Users\Administrator\Desktop\gopaas\middleware\domain\model\middle_type.go

package model

//中间件类型
type MiddleType struct {
	ID int64 `gorm:"primary_key;not_null;auto_increment" json:"id"`
	//中间件类型名称
	MiddleTypeName string `json:"middle_type_name"`
	//中间件图片地址
	MiddleTypeImageSrc string `json:"middle_type_image_src"`
	//中间件的版本
	MiddleVersion[] MiddleVersion `gorm:"ForeignKey:MiddleTypeID" json:"middle_version"`
}

C:\Users\Administrator\Desktop\gopaas\middleware\domain\model\middle_version.go

package model

type MiddleVersion struct {
	ID           int64 `gorm:"primary_key;not_null;auto_increment" json:"id"`
	MiddleTypeID int64 `json:"middle_type_id"`
	//镜像地址
	MiddleDockerImage string `json:"middle_docker_image"`
	//镜像版本
	MiddleVS string `json:"middle_vs"`
	//MiddleDockerImage:MiddleVS
	//其它
}

10-7 Middleware middleware repository development 

C:\Users\Administrator\Desktop\gopaas\middleware\domain\repository\middleware_repository.go

package repository

import (
	"github.com/jinzhu/gorm"
	"github.com/yunixiangfeng/gopaas/middleware/domain/model"
)

//创建需要实现的接口
type IMiddlewareRepository interface {
	//初始化表
	InitTable() error
	//根据ID查处找数据
	FindMiddlewareByID(int64) (*model.Middleware, error)
	//创建一条 middleware 数据
	CreateMiddleware(*model.Middleware) (int64, error)
	//根据ID删除一条 middleware 数据
	DeleteMiddlewareByID(int64) error
	//修改更新数据
	UpdateMiddleware(*model.Middleware) error
	//查找middleware所有数据
	FindAll() ([]model.Middleware, error)

	//根据类型查找所有中间件
	FindAllByTypeID(int64) ([]model.Middleware, error)
}

//创建middlewareRepository
func NewMiddlewareRepository(db *gorm.DB) IMiddlewareRepository {
	return &MiddlewareRepository{mysqlDb: db}
}

type MiddlewareRepository struct {
	mysqlDb *gorm.DB
}

//初始化表
func (u *MiddlewareRepository) InitTable() error {
	return u.mysqlDb.CreateTable(&model.Middleware{}, &model.MiddleConfig{}, &model.MiddlePort{}, &model.MiddleEnv{}, &model.MiddleStorage{}).Error
}

//根据ID查找Middleware信息
func (u *MiddlewareRepository) FindMiddlewareByID(middlewareID int64) (middleware *model.Middleware, err error) {
	middleware = &model.Middleware{}
	//要多个则添加 Preload
	return middleware, u.mysqlDb.First(middleware, middlewareID).Error
}

//创建Middleware信息
func (u *MiddlewareRepository) CreateMiddleware(middleware *model.Middleware) (int64, error) {
	return middleware.ID, u.mysqlDb.Create(middleware).Error
}

//根据ID删除Middleware信息
func (u *MiddlewareRepository) DeleteMiddlewareByID(middlewareID int64) error {
	//开启事物
	tx := u.mysqlDb.Begin()
	//遇到问题回滚
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
		}
	}()
	//遇到错误返回
	if tx.Error != nil {
		return tx.Error
	}
	//删除中间件
	if err := u.mysqlDb.Where("id = ?", middlewareID).Delete(&model.Middleware{}).Error; err != nil {
		tx.Rollback()
		return err
	}
	//删除中间件的配置
	if err := u.mysqlDb.Where("middle_id = ?", middlewareID).Delete(&model.MiddleConfig{}).Error; err != nil {
		tx.Rollback()
		return err
	}
	//删除端口
	if err := u.mysqlDb.Where("middle_id = ?", middlewareID).Delete(&model.MiddlePort{}).Error; err != nil {
		tx.Rollback()
		return err
	}
	//删除中间件环境变量
	if err := u.mysqlDb.Where("middle_id = ?", middlewareID).Delete(&model.MiddleEnv{}).Error; err != nil {
		tx.Rollback()
		return err
	}
	//删除中间件存储
	if err := u.mysqlDb.Where("middle_id = ?", middlewareID).Delete(&model.MiddleStorage{}).Error; err != nil {
		tx.Rollback()
		return err
	}
	return tx.Commit().Error
}

//更新Middleware信息
func (u *MiddlewareRepository) UpdateMiddleware(middleware *model.Middleware) error {
	return u.mysqlDb.Model(middleware).Update(middleware).Error
}

//获取结果集
func (u *MiddlewareRepository) FindAll() (middlewareAll []model.Middleware, err error) {
	//要多个则添加 Preload
	return middlewareAll, u.mysqlDb.Find(&middlewareAll).Error
}

func (u *MiddlewareRepository) FindAllByTypeID(typeID int64) (middlewareAll []model.Middleware, err error) {
	//要多个则添加 Preload
	return middlewareAll, u.mysqlDb.Where("middle_type_id = ?", typeID).Find(&middlewareAll).Error
}

10-8 Middleware type type and verison repository development 

C:\Users\Administrator\Desktop\gopaas\middleware\domain\repository\middle_type_repository.go

package repository

import (
	"github.com/jinzhu/gorm"
	"github.com/yunixiangfeng/gopaas/middleware/domain/model"
)

//创建需要实现的接口
type IMiddleTypeRepository interface {
	//初始化表
	InitTable() error
	//根据ID查处找数据
	FindTypeByID(int64) (*model.MiddleType, error)
	//创建一条 middleware 数据
	CreateMiddleType(*model.MiddleType) (int64, error)
	//根据ID删除一条 middleware 数据
	DeleteMiddleTypeByID(int64) error
	//修改更新数据
	UpdateMiddleType(*model.MiddleType) error
	//查找middleware所有数据
	FindAll() ([]model.MiddleType, error)

	FindVersionByID(int64) (*model.MiddleVersion, error)
	FindAllVersionByTypeID(int64) ([]model.MiddleVersion, error)
}

//创建MiddleTypeRepository
func NewMiddleTypeRepository(db *gorm.DB) IMiddleTypeRepository {
	return &MiddleTypeRepository{mysqlDb: db}
}

type MiddleTypeRepository struct {
	mysqlDb *gorm.DB
}

//初始化表
func (u *MiddleTypeRepository) InitTable() error {
	return u.mysqlDb.CreateTable(&model.MiddleType{}, &model.MiddleVersion{}).Error
}

//按照 ID 查找中间件类型
func (u *MiddleTypeRepository) FindTypeByID(middleTypeID int64) (middleType *model.MiddleType, err error) {
	middleType = &model.MiddleType{}
	return middleType, u.mysqlDb.Preload("MiddleVersion").First(middleType, middleTypeID).Error
}

//创建中间件
func (u *MiddleTypeRepository) CreateMiddleType(middleType *model.MiddleType) (int64, error) {
	return middleType.ID, u.mysqlDb.Create(middleType).Error
}

//删除中间件
func (u *MiddleTypeRepository) DeleteMiddleTypeByID(middleTypeID int64) error {
	tx := u.mysqlDb.Begin()
	//遇到问题回滚
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
		}
	}()
	//遇到错误返回
	if tx.Error != nil {
		return tx.Error
	}
	//删除中间件类型
	if err := u.mysqlDb.Where("id = ?", middleTypeID).Delete(&model.MiddleType{}).Error; err != nil {
		tx.Rollback()
		return err
	}
	//开始删除版本
	if err := u.mysqlDb.Where("middle_type_id = ?", middleTypeID).Delete(&model.MiddleVersion{}).Error; err != nil {
		tx.Rollback()
		return err
	}
	return tx.Commit().Error
}

//更新middleware 信息
func (u *MiddleTypeRepository) UpdateMiddleType(middleType *model.MiddleType) error {
	return u.mysqlDb.Model(middleType).Update(middleType).Error
}

//获取类型的结果集
func (u *MiddleTypeRepository) FindAll() (middleTypeAll []model.MiddleType, err error) {
	return middleTypeAll, u.mysqlDb.Find(&middleTypeAll).Error
}

//根据ID查找单个版本
func (u *MiddleTypeRepository) FindVersionByID(middleVersionID int64) (middleVersion *model.MiddleVersion, err error) {
	middleVersion = &model.MiddleVersion{}
	return middleVersion, u.mysqlDb.First(middleVersion, middleVersionID).Error
}

//根据中间件类型查找所有版本
func (u *MiddleTypeRepository) FindAllVersionByTypeID(middleTypeID int64) (middleVersionAll []model.MiddleVersion, err error) {
	return middleVersionAll, u.mysqlDb.Where("middle_type_id = ?", middleTypeID).Find(&middleVersionAll).Error
}

10-9 Middleware proto file development 

C:\Users\Administrator\Desktop\gopaas\middleware\proto\middleware\middleware.proto

syntax = "proto3";

package middleware;

option go_package = "./proto/middleware;middleware";

service Middleware {
	//对外提供添加服务
	rpc AddMiddleware(MiddlewareInfo) returns (Response) {}
	rpc DeleteMiddleware(MiddlewareId) returns (Response) {}
	rpc UpdateMiddleware(MiddlewareInfo) returns (Response) {}
	rpc FindMiddlewareByID(MiddlewareId) returns (MiddlewareInfo) {}
	rpc FindAllMiddleware(FindAll) returns (AllMiddleware) {}
	//根据中间件的类型查找所有中间件
	rpc FindAllMiddlewareByTypeID(FindAllByTypeId) returns (AllMiddleware){}
	//获取中间件类型
	rpc FindMiddleTypeByID(MiddleTypeId) returns (MiddleTypeInfo){}
	rpc AddMiddleType(MiddleTypeInfo) returns (Response){}
	rpc DeleteMiddleTypeByID(MiddleTypeId) returns (Response){}
	rpc UpdateMiddleType(MiddleTypeInfo) returns(Response){}
	rpc FindAllMiddleType(FindAll) returns (AllMiddleType){}
}

message MiddlewareInfo {
	int64 id = 1;
	string middle_name = 2;
	string middle_namespace =3;
	int64 middle_type_id = 4;
	int64 middle_version_id =5;
	repeated MiddlePort middle_port=6;
	MiddleConfig middle_config=7;
	repeated MiddleEnv middle_env=8;
	float middle_cpu=9;
	float middle_memory=10;
	repeated MiddleStorage middle_storage =11;
	int32 middle_replicas = 12;
	//添加需要的镜像版本
	string middle_docker_image_version=13;
}
//中间件的端口
message MiddlePort {
	int64 middle_id=1;
	int32 middle_port=2;
	string middle_protocol=3;
}
//中间的配置
message MiddleConfig{
	int64 middle_id =1;
	string middle_config_root_user =2;
	string middle_config_root_pwd =3;
	string middle_config_user =4;
	string middle_config_pwd =5;
	string middle_config_data_base =6;
}
//中间件环境变量
message MiddleEnv {
	int64 middle_id=1;
	string env_key=2;
	string env_value=3;
}

//中间件存储
message MiddleStorage{
	int64 middle_id =1;
	string middle_storage_name=2;
	float middle_storage_size=3;
	string middle_storage_path=4;
	string middle_storage_class=5;
	string middle_storage_access_mode=6;
}

message FindAllByTypeId {
    int64 type_id =1;
}

message MiddleTypeId {
	int64 id=1;
}

message MiddlewareId {
	int64 id = 1;
}

message FindAll {

}

message Response {
	string msg =1 ;
}

message AllMiddleware {
	repeated MiddlewareInfo middleware_info = 1;
}

message MiddleTypeInfo {
	int64 id =1;
	string middle_type_name=2;
	string middle_type_image_src=3;
	repeated MiddleVersion middle_version=4;
}

message MiddleVersion{
	int64 middle_type_id =1;
	string middle_docker_image=2;
	string middle_vs=3;
}

message AllMiddleType{
	repeated MiddleTypeInfo middle_type_info =1;
}


 yu-v3 --proto_path=. --micro_out=. --go_out=:. ./proto/middleware/middleware.proto  

10-10 Middleware service development (1) 

C:\Users\Administrator\Desktop\gopaas\middleware\domain\service\middleware_data_service.go

package service

import (
	"context"
	"errors"
	"strconv"

	"github.com/yunixiangfeng/gopaas/common"
	"github.com/yunixiangfeng/gopaas/middleware/domain/model"
	"github.com/yunixiangfeng/gopaas/middleware/domain/repository"
	"github.com/yunixiangfeng/gopaas/middleware/proto/middleware"
	v1 "k8s.io/api/apps/v1"
	v13 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	v12 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
)

//这里是接口类型
type IMiddlewareDataService interface {
	AddMiddleware(*model.Middleware) (int64, error)
	DeleteMiddleware(int64) error
	UpdateMiddleware(*model.Middleware) error
	FindMiddlewareByID(int64) (*model.Middleware, error)
	FindAllMiddleware() ([]model.Middleware, error)
	//根据类型查找中间件
	FindAllMiddlewareByTypeID(int64) ([]model.Middleware, error)
	//操作中间件s
	CreateToK8s(*middleware.MiddlewareInfo) error
	DeleteFromK8s(*model.Middleware) error
	UpdateToK8s(*middleware.MiddlewareInfo) error
}

//创建
//注意:返回值 IMiddlewareDataService 接口类型
func NewMiddlewareDataService(middlewareRepository repository.IMiddlewareRepository, clientSet *kubernetes.Clientset) IMiddlewareDataService {
	return &MiddlewareDataService{MiddlewareRepository: middlewareRepository, K8sClientSet: clientSet}
}

type MiddlewareDataService struct {
	//注意:这里是 IMiddlewareRepository 类型
	MiddlewareRepository repository.IMiddlewareRepository
	K8sClientSet         *kubernetes.Clientset
}

//更新中间件到k8s
func (u *MiddlewareDataService) UpdateToK8s(info *middleware.MiddlewareInfo) error {
	statefulSet := u.setStatefulSet(info)
	if _, err := u.K8sClientSet.AppsV1().StatefulSets(info.MiddleNamespace).Get(context.TODO(), info.MiddleName, v12.GetOptions{}); err != nil {
		common.Error(err)
		return errors.New("中间件 " + info.MiddleName + " 不存在请先创建")
	} else {
		if _, err = u.K8sClientSet.AppsV1().StatefulSets(info.MiddleNamespace).Update(context.TODO(), statefulSet, v12.UpdateOptions{}); err != nil {
			common.Error(err)
			return err
		}
		common.Info("中间件 " + info.MiddleName + " 更新成功!")
		return nil
	}

}

//删除中间件
func (u *MiddlewareDataService) DeleteFromK8s(middleware *model.Middleware) (err error) {
	if err := u.K8sClientSet.AppsV1().StatefulSets(middleware.MiddleNamespace).Delete(context.TODO(), middleware.MiddleName, v12.DeleteOptions{}); err != nil {
		common.Error(err)
		return err
	} else {
		if err := u.DeleteMiddleware(middleware.ID); err != nil {
			common.Error(err)
			return err
		}
		common.Info("删除中间件:" + middleware.MiddleName + "成功!")
		return nil

	}

}

//在k8s中创建中间件
func (u *MiddlewareDataService) CreateToK8s(info *middleware.MiddlewareInfo) error {
	statefulSet := u.setStatefulSet(info)
	if _, err := u.K8sClientSet.AppsV1().StatefulSets(info.MiddleNamespace).Get(context.TODO(), info.MiddleName, v12.GetOptions{}); err != nil {
		//如果没有获取到
		if _, err = u.K8sClientSet.AppsV1().StatefulSets(info.MiddleNamespace).Create(context.TODO(), statefulSet, v12.CreateOptions{}); err != nil {
			common.Error(err)
			return err
		}
		common.Info("中间件:" + info.MiddleName + "创建成功")
		return nil
	} else {
		common.Error("中间件:" + info.MiddleName + "创建失败")
		return errors.New("中间件:" + info.MiddleName + "创建失败")
	}
}

//根据info信息设置值
func (u *MiddlewareDataService) setStatefulSet(info *middleware.MiddlewareInfo) *v1.StatefulSet {
	statefulSet := &v1.StatefulSet{}
	statefulSet.TypeMeta = v12.TypeMeta{
		Kind:       "StatefulSet",
		APIVersion: "v1",
	}
	//设置详情
	statefulSet.ObjectMeta = v12.ObjectMeta{
		Name:      info.MiddleName,
		Namespace: info.MiddleNamespace,
		//设置label标签
		Labels: map[string]string{
			"app-name": info.MiddleName,
			"author":   "wu123",
		},
	}
	statefulSet.Name = info.MiddleName
	statefulSet.Spec = v1.StatefulSetSpec{
		//副本数
		Replicas: &info.MiddleReplicas,
		Selector: &v12.LabelSelector{
			MatchLabels: map[string]string{
				"app-name": info.MiddleName,
			},
		},
		//设置容器模版
		Template: v13.PodTemplateSpec{
			ObjectMeta: v12.ObjectMeta{
				Labels: map[string]string{
					"app-name": info.MiddleName,
				},
			},
			//设置容器详情
			Spec: v13.PodSpec{
				Containers: []v13.Container{
					{
						Name:  info.MiddleName,
						Image: info.MiddleDockerImageVersion,
						//获取容器的端口
						Ports: u.getContainerPort(info),
						//获取环境变量
						Env: u.getEnv(info),
						//获取容器CPU,内存
						Resources: u.getResources(info),
						//设置挂载目录
						VolumeMounts: u.setMounts(info),
					},
				},
				//不能设置为0,这样不安全
				//https://kubernetes.io/docs/tasks/run-application/force-delete-stateful-set-pod/
				TerminationGracePeriodSeconds: u.getTime("10"),
				//私有仓库设置密钥
				ImagePullSecrets: nil,
			},
		},
		VolumeClaimTemplates: u.getPVC(info),
		ServiceName:          info.MiddleName,
	}
	return statefulSet

}

func (u *MiddlewareDataService) getTime(stringTime string) *int64 {
	b, err := strconv.ParseInt(stringTime, 10, 64)
	if err != nil {
		common.Error(err)
		return nil
	}
	return &b
}

//设置存储路径
func (u *MiddlewareDataService) setMounts(info *middleware.MiddlewareInfo) (mount []v13.VolumeMount) {
	if len(info.MiddleStorage) == 0 {
		return
	}
	for _, v := range info.MiddleStorage {
		mt := &v13.VolumeMount{
			Name:      v.MiddleStorageName,
			MountPath: v.MiddleStoragePath,
		}
		mount = append(mount, *mt)
	}
	return
}

//获取pvc
func (u *MiddlewareDataService) getPVC(info *middleware.MiddlewareInfo) (pvcAll []v13.PersistentVolumeClaim) {
	if len(info.MiddleStorage) == 0 {
		return
	}
	for _, v := range info.MiddleStorage {
		pvc := &v13.PersistentVolumeClaim{
			TypeMeta: v12.TypeMeta{
				Kind:       "PersistentVolumeClaim",
				APIVersion: "v1",
			},
			ObjectMeta: v12.ObjectMeta{
				Name:      v.MiddleStorageName,
				Namespace: info.MiddleNamespace,
				Annotations: map[string]string{
					"pv.kubernetes.io/bound-by-controller":          "yes",
					"volume.beta.kubernetes.io/storage-provisioner": "rbd.csi.ceph.com",
				},
			},
			Spec: v13.PersistentVolumeClaimSpec{
				AccessModes:      u.getAccessModes(v.MiddleStorageAccessMode),
				Resources:        u.getPvcResource(v.MiddleStorageSize),
				VolumeName:       v.MiddleStorageName,
				StorageClassName: &v.MiddleStorageClass,
			},
		}
		pvcAll = append(pvcAll, *pvc)
	}
	return
}

//获取大小
func (u *MiddlewareDataService) getPvcResource(size float32) (source v13.ResourceRequirements) {
	source.Requests = v13.ResourceList{
		"storage": resource.MustParse(strconv.FormatFloat(float64(size), 'f', 6, 64) + "Gi"),
	}
	return
}

//获取权限的
func (u *MiddlewareDataService) getAccessModes(accessMode string) (pvam []v13.PersistentVolumeAccessMode) {
	var pm v13.PersistentVolumeAccessMode
	switch accessMode {
	case "ReadWriteOnce":
		pm = v13.ReadWriteOnce
	case "ReadOnlyMany":
		pm = v13.ReadOnlyMany
	case "ReadWriteMany":
		pm = v13.ReadWriteMany
	case "ReadWriteOncePod":
		pm = v13.ReadWriteOncePod
	default:
		pm = v13.ReadWriteOnce
	}
	pvam = append(pvam, pm)
	return pvam
}

//获取容器的端口
func (u *MiddlewareDataService) getContainerPort(info *middleware.MiddlewareInfo) (containerPort []v13.ContainerPort) {
	for _, v := range info.MiddlePort {
		containerPort = append(containerPort, v13.ContainerPort{
			Name:          "middle-port-" + strconv.FormatInt(int64(v.MiddlePort), 10),
			ContainerPort: v.MiddlePort,
			Protocol:      u.getProtocol(v.MiddleProtocol),
		})
	}
	return
}

//获取protocol 协议
func (u *MiddlewareDataService) getProtocol(protocol string) v13.Protocol {
	switch protocol {
	case "TCP":
		return "TCP"
	case "UDP":
		return "UDP"
	case "SCTP":
		return "SCTP"
	default:
		return "TCP"
	}

}

10-11 Middleware service development (2) 

C:\Users\Administrator\Desktop\gopaas\middleware\domain\service\middleware_data_service.go

package service

import (
	"context"
	"errors"
	"strconv"

	"github.com/yunixiangfeng/gopaas/common"
	"github.com/yunixiangfeng/gopaas/middleware/domain/model"
	"github.com/yunixiangfeng/gopaas/middleware/domain/repository"
	"github.com/yunixiangfeng/gopaas/middleware/proto/middleware"
	v1 "k8s.io/api/apps/v1"
	v13 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	v12 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
)

//这里是接口类型
type IMiddlewareDataService interface {
	AddMiddleware(*model.Middleware) (int64, error)
	DeleteMiddleware(int64) error
	UpdateMiddleware(*model.Middleware) error
	FindMiddlewareByID(int64) (*model.Middleware, error)
	FindAllMiddleware() ([]model.Middleware, error)
	//根据类型查找中间件
	FindAllMiddlewareByTypeID(int64) ([]model.Middleware, error)
	//操作中间件s
	CreateToK8s(*middleware.MiddlewareInfo) error
	DeleteFromK8s(*model.Middleware) error
	UpdateToK8s(*middleware.MiddlewareInfo) error
}

//创建
//注意:返回值 IMiddlewareDataService 接口类型
func NewMiddlewareDataService(middlewareRepository repository.IMiddlewareRepository, clientSet *kubernetes.Clientset) IMiddlewareDataService {
	return &MiddlewareDataService{MiddlewareRepository: middlewareRepository, K8sClientSet: clientSet}
}

type MiddlewareDataService struct {
	//注意:这里是 IMiddlewareRepository 类型
	MiddlewareRepository repository.IMiddlewareRepository
	K8sClientSet         *kubernetes.Clientset
}

//更新中间件到k8s
func (u *MiddlewareDataService) UpdateToK8s(info *middleware.MiddlewareInfo) error {
	statefulSet := u.setStatefulSet(info)
	if _, err := u.K8sClientSet.AppsV1().StatefulSets(info.MiddleNamespace).Get(context.TODO(), info.MiddleName, v12.GetOptions{}); err != nil {
		common.Error(err)
		return errors.New("中间件 " + info.MiddleName + " 不存在请先创建")
	} else {
		if _, err = u.K8sClientSet.AppsV1().StatefulSets(info.MiddleNamespace).Update(context.TODO(), statefulSet, v12.UpdateOptions{}); err != nil {
			common.Error(err)
			return err
		}
		common.Info("中间件 " + info.MiddleName + " 更新成功!")
		return nil
	}

}

//删除中间件
func (u *MiddlewareDataService) DeleteFromK8s(middleware *model.Middleware) (err error) {
	if err := u.K8sClientSet.AppsV1().StatefulSets(middleware.MiddleNamespace).Delete(context.TODO(), middleware.MiddleName, v12.DeleteOptions{}); err != nil {
		common.Error(err)
		return err
	} else {
		if err := u.DeleteMiddleware(middleware.ID); err != nil {
			common.Error(err)
			return err
		}
		common.Info("删除中间件:" + middleware.MiddleName + "成功!")
		return nil

	}

}

//在k8s中创建中间件
func (u *MiddlewareDataService) CreateToK8s(info *middleware.MiddlewareInfo) error {
	statefulSet := u.setStatefulSet(info)
	if _, err := u.K8sClientSet.AppsV1().StatefulSets(info.MiddleNamespace).Get(context.TODO(), info.MiddleName, v12.GetOptions{}); err != nil {
		//如果没有获取到
		if _, err = u.K8sClientSet.AppsV1().StatefulSets(info.MiddleNamespace).Create(context.TODO(), statefulSet, v12.CreateOptions{}); err != nil {
			common.Error(err)
			return err
		}
		common.Info("中间件:" + info.MiddleName + "创建成功")
		return nil
	} else {
		common.Error("中间件:" + info.MiddleName + "创建失败")
		return errors.New("中间件:" + info.MiddleName + "创建失败")
	}
}

//根据info信息设置值
func (u *MiddlewareDataService) setStatefulSet(info *middleware.MiddlewareInfo) *v1.StatefulSet {
	statefulSet := &v1.StatefulSet{}
	statefulSet.TypeMeta = v12.TypeMeta{
		Kind:       "StatefulSet",
		APIVersion: "v1",
	}
	//设置详情
	statefulSet.ObjectMeta = v12.ObjectMeta{
		Name:      info.MiddleName,
		Namespace: info.MiddleNamespace,
		//设置label标签
		Labels: map[string]string{
			"app-name": info.MiddleName,
			"author":   "wu123",
		},
	}
	statefulSet.Name = info.MiddleName
	statefulSet.Spec = v1.StatefulSetSpec{
		//副本数
		Replicas: &info.MiddleReplicas,
		Selector: &v12.LabelSelector{
			MatchLabels: map[string]string{
				"app-name": info.MiddleName,
			},
		},
		//设置容器模版
		Template: v13.PodTemplateSpec{
			ObjectMeta: v12.ObjectMeta{
				Labels: map[string]string{
					"app-name": info.MiddleName,
				},
			},
			//设置容器详情
			Spec: v13.PodSpec{
				Containers: []v13.Container{
					{
						Name:  info.MiddleName,
						Image: info.MiddleDockerImageVersion,
						//获取容器的端口
						Ports: u.getContainerPort(info),
						//获取环境变量
						Env: u.getEnv(info),
						//获取容器CPU,内存
						Resources: u.getResources(info),
						//设置挂载目录
						VolumeMounts: u.setMounts(info),
					},
				},
				//不能设置为0,这样不安全
				//https://kubernetes.io/docs/tasks/run-application/force-delete-stateful-set-pod/
				TerminationGracePeriodSeconds: u.getTime("10"),
				//私有仓库设置密钥
				ImagePullSecrets: nil,
			},
		},
		VolumeClaimTemplates: u.getPVC(info),
		ServiceName:          info.MiddleName,
	}
	return statefulSet

}

func (u *MiddlewareDataService) getTime(stringTime string) *int64 {
	b, err := strconv.ParseInt(stringTime, 10, 64)
	if err != nil {
		common.Error(err)
		return nil
	}
	return &b
}

//设置存储路径
func (u *MiddlewareDataService) setMounts(info *middleware.MiddlewareInfo) (mount []v13.VolumeMount) {
	if len(info.MiddleStorage) == 0 {
		return
	}
	for _, v := range info.MiddleStorage {
		mt := &v13.VolumeMount{
			Name:      v.MiddleStorageName,
			MountPath: v.MiddleStoragePath,
		}
		mount = append(mount, *mt)
	}
	return
}

//获取pvc
func (u *MiddlewareDataService) getPVC(info *middleware.MiddlewareInfo) (pvcAll []v13.PersistentVolumeClaim) {
	if len(info.MiddleStorage) == 0 {
		return
	}
	for _, v := range info.MiddleStorage {
		pvc := &v13.PersistentVolumeClaim{
			TypeMeta: v12.TypeMeta{
				Kind:       "PersistentVolumeClaim",
				APIVersion: "v1",
			},
			ObjectMeta: v12.ObjectMeta{
				Name:      v.MiddleStorageName,
				Namespace: info.MiddleNamespace,
				Annotations: map[string]string{
					"pv.kubernetes.io/bound-by-controller":          "yes",
					"volume.beta.kubernetes.io/storage-provisioner": "rbd.csi.ceph.com",
				},
			},
			Spec: v13.PersistentVolumeClaimSpec{
				AccessModes:      u.getAccessModes(v.MiddleStorageAccessMode),
				Resources:        u.getPvcResource(v.MiddleStorageSize),
				VolumeName:       v.MiddleStorageName,
				StorageClassName: &v.MiddleStorageClass,
			},
		}
		pvcAll = append(pvcAll, *pvc)
	}
	return
}

//获取大小
func (u *MiddlewareDataService) getPvcResource(size float32) (source v13.ResourceRequirements) {
	source.Requests = v13.ResourceList{
		"storage": resource.MustParse(strconv.FormatFloat(float64(size), 'f', 6, 64) + "Gi"),
	}
	return
}

//获取权限的
func (u *MiddlewareDataService) getAccessModes(accessMode string) (pvam []v13.PersistentVolumeAccessMode) {
	var pm v13.PersistentVolumeAccessMode
	switch accessMode {
	case "ReadWriteOnce":
		pm = v13.ReadWriteOnce
	case "ReadOnlyMany":
		pm = v13.ReadOnlyMany
	case "ReadWriteMany":
		pm = v13.ReadWriteMany
	case "ReadWriteOncePod":
		pm = v13.ReadWriteOncePod
	default:
		pm = v13.ReadWriteOnce
	}
	pvam = append(pvam, pm)
	return pvam
}

//获取容器的端口
func (u *MiddlewareDataService) getContainerPort(info *middleware.MiddlewareInfo) (containerPort []v13.ContainerPort) {
	for _, v := range info.MiddlePort {
		containerPort = append(containerPort, v13.ContainerPort{
			Name:          "middle-port-" + strconv.FormatInt(int64(v.MiddlePort), 10),
			ContainerPort: v.MiddlePort,
			Protocol:      u.getProtocol(v.MiddleProtocol),
		})
	}
	return
}

//获取protocol 协议
func (u *MiddlewareDataService) getProtocol(protocol string) v13.Protocol {
	switch protocol {
	case "TCP":
		return "TCP"
	case "UDP":
		return "UDP"
	case "SCTP":
		return "SCTP"
	default:
		return "TCP"
	}

}

//获取中间件的环境变量
func (u *MiddlewareDataService) getEnv(info *middleware.MiddlewareInfo) (envVar []v13.EnvVar) {
	for _, v := range info.MiddleEnv {
		envVar = append(envVar, v13.EnvVar{
			Name:      v.EnvKey,
			Value:     v.EnvValue,
			ValueFrom: nil,
		})
	}
	return
}

//获取中间件需要的资源
func (u *MiddlewareDataService) getResources(info *middleware.MiddlewareInfo) (source v13.ResourceRequirements) {
	//最大能够使用的资源
	source.Limits = v13.ResourceList{
		"cpu":    resource.MustParse(strconv.FormatFloat(float64(info.MiddleCpu), 'f', 6, 64)),
		"memory": resource.MustParse(strconv.FormatFloat(float64(info.MiddleMemory), 'f', 6, 64)),
	}
	//最小请求资源
	source.Requests = v13.ResourceList{
		"cpu":    resource.MustParse(strconv.FormatFloat(float64(info.MiddleCpu), 'f', 6, 64)),
		"memory": resource.MustParse(strconv.FormatFloat(float64(info.MiddleMemory), 'f', 6, 64)),
	}
	return
}

//插入
func (u *MiddlewareDataService) AddMiddleware(middleware *model.Middleware) (int64, error) {
	return u.MiddlewareRepository.CreateMiddleware(middleware)
}

//删除
func (u *MiddlewareDataService) DeleteMiddleware(middlewareID int64) error {
	return u.MiddlewareRepository.DeleteMiddlewareByID(middlewareID)
}

//更新
func (u *MiddlewareDataService) UpdateMiddleware(middleware *model.Middleware) error {
	return u.MiddlewareRepository.UpdateMiddleware(middleware)
}

//查找
func (u *MiddlewareDataService) FindMiddlewareByID(middlewareID int64) (*model.Middleware, error) {
	return u.MiddlewareRepository.FindMiddlewareByID(middlewareID)
}

//查找
func (u *MiddlewareDataService) FindAllMiddleware() ([]model.Middleware, error) {
	return u.MiddlewareRepository.FindAll()
}

//根据类型查找所有的中间件
func (u *MiddlewareDataService) FindAllMiddlewareByTypeID(typeID int64) ([]model.Middleware, error) {
	return u.MiddlewareRepository.FindAllByTypeID(typeID)
}

10-12 Middleware middleware service development (3)

C:\Users\Administrator\Desktop\gopaas\middleware\domain\service\middleware_data_service.go

package service

import (
	"context"
	"errors"
	"strconv"

	"github.com/yunixiangfeng/gopaas/common"
	"github.com/yunixiangfeng/gopaas/middleware/domain/model"
	"github.com/yunixiangfeng/gopaas/middleware/domain/repository"
	"github.com/yunixiangfeng/gopaas/middleware/proto/middleware"
	v1 "k8s.io/api/apps/v1"
	v13 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	v12 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
)

//这里是接口类型
type IMiddlewareDataService interface {
	AddMiddleware(*model.Middleware) (int64, error)
	DeleteMiddleware(int64) error
	UpdateMiddleware(*model.Middleware) error
	FindMiddlewareByID(int64) (*model.Middleware, error)
	FindAllMiddleware() ([]model.Middleware, error)
	//根据类型查找中间件
	FindAllMiddlewareByTypeID(int64) ([]model.Middleware, error)
	//操作中间件s
	CreateToK8s(*middleware.MiddlewareInfo) error
	DeleteFromK8s(*model.Middleware) error
	UpdateToK8s(*middleware.MiddlewareInfo) error
}

//创建
//注意:返回值 IMiddlewareDataService 接口类型
func NewMiddlewareDataService(middlewareRepository repository.IMiddlewareRepository, clientSet *kubernetes.Clientset) IMiddlewareDataService {
	return &MiddlewareDataService{MiddlewareRepository: middlewareRepository, K8sClientSet: clientSet}
}

type MiddlewareDataService struct {
	//注意:这里是 IMiddlewareRepository 类型
	MiddlewareRepository repository.IMiddlewareRepository
	K8sClientSet         *kubernetes.Clientset
}

//更新中间件到k8s
func (u *MiddlewareDataService) UpdateToK8s(info *middleware.MiddlewareInfo) error {
	statefulSet := u.setStatefulSet(info)
	if _, err := u.K8sClientSet.AppsV1().StatefulSets(info.MiddleNamespace).Get(context.TODO(), info.MiddleName, v12.GetOptions{}); err != nil {
		common.Error(err)
		return errors.New("中间件 " + info.MiddleName + " 不存在请先创建")
	} else {
		if _, err = u.K8sClientSet.AppsV1().StatefulSets(info.MiddleNamespace).Update(context.TODO(), statefulSet, v12.UpdateOptions{}); err != nil {
			common.Error(err)
			return err
		}
		common.Info("中间件 " + info.MiddleName + " 更新成功!")
		return nil
	}

}

//删除中间件
func (u *MiddlewareDataService) DeleteFromK8s(middleware *model.Middleware) (err error) {
	if err := u.K8sClientSet.AppsV1().StatefulSets(middleware.MiddleNamespace).Delete(context.TODO(), middleware.MiddleName, v12.DeleteOptions{}); err != nil {
		common.Error(err)
		return err
	} else {
		if err := u.DeleteMiddleware(middleware.ID); err != nil {
			common.Error(err)
			return err
		}
		common.Info("删除中间件:" + middleware.MiddleName + "成功!")
		return nil

	}

}

//在k8s中创建中间件
func (u *MiddlewareDataService) CreateToK8s(info *middleware.MiddlewareInfo) error {
	statefulSet := u.setStatefulSet(info)
	if _, err := u.K8sClientSet.AppsV1().StatefulSets(info.MiddleNamespace).Get(context.TODO(), info.MiddleName, v12.GetOptions{}); err != nil {
		//如果没有获取到
		if _, err = u.K8sClientSet.AppsV1().StatefulSets(info.MiddleNamespace).Create(context.TODO(), statefulSet, v12.CreateOptions{}); err != nil {
			common.Error(err)
			return err
		}
		common.Info("中间件:" + info.MiddleName + "创建成功")
		return nil
	} else {
		common.Error("中间件:" + info.MiddleName + "创建失败")
		return errors.New("中间件:" + info.MiddleName + "创建失败")
	}
}

//根据info信息设置值
func (u *MiddlewareDataService) setStatefulSet(info *middleware.MiddlewareInfo) *v1.StatefulSet {
	statefulSet := &v1.StatefulSet{}
	statefulSet.TypeMeta = v12.TypeMeta{
		Kind:       "StatefulSet",
		APIVersion: "v1",
	}
	//设置详情
	statefulSet.ObjectMeta = v12.ObjectMeta{
		Name:      info.MiddleName,
		Namespace: info.MiddleNamespace,
		//设置label标签
		Labels: map[string]string{
			"app-name": info.MiddleName,
			"author":   "wu123",
		},
	}
	statefulSet.Name = info.MiddleName
	statefulSet.Spec = v1.StatefulSetSpec{
		//副本数
		Replicas: &info.MiddleReplicas,
		Selector: &v12.LabelSelector{
			MatchLabels: map[string]string{
				"app-name": info.MiddleName,
			},
		},
		//设置容器模版
		Template: v13.PodTemplateSpec{
			ObjectMeta: v12.ObjectMeta{
				Labels: map[string]string{
					"app-name": info.MiddleName,
				},
			},
			//设置容器详情
			Spec: v13.PodSpec{
				Containers: []v13.Container{
					{
						Name:  info.MiddleName,
						Image: info.MiddleDockerImageVersion,
						//获取容器的端口
						Ports: u.getContainerPort(info),
						//获取环境变量
						Env: u.getEnv(info),
						//获取容器CPU,内存
						Resources: u.getResources(info),
						//设置挂载目录
						VolumeMounts: u.setMounts(info),
					},
				},
				//不能设置为0,这样不安全
				//https://kubernetes.io/docs/tasks/run-application/force-delete-stateful-set-pod/
				TerminationGracePeriodSeconds: u.getTime("10"),
				//私有仓库设置密钥
				ImagePullSecrets: nil,
			},
		},
		VolumeClaimTemplates: u.getPVC(info),
		ServiceName:          info.MiddleName,
	}
	return statefulSet

}

func (u *MiddlewareDataService) getTime(stringTime string) *int64 {
	b, err := strconv.ParseInt(stringTime, 10, 64)
	if err != nil {
		common.Error(err)
		return nil
	}
	return &b
}

//设置存储路径
func (u *MiddlewareDataService) setMounts(info *middleware.MiddlewareInfo) (mount []v13.VolumeMount) {
	if len(info.MiddleStorage) == 0 {
		return
	}
	for _, v := range info.MiddleStorage {
		mt := &v13.VolumeMount{
			Name:      v.MiddleStorageName,
			MountPath: v.MiddleStoragePath,
		}
		mount = append(mount, *mt)
	}
	return
}

//获取pvc
func (u *MiddlewareDataService) getPVC(info *middleware.MiddlewareInfo) (pvcAll []v13.PersistentVolumeClaim) {
	if len(info.MiddleStorage) == 0 {
		return
	}
	for _, v := range info.MiddleStorage {
		pvc := &v13.PersistentVolumeClaim{
			TypeMeta: v12.TypeMeta{
				Kind:       "PersistentVolumeClaim",
				APIVersion: "v1",
			},
			ObjectMeta: v12.ObjectMeta{
				Name:      v.MiddleStorageName,
				Namespace: info.MiddleNamespace,
				Annotations: map[string]string{
					"pv.kubernetes.io/bound-by-controller":          "yes",
					"volume.beta.kubernetes.io/storage-provisioner": "rbd.csi.ceph.com",
				},
			},
			Spec: v13.PersistentVolumeClaimSpec{
				AccessModes:      u.getAccessModes(v.MiddleStorageAccessMode),
				Resources:        u.getPvcResource(v.MiddleStorageSize),
				VolumeName:       v.MiddleStorageName,
				StorageClassName: &v.MiddleStorageClass,
			},
		}
		pvcAll = append(pvcAll, *pvc)
	}
	return
}

//获取大小
func (u *MiddlewareDataService) getPvcResource(size float32) (source v13.ResourceRequirements) {
	source.Requests = v13.ResourceList{
		"storage": resource.MustParse(strconv.FormatFloat(float64(size), 'f', 6, 64) + "Gi"),
	}
	return
}

//获取权限的
func (u *MiddlewareDataService) getAccessModes(accessMode string) (pvam []v13.PersistentVolumeAccessMode) {
	var pm v13.PersistentVolumeAccessMode
	switch accessMode {
	case "ReadWriteOnce":
		pm = v13.ReadWriteOnce
	case "ReadOnlyMany":
		pm = v13.ReadOnlyMany
	case "ReadWriteMany":
		pm = v13.ReadWriteMany
	case "ReadWriteOncePod":
		pm = v13.ReadWriteOncePod
	default:
		pm = v13.ReadWriteOnce
	}
	pvam = append(pvam, pm)
	return pvam
}

//获取容器的端口
func (u *MiddlewareDataService) getContainerPort(info *middleware.MiddlewareInfo) (containerPort []v13.ContainerPort) {
	for _, v := range info.MiddlePort {
		containerPort = append(containerPort, v13.ContainerPort{
			Name:          "middle-port-" + strconv.FormatInt(int64(v.MiddlePort), 10),
			ContainerPort: v.MiddlePort,
			Protocol:      u.getProtocol(v.MiddleProtocol),
		})
	}
	return
}

//获取protocol 协议
func (u *MiddlewareDataService) getProtocol(protocol string) v13.Protocol {
	switch protocol {
	case "TCP":
		return "TCP"
	case "UDP":
		return "UDP"
	case "SCTP":
		return "SCTP"
	default:
		return "TCP"
	}

}

//获取中间件的环境变量
func (u *MiddlewareDataService) getEnv(info *middleware.MiddlewareInfo) (envVar []v13.EnvVar) {
	for _, v := range info.MiddleEnv {
		envVar = append(envVar, v13.EnvVar{
			Name:      v.EnvKey,
			Value:     v.EnvValue,
			ValueFrom: nil,
		})
	}
	return
}

//获取中间件需要的资源
func (u *MiddlewareDataService) getResources(info *middleware.MiddlewareInfo) (source v13.ResourceRequirements) {
	//最大能够使用的资源
	source.Limits = v13.ResourceList{
		"cpu":    resource.MustParse(strconv.FormatFloat(float64(info.MiddleCpu), 'f', 6, 64)),
		"memory": resource.MustParse(strconv.FormatFloat(float64(info.MiddleMemory), 'f', 6, 64)),
	}
	//最小请求资源
	source.Requests = v13.ResourceList{
		"cpu":    resource.MustParse(strconv.FormatFloat(float64(info.MiddleCpu), 'f', 6, 64)),
		"memory": resource.MustParse(strconv.FormatFloat(float64(info.MiddleMemory), 'f', 6, 64)),
	}
	return
}

//插入
func (u *MiddlewareDataService) AddMiddleware(middleware *model.Middleware) (int64, error) {
	return u.MiddlewareRepository.CreateMiddleware(middleware)
}

//删除
func (u *MiddlewareDataService) DeleteMiddleware(middlewareID int64) error {
	return u.MiddlewareRepository.DeleteMiddlewareByID(middlewareID)
}

//更新
func (u *MiddlewareDataService) UpdateMiddleware(middleware *model.Middleware) error {
	return u.MiddlewareRepository.UpdateMiddleware(middleware)
}

//查找
func (u *MiddlewareDataService) FindMiddlewareByID(middlewareID int64) (*model.Middleware, error) {
	return u.MiddlewareRepository.FindMiddlewareByID(middlewareID)
}

//查找
func (u *MiddlewareDataService) FindAllMiddleware() ([]model.Middleware, error) {
	return u.MiddlewareRepository.FindAll()
}

//根据类型查找所有的中间件
func (u *MiddlewareDataService) FindAllMiddlewareByTypeID(typeID int64) ([]model.Middleware, error) {
	return u.MiddlewareRepository.FindAllByTypeID(typeID)
}

10-13 Middleware middleware service development (4) 

C:\Users\Administrator\Desktop\gopaas\middleware\domain\service\middleware_data_service.go

package service

import (
	"context"
	"errors"
	"strconv"

	"github.com/yunixiangfeng/gopaas/common"
	"github.com/yunixiangfeng/gopaas/middleware/domain/model"
	"github.com/yunixiangfeng/gopaas/middleware/domain/repository"
	"github.com/yunixiangfeng/gopaas/middleware/proto/middleware"
	v1 "k8s.io/api/apps/v1"
	v13 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	v12 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
)

//这里是接口类型
type IMiddlewareDataService interface {
	AddMiddleware(*model.Middleware) (int64, error)
	DeleteMiddleware(int64) error
	UpdateMiddleware(*model.Middleware) error
	FindMiddlewareByID(int64) (*model.Middleware, error)
	FindAllMiddleware() ([]model.Middleware, error)
	//根据类型查找中间件
	FindAllMiddlewareByTypeID(int64) ([]model.Middleware, error)
	//操作中间件s
	CreateToK8s(*middleware.MiddlewareInfo) error
	DeleteFromK8s(*model.Middleware) error
	UpdateToK8s(*middleware.MiddlewareInfo) error
}

//创建
//注意:返回值 IMiddlewareDataService 接口类型
func NewMiddlewareDataService(middlewareRepository repository.IMiddlewareRepository, clientSet *kubernetes.Clientset) IMiddlewareDataService {
	return &MiddlewareDataService{MiddlewareRepository: middlewareRepository, K8sClientSet: clientSet}
}

type MiddlewareDataService struct {
	//注意:这里是 IMiddlewareRepository 类型
	MiddlewareRepository repository.IMiddlewareRepository
	K8sClientSet         *kubernetes.Clientset
}

//更新中间件到k8s
func (u *MiddlewareDataService) UpdateToK8s(info *middleware.MiddlewareInfo) error {
	statefulSet := u.setStatefulSet(info)
	if _, err := u.K8sClientSet.AppsV1().StatefulSets(info.MiddleNamespace).Get(context.TODO(), info.MiddleName, v12.GetOptions{}); err != nil {
		common.Error(err)
		return errors.New("中间件 " + info.MiddleName + " 不存在请先创建")
	} else {
		if _, err = u.K8sClientSet.AppsV1().StatefulSets(info.MiddleNamespace).Update(context.TODO(), statefulSet, v12.UpdateOptions{}); err != nil {
			common.Error(err)
			return err
		}
		common.Info("中间件 " + info.MiddleName + " 更新成功!")
		return nil
	}

}

//删除中间件
func (u *MiddlewareDataService) DeleteFromK8s(middleware *model.Middleware) (err error) {
	if err := u.K8sClientSet.AppsV1().StatefulSets(middleware.MiddleNamespace).Delete(context.TODO(), middleware.MiddleName, v12.DeleteOptions{}); err != nil {
		common.Error(err)
		return err
	} else {
		if err := u.DeleteMiddleware(middleware.ID); err != nil {
			common.Error(err)
			return err
		}
		common.Info("删除中间件:" + middleware.MiddleName + "成功!")
		return nil

	}

}

//在k8s中创建中间件
func (u *MiddlewareDataService) CreateToK8s(info *middleware.MiddlewareInfo) error {
	statefulSet := u.setStatefulSet(info)
	if _, err := u.K8sClientSet.AppsV1().StatefulSets(info.MiddleNamespace).Get(context.TODO(), info.MiddleName, v12.GetOptions{}); err != nil {
		//如果没有获取到
		if _, err = u.K8sClientSet.AppsV1().StatefulSets(info.MiddleNamespace).Create(context.TODO(), statefulSet, v12.CreateOptions{}); err != nil {
			common.Error(err)
			return err
		}
		common.Info("中间件:" + info.MiddleName + "创建成功")
		return nil
	} else {
		common.Error("中间件:" + info.MiddleName + "创建失败")
		return errors.New("中间件:" + info.MiddleName + "创建失败")
	}
}

//根据info信息设置值
func (u *MiddlewareDataService) setStatefulSet(info *middleware.MiddlewareInfo) *v1.StatefulSet {
	statefulSet := &v1.StatefulSet{}
	statefulSet.TypeMeta = v12.TypeMeta{
		Kind:       "StatefulSet",
		APIVersion: "v1",
	}
	//设置详情
	statefulSet.ObjectMeta = v12.ObjectMeta{
		Name:      info.MiddleName,
		Namespace: info.MiddleNamespace,
		//设置label标签
		Labels: map[string]string{
			"app-name": info.MiddleName,
			"author":   "wu123",
		},
	}
	statefulSet.Name = info.MiddleName
	statefulSet.Spec = v1.StatefulSetSpec{
		//副本数
		Replicas: &info.MiddleReplicas,
		Selector: &v12.LabelSelector{
			MatchLabels: map[string]string{
				"app-name": info.MiddleName,
			},
		},
		//设置容器模版
		Template: v13.PodTemplateSpec{
			ObjectMeta: v12.ObjectMeta{
				Labels: map[string]string{
					"app-name": info.MiddleName,
				},
			},
			//设置容器详情
			Spec: v13.PodSpec{
				Containers: []v13.Container{
					{
						Name:  info.MiddleName,
						Image: info.MiddleDockerImageVersion,
						//获取容器的端口
						Ports: u.getContainerPort(info),
						//获取环境变量
						Env: u.getEnv(info),
						//获取容器CPU,内存
						Resources: u.getResources(info),
						//设置挂载目录
						VolumeMounts: u.setMounts(info),
					},
				},
				//不能设置为0,这样不安全
				//https://kubernetes.io/docs/tasks/run-application/force-delete-stateful-set-pod/
				TerminationGracePeriodSeconds: u.getTime("10"),
				//私有仓库设置密钥
				ImagePullSecrets: nil,
			},
		},
		VolumeClaimTemplates: u.getPVC(info),
		ServiceName:          info.MiddleName,
	}
	return statefulSet

}

func (u *MiddlewareDataService) getTime(stringTime string) *int64 {
	b, err := strconv.ParseInt(stringTime, 10, 64)
	if err != nil {
		common.Error(err)
		return nil
	}
	return &b
}

//设置存储路径
func (u *MiddlewareDataService) setMounts(info *middleware.MiddlewareInfo) (mount []v13.VolumeMount) {
	if len(info.MiddleStorage) == 0 {
		return
	}
	for _, v := range info.MiddleStorage {
		mt := &v13.VolumeMount{
			Name:      v.MiddleStorageName,
			MountPath: v.MiddleStoragePath,
		}
		mount = append(mount, *mt)
	}
	return
}

//获取pvc
func (u *MiddlewareDataService) getPVC(info *middleware.MiddlewareInfo) (pvcAll []v13.PersistentVolumeClaim) {
	if len(info.MiddleStorage) == 0 {
		return
	}
	for _, v := range info.MiddleStorage {
		pvc := &v13.PersistentVolumeClaim{
			TypeMeta: v12.TypeMeta{
				Kind:       "PersistentVolumeClaim",
				APIVersion: "v1",
			},
			ObjectMeta: v12.ObjectMeta{
				Name:      v.MiddleStorageName,
				Namespace: info.MiddleNamespace,
				Annotations: map[string]string{
					"pv.kubernetes.io/bound-by-controller":          "yes",
					"volume.beta.kubernetes.io/storage-provisioner": "rbd.csi.ceph.com",
				},
			},
			Spec: v13.PersistentVolumeClaimSpec{
				AccessModes:      u.getAccessModes(v.MiddleStorageAccessMode),
				Resources:        u.getPvcResource(v.MiddleStorageSize),
				VolumeName:       v.MiddleStorageName,
				StorageClassName: &v.MiddleStorageClass,
			},
		}
		pvcAll = append(pvcAll, *pvc)
	}
	return
}

//获取大小
func (u *MiddlewareDataService) getPvcResource(size float32) (source v13.ResourceRequirements) {
	source.Requests = v13.ResourceList{
		"storage": resource.MustParse(strconv.FormatFloat(float64(size), 'f', 6, 64) + "Gi"),
	}
	return
}

//获取权限的
func (u *MiddlewareDataService) getAccessModes(accessMode string) (pvam []v13.PersistentVolumeAccessMode) {
	var pm v13.PersistentVolumeAccessMode
	switch accessMode {
	case "ReadWriteOnce":
		pm = v13.ReadWriteOnce
	case "ReadOnlyMany":
		pm = v13.ReadOnlyMany
	case "ReadWriteMany":
		pm = v13.ReadWriteMany
	case "ReadWriteOncePod":
		pm = v13.ReadWriteOncePod
	default:
		pm = v13.ReadWriteOnce
	}
	pvam = append(pvam, pm)
	return pvam
}

//获取容器的端口
func (u *MiddlewareDataService) getContainerPort(info *middleware.MiddlewareInfo) (containerPort []v13.ContainerPort) {
	for _, v := range info.MiddlePort {
		containerPort = append(containerPort, v13.ContainerPort{
			Name:          "middle-port-" + strconv.FormatInt(int64(v.MiddlePort), 10),
			ContainerPort: v.MiddlePort,
			Protocol:      u.getProtocol(v.MiddleProtocol),
		})
	}
	return
}

//获取protocol 协议
func (u *MiddlewareDataService) getProtocol(protocol string) v13.Protocol {
	switch protocol {
	case "TCP":
		return "TCP"
	case "UDP":
		return "UDP"
	case "SCTP":
		return "SCTP"
	default:
		return "TCP"
	}

}

//获取中间件的环境变量
func (u *MiddlewareDataService) getEnv(info *middleware.MiddlewareInfo) (envVar []v13.EnvVar) {
	for _, v := range info.MiddleEnv {
		envVar = append(envVar, v13.EnvVar{
			Name:      v.EnvKey,
			Value:     v.EnvValue,
			ValueFrom: nil,
		})
	}
	return
}

//获取中间件需要的资源
func (u *MiddlewareDataService) getResources(info *middleware.MiddlewareInfo) (source v13.ResourceRequirements) {
	//最大能够使用的资源
	source.Limits = v13.ResourceList{
		"cpu":    resource.MustParse(strconv.FormatFloat(float64(info.MiddleCpu), 'f', 6, 64)),
		"memory": resource.MustParse(strconv.FormatFloat(float64(info.MiddleMemory), 'f', 6, 64)),
	}
	//最小请求资源
	source.Requests = v13.ResourceList{
		"cpu":    resource.MustParse(strconv.FormatFloat(float64(info.MiddleCpu), 'f', 6, 64)),
		"memory": resource.MustParse(strconv.FormatFloat(float64(info.MiddleMemory), 'f', 6, 64)),
	}
	return
}

//插入
func (u *MiddlewareDataService) AddMiddleware(middleware *model.Middleware) (int64, error) {
	return u.MiddlewareRepository.CreateMiddleware(middleware)
}

//删除
func (u *MiddlewareDataService) DeleteMiddleware(middlewareID int64) error {
	return u.MiddlewareRepository.DeleteMiddlewareByID(middlewareID)
}

//更新
func (u *MiddlewareDataService) UpdateMiddleware(middleware *model.Middleware) error {
	return u.MiddlewareRepository.UpdateMiddleware(middleware)
}

//查找
func (u *MiddlewareDataService) FindMiddlewareByID(middlewareID int64) (*model.Middleware, error) {
	return u.MiddlewareRepository.FindMiddlewareByID(middlewareID)
}

//查找
func (u *MiddlewareDataService) FindAllMiddleware() ([]model.Middleware, error) {
	return u.MiddlewareRepository.FindAll()
}

//根据类型查找所有的中间件
func (u *MiddlewareDataService) FindAllMiddlewareByTypeID(typeID int64) ([]model.Middleware, error) {
	return u.MiddlewareRepository.FindAllByTypeID(typeID)
}

10-14 Middleware service corresponding version service code development 

C:\Users\Administrator\Desktop\gopaas\middleware\domain\service\middle_type_data_service.go

package service

import (
	"github.com/yunixiangfeng/gopaas/middleware/domain/model"
	"github.com/yunixiangfeng/gopaas/middleware/domain/repository"
)

//定义接口类型
type IMiddleTypeDataService interface {
	AddMiddleType(*model.MiddleType) (int64, error)
	DeleteMiddleType(int64) error
	UpdateMiddleType(*model.MiddleType) error
	FindMiddleTypeByID(int64) (*model.MiddleType, error)
	FindAllMiddleType() ([]model.MiddleType, error)
	//根据ID返回地址
	FindImageVersionByID(int64) (string, error)

	FindVersionByID(int64) (*model.MiddleVersion, error)
	FindAllVersionByTypeID(int64) ([]model.MiddleVersion, error)
}

//注意:返回值的类型
func NewMiddleTypeDataService(repository repository.IMiddleTypeRepository) IMiddleTypeDataService {
	return &MiddleTypeDataService{MiddleTypeRepository: repository}

}

type MiddleTypeDataService struct {
	MiddleTypeRepository repository.IMiddleTypeRepository
}

//插入
func (u *MiddleTypeDataService) AddMiddleType(middleType *model.MiddleType) (int64, error) {
	return u.MiddleTypeRepository.CreateMiddleType(middleType)
}

//删除
func (u *MiddleTypeDataService) DeleteMiddleType(middleTypeID int64) error {
	return u.MiddleTypeRepository.DeleteMiddleTypeByID(middleTypeID)
}

//更新
func (u *MiddleTypeDataService) UpdateMiddleType(middleType *model.MiddleType) error {
	return u.MiddleTypeRepository.UpdateMiddleType(middleType)
}

//查找
func (u *MiddleTypeDataService) FindMiddleTypeByID(middleTypeID int64) (*model.MiddleType, error) {
	return u.MiddleTypeRepository.FindTypeByID(middleTypeID)

}

//查找所有
func (u *MiddleTypeDataService) FindAllMiddleType() ([]model.MiddleType, error) {
	return u.MiddleTypeRepository.FindAll()
}

//根据version ID查找镜像地址
func (u *MiddleTypeDataService) FindImageVersionByID(middleVersionID int64) (string, error) {
	version, err := u.MiddleTypeRepository.FindVersionByID(middleVersionID)
	if err != nil {
		return "", err
	}
	//返回需要的镜像地址
	return version.MiddleDockerImage + ":" + version.MiddleVS, nil

}

//根据versionID 查找单个镜像
func (u *MiddleTypeDataService) FindVersionByID(middleVersionID int64) (*model.MiddleVersion, error) {
	return u.MiddleTypeRepository.FindVersionByID(middleVersionID)
}

//根据中间件类型查找对应的所有版本
func (u *MiddleTypeDataService) FindAllVersionByTypeID(middleTypeID int64) ([]model.MiddleVersion, error) {
	return u.MiddleTypeRepository.FindAllVersionByTypeID(middleTypeID)
}

10-15 Middleware main adjustment and handler development (Part 1)

C:\Users\Administrator\Desktop\gopaas\middleware\main.go

	//只能执行一遍
	//err = repository.NewMiddlewareRepository(db).InitTable()
	//if err != nil {
	//	common.Fatal(err)
	//}
	//if err:= repository.NewMiddleTypeRepository(db).InitTable();err!=nil{
	//	common.Fatal(err)
	//}

	// 注册句柄,可以快速操作已开发的服务
	middlewareDataService := service2.NewMiddlewareDataService(repository.NewMiddlewareRepository(db), clientset)
	middleTypeDataService := service2.NewMiddleTypeDataService(repository.NewMiddleTypeRepository(db))
	middleware.RegisterMiddlewareHandler(service.Server(), &handler.MiddlewareHandler{MiddlewareDataService: middlewareDataService, MiddleTypeDataService: middleTypeDataService})

C:\Users\Administrator\Desktop\gopaas\middleware\handler\middlewareHandler.go

package handler

import (
	"context"
	"strconv"

	log "github.com/asim/go-micro/v3/logger"
	"github.com/yunixiangfeng/gopaas/common"
	"github.com/yunixiangfeng/gopaas/middleware/domain/model"
	"github.com/yunixiangfeng/gopaas/middleware/domain/service"
	middleware "github.com/yunixiangfeng/gopaas/middleware/proto/middleware"
)

type MiddlewareHandler struct {
	//注意这里的类型是 IMiddlewareDataService 接口类型
	MiddlewareDataService service.IMiddlewareDataService
	// 添加中间件类型服务
	MiddleTypeDataService service.IMiddleTypeDataService
}

// Call is a single request handler called via client.Call or the generated client code
func (e *MiddlewareHandler) AddMiddleware(ctx context.Context, info *middleware.MiddlewareInfo, rsp *middleware.Response) error {
	log.Info("Received *middleware.AddMiddleware request")
	middleModel := &model.Middleware{}
	if err := common.SwapTo(info, middleModel); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	//调用其它的服务处理数据
	//根据ID产销需要的镜像地址
	imageAddress, err := e.MiddleTypeDataService.FindImageVersionByID(info.MiddleVersionId)
	if err != nil {
		common.Error(err)
		return err
	}
	//赋值
	info.MiddleDockerImageVersion = imageAddress
	//在k8s 中创建资源
	if err := e.MiddlewareDataService.CreateToK8s(info); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	} else {
		//插入数据库
		middleID, err := e.MiddlewareDataService.AddMiddleware(middleModel)
		if err != nil {
			common.Error(err)
			rsp.Msg = err.Error()
			return err
		}
		rsp.Msg = "中间件添加成功 ID 号为:" + strconv.FormatInt(middleID, 10)
		common.Info(rsp.Msg)
	}
	return nil
}

func (e *MiddlewareHandler) DeleteMiddleware(ctx context.Context, req *middleware.MiddlewareId, rsp *middleware.Response) error {
	log.Info("Received *middleware.DeleteMiddleware request")
	middleModel, err := e.MiddlewareDataService.FindMiddlewareByID(req.Id)
	if err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	//删除k8s中资源
	if err := e.MiddlewareDataService.DeleteFromK8s(middleModel); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	return nil
}

func (e *MiddlewareHandler) UpdateMiddleware(ctx context.Context, req *middleware.MiddlewareInfo, rsp *middleware.Response) error {
	log.Info("Received *middleware.UpdateMiddleware request")
	if err := e.MiddlewareDataService.UpdateToK8s(req); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	//查询中间件相关的信息
	middleModle, err := e.MiddlewareDataService.FindMiddlewareByID(req.Id)
	if err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	//更新model数据
	if err := common.SwapTo(req, middleModle); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	//更新model
	if err := e.MiddlewareDataService.UpdateMiddleware(middleModle); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	return nil
}

func (e *MiddlewareHandler) FindMiddlewareByID(ctx context.Context, req *middleware.MiddlewareId, rsp *middleware.MiddlewareInfo) error {
	log.Info("Received *middleware.FindMiddlewareByID request")
	//查询中间件
	middlewareModel, err := e.MiddlewareDataService.FindMiddlewareByID(req.Id)
	if err != nil {
		common.Error(err)
		return err
	}
	if err := common.SwapTo(middlewareModel, rsp); err != nil {
		common.Error(err)
		return err
	}
	return nil
}

func (e *MiddlewareHandler) FindAllMiddleware(ctx context.Context, req *middleware.FindAll, rsp *middleware.AllMiddleware) error {
	log.Info("Received *middleware.FindAllMiddleware request")
	allMiddleware, err := e.MiddlewareDataService.FindAllMiddleware()
	if err != nil {
		common.Error(err)
		return err
	}
	//整理格式
	for _, v := range allMiddleware {
		middleInfo := &middleware.MiddlewareInfo{}
		if err := common.SwapTo(v, middleInfo); err != nil {
			common.Error(err)
			return err
		}
		rsp.MiddlewareInfo = append(rsp.MiddlewareInfo, middleInfo)
	}
	return nil
}

make proto 

10-16 Middleware main adjustment and handler development (Part 2) 

C:\Users\Administrator\Desktop\gopaas\middleware\main.go

package main

import (
	"flag"
	"fmt"
	"path/filepath"

	"github.com/yunixiangfeng/gopaas/common"
	"github.com/yunixiangfeng/gopaas/middleware/domain/repository"

	//"github.com/afex/hystrix-go/hystrix"
	"github.com/asim/go-micro/plugins/registry/consul/v3"
	ratelimit "github.com/asim/go-micro/plugins/wrapper/ratelimiter/uber/v3"
	opentracing2 "github.com/asim/go-micro/plugins/wrapper/trace/opentracing/v3"
	"github.com/asim/go-micro/v3"
	"github.com/asim/go-micro/v3/registry"
	"github.com/asim/go-micro/v3/server"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
	"github.com/opentracing/opentracing-go"
	service2 "github.com/yunixiangfeng/gopaas/middleware/domain/service"
	"github.com/yunixiangfeng/gopaas/middleware/handler"

	//hystrix2 "github.com/yunixiangfeng/gopaas/middleware/plugin/hystrix"
	"strconv"

	middleware "github.com/yunixiangfeng/gopaas/middleware/proto/middleware"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/client-go/util/homedir"
)

var (
	//服务地址
	hostIp = "192.168.204.130"
	//服务地址
	serviceHost = hostIp
	//服务端口
	servicePort = "8089"

	//注册中心配置
	consulHost       = hostIp
	consulPort int64 = 8500
	//链路追踪
	tracerHost = hostIp
	tracerPort = 6831
	//熔断端口,每个服务不能重复
	//hystrixPort = 9099
	//监控端口,每个服务不能重复
	prometheusPort = 9199
)

func main() {
	//需要本地启动,mysql,consul中间件服务
	//1.注册中心
	consul := consul.NewRegistry(func(options *registry.Options) {
		options.Addrs = []string{
			consulHost + ":" + strconv.FormatInt(consulPort, 10),
		}
	})
	//2.配置中心,存放经常变动的变量
	consulConfig, err := common.GetConsulConfig(consulHost, consulPort, "/micro/config")
	if err != nil {
		common.Error(err)
	}
	//3.使用配置中心连接 mysql
	mysqlInfo := common.GetMysqlFromConsul(consulConfig, "mysql")
	//初始化数据库
	db, err := gorm.Open("mysql", mysqlInfo.User+":"+mysqlInfo.Pwd+"@("+mysqlInfo.Host+":3306)/"+mysqlInfo.Database+"?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		//命令行输出下,方便查看错误
		fmt.Println(err)
		common.Fatal(err)
	}
	defer db.Close()
	//禁止复表
	db.SingularTable(true)

	//4.添加链路追踪
	t, io, err := common.NewTracer("go.micro.service.middleware", tracerHost+":"+strconv.Itoa(tracerPort))
	if err != nil {
		common.Error(err)
	}
	defer io.Close()
	opentracing.SetGlobalTracer(t)

	//添加熔断器,作为客户端需要启用
	//hystrixStreamHandler := hystrix.NewStreamHandler()
	//hystrixStreamHandler.Start()

	//添加日志中心
	//1)需要程序日志打入到日志文件中
	//2)在程序中添加filebeat.yml 文件
	//3) 启动filebeat,启动命令 ./filebeat -e -c filebeat.yml
	fmt.Println("日志统一记录在根目录 micro.log 文件中,请点击查看日志!")

	//启动监听程序
	//go func() {
	//	//http://192.168.204.130:9092/turbine/turbine.stream
	//	//看板访问地址 http://127.0.0.1:9002/hystrix,url后面一定要带 /hystrix
	//	err = http.ListenAndServe(net.JoinHostPort("0.0.0.0",strconv.Itoa(hystrixPort)),hystrixStreamHandler)
	//	if err !=nil {
	//		common.Error(err)
	//	}
	//}()

	//5.添加监控
	common.PrometheusBoot(prometheusPort)

	//下载kubectl:https://kubernetes.io/docs/tasks/tools/#tabset-2
	//macos:
	// 1.curl -LO "https://dl.k8s.io/release/v1.21.0/bin/darwin/amd64/kubectl"
	// 2.chmod +x ./kubectl
	// 3.sudo mv ./kubectl /usr/local/bin/kubectl
	//   sudo chown root: /usr/local/bin/kubectl
	// 5.kubectl version --client
	// 6.集群模式下直接拷贝服务端~/.kube/config 文件到本机 ~/.kube/confg 中
	//   注意:- config中的域名要能解析正确
	//        - 生产环境可以创建另一个证书
	// 7.kubectl get ns 查看是否正常
	//
	//6.创建k8s连接
	//在集群外面连接
	var kubeconfig *string
	if home := homedir.HomeDir(); home != "" {
		kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
	} else {
		kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
	}
	flag.Parse()
	config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
	if err != nil {
		common.Fatal(err.Error())
	}

	//在集群中外的配置
	//config, err := rest.InClusterConfig()
	//if err != nil {
	//	panic(err.Error())
	//}

	// create the clientset
	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		common.Fatal(err.Error())
	}

	//7.创建服务
	service := micro.NewService(
		//自定义服务地址,且必须写在其它参数前面
		micro.Server(server.NewServer(func(options *server.Options) {
			options.Advertise = serviceHost + ":" + servicePort
		})),
		micro.Name("go.micro.service.middleware"),
		micro.Version("latest"),
		//指定服务端口
		micro.Address(":"+servicePort),
		//添加注册中心
		micro.Registry(consul),
		//添加链路追踪
		micro.WrapHandler(opentracing2.NewHandlerWrapper(opentracing.GlobalTracer())),
		micro.WrapClient(opentracing2.NewClientWrapper(opentracing.GlobalTracer())),
		//只作为客户端的时候起作用,如果存在调用别人的情况,原则上不去主动调用
		//micro.WrapClient(hystrix2.NewClientHystrixWrapper()),
		//添加限流
		micro.WrapHandler(ratelimit.NewHandlerWrapper(1000)),
	)

	service.Init()

	//只能执行一遍
	//err = repository.NewMiddlewareRepository(db).InitTable()
	//if err != nil {
	//	common.Fatal(err)
	//}
	//if err:= repository.NewMiddleTypeRepository(db).InitTable();err!=nil{
	//	common.Fatal(err)
	//}

	// 注册句柄,可以快速操作已开发的服务
	middlewareDataService := service2.NewMiddlewareDataService(repository.NewMiddlewareRepository(db), clientset)
	middleTypeDataService := service2.NewMiddleTypeDataService(repository.NewMiddleTypeRepository(db))
	middleware.RegisterMiddlewareHandler(service.Server(), &handler.MiddlewareHandler{MiddlewareDataService: middlewareDataService, MiddleTypeDataService: middleTypeDataService})

	// 启动服务
	if err := service.Run(); err != nil {
		//输出启动失败信息
		common.Fatal(err)
	}
}

C:\Users\Administrator\Desktop\gopaas\middleware\handler\middlewareHandler.go

package handler

import (
	"context"
	"strconv"

	log "github.com/asim/go-micro/v3/logger"
	"github.com/yunixiangfeng/gopaas/common"
	"github.com/yunixiangfeng/gopaas/middleware/domain/model"
	"github.com/yunixiangfeng/gopaas/middleware/domain/service"
	middleware "github.com/yunixiangfeng/gopaas/middleware/proto/middleware"
)

type MiddlewareHandler struct {
	//注意这里的类型是 IMiddlewareDataService 接口类型
	MiddlewareDataService service.IMiddlewareDataService
	// 添加中间件类型服务
	MiddleTypeDataService service.IMiddleTypeDataService
}

func (e *MiddlewareHandler) DeleteMiddleTypeById(context.Context, *middleware.MiddleTypeId, *middleware.Response) error {
	panic("implement me")
}

// Call is a single request handler called via client.Call or the generated client code
func (e *MiddlewareHandler) AddMiddleware(ctx context.Context, info *middleware.MiddlewareInfo, rsp *middleware.Response) error {
	log.Info("Received *middleware.AddMiddleware request")
	middleModel := &model.Middleware{}
	if err := common.SwapTo(info, middleModel); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	//调用其它的服务处理数据
	//根据ID产销需要的镜像地址
	imageAddress, err := e.MiddleTypeDataService.FindImageVersionByID(info.MiddleVersionId)
	if err != nil {
		common.Error(err)
		return err
	}
	//赋值
	info.MiddleDockerImageVersion = imageAddress
	//在k8s 中创建资源
	if err := e.MiddlewareDataService.CreateToK8s(info); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	} else {
		//插入数据库
		middleID, err := e.MiddlewareDataService.AddMiddleware(middleModel)
		if err != nil {
			common.Error(err)
			rsp.Msg = err.Error()
			return err
		}
		rsp.Msg = "中间件添加成功 ID 号为:" + strconv.FormatInt(middleID, 10)
		common.Info(rsp.Msg)
	}
	return nil
}

func (e *MiddlewareHandler) DeleteMiddleware(ctx context.Context, req *middleware.MiddlewareId, rsp *middleware.Response) error {
	log.Info("Received *middleware.DeleteMiddleware request")
	middleModel, err := e.MiddlewareDataService.FindMiddlewareByID(req.Id)
	if err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	//删除k8s中资源
	if err := e.MiddlewareDataService.DeleteFromK8s(middleModel); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	return nil
}

func (e *MiddlewareHandler) UpdateMiddleware(ctx context.Context, req *middleware.MiddlewareInfo, rsp *middleware.Response) error {
	log.Info("Received *middleware.UpdateMiddleware request")
	if err := e.MiddlewareDataService.UpdateToK8s(req); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	//查询中间件相关的信息
	middleModle, err := e.MiddlewareDataService.FindMiddlewareByID(req.Id)
	if err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	//更新model数据
	if err := common.SwapTo(req, middleModle); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	//更新model
	if err := e.MiddlewareDataService.UpdateMiddleware(middleModle); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	return nil
}

//查询中间件
func (e *MiddlewareHandler) FindMiddlewareByID(ctx context.Context, req *middleware.MiddlewareId, rsp *middleware.MiddlewareInfo) error {
	log.Info("Received *middleware.FindMiddlewareByID request")
	//查询中间件
	middlewareModel, err := e.MiddlewareDataService.FindMiddlewareByID(req.Id)
	if err != nil {
		common.Error(err)
		return err
	}
	if err := common.SwapTo(middlewareModel, rsp); err != nil {
		common.Error(err)
		return err
	}
	return nil
}

//查找所有的中间件
func (e *MiddlewareHandler) FindAllMiddleware(ctx context.Context, req *middleware.FindAll, rsp *middleware.AllMiddleware) error {
	log.Info("Received *middleware.FindAllMiddleware request")
	allMiddleware, err := e.MiddlewareDataService.FindAllMiddleware()
	if err != nil {
		common.Error(err)
		return err
	}
	//整理格式
	for _, v := range allMiddleware {
		middleInfo := &middleware.MiddlewareInfo{}
		if err := common.SwapTo(v, middleInfo); err != nil {
			common.Error(err)
			return err
		}
		rsp.MiddlewareInfo = append(rsp.MiddlewareInfo, middleInfo)
	}
	return nil
}

//查找所有的中间件
func (e *MiddlewareHandler) FindAllMiddlewareByTypeID(ctx context.Context, req *middleware.FindAllByTypeId, rsp *middleware.AllMiddleware) error {
	log.Info("Received *middleware.FindAllMiddleware request")
	allMiddleware, err := e.MiddlewareDataService.FindAllMiddlewareByTypeID(req.TypeId)
	if err != nil {
		common.Error(err)
		return err
	}
	//整理格式
	for _, v := range allMiddleware {
		middleInfo := &middleware.MiddlewareInfo{}
		if err := common.SwapTo(v, middleInfo); err != nil {
			common.Error(err)
			return err
		}
		rsp.MiddlewareInfo = append(rsp.MiddlewareInfo, middleInfo)
	}
	return nil
}

//根据ID查找中间件类型信息
func (e *MiddlewareHandler) FindMiddleTypeByID(ctx context.Context, req *middleware.MiddleTypeId, rsp *middleware.MiddleTypeInfo) error {
	typeModel, err := e.MiddleTypeDataService.FindMiddleTypeByID(req.Id)
	if err != nil {
		common.Error(err)
		return err
	}
	if err := common.SwapTo(typeModel, rsp); err != nil {
		common.Error(err)
		return err
	}
	return nil
}

//添加中间件
func (e *MiddlewareHandler) AddMiddleType(ctx context.Context, info *middleware.MiddleTypeInfo, rsp *middleware.Response) error {
	typeModel := &model.MiddleType{}
	if err := common.SwapTo(info, typeModel); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	id, err := e.MiddleTypeDataService.AddMiddleType(typeModel)
	if err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	rsp.Msg = "中间件类型 ID 号为: " + strconv.FormatInt(id, 10)
	common.Info(rsp.Msg)
	return nil
}

//删除中间件类型
func (e *MiddlewareHandler) DeleteMiddleTypeByID(ctx context.Context, req *middleware.MiddleTypeId, rsp *middleware.Response) error {
	if err := e.MiddleTypeDataService.DeleteMiddleType(req.Id); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	return nil
}

//更新中间件类型
func (e *MiddlewareHandler) UpdateMiddleType(ctx context.Context, req *middleware.MiddleTypeInfo, rsp *middleware.Response) error {
	typeModel, err := e.MiddleTypeDataService.FindMiddleTypeByID(req.Id)
	if err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	if err := common.SwapTo(req, typeModel); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	if err := e.MiddleTypeDataService.UpdateMiddleType(typeModel); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	return nil
}

//查找所有的类型
func (e *MiddlewareHandler) FindAllMiddleType(ctx context.Context, req *middleware.FindAll, rsp *middleware.AllMiddleType) error {
	//查询所有中间件
	allMiddleType, err := e.MiddleTypeDataService.FindAllMiddleType()
	if err != nil {
		common.Error(err)
		return err
	}
	//整理格式
	for _, v := range allMiddleType {
		middleInfo := &middleware.MiddleTypeInfo{}
		if err := common.SwapTo(v, middleInfo); err != nil {
			common.Error(err)
			return err
		}
		rsp.MiddleTypeInfo = append(rsp.MiddleTypeInfo, middleInfo)
	}
	return nil
}

make proto 

10-17 Middleware front-end page and core API development (Part 1)

PS C:\Users\Administrator\Desktop\gopaas> .\yu-tool\yu-tool.exe  newService github.com/yunixiangfeng/gopaas/middlewareApi

make proto 

go mod tidy

C:\Users\Administrator\Desktop\gopaas\middlewareapi\proto\middlewareApi\middlewareApi.proto

syntax = "proto3";

package middlewareApi;

option go_package = "./proto/middlewareApi;middlewareApi";

service MiddlewareApi {
    rpc FindMiddlewareById(Request) returns (Response){}
	rpc AddMiddleware(Request) returns (Response){}
	rpc DeleteMiddlewareById(Request) returns (Response){}
	rpc UpdateMiddleware(Request) returns (Response){}
	//默认接口
	rpc Call(Request) returns (Response){}
	//根据类型获取所有中间件
	rpc FindAllMiddlewareByTypeId(Request) returns (Response){}
	//中间件类型对外开发的API
	rpc FindMiddleTypeById(Request) returns (Response){}
	rpc AddMiddleType(Request) returns (Response){}
	rpc DeleteMiddleTypeById(Request) returns (Response){}
	rpc UpdateMiddleType(Request) returns (Response){}
	rpc FindAllMiddleType(Request) returns (Response){}
}

message Pair {
	string key = 1;
	repeated string values = 2;
}


message Request {
	string method = 1;
	string path = 2;
	map<string, Pair> header = 3;
	map<string, Pair> get = 4;
	map<string, Pair> post = 5;
	string body = 6;
	string url = 7;
}


message Response {
	int32 statusCode = 1;
	map<string, Pair> header = 2;
	string body = 3;
}

 make proto

C:\Users\Administrator\Desktop\gopaas\middlewareapi\proto\middlewareApi\middlewareApi.pb.micro.go

// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: proto/middlewareApi/middlewareApi.proto

package middlewareApi

import (
	fmt "fmt"
	proto "google.golang.org/protobuf/proto"
	math "math"
)

import (
	context "context"
	api "github.com/asim/go-micro/v3/api"
	client "github.com/asim/go-micro/v3/client"
	server "github.com/asim/go-micro/v3/server"
)

// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf

// Reference imports to suppress errors if they are not otherwise used.
var _ api.Endpoint
var _ context.Context
var _ client.Option
var _ server.Option

// Api Endpoints for MiddlewareApi service

func NewMiddlewareApiEndpoints() []*api.Endpoint {
	return []*api.Endpoint{}
}

// Client API for MiddlewareApi service

type MiddlewareApiService interface {
	FindMiddlewareById(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error)
	AddMiddleware(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error)
	DeleteMiddlewareById(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error)
	UpdateMiddleware(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error)
	//默认接口
	Call(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error)
	//根据类型获取所有中间件
	FindAllMiddlewareByTypeId(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error)
	//中间件类型对外开发的API
	FindMiddleTypeById(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error)
	AddMiddleType(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error)
	DeleteMiddleTypeById(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error)
	UpdateMiddleType(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error)
	FindAllMiddleType(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error)
}

type middlewareApiService struct {
	c    client.Client
	name string
}

func NewMiddlewareApiService(name string, c client.Client) MiddlewareApiService {
	return &middlewareApiService{
		c:    c,
		name: name,
	}
}

func (c *middlewareApiService) FindMiddlewareById(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) {
	req := c.c.NewRequest(c.name, "MiddlewareApi.FindMiddlewareById", in)
	out := new(Response)
	err := c.c.Call(ctx, req, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func (c *middlewareApiService) AddMiddleware(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) {
	req := c.c.NewRequest(c.name, "MiddlewareApi.AddMiddleware", in)
	out := new(Response)
	err := c.c.Call(ctx, req, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func (c *middlewareApiService) DeleteMiddlewareById(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) {
	req := c.c.NewRequest(c.name, "MiddlewareApi.DeleteMiddlewareById", in)
	out := new(Response)
	err := c.c.Call(ctx, req, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func (c *middlewareApiService) UpdateMiddleware(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) {
	req := c.c.NewRequest(c.name, "MiddlewareApi.UpdateMiddleware", in)
	out := new(Response)
	err := c.c.Call(ctx, req, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func (c *middlewareApiService) Call(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) {
	req := c.c.NewRequest(c.name, "MiddlewareApi.Call", in)
	out := new(Response)
	err := c.c.Call(ctx, req, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func (c *middlewareApiService) FindAllMiddlewareByTypeId(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) {
	req := c.c.NewRequest(c.name, "MiddlewareApi.FindAllMiddlewareByTypeId", in)
	out := new(Response)
	err := c.c.Call(ctx, req, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func (c *middlewareApiService) FindMiddleTypeById(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) {
	req := c.c.NewRequest(c.name, "MiddlewareApi.FindMiddleTypeById", in)
	out := new(Response)
	err := c.c.Call(ctx, req, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func (c *middlewareApiService) AddMiddleType(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) {
	req := c.c.NewRequest(c.name, "MiddlewareApi.AddMiddleType", in)
	out := new(Response)
	err := c.c.Call(ctx, req, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func (c *middlewareApiService) DeleteMiddleTypeById(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) {
	req := c.c.NewRequest(c.name, "MiddlewareApi.DeleteMiddleTypeById", in)
	out := new(Response)
	err := c.c.Call(ctx, req, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func (c *middlewareApiService) UpdateMiddleType(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) {
	req := c.c.NewRequest(c.name, "MiddlewareApi.UpdateMiddleType", in)
	out := new(Response)
	err := c.c.Call(ctx, req, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func (c *middlewareApiService) FindAllMiddleType(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) {
	req := c.c.NewRequest(c.name, "MiddlewareApi.FindAllMiddleType", in)
	out := new(Response)
	err := c.c.Call(ctx, req, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

// Server API for MiddlewareApi service

type MiddlewareApiHandler interface {
	FindMiddlewareById(context.Context, *Request, *Response) error
	AddMiddleware(context.Context, *Request, *Response) error
	DeleteMiddlewareById(context.Context, *Request, *Response) error
	UpdateMiddleware(context.Context, *Request, *Response) error
	//默认接口
	Call(context.Context, *Request, *Response) error
	//根据类型获取所有中间件
	FindAllMiddlewareByTypeId(context.Context, *Request, *Response) error
	//中间件类型对外开发的API
	FindMiddleTypeById(context.Context, *Request, *Response) error
	AddMiddleType(context.Context, *Request, *Response) error
	DeleteMiddleTypeById(context.Context, *Request, *Response) error
	UpdateMiddleType(context.Context, *Request, *Response) error
	FindAllMiddleType(context.Context, *Request, *Response) error
}

func RegisterMiddlewareApiHandler(s server.Server, hdlr MiddlewareApiHandler, opts ...server.HandlerOption) error {
	type middlewareApi interface {
		FindMiddlewareById(ctx context.Context, in *Request, out *Response) error
		AddMiddleware(ctx context.Context, in *Request, out *Response) error
		DeleteMiddlewareById(ctx context.Context, in *Request, out *Response) error
		UpdateMiddleware(ctx context.Context, in *Request, out *Response) error
		Call(ctx context.Context, in *Request, out *Response) error
		FindAllMiddlewareByTypeId(ctx context.Context, in *Request, out *Response) error
		FindMiddleTypeById(ctx context.Context, in *Request, out *Response) error
		AddMiddleType(ctx context.Context, in *Request, out *Response) error
		DeleteMiddleTypeById(ctx context.Context, in *Request, out *Response) error
		UpdateMiddleType(ctx context.Context, in *Request, out *Response) error
		FindAllMiddleType(ctx context.Context, in *Request, out *Response) error
	}
	type MiddlewareApi struct {
		middlewareApi
	}
	h := &middlewareApiHandler{hdlr}
	return s.Handle(s.NewHandler(&MiddlewareApi{h}, opts...))
}

type middlewareApiHandler struct {
	MiddlewareApiHandler
}

func (h *middlewareApiHandler) FindMiddlewareById(ctx context.Context, in *Request, out *Response) error {
	return h.MiddlewareApiHandler.FindMiddlewareById(ctx, in, out)
}

func (h *middlewareApiHandler) AddMiddleware(ctx context.Context, in *Request, out *Response) error {
	return h.MiddlewareApiHandler.AddMiddleware(ctx, in, out)
}

func (h *middlewareApiHandler) DeleteMiddlewareById(ctx context.Context, in *Request, out *Response) error {
	return h.MiddlewareApiHandler.DeleteMiddlewareById(ctx, in, out)
}

func (h *middlewareApiHandler) UpdateMiddleware(ctx context.Context, in *Request, out *Response) error {
	return h.MiddlewareApiHandler.UpdateMiddleware(ctx, in, out)
}

func (h *middlewareApiHandler) Call(ctx context.Context, in *Request, out *Response) error {
	return h.MiddlewareApiHandler.Call(ctx, in, out)
}

func (h *middlewareApiHandler) FindAllMiddlewareByTypeId(ctx context.Context, in *Request, out *Response) error {
	return h.MiddlewareApiHandler.FindAllMiddlewareByTypeId(ctx, in, out)
}

func (h *middlewareApiHandler) FindMiddleTypeById(ctx context.Context, in *Request, out *Response) error {
	return h.MiddlewareApiHandler.FindMiddleTypeById(ctx, in, out)
}

func (h *middlewareApiHandler) AddMiddleType(ctx context.Context, in *Request, out *Response) error {
	return h.MiddlewareApiHandler.AddMiddleType(ctx, in, out)
}

func (h *middlewareApiHandler) DeleteMiddleTypeById(ctx context.Context, in *Request, out *Response) error {
	return h.MiddlewareApiHandler.DeleteMiddleTypeById(ctx, in, out)
}

func (h *middlewareApiHandler) UpdateMiddleType(ctx context.Context, in *Request, out *Response) error {
	return h.MiddlewareApiHandler.UpdateMiddleType(ctx, in, out)
}

func (h *middlewareApiHandler) FindAllMiddleType(ctx context.Context, in *Request, out *Response) error {
	return h.MiddlewareApiHandler.FindAllMiddleType(ctx, in, out)
}

C:\Users\Administrator\Desktop\gopaas\middlewareapi\proto\middlewareApi\middlewareApi.pb.go

// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// 	protoc-gen-go v1.27.1
// 	protoc        v3.15.7
// source: proto/middlewareApi/middlewareApi.proto

package middlewareApi

import (
	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
	reflect "reflect"
	sync "sync"
)

const (
	// Verify that this generated code is sufficiently up-to-date.
	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
	// Verify that runtime/protoimpl is sufficiently up-to-date.
	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)

type Pair struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	Key    string   `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
	Values []string `protobuf:"bytes,2,rep,name=values,proto3" json:"values,omitempty"`
}

func (x *Pair) Reset() {
	*x = Pair{}
	if protoimpl.UnsafeEnabled {
		mi := &file_proto_middlewareApi_middlewareApi_proto_msgTypes[0]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

func (x *Pair) String() string {
	return protoimpl.X.MessageStringOf(x)
}

func (*Pair) ProtoMessage() {}

func (x *Pair) ProtoReflect() protoreflect.Message {
	mi := &file_proto_middlewareApi_middlewareApi_proto_msgTypes[0]
	if protoimpl.UnsafeEnabled && x != nil {
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		if ms.LoadMessageInfo() == nil {
			ms.StoreMessageInfo(mi)
		}
		return ms
	}
	return mi.MessageOf(x)
}

// Deprecated: Use Pair.ProtoReflect.Descriptor instead.
func (*Pair) Descriptor() ([]byte, []int) {
	return file_proto_middlewareApi_middlewareApi_proto_rawDescGZIP(), []int{0}
}

func (x *Pair) GetKey() string {
	if x != nil {
		return x.Key
	}
	return ""
}

func (x *Pair) GetValues() []string {
	if x != nil {
		return x.Values
	}
	return nil
}

type Request struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	Method string           `protobuf:"bytes,1,opt,name=method,proto3" json:"method,omitempty"`
	Path   string           `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"`
	Header map[string]*Pair `protobuf:"bytes,3,rep,name=header,proto3" json:"header,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
	Get    map[string]*Pair `protobuf:"bytes,4,rep,name=get,proto3" json:"get,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
	Post   map[string]*Pair `protobuf:"bytes,5,rep,name=post,proto3" json:"post,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
	Body   string           `protobuf:"bytes,6,opt,name=body,proto3" json:"body,omitempty"`
	Url    string           `protobuf:"bytes,7,opt,name=url,proto3" json:"url,omitempty"`
}

func (x *Request) Reset() {
	*x = Request{}
	if protoimpl.UnsafeEnabled {
		mi := &file_proto_middlewareApi_middlewareApi_proto_msgTypes[1]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

func (x *Request) String() string {
	return protoimpl.X.MessageStringOf(x)
}

func (*Request) ProtoMessage() {}

func (x *Request) ProtoReflect() protoreflect.Message {
	mi := &file_proto_middlewareApi_middlewareApi_proto_msgTypes[1]
	if protoimpl.UnsafeEnabled && x != nil {
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		if ms.LoadMessageInfo() == nil {
			ms.StoreMessageInfo(mi)
		}
		return ms
	}
	return mi.MessageOf(x)
}

// Deprecated: Use Request.ProtoReflect.Descriptor instead.
func (*Request) Descriptor() ([]byte, []int) {
	return file_proto_middlewareApi_middlewareApi_proto_rawDescGZIP(), []int{1}
}

func (x *Request) GetMethod() string {
	if x != nil {
		return x.Method
	}
	return ""
}

func (x *Request) GetPath() string {
	if x != nil {
		return x.Path
	}
	return ""
}

func (x *Request) GetHeader() map[string]*Pair {
	if x != nil {
		return x.Header
	}
	return nil
}

func (x *Request) GetGet() map[string]*Pair {
	if x != nil {
		return x.Get
	}
	return nil
}

func (x *Request) GetPost() map[string]*Pair {
	if x != nil {
		return x.Post
	}
	return nil
}

func (x *Request) GetBody() string {
	if x != nil {
		return x.Body
	}
	return ""
}

func (x *Request) GetUrl() string {
	if x != nil {
		return x.Url
	}
	return ""
}

type Response struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	StatusCode int32            `protobuf:"varint,1,opt,name=statusCode,proto3" json:"statusCode,omitempty"`
	Header     map[string]*Pair `protobuf:"bytes,2,rep,name=header,proto3" json:"header,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
	Body       string           `protobuf:"bytes,3,opt,name=body,proto3" json:"body,omitempty"`
}

func (x *Response) Reset() {
	*x = Response{}
	if protoimpl.UnsafeEnabled {
		mi := &file_proto_middlewareApi_middlewareApi_proto_msgTypes[2]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

func (x *Response) String() string {
	return protoimpl.X.MessageStringOf(x)
}

func (*Response) ProtoMessage() {}

func (x *Response) ProtoReflect() protoreflect.Message {
	mi := &file_proto_middlewareApi_middlewareApi_proto_msgTypes[2]
	if protoimpl.UnsafeEnabled && x != nil {
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		if ms.LoadMessageInfo() == nil {
			ms.StoreMessageInfo(mi)
		}
		return ms
	}
	return mi.MessageOf(x)
}

// Deprecated: Use Response.ProtoReflect.Descriptor instead.
func (*Response) Descriptor() ([]byte, []int) {
	return file_proto_middlewareApi_middlewareApi_proto_rawDescGZIP(), []int{2}
}

func (x *Response) GetStatusCode() int32 {
	if x != nil {
		return x.StatusCode
	}
	return 0
}

func (x *Response) GetHeader() map[string]*Pair {
	if x != nil {
		return x.Header
	}
	return nil
}

func (x *Response) GetBody() string {
	if x != nil {
		return x.Body
	}
	return ""
}

var File_proto_middlewareApi_middlewareApi_proto protoreflect.FileDescriptor

var file_proto_middlewareApi_middlewareApi_proto_rawDesc = []byte{
	0x0a, 0x27, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61,
	0x72, 0x65, 0x41, 0x70, 0x69, 0x2f, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65,
	0x41, 0x70, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0d, 0x6d, 0x69, 0x64, 0x64, 0x6c,
	0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x22, 0x30, 0x0a, 0x04, 0x50, 0x61, 0x69, 0x72,
	0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
	0x65, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03,
	0x28, 0x09, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0xeb, 0x03, 0x0a, 0x07, 0x52,
	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64,
	0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x12,
	0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61,
	0x74, 0x68, 0x12, 0x3a, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x03,
	0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x41,
	0x70, 0x69, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65,
	0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x31,
	0x0a, 0x03, 0x67, 0x65, 0x74, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6d, 0x69,
	0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x71, 0x75,
	0x65, 0x73, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x03, 0x67, 0x65,
	0x74, 0x12, 0x34, 0x0a, 0x04, 0x70, 0x6f, 0x73, 0x74, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32,
	0x20, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e,
	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x45, 0x6e, 0x74, 0x72,
	0x79, 0x52, 0x04, 0x70, 0x6f, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18,
	0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x75,
	0x72, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x1a, 0x4e, 0x0a,
	0x0b, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03,
	0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29,
	0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e,
	0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x50, 0x61,
	0x69, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x4b, 0x0a,
	0x08, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
	0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x05, 0x76,
	0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6d, 0x69, 0x64,
	0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x52,
	0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x4c, 0x0a, 0x09, 0x50, 0x6f,
	0x73, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01,
	0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c,
	0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c,
	0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x52, 0x05, 0x76,
	0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xcb, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73,
	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43,
	0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75,
	0x73, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x3b, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18,
	0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61,
	0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x48,
	0x65, 0x61, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64,
	0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
	0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x1a, 0x4e, 0x0a, 0x0b, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72,
	0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
	0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
	0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77,
	0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c,
	0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x32, 0xa0, 0x06, 0x0a, 0x0d, 0x4d, 0x69, 0x64, 0x64, 0x6c,
	0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x12, 0x47, 0x0a, 0x12, 0x46, 0x69, 0x6e, 0x64,
	0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x42, 0x79, 0x49, 0x64, 0x12, 0x16,
	0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x52,
	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77,
	0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
	0x00, 0x12, 0x42, 0x0a, 0x0d, 0x41, 0x64, 0x64, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61,
	0x72, 0x65, 0x12, 0x16, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x41,
	0x70, 0x69, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6d, 0x69, 0x64,
	0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f,
	0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x49, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d,
	0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x42, 0x79, 0x49, 0x64, 0x12, 0x16, 0x2e,
	0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x52, 0x65,
	0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61,
	0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00,
	0x12, 0x45, 0x0a, 0x10, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65,
	0x77, 0x61, 0x72, 0x65, 0x12, 0x16, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72,
	0x65, 0x41, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6d,
	0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x73,
	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x39, 0x0a, 0x04, 0x43, 0x61, 0x6c, 0x6c, 0x12,
	0x16, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e,
	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65,
	0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
	0x22, 0x00, 0x12, 0x4e, 0x0a, 0x19, 0x46, 0x69, 0x6e, 0x64, 0x41, 0x6c, 0x6c, 0x4d, 0x69, 0x64,
	0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x42, 0x79, 0x54, 0x79, 0x70, 0x65, 0x49, 0x64, 0x12,
	0x16, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e,
	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65,
	0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
	0x22, 0x00, 0x12, 0x47, 0x0a, 0x12, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65,
	0x54, 0x79, 0x70, 0x65, 0x42, 0x79, 0x49, 0x64, 0x12, 0x16, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c,
	0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
	0x1a, 0x17, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69,
	0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x0d, 0x41,
	0x64, 0x64, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x2e, 0x6d,
	0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x71,
	0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72,
	0x65, 0x41, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12,
	0x49, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x54,
	0x79, 0x70, 0x65, 0x42, 0x79, 0x49, 0x64, 0x12, 0x16, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65,
	0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
	0x17, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e,
	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x45, 0x0a, 0x10, 0x55, 0x70,
	0x64, 0x61, 0x74, 0x65, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16,
	0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x52,
	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77,
	0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
	0x00, 0x12, 0x46, 0x0a, 0x11, 0x46, 0x69, 0x6e, 0x64, 0x41, 0x6c, 0x6c, 0x4d, 0x69, 0x64, 0x64,
	0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77,
	0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17,
	0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x52,
	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x25, 0x5a, 0x23, 0x2e, 0x2f, 0x70,
	0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x41,
	0x70, 0x69, 0x3b, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69,
	0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}

var (
	file_proto_middlewareApi_middlewareApi_proto_rawDescOnce sync.Once
	file_proto_middlewareApi_middlewareApi_proto_rawDescData = file_proto_middlewareApi_middlewareApi_proto_rawDesc
)

func file_proto_middlewareApi_middlewareApi_proto_rawDescGZIP() []byte {
	file_proto_middlewareApi_middlewareApi_proto_rawDescOnce.Do(func() {
		file_proto_middlewareApi_middlewareApi_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_middlewareApi_middlewareApi_proto_rawDescData)
	})
	return file_proto_middlewareApi_middlewareApi_proto_rawDescData
}

var file_proto_middlewareApi_middlewareApi_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
var file_proto_middlewareApi_middlewareApi_proto_goTypes = []interface{}{
	(*Pair)(nil),     // 0: middlewareApi.Pair
	(*Request)(nil),  // 1: middlewareApi.Request
	(*Response)(nil), // 2: middlewareApi.Response
	nil,              // 3: middlewareApi.Request.HeaderEntry
	nil,              // 4: middlewareApi.Request.GetEntry
	nil,              // 5: middlewareApi.Request.PostEntry
	nil,              // 6: middlewareApi.Response.HeaderEntry
}
var file_proto_middlewareApi_middlewareApi_proto_depIdxs = []int32{
	3,  // 0: middlewareApi.Request.header:type_name -> middlewareApi.Request.HeaderEntry
	4,  // 1: middlewareApi.Request.get:type_name -> middlewareApi.Request.GetEntry
	5,  // 2: middlewareApi.Request.post:type_name -> middlewareApi.Request.PostEntry
	6,  // 3: middlewareApi.Response.header:type_name -> middlewareApi.Response.HeaderEntry
	0,  // 4: middlewareApi.Request.HeaderEntry.value:type_name -> middlewareApi.Pair
	0,  // 5: middlewareApi.Request.GetEntry.value:type_name -> middlewareApi.Pair
	0,  // 6: middlewareApi.Request.PostEntry.value:type_name -> middlewareApi.Pair
	0,  // 7: middlewareApi.Response.HeaderEntry.value:type_name -> middlewareApi.Pair
	1,  // 8: middlewareApi.MiddlewareApi.FindMiddlewareById:input_type -> middlewareApi.Request
	1,  // 9: middlewareApi.MiddlewareApi.AddMiddleware:input_type -> middlewareApi.Request
	1,  // 10: middlewareApi.MiddlewareApi.DeleteMiddlewareById:input_type -> middlewareApi.Request
	1,  // 11: middlewareApi.MiddlewareApi.UpdateMiddleware:input_type -> middlewareApi.Request
	1,  // 12: middlewareApi.MiddlewareApi.Call:input_type -> middlewareApi.Request
	1,  // 13: middlewareApi.MiddlewareApi.FindAllMiddlewareByTypeId:input_type -> middlewareApi.Request
	1,  // 14: middlewareApi.MiddlewareApi.FindMiddleTypeById:input_type -> middlewareApi.Request
	1,  // 15: middlewareApi.MiddlewareApi.AddMiddleType:input_type -> middlewareApi.Request
	1,  // 16: middlewareApi.MiddlewareApi.DeleteMiddleTypeById:input_type -> middlewareApi.Request
	1,  // 17: middlewareApi.MiddlewareApi.UpdateMiddleType:input_type -> middlewareApi.Request
	1,  // 18: middlewareApi.MiddlewareApi.FindAllMiddleType:input_type -> middlewareApi.Request
	2,  // 19: middlewareApi.MiddlewareApi.FindMiddlewareById:output_type -> middlewareApi.Response
	2,  // 20: middlewareApi.MiddlewareApi.AddMiddleware:output_type -> middlewareApi.Response
	2,  // 21: middlewareApi.MiddlewareApi.DeleteMiddlewareById:output_type -> middlewareApi.Response
	2,  // 22: middlewareApi.MiddlewareApi.UpdateMiddleware:output_type -> middlewareApi.Response
	2,  // 23: middlewareApi.MiddlewareApi.Call:output_type -> middlewareApi.Response
	2,  // 24: middlewareApi.MiddlewareApi.FindAllMiddlewareByTypeId:output_type -> middlewareApi.Response
	2,  // 25: middlewareApi.MiddlewareApi.FindMiddleTypeById:output_type -> middlewareApi.Response
	2,  // 26: middlewareApi.MiddlewareApi.AddMiddleType:output_type -> middlewareApi.Response
	2,  // 27: middlewareApi.MiddlewareApi.DeleteMiddleTypeById:output_type -> middlewareApi.Response
	2,  // 28: middlewareApi.MiddlewareApi.UpdateMiddleType:output_type -> middlewareApi.Response
	2,  // 29: middlewareApi.MiddlewareApi.FindAllMiddleType:output_type -> middlewareApi.Response
	19, // [19:30] is the sub-list for method output_type
	8,  // [8:19] is the sub-list for method input_type
	8,  // [8:8] is the sub-list for extension type_name
	8,  // [8:8] is the sub-list for extension extendee
	0,  // [0:8] is the sub-list for field type_name
}

func init() { file_proto_middlewareApi_middlewareApi_proto_init() }
func file_proto_middlewareApi_middlewareApi_proto_init() {
	if File_proto_middlewareApi_middlewareApi_proto != nil {
		return
	}
	if !protoimpl.UnsafeEnabled {
		file_proto_middlewareApi_middlewareApi_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*Pair); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
		file_proto_middlewareApi_middlewareApi_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*Request); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
		file_proto_middlewareApi_middlewareApi_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*Response); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
	}
	type x struct{}
	out := protoimpl.TypeBuilder{
		File: protoimpl.DescBuilder{
			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
			RawDescriptor: file_proto_middlewareApi_middlewareApi_proto_rawDesc,
			NumEnums:      0,
			NumMessages:   7,
			NumExtensions: 0,
			NumServices:   1,
		},
		GoTypes:           file_proto_middlewareApi_middlewareApi_proto_goTypes,
		DependencyIndexes: file_proto_middlewareApi_middlewareApi_proto_depIdxs,
		MessageInfos:      file_proto_middlewareApi_middlewareApi_proto_msgTypes,
	}.Build()
	File_proto_middlewareApi_middlewareApi_proto = out.File
	file_proto_middlewareApi_middlewareApi_proto_rawDesc = nil
	file_proto_middlewareApi_middlewareApi_proto_goTypes = nil
	file_proto_middlewareApi_middlewareApi_proto_depIdxs = nil
}

Guess you like

Origin blog.csdn.net/niwoxiangyu/article/details/130819155