From bd491cbde48ec5a5ec742214b490de2e8bc83bbc Mon Sep 17 00:00:00 2001
From: dqzboy <2890763@qq.com>
Date: Sun, 25 Sep 2022 21:05:00 +0800
Subject: [PATCH] DKube Init
---
.idea/.gitignore | 8 +
.idea/k8s-platform.iml | 9 +
.idea/modules.xml | 8 +
.idea/vcs.xml | 6 +
config/config.go | 27 ++
controller/pod.go | 224 ++++++++++++++++
controller/router.go | 24 ++
db/init.go | 52 ++++
go.mod | 64 +++++
go.sum | 547 ++++++++++++++++++++++++++++++++++++++++
main.go | 19 ++
service/dataselector.go | 161 ++++++++++++
service/deployment.go | 307 ++++++++++++++++++++++
service/ingress.go | 183 ++++++++++++++
service/init.go | 32 +++
service/pod.go | 190 ++++++++++++++
service/service.go | 161 ++++++++++++
17 files changed, 2022 insertions(+)
create mode 100644 .idea/.gitignore
create mode 100644 .idea/k8s-platform.iml
create mode 100644 .idea/modules.xml
create mode 100644 .idea/vcs.xml
create mode 100644 config/config.go
create mode 100644 controller/pod.go
create mode 100644 controller/router.go
create mode 100644 db/init.go
create mode 100644 go.mod
create mode 100644 go.sum
create mode 100644 main.go
create mode 100644 service/dataselector.go
create mode 100644 service/deployment.go
create mode 100644 service/ingress.go
create mode 100644 service/init.go
create mode 100644 service/pod.go
create mode 100644 service/service.go
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..35410ca
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
+# 基于编辑器的 HTTP 客户端请求
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/k8s-platform.iml b/.idea/k8s-platform.iml
new file mode 100644
index 0000000..5e764c4
--- /dev/null
+++ b/.idea/k8s-platform.iml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..e430014
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/config/config.go b/config/config.go
new file mode 100644
index 0000000..8ad3708
--- /dev/null
+++ b/config/config.go
@@ -0,0 +1,27 @@
+package config
+
+import "time"
+
+const (
+ ListenAddr = "0.0.0.0:9090"
+ //k8s集群kube-config文件存放位置
+ Kubeconfig = "H:\\project\\kubeconfig\\config"
+ //tail的日志行数
+ PodLogTailLine = 2000
+
+ //数据库配置
+ DbType = "mysql"
+ DbHost = "192.168.66.62"
+ DbPort = 3306
+ DbName = "dkube"
+ DbUser = "root"
+ DbPwd = "123456"
+
+ //打印Mysql debug的sql日志
+ LogMode = false
+
+ //连接池的配置
+ MaxIdleConns = 10 //最大空闲连接
+ MaxOpenConns = 100 //最大连接数
+ MaxLifeTime = 30 * time.Second //最大生存时间
+)
diff --git a/controller/pod.go b/controller/pod.go
new file mode 100644
index 0000000..eea41bd
--- /dev/null
+++ b/controller/pod.go
@@ -0,0 +1,224 @@
+package controller
+
+import (
+ "dkube/service"
+ "github.com/gin-gonic/gin"
+ "github.com/wonderivan/logger"
+ "net/http"
+)
+
+var Pod pod
+
+type pod struct{}
+
+//Controller中的方法入参是gin.Context,用于从上下文中获取请求参数以及定义响应内容
+//流程:绑定参数 --> 调用service代码 --> 根据调用结果响应具体内容
+
+// GetPods 获取Pod列表,支持分页、过滤、排序
+func (p *pod) GetPods(ctx *gin.Context) {
+ //处理入参
+ //匿名结构体用于定义入参,get请求为form格式,其他请求为json格式
+ params := new(struct {
+ FilterName string `form:"filter_name"`
+ Namespace string `form:"namespace"`
+ Limit int `form:"limit"`
+ Page int `form:"page"`
+ })
+ //form格式使用Bind方法,json格式使用ShouldBindJSON方法
+ if err := ctx.Bind(params); err != nil {
+ logger.Error("Bind绑定参数失败" + err.Error())
+ ctx.JSON(http.StatusInternalServerError, gin.H{
+ "msg": "Bind参数绑定失败" + err.Error(),
+ "data": nil,
+ })
+ return
+ }
+
+ data, err := service.Pod.GetPods(params.FilterName, params.Namespace, params.Limit, params.Page)
+ if err != nil {
+ ctx.JSON(http.StatusInternalServerError, gin.H{
+ "msg": err.Error(),
+ "data": nil,
+ })
+ return
+ }
+ ctx.JSON(http.StatusOK, gin.H{
+ "msg": "获取Pod列表成功",
+ "data": data,
+ })
+}
+
+//获取Pod详情
+func (p *pod) GetPodDetail(ctx *gin.Context) {
+ //处理入参
+ //匿名结构体用于定义入参,get请求为form格式,其他请求为json格式
+ params := new(struct {
+ PodName string `form:"pod_name"`
+ Namespace string `form:"namespace"`
+ })
+ //form格式使用Bind方法,json格式使用ShouldBindJSON方法
+ if err := ctx.Bind(params); err != nil {
+ logger.Error("Bind绑定参数失败" + err.Error())
+ ctx.JSON(http.StatusInternalServerError, gin.H{
+ "msg": "Bind参数绑定失败" + err.Error(),
+ "data": nil,
+ })
+ return
+ }
+
+ data, err := service.Pod.GetPodDetail(params.PodName, params.Namespace)
+ if err != nil {
+ ctx.JSON(http.StatusInternalServerError, gin.H{
+ "msg": err.Error(),
+ "data": nil,
+ })
+ return
+ }
+ ctx.JSON(http.StatusOK, gin.H{
+ "msg": "获取Pod列表成功",
+ "data": data,
+ })
+}
+
+//删除Pod
+func (p *pod) DeletePod(ctx *gin.Context) {
+ //处理入参
+ //匿名结构体用于定义入参,get请求为form格式,其他请求为json格式
+ params := new(struct {
+ PodName string `json:"pod_name"`
+ Namespace string `json:"namespace"`
+ })
+ //form格式使用Bind方法,json格式使用ShouldBindJSON方法
+ if err := ctx.ShouldBindJSON(params); err != nil {
+ logger.Error("Bind绑定参数失败" + err.Error())
+ ctx.JSON(http.StatusInternalServerError, gin.H{
+ "msg": "Bind参数绑定失败" + err.Error(),
+ "data": nil,
+ })
+ return
+ }
+
+ err := service.Pod.DeletePod(params.PodName, params.Namespace)
+ if err != nil {
+ ctx.JSON(http.StatusInternalServerError, gin.H{
+ "msg": err.Error(),
+ "data": nil,
+ })
+ return
+ }
+ ctx.JSON(http.StatusOK, gin.H{
+ "msg": "删除Pod成功",
+ "data": nil,
+ })
+}
+
+//更新Pod
+func (p *pod) UpdatePod(ctx *gin.Context) {
+ //处理入参
+ //匿名结构体用于定义入参,get请求为form格式,其他请求为json格式
+ params := new(struct {
+ Namespace string `json:"namespace"`
+ Content string `json:"content"`
+ })
+ //PUT请求,绑定参数方法改为ctx.ShouldBindJSON方法
+ if err := ctx.ShouldBindJSON(params); err != nil {
+ logger.Error("Bind绑定参数失败" + err.Error())
+ ctx.JSON(http.StatusInternalServerError, gin.H{
+ "msg": "Bind参数绑定失败" + err.Error(),
+ "data": nil,
+ })
+ return
+ }
+
+ err := service.Pod.UpdatePod(params.Namespace, params.Content)
+ if err != nil {
+ ctx.JSON(http.StatusInternalServerError, gin.H{
+ "msg": err.Error(),
+ "data": nil,
+ })
+ return
+ }
+ ctx.JSON(http.StatusOK, gin.H{
+ "msg": "更新Pod成功",
+ "data": nil,
+ })
+}
+
+//获取Pod容器
+func (p *pod) GetPodContainer(ctx *gin.Context) {
+ //处理入参
+ params := new(struct {
+ PodName string `form:"pod_name"`
+ Namespace string `form:"namespace"`
+ })
+ //GET请求,绑定参数方法改为ctx.Bind方法
+ if err := ctx.Bind(params); err != nil {
+ logger.Error("Bind请求参数失败" + err.Error())
+ ctx.JSON(http.StatusInternalServerError, gin.H{
+ "msg": err.Error(),
+ "data": nil,
+ })
+ return
+ }
+
+ data, err := service.Pod.GetPodContainer(params.PodName, params.Namespace)
+ if err != nil {
+ ctx.JSON(http.StatusInternalServerError, gin.H{
+ "msg": err.Error(),
+ "data": nil,
+ })
+ return
+ }
+ ctx.JSON(http.StatusOK, gin.H{
+ "msg": "获取Pod容器成功",
+ "data": data,
+ })
+}
+
+//获取Pod容器日志
+func (p *pod) GetPodLog(ctx *gin.Context) {
+ //处理入参
+ params := new(struct {
+ ContainerName string `form:"container_name"`
+ PodName string `form:"pod_name"`
+ Namespace string `form:"namespace"`
+ })
+ //GET请求,绑定参数方法改为ctx.Bind方法
+ if err := ctx.Bind(params); err != nil {
+ logger.Error("Bind请求参数失败" + err.Error())
+ ctx.JSON(http.StatusInternalServerError, gin.H{
+ "msg": err.Error(),
+ "data": nil,
+ })
+ return
+ }
+
+ data, err := service.Pod.GetPodLog(params.ContainerName, params.PodName, params.Namespace)
+ if err != nil {
+ ctx.JSON(http.StatusInternalServerError, gin.H{
+ "msg": err.Error(),
+ "data": nil,
+ })
+ return
+ }
+ ctx.JSON(http.StatusOK, gin.H{
+ "msg": "获取Pod容器日志成功",
+ "data": data,
+ })
+}
+
+//获取每个Namespace的Pod数量
+func (p *pod) GetPodNumPerNp(ctx *gin.Context) {
+ data, err := service.Pod.GetPodNumPerNp()
+ if err != nil {
+ ctx.JSON(http.StatusInternalServerError, gin.H{
+ "msg": err.Error(),
+ "data": nil,
+ })
+ return
+ }
+ ctx.JSON(http.StatusOK, gin.H{
+ "msg": "获取每个namespace的Pod数量成功",
+ "data": data,
+ })
+}
diff --git a/controller/router.go b/controller/router.go
new file mode 100644
index 0000000..125b704
--- /dev/null
+++ b/controller/router.go
@@ -0,0 +1,24 @@
+package controller
+
+import (
+ "github.com/gin-gonic/gin"
+)
+
+// Router 初始化router类型的对象,首字母大写,用于跨包调用(跨目录调用)
+var Router router
+
+//声明router结构体
+type router struct{}
+
+// InitApiRouter 初始化路由规则,创建api测试接口
+func (r *router) InitApiRouter(router *gin.Engine) {
+ router.
+ //pod操作
+ GET("/api/k8s/pods", Pod.GetPods).
+ GET("/api/k8s/pod/detail", Pod.GetPodDetail).
+ DELETE("/api/k8s/pod/del", Pod.DeletePod).
+ PUT("/api/k8s/pod/update", Pod.UpdatePod).
+ GET("/api/k8s/pod/container", Pod.GetPodContainer).
+ GET("/api/k8s/pod/log", Pod.GetPodLog).
+ GET("/api/k8s/pod/numnp", Pod.GetPodNumPerNp)
+}
diff --git a/db/init.go b/db/init.go
new file mode 100644
index 0000000..c4ffca3
--- /dev/null
+++ b/db/init.go
@@ -0,0 +1,52 @@
+package db
+
+import (
+ "dkube/config"
+ "fmt"
+ "github.com/jinzhu/gorm"
+ _ "github.com/jinzhu/gorm/dialects/mysql"
+ "github.com/wonderivan/logger"
+ "time"
+)
+
+var (
+ isInit bool
+ GORM *gorm.DB
+ err error
+)
+
+//db的初始化函数,与数据库建立连接
+func Init() {
+ //判断是否已经初始化了
+ if isInit {
+ return
+ }
+ //组装连接配置;loc是MySQL的时区设置
+ dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
+ config.DbUser,
+ config.DbPwd,
+ config.DbHost,
+ config.DbPort,
+ config.DbName)
+ GORM, err = gorm.Open(config.DbType, dsn)
+ if err != nil {
+ panic("数据库连接失败," + err.Error())
+ }
+
+ //打印sql语句
+ GORM.LogMode(config.LogMode)
+
+ //开启连接池
+ GORM.DB().SetMaxIdleConns(config.MaxIdleConns)
+ GORM.DB().SetMaxOpenConns(config.MaxOpenConns)
+ GORM.DB().SetConnMaxLifetime(time.Duration(config.MaxLifeTime))
+
+ isInit = true
+ logger.Info("数据库初始化成功!")
+}
+
+//关闭数据库连接
+func Close() error {
+ fmt.Println("测试db是否关闭")
+ return GORM.Close()
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..b5f02dd
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,64 @@
+module dkube
+
+go 1.18
+
+require (
+ github.com/gin-gonic/gin v1.8.1
+ github.com/jinzhu/gorm v1.9.16
+ github.com/wonderivan/logger v1.0.0
+ k8s.io/api v0.25.2
+ k8s.io/apimachinery v0.25.2
+ k8s.io/client-go v0.25.2
+)
+
+require (
+ github.com/PuerkitoBio/purell v1.1.1 // indirect
+ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
+ github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/emicklei/go-restful/v3 v3.8.0 // indirect
+ github.com/gin-contrib/sse v0.1.0 // indirect
+ github.com/go-logr/logr v1.2.3 // indirect
+ github.com/go-openapi/jsonpointer v0.19.5 // indirect
+ github.com/go-openapi/jsonreference v0.19.5 // indirect
+ github.com/go-openapi/swag v0.19.14 // indirect
+ github.com/go-playground/locales v0.14.0 // indirect
+ github.com/go-playground/universal-translator v0.18.0 // indirect
+ github.com/go-playground/validator/v10 v10.10.0 // indirect
+ github.com/go-sql-driver/mysql v1.5.0 // indirect
+ github.com/goccy/go-json v0.9.7 // indirect
+ github.com/gogo/protobuf v1.3.2 // indirect
+ github.com/golang/protobuf v1.5.2 // indirect
+ github.com/google/gnostic v0.5.7-v3refs // indirect
+ github.com/google/gofuzz v1.1.0 // indirect
+ github.com/imdario/mergo v0.3.6 // indirect
+ github.com/jinzhu/inflection v1.0.0 // indirect
+ github.com/josharian/intern v1.0.0 // indirect
+ github.com/json-iterator/go v1.1.12 // indirect
+ github.com/leodido/go-urn v1.2.1 // indirect
+ github.com/mailru/easyjson v0.7.6 // indirect
+ github.com/mattn/go-isatty v0.0.14 // indirect
+ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+ github.com/modern-go/reflect2 v1.0.2 // indirect
+ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
+ github.com/pelletier/go-toml/v2 v2.0.1 // indirect
+ github.com/spf13/pflag v1.0.5 // indirect
+ github.com/ugorji/go/codec v1.2.7 // indirect
+ golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect
+ golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
+ golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
+ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
+ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
+ golang.org/x/text v0.3.7 // indirect
+ golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
+ google.golang.org/appengine v1.6.7 // indirect
+ google.golang.org/protobuf v1.28.0 // indirect
+ gopkg.in/inf.v0 v0.9.1 // indirect
+ gopkg.in/yaml.v2 v2.4.0 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
+ k8s.io/klog/v2 v2.70.1 // indirect
+ k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect
+ k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect
+ sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
+ sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
+ sigs.k8s.io/yaml v1.2.0 // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..061e290
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,547 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
+cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
+cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
+cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
+cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
+cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
+cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
+cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
+cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
+cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
+cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
+cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
+cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
+cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
+cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
+cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
+cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
+cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
+cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
+cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
+cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
+cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
+cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
+cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
+cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
+cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
+cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
+cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
+cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
+cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
+dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
+github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
+github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
+github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
+github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
+github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM=
+github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
+github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
+github.com/emicklei/go-restful/v3 v3.8.0 h1:eCZ8ulSerjdAiaNpF7GxXIE7ZCMo1moN1qX+S609eVw=
+github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
+github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
+github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
+github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
+github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
+github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8=
+github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
+github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
+github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
+github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
+github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM=
+github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
+github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng=
+github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
+github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
+github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
+github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
+github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
+github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
+github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
+github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0=
+github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
+github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
+github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM=
+github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
+github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
+github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
+github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54=
+github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
+github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
+github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
+github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
+github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
+github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
+github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
+github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
+github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
+github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o=
+github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs=
+github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
+github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
+github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M=
+github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
+github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
+github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
+github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
+github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
+github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
+github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
+github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
+github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
+github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
+github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
+github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
+github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA=
+github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
+github.com/onsi/ginkgo/v2 v2.1.6 h1:Fx2POJZfKRQcM1pH49qSZiYeu319wji004qX+GDovrU=
+github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q=
+github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU=
+github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
+github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
+github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
+github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
+github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
+github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
+github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
+github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
+github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
+github.com/wonderivan/logger v1.0.0 h1:Z6Nz+3SNcizolx3ARH11axdD4DXjFpb2J+ziGUVlv/U=
+github.com/wonderivan/logger v1.0.0/go.mod h1:NObMfQ3WOLKfYEZuGeZQfuQfSPE5+QNgRddVMzsAT/k=
+github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
+go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
+go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38=
+golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
+golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
+golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
+golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
+golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
+golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
+golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
+golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
+golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
+golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
+golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg=
+golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44=
+golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
+golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
+golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
+golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
+google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
+google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
+google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
+google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
+google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
+google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
+google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
+google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
+google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
+google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
+google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
+google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
+google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
+google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
+google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
+google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
+gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
+honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+k8s.io/api v0.25.2 h1:v6G8RyFcwf0HR5jQGIAYlvtRNrxMJQG1xJzaSeVnIS8=
+k8s.io/api v0.25.2/go.mod h1:qP1Rn4sCVFwx/xIhe+we2cwBLTXNcheRyYXwajonhy0=
+k8s.io/apimachinery v0.25.2 h1:WbxfAjCx+AeN8Ilp9joWnyJ6xu9OMeS/fsfjK/5zaQs=
+k8s.io/apimachinery v0.25.2/go.mod h1:hqqA1X0bsgsxI6dXsJ4HnNTBOmJNxyPp8dw3u2fSHwA=
+k8s.io/client-go v0.25.2 h1:SUPp9p5CwM0yXGQrwYurw9LWz+YtMwhWd0GqOsSiefo=
+k8s.io/client-go v0.25.2/go.mod h1:i7cNU7N+yGQmJkewcRD2+Vuj4iz7b30kI8OcL3horQ4=
+k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
+k8s.io/klog/v2 v2.70.1 h1:7aaoSdahviPmR+XkS7FyxlkkXs6tHISSG03RxleQAVQ=
+k8s.io/klog/v2 v2.70.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
+k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 h1:MQ8BAZPZlWk3S9K4a9NCkIFQtZShWqoha7snGixVgEA=
+k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU=
+k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed h1:jAne/RjBTyawwAy0utX5eqigAwz/lQhTmy+Hr/Cpue4=
+k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
+rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
+rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
+rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k=
+sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
+sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE=
+sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E=
+sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
+sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..dec2beb
--- /dev/null
+++ b/main.go
@@ -0,0 +1,19 @@
+package main
+
+import (
+ "dkube/config"
+ "dkube/controller"
+ "dkube/service"
+ "github.com/gin-gonic/gin"
+)
+
+func main() {
+ //初始化k8s client
+ service.K8s.Init() //可以使用service.K8s.ClientSet 跨包调用
+ //初始化Gin对象
+ r := gin.Default()
+ //跨包调用router的方法,初始化路由规则
+ controller.Router.InitApiRouter(r)
+ //启动gin程序
+ r.Run(config.ListenAddr)
+}
diff --git a/service/dataselector.go b/service/dataselector.go
new file mode 100644
index 0000000..d603907
--- /dev/null
+++ b/service/dataselector.go
@@ -0,0 +1,161 @@
+package service
+
+import (
+ appsv1 "k8s.io/api/apps/v1" //引入K8s的APPSv1的包.定义别名为appsv1
+ corev1 "k8s.io/api/core/v1" //引入K8s的corev1的包.定义别名为corev1
+ nwv1 "k8s.io/api/networking/v1"
+ "sort"
+ "strings"
+ "time"
+)
+
+//定义结构体:dataSelector,用于封装排序、过滤、分页的数据类型
+type dataSelector struct { //包内调用,首字母小写
+ GenericDataList []DataCell
+ DataSelect *DataSelectQuery
+}
+
+// DataCell DataCell接口用于各种资源List类型的转换,转换后可以使用dataSelector的排序、过滤、分页方法
+type DataCell interface {
+ GetCreation() time.Time
+ GetName() string
+}
+
+// DataSelectQuery 定义过滤和分页结构体,过滤使用Name过滤,分页使用Limit和page
+//Limit 是单页的数据条数
+//Page是第几页
+type DataSelectQuery struct {
+ Filter *FilterQuery
+ Paginate *PaginateQuery
+}
+
+type FilterQuery struct {
+ Name string
+}
+
+type PaginateQuery struct {
+ Limit int
+ Page int
+}
+
+// Len 实现自定义结构体的排序,需要重写Len、Swap、Less方法
+//Len方法用于获取数组的长度
+func (d *dataSelector) Len() int {
+ return len(d.GenericDataList)
+}
+
+// Swap 方法用于数据比较大小之后的位置变更
+func (d *dataSelector) Swap(i, j int) {
+ //临时变量对调 i,j ==> j.i
+ d.GenericDataList[i], d.GenericDataList[j] = d.GenericDataList[j], d.GenericDataList[i]
+}
+
+// Less 方法用于比较大小
+func (d *dataSelector) Less(i, j int) bool {
+ a := d.GenericDataList[i].GetCreation()
+ b := d.GenericDataList[j].GetCreation()
+ //比较B的时间是否在A之前,触发位置调换
+ return b.Before(a)
+}
+
+//重写以上三个方法,使用sort.Sort进行排序
+func (d *dataSelector) Sort() *dataSelector {
+ sort.Sort(d)
+ return d
+}
+
+// Filter 方法用于过滤数据,比较数据的Name属性,如果包含则返回
+func (d *dataSelector) Filter() *dataSelector {
+ //判断入参是否为空,如果为空则返回所有数据
+ if d.DataSelect.Filter.Name == "" {
+ return d
+ }
+ //如果不为空,则按照入参Name进行过滤
+ //声明一个新的数组。若Name包含,则把数据放进数组,返回出去
+ filtered := []DataCell{}
+ for _, value := range d.GenericDataList {
+ //定义是否匹配的标签变量,然后默认是匹配
+ matches := true
+ objName := value.GetName()
+ if strings.Contains(objName, d.DataSelect.Filter.Name) {
+ matches = false
+ continue //跳过当前循环,执行下一次循环
+ }
+ if matches {
+ filtered = append(filtered, value)
+ }
+ }
+
+ d.GenericDataList = filtered
+ return d
+}
+
+// Paginate 方法用于数组的分页,根据Limit和Page的传参,取一定范围内的数据,返回
+func (d *dataSelector) Paginate() *dataSelector {
+ //根据Limit和Page的入参,定义快捷变量
+ limit := d.DataSelect.Paginate.Limit
+ page := d.DataSelect.Paginate.Page
+
+ //检验参数的合法性
+ if limit <= 0 || page <= 0 {
+ return d
+ }
+
+ //定义取数范围需要的starIndex和endIndex
+ startIndex := limit * (page - 1)
+ endIndex := limit*page - 1
+
+ //出来endIndex
+ if endIndex > len(d.GenericDataList) {
+ endIndex = len(d.GenericDataList)
+ }
+
+ d.GenericDataList = d.GenericDataList[startIndex:endIndex]
+
+ return d
+}
+
+// 定义podCell类型,重写GetCreateion和GetName方法后,可进行数据转换
+//corev1.Pod --> podCell --> DataCell
+//appsv1.Deployment --> deployCell --> DataCell
+type podCell corev1.Pod
+
+// GetCreation 重写DataCell接口的两个方法
+func (p podCell) GetCreation() time.Time {
+ return p.CreationTimestamp.Time
+}
+
+func (p podCell) GetName() string {
+ return p.Name
+}
+
+type deploymentCell appsv1.Deployment
+
+func (d deploymentCell) GetCreation() time.Time {
+ return d.CreationTimestamp.Time
+}
+
+func (d deploymentCell) GetName() string {
+ return d.Name
+}
+
+type serviceCell corev1.Service
+
+func (s serviceCell) GetCreation() time.Time {
+ return s.CreationTimestamp.Time
+}
+
+func (s serviceCell) GetName() string {
+ return s.Name
+}
+
+//追加ingress DataCell接口相关代码
+type ingressCell nwv1.Ingress
+
+func (i ingressCell) GetCreation() time.Time {
+ return i.CreationTimestamp.Time
+}
+
+func (i ingressCell) GetName() string {
+ return i.Name
+}
diff --git a/service/deployment.go b/service/deployment.go
new file mode 100644
index 0000000..acc1992
--- /dev/null
+++ b/service/deployment.go
@@ -0,0 +1,307 @@
+package service
+
+import (
+ "context"
+ "encoding/json"
+ "errors"
+ "github.com/wonderivan/logger"
+ appsv1 "k8s.io/api/apps/v1"
+ corev1 "k8s.io/api/core/v1"
+ "k8s.io/apimachinery/pkg/api/resource"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/util/intstr"
+ "strconv"
+ "time"
+)
+
+var Deployment deployment
+
+type deployment struct{}
+
+//定义列表的返回内容,Items是deployment元素列表,Total为deployment元素数量
+type DeploymentsResp struct {
+ Items []appsv1.Deployment `json:"items"`
+ Total int `json:"total"`
+}
+
+//定义DeployCreate结构体,用于创建deployment需要的参数属性的定义
+type DeployCreate struct {
+ Name string `json:"name"`
+ Namespace string `json:"namespace"`
+ Replicas int32 `json:"replicas"`
+ Image string `json:"image"`
+ Label map[string]string `json:"label"`
+ Cpu string `json:"cpu"`
+ Memory string `json:"memory"`
+ ContainerPort int32 `json:"container_port"`
+ HealthCheck bool `json:"health_check"`
+ HealthPath string `json:"health_path"`
+}
+
+//定义DeploysNp类型,用于返回namespace中deployment的数量
+type DeploysNp struct {
+ Namespace string `json:"namespace"`
+ DeployNum int `json:"deployment_num"`
+}
+
+//获取deployment列表,支持过滤、排序、分页
+func (d *deployment) GetDeployments(filterName, namespace string, limit, page int) (deploymentsResp *DeploymentsResp, err error) {
+ //获取deploymentList类型的deployment列表
+ deploymentList, err := K8s.ClientSet.AppsV1().Deployments(namespace).List(context.TODO(), metav1.ListOptions{})
+ if err != nil {
+ logger.Error(errors.New("获取Deployment列表失败, " + err.Error()))
+ return nil, errors.New("获取Deployment列表失败, " + err.Error())
+ }
+ //将deploymentList中的deployment列表(Items),放进dataselector对象中,进行排序
+ selectableData := &dataSelector{
+ GenericDataList: d.toCells(deploymentList.Items),
+ DataSelect: &DataSelectQuery{
+ Filter: &FilterQuery{Name: filterName},
+ Paginate: &PaginateQuery{
+ Limit: limit,
+ Page: page,
+ },
+ },
+ }
+
+ filtered := selectableData.Filter()
+ total := len(filtered.GenericDataList)
+ data := filtered.Sort().Paginate()
+
+ //将[]DataCell类型的deployment列表转为appsv1.deployment列表
+ deployments := d.fromCells(data.GenericDataList)
+
+ return &DeploymentsResp{
+ Items: deployments,
+ Total: total,
+ }, nil
+}
+
+//获取deployment详情
+func (d *deployment) GetDeploymentDetail(deploymentName, namespace string) (deployment *appsv1.Deployment, err error) {
+ deployment, err = K8s.ClientSet.AppsV1().Deployments(namespace).Get(context.TODO(), deploymentName, metav1.GetOptions{})
+ if err != nil {
+ logger.Error(errors.New("获取Deployment详情失败, " + err.Error()))
+ return nil, errors.New("获取Deployment详情失败, " + err.Error())
+ }
+
+ return deployment, nil
+}
+
+//设置deployment副本数
+func (d *deployment) ScaleDeployment(deploymentName, namespace string, scaleNum int) (replica int32, err error) {
+ //获取autoscalingv1.Scale类型的对象,能点出当前的副本数
+ scale, err := K8s.ClientSet.AppsV1().Deployments(namespace).GetScale(context.TODO(), deploymentName, metav1.GetOptions{})
+ if err != nil {
+ logger.Error(errors.New("获取Deployment副本数信息失败, " + err.Error()))
+ return 0, errors.New("获取Deployment副本数信息失败, " + err.Error())
+ }
+ //修改副本数
+ scale.Spec.Replicas = int32(scaleNum)
+ //更新副本数,传入scale对象
+ newScale, err := K8s.ClientSet.AppsV1().Deployments(namespace).UpdateScale(context.TODO(), deploymentName, scale, metav1.UpdateOptions{})
+ if err != nil {
+ logger.Error(errors.New("更新Deployment副本数信息失败, " + err.Error()))
+ return 0, errors.New("更新Deployment副本数信息失败, " + err.Error())
+ }
+
+ return newScale.Spec.Replicas, nil
+}
+
+//创建deployment,接收DeployCreate对象
+func (d *deployment) CreateDeployment(data *DeployCreate) (err error) {
+ //初始化appsv1.Deployment类型的对象,并将入参的data数据放进去
+ deployment := &appsv1.Deployment{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: data.Name,
+ Namespace: data.Namespace,
+ Labels: data.Label,
+ },
+ Spec: appsv1.DeploymentSpec{
+ Replicas: &data.Replicas,
+ Selector: &metav1.LabelSelector{
+ MatchLabels: data.Label,
+ },
+ Template: corev1.PodTemplateSpec{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: data.Name,
+ Labels: data.Label,
+ },
+ Spec: corev1.PodSpec{
+ Containers: []corev1.Container{
+ {
+ Name: data.Name,
+ Image: data.Image,
+ Ports: []corev1.ContainerPort{
+ {
+ Name: "http",
+ Protocol: corev1.ProtocolTCP,
+ ContainerPort: data.ContainerPort,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ Status: appsv1.DeploymentStatus{},
+ }
+ //判断健康检查功能是否打开,若打开,则增加健康检查功能
+ if data.HealthCheck {
+ deployment.Spec.Template.Spec.Containers[0].ReadinessProbe = &corev1.Probe{
+ ProbeHandler: corev1.ProbeHandler{
+ HTTPGet: &corev1.HTTPGetAction{
+ Path: data.HealthPath,
+ Port: intstr.IntOrString{
+ Type: 0,
+ IntVal: data.ContainerPort,
+ },
+ },
+ },
+ InitialDelaySeconds: 5,
+ TimeoutSeconds: 5,
+ PeriodSeconds: 5,
+ }
+ deployment.Spec.Template.Spec.Containers[0].LivenessProbe = &corev1.Probe{
+ ProbeHandler: corev1.ProbeHandler{
+ HTTPGet: &corev1.HTTPGetAction{
+ Path: data.HealthPath,
+ Port: intstr.IntOrString{
+ Type: 0,
+ IntVal: data.ContainerPort,
+ },
+ },
+ },
+ InitialDelaySeconds: 15,
+ TimeoutSeconds: 5,
+ PeriodSeconds: 5,
+ }
+ }
+ //定义容器的limit和request资源
+ deployment.Spec.Template.Spec.Containers[0].Resources.Limits = map[corev1.ResourceName]resource.Quantity{
+ corev1.ResourceCPU: resource.MustParse(data.Cpu),
+ corev1.ResourceMemory: resource.MustParse(data.Memory),
+ }
+ deployment.Spec.Template.Spec.Containers[0].Resources.Requests = map[corev1.ResourceName]resource.Quantity{
+ corev1.ResourceCPU: resource.MustParse(data.Cpu),
+ corev1.ResourceMemory: resource.MustParse(data.Memory),
+ }
+
+ //调用sdk创建deployment
+ _, err = K8s.ClientSet.AppsV1().Deployments(data.Namespace).
+ Create(context.TODO(), deployment, metav1.CreateOptions{})
+ if err != nil {
+ logger.Error(errors.New("创建Deployment失败, " + err.Error()))
+ return errors.New("创建Deployment失败, " + err.Error())
+ }
+
+ return nil
+}
+
+//删除deployment
+func (d *deployment) DeleteDeployment(deploymentName, namespace string) (err error) {
+ err = K8s.ClientSet.AppsV1().Deployments(namespace).Delete(context.TODO(), deploymentName, metav1.DeleteOptions{})
+ if err != nil {
+ logger.Error(errors.New("删除Deployment失败, " + err.Error()))
+ return errors.New("删除Deployment失败, " + err.Error())
+ }
+
+ return nil
+}
+
+//重启deployment
+func (d *deployment) RestartDeployment(deploymentName, namespace string) (err error) {
+ //此功能等同于一下kubectl命令
+ //kubectl deployment ${service} -p \
+ //'{"spec":{"template":{"spec":{"containers":[{"name":"'"${service}"'","env":[{"name":"RESTART_","value":"'$(date +%s)'"}]}]}}}}'
+
+ //使用patchData Map组装数据
+ patchData := map[string]interface{}{
+ "spec": map[string]interface{}{
+ "template": map[string]interface{}{
+ "spec": map[string]interface{}{
+ "containers": []map[string]interface{}{
+ {"name": deploymentName,
+ "env": []map[string]string{{
+ "name": "RESTART_",
+ "value": strconv.FormatInt(time.Now().Unix(), 10),
+ }},
+ },
+ },
+ },
+ },
+ },
+ }
+ //序列化为字节,因为patch方法只接收字节类型参数
+ patchByte, err := json.Marshal(patchData)
+ if err != nil {
+ logger.Error(errors.New("json序列化失败, " + err.Error()))
+ return errors.New("json序列化失败, " + err.Error())
+ }
+ //调用patch方法更新deployment
+ _, err = K8s.ClientSet.AppsV1().Deployments(namespace).Patch(context.TODO(), deploymentName, "application/strategic-merge-patch+json", patchByte, metav1.PatchOptions{})
+ if err != nil {
+ logger.Error(errors.New("重启Deployment失败, " + err.Error()))
+ return errors.New("重启Deployment失败, " + err.Error())
+ }
+
+ return nil
+}
+
+//更新deployment
+func (d *deployment) UpdateDeployment(namespace, content string) (err error) {
+ var deploy = &appsv1.Deployment{}
+
+ err = json.Unmarshal([]byte(content), deploy)
+ if err != nil {
+ logger.Error(errors.New("反序列化失败, " + err.Error()))
+ return errors.New("反序列化失败, " + err.Error())
+ }
+
+ _, err = K8s.ClientSet.AppsV1().Deployments(namespace).Update(context.TODO(), deploy, metav1.UpdateOptions{})
+ if err != nil {
+ logger.Error(errors.New("更新Deployment失败, " + err.Error()))
+ return errors.New("更新Deployment失败, " + err.Error())
+ }
+ return nil
+}
+
+//获取每个namespace的deployment数量
+func (d *deployment) GetDeployNumPerNp() (deploysNps []*DeploysNp, err error) {
+ namespaceList, err := K8s.ClientSet.CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{})
+ if err != nil {
+ return nil, err
+ }
+ for _, namespace := range namespaceList.Items {
+ deploymentList, err := K8s.ClientSet.AppsV1().Deployments(namespace.Name).List(context.TODO(), metav1.ListOptions{})
+ if err != nil {
+ return nil, err
+ }
+
+ deploysNp := &DeploysNp{
+ Namespace: namespace.Name,
+ DeployNum: len(deploymentList.Items),
+ }
+
+ deploysNps = append(deploysNps, deploysNp)
+ }
+ return deploysNps, nil
+}
+
+//类型转换
+func (d *deployment) toCells(deployments []appsv1.Deployment) []DataCell {
+ cells := make([]DataCell, len(deployments))
+ for i := range deployments {
+ cells[i] = deploymentCell(deployments[i])
+ }
+ return cells
+}
+
+func (d *deployment) fromCells(cells []DataCell) []appsv1.Deployment {
+ deployments := make([]appsv1.Deployment, len(cells))
+ for i := range cells {
+ //cells[i].(podCell)是将DataCell类型转成podCell
+ deployments[i] = appsv1.Deployment(cells[i].(deploymentCell))
+ }
+ return deployments
+}
diff --git a/service/ingress.go b/service/ingress.go
new file mode 100644
index 0000000..aca9a2e
--- /dev/null
+++ b/service/ingress.go
@@ -0,0 +1,183 @@
+package service
+
+import (
+ "context"
+ "encoding/json"
+ "errors"
+ "github.com/wonderivan/logger"
+ nwv1 "k8s.io/api/networking/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+var Ingress ingress
+
+type ingress struct{}
+
+type IngressesResp struct {
+ Items []nwv1.Ingress `json:"items"`
+ Total int `json:"total"`
+}
+
+//定义ServiceCreate结构体,用于创建service需要的参数属性的定义
+type IngressCreate struct {
+ Name string `json:"name"`
+ Namespace string `json:"namespace"`
+ Label map[string]string `json:"label"`
+ Hosts map[string][]*HttpPath `json:"hosts"`
+}
+
+//定义ingress的path结构体
+type HttpPath struct {
+ Path string `json:"path"`
+ PathType nwv1.PathType `json:"path_type"`
+ ServiceName string `json:"service_name"`
+ ServicePort int32 `json:"service_port"`
+}
+
+//获取ingress列表,支持过滤、排序、分页
+func (i *ingress) GetIngresses(filterName, namespace string, limit, page int) (ingressesResp *IngressesResp, err error) {
+ //获取ingressList类型的ingress列表
+ ingressList, err := K8s.ClientSet.NetworkingV1().Ingresses(namespace).List(context.TODO(), metav1.ListOptions{})
+ if err != nil {
+ logger.Error(errors.New("获取Ingress列表失败, " + err.Error()))
+ return nil, errors.New("获取Ingress列表失败, " + err.Error())
+ }
+ //将ingressList中的ingress列表(Items),放进dataselector对象中,进行排序
+ selectableData := &dataSelector{
+ GenericDataList: i.toCells(ingressList.Items),
+ DataSelect: &DataSelectQuery{
+ Filter: &FilterQuery{Name: filterName},
+ Paginate: &PaginateQuery{
+ Limit: limit,
+ Page: page,
+ },
+ },
+ }
+
+ filtered := selectableData.Filter()
+ total := len(filtered.GenericDataList)
+ data := filtered.Sort().Paginate()
+
+ //将[]DataCell类型的ingress列表转为v1.ingress列表
+ ingresss := i.fromCells(data.GenericDataList)
+
+ return &IngressesResp{
+ Items: ingresss,
+ Total: total,
+ }, nil
+}
+
+//获取ingress详情
+func (i *ingress) GetIngresstDetail(ingressName, namespace string) (ingress *nwv1.Ingress, err error) {
+ ingress, err = K8s.ClientSet.NetworkingV1().Ingresses(namespace).Get(context.TODO(), ingressName, metav1.GetOptions{})
+ if err != nil {
+ logger.Error(errors.New("获取Ingress详情失败, " + err.Error()))
+ return nil, errors.New("获取Ingress详情失败, " + err.Error())
+ }
+
+ return ingress, nil
+}
+
+//创建ingress
+func (i *ingress) CreateIngress(data *IngressCreate) (err error) {
+ //声明nwv1.IngressRule和nwv1.HTTPIngressPath变量,后面组装数据于鏊用到
+ var ingressRules []nwv1.IngressRule
+ var httpIngressPATHs []nwv1.HTTPIngressPath
+ //将data中的数据组装成nwv1.Ingress对象
+ ingress := &nwv1.Ingress{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: data.Name,
+ Namespace: data.Namespace,
+ Labels: data.Label,
+ },
+ Status: nwv1.IngressStatus{},
+ }
+ //第一层for循环是将host组装成nwv1.IngressRule类型的对象
+ // 一个host对应一个ingressrule,每个ingressrule中包含一个host和多个path
+ for key, value := range data.Hosts {
+ ir := nwv1.IngressRule{
+ Host: key,
+ //这里现将nwv1.HTTPIngressRuleValue类型中的Paths置为空,后面组装好数据再赋值
+ IngressRuleValue: nwv1.IngressRuleValue{
+ HTTP: &nwv1.HTTPIngressRuleValue{Paths: nil},
+ },
+ }
+ //第二层for循环是将path组装成nwv1.HTTPIngressPath类型的对象
+ for _, httpPath := range value {
+ hip := nwv1.HTTPIngressPath{
+ Path: httpPath.Path,
+ PathType: &httpPath.PathType,
+ Backend: nwv1.IngressBackend{
+ Service: &nwv1.IngressServiceBackend{
+ Name: httpPath.ServiceName,
+ Port: nwv1.ServiceBackendPort{
+ Number: httpPath.ServicePort,
+ },
+ },
+ },
+ }
+ //将每个hip对象组装成数组
+ httpIngressPATHs = append(httpIngressPATHs, hip)
+ }
+ //给Paths赋值,前面置为空了
+ ir.IngressRuleValue.HTTP.Paths = httpIngressPATHs
+ //将每个ir对象组装成数组,这个ir对象就是IngressRule,每个元素是一个host和多个path
+ ingressRules = append(ingressRules, ir)
+ }
+ //将ingressRules对象加入到ingress的规则中
+ ingress.Spec.Rules = ingressRules
+ //创建ingress
+ _, err = K8s.ClientSet.NetworkingV1().Ingresses(data.Namespace).Create(context.TODO(), ingress, metav1.CreateOptions{})
+ if err != nil {
+ logger.Error(errors.New("创建Ingress失败, " + err.Error()))
+ return errors.New("创建Ingress失败, " + err.Error())
+ }
+
+ return nil
+}
+
+//删除ingress
+func (i *ingress) DeleteIngress(ingressName, namespace string) (err error) {
+ err = K8s.ClientSet.NetworkingV1().Ingresses(namespace).Delete(context.TODO(), ingressName, metav1.DeleteOptions{})
+ if err != nil {
+ logger.Error(errors.New("删除Ingress失败, " + err.Error()))
+ return errors.New("删除Ingress失败, " + err.Error())
+ }
+
+ return nil
+}
+
+//更新ingress
+func (i *ingress) UpdateIngress(namespace, content string) (err error) {
+ var ingress = &nwv1.Ingress{}
+
+ err = json.Unmarshal([]byte(content), ingress)
+ if err != nil {
+ logger.Error(errors.New("反序列化失败, " + err.Error()))
+ return errors.New("反序列化失败, " + err.Error())
+ }
+
+ _, err = K8s.ClientSet.NetworkingV1().Ingresses(namespace).Update(context.TODO(), ingress, metav1.UpdateOptions{})
+ if err != nil {
+ logger.Error(errors.New("更新ingress失败, " + err.Error()))
+ return errors.New("更新ingress失败, " + err.Error())
+ }
+ return nil
+}
+
+func (i *ingress) toCells(std []nwv1.Ingress) []DataCell {
+ cells := make([]DataCell, len(std))
+ for i := range std {
+ cells[i] = ingressCell(std[i])
+ }
+ return cells
+}
+
+func (i *ingress) fromCells(cells []DataCell) []nwv1.Ingress {
+ ingresss := make([]nwv1.Ingress, len(cells))
+ for i := range cells {
+ ingresss[i] = nwv1.Ingress(cells[i].(ingressCell))
+ }
+
+ return ingresss
+}
diff --git a/service/init.go b/service/init.go
new file mode 100644
index 0000000..c5c0aac
--- /dev/null
+++ b/service/init.go
@@ -0,0 +1,32 @@
+package service
+
+import (
+ "dkube/config"
+ "github.com/wonderivan/logger"
+ "k8s.io/client-go/kubernetes"
+ "k8s.io/client-go/tools/clientcmd"
+)
+
+//用于初始化k8s clientset
+var K8s k8s
+
+type k8s struct {
+ ClientSet *kubernetes.Clientset
+}
+
+//初始化方法
+func (k *k8s) Init() {
+ conf, err := clientcmd.BuildConfigFromFlags("", config.Kubeconfig)
+ if err != nil {
+ panic("获取k8s clinet配置失败," + err.Error())
+ }
+
+ clientset, err := kubernetes.NewForConfig(conf)
+ if err != nil {
+ panic("创建k8s clinet失败," + err.Error())
+ } else {
+ logger.Info("k8s client 初始化成功")
+ }
+
+ k.ClientSet = clientset
+}
diff --git a/service/pod.go b/service/pod.go
new file mode 100644
index 0000000..3567115
--- /dev/null
+++ b/service/pod.go
@@ -0,0 +1,190 @@
+package service
+
+import (
+ "bytes"
+ "context"
+ "dkube/config"
+ "encoding/json"
+ "errors"
+ "github.com/wonderivan/logger"
+ "io"
+ corev1 "k8s.io/api/core/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+var Pod pod
+
+type pod struct{}
+
+// PodsResp 定义列表的返回内容,Items是Pod的元素列表,Total是元素数量
+type PodsResp struct {
+ Total int `json:"total"`
+ Items []corev1.Pod `json:"items"`
+}
+
+//声明类型
+type PodsNp struct {
+ Namespace string
+ PodNum int
+}
+
+// GetPods 获取Pod列表,支持过滤、排序、分页
+//context.TODD()用于声明一个空的context上下文,用于List方法内设置这个请求的超时(源码),这里是常用用法
+//metav1.ListOptions{}用于过滤List数据,如使用label,field等
+func (p *pod) GetPods(filterName, namespace string, limit, page int) (podsResp *PodsResp, err error) {
+ podList, err := K8s.ClientSet.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{})
+ if err != nil {
+ //打印日志,排错
+ logger.Info("获取Pod列表失败," + err.Error())
+ //返回给上一层,最终返回给前端,前端打印出该error
+ return nil, errors.New("获取Pod列表失败," + err.Error())
+ }
+
+ //实例化dataSelector结构体,组装数据
+ selectableData := &dataSelector{
+ GenericDataList: p.toCells(podList.Items),
+ DataSelect: &DataSelectQuery{
+ Filter: &FilterQuery{Name: filterName},
+ Paginate: &PaginateQuery{
+ Limit: limit,
+ Page: page,
+ },
+ },
+ }
+
+ //先过滤
+ filtered := selectableData.Filter()
+ total := len(filtered.GenericDataList)
+ //排序和分页
+ data := filtered.Sort().Paginate()
+
+ //将DataCell类型转换成Pod
+ pods := p.fromCells(data.GenericDataList)
+
+ return &PodsResp{
+ Total: total,
+ Items: pods,
+ }, nil
+
+}
+
+//获取Pod详情
+func (p *pod) GetPodDetail(podName, namespace string) (pod *corev1.Pod, err error) {
+ pod, err = K8s.ClientSet.CoreV1().Pods(namespace).Get(context.TODO(), podName, metav1.GetOptions{})
+ if err != nil {
+ logger.Error("获取Pod详情失败," + err.Error())
+ return nil, errors.New("获取Pod详情失败," + err.Error())
+ }
+ return pod, nil
+}
+
+//删除Pod
+func (p *pod) DeletePod(podName, namespace string) (err error) {
+ err = K8s.ClientSet.CoreV1().Pods(namespace).Delete(context.TODO(), podName, metav1.DeleteOptions{})
+ if err != nil {
+ logger.Error("删除Pod失败," + err.Error())
+ return errors.New("删除Pod失败," + err.Error())
+ }
+ return nil
+}
+
+//更新Pod
+func (p *pod) UpdatePod(namespace, content string) (err error) {
+ pod := &corev1.Pod{}
+ //将json反序列化为pod类型
+ err = json.Unmarshal([]byte(content), pod)
+ if err != nil {
+ logger.Error("反序列化失败," + err.Error())
+ return errors.New("反序列化失败," + err.Error())
+ }
+ _, err = K8s.ClientSet.CoreV1().Pods(namespace).Update(context.TODO(), pod, metav1.UpdateOptions{})
+ if err != nil {
+ logger.Error("更新Pod失败," + err.Error())
+ return errors.New("更新Pod失败," + err.Error())
+ }
+ return nil
+}
+
+//获取Pod容器名列表
+func (p *pod) GetPodContainer(podName, namespace string) (containers []string, err error) {
+ //获取Pod详情
+ pod, err := p.GetPodDetail(podName, namespace)
+ if err != nil {
+ return nil, err
+ }
+ //从Pod对象中拿到容器名
+ for _, container := range pod.Spec.Containers {
+ containers = append(containers, container.Name)
+ }
+ return containers, nil
+}
+
+//获取Pod内容器日志
+func (p *pod) GetPodLog(containerName, podName, namespace string) (log string, err error) {
+ //设置日志的配置,容器名、获取内容的配置
+ lineLimit := int64(config.PodLogTailLine)
+ option := &corev1.PodLogOptions{
+ Container: containerName,
+ TailLines: &lineLimit,
+ }
+ //获取request实例
+ req := K8s.ClientSet.CoreV1().Pods(namespace).GetLogs(podName, option)
+ //发起request请求,返回一个io.ReadCloser类型(等同于response.body)
+ podLogs, err := req.Stream(context.TODO())
+ if err != nil {
+ logger.Error(errors.New("获取PodLogs失败," + err.Error()))
+ return "", errors.New("获取PodLogs失败," + err.Error())
+ }
+ defer podLogs.Close() //处理完关闭
+ //将response body写入到缓冲区,目的是为了转成string返回
+ buf := new(bytes.Buffer)
+ _, err = io.Copy(buf, podLogs)
+ if err != nil {
+ logger.Error(errors.New("获取PodLogs失败," + err.Error()))
+ return "", errors.New("获取PodLogs失败," + err.Error())
+ }
+
+ return buf.String(), nil
+}
+
+//获取每个namespace的pod数量
+func (p *pod) GetPodNumPerNp() (podsNps []*PodsNp, err error) {
+ //获取namespace列表
+ namespaceList, err := K8s.ClientSet.CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{})
+ if err != nil {
+ return nil, err
+ }
+ for _, namespace := range namespaceList.Items {
+ //获取pod列表
+ podList, err := K8s.ClientSet.CoreV1().Pods(namespace.Name).List(context.TODO(), metav1.ListOptions{})
+ if err != nil {
+ return nil, err
+ }
+ //组装数据
+ podsNp := &PodsNp{
+ Namespace: namespace.Name,
+ PodNum: len(podList.Items),
+ }
+ //添加到podsNps数组中
+ podsNps = append(podsNps, podsNp)
+ }
+ return podsNps, nil
+}
+
+//类型转换的方法 corev1.Pod -> DataCell, DataCell --> corev1.Pod
+func (p *pod) toCells(pods []corev1.Pod) []DataCell {
+ cells := make([]DataCell, len(pods))
+ for i := range pods {
+ cells[i] = podCell(pods[i])
+ }
+ return cells
+}
+
+func (p *pod) fromCells(cells []DataCell) []corev1.Pod {
+ pods := make([]corev1.Pod, len(cells))
+ for i := range cells {
+ //cells[i].(podCell)是将DataCell类型转成podCell类型
+ pods[i] = corev1.Pod(cells[i].(podCell))
+ }
+ return pods
+}
diff --git a/service/service.go b/service/service.go
new file mode 100644
index 0000000..4c35b0a
--- /dev/null
+++ b/service/service.go
@@ -0,0 +1,161 @@
+package service
+
+import (
+ "context"
+ "encoding/json"
+ "errors"
+ "github.com/wonderivan/logger"
+ corev1 "k8s.io/api/core/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/util/intstr"
+)
+
+var Servicev1 servicev1
+
+type servicev1 struct{}
+
+type ServicesResp struct {
+ Items []corev1.Service `json:"items"`
+ Total int `json:"total"`
+}
+
+type ServiceCreate struct {
+ Name string `json:"name"`
+ Namespace string `json:"namespace"`
+ Type string `json:"type"`
+ ContainerPort int32 `json:"container_port"`
+ Port int32 `json:"port"`
+ NodePort int32 `json:"node_port"`
+ Label map[string]string `json:"label"`
+}
+
+//获取service列表,支持过滤、排序、分页
+func (s *servicev1) GetServices(filterName, namespace string, limit, page int) (servicesResp *ServicesResp, err error) {
+ //获取serviceList类型的service列表
+ serviceList, err := K8s.ClientSet.CoreV1().Services(namespace).List(context.TODO(), metav1.ListOptions{})
+ if err != nil {
+ logger.Error(errors.New("获取Service列表失败, " + err.Error()))
+ return nil, errors.New("获取Service列表失败, " + err.Error())
+ }
+ //将serviceList中的service列表(Items),放进dataselector对象中,进行排序
+ selectableData := &dataSelector{
+ GenericDataList: s.toCells(serviceList.Items),
+ DataSelect: &DataSelectQuery{
+ Filter: &FilterQuery{Name: filterName},
+ Paginate: &PaginateQuery{
+ Limit: limit,
+ Page: page,
+ },
+ },
+ }
+
+ filtered := selectableData.Filter()
+ total := len(filtered.GenericDataList)
+ data := filtered.Sort().Paginate()
+
+ //将[]DataCell类型的service列表转为v1.service列表
+ services := s.fromCells(data.GenericDataList)
+
+ return &ServicesResp{
+ Items: services,
+ Total: total,
+ }, nil
+}
+
+//获取service详情
+func (s *servicev1) GetServicetDetail(serviceName, namespace string) (service *corev1.Service, err error) {
+ service, err = K8s.ClientSet.CoreV1().Services(namespace).Get(context.TODO(), serviceName, metav1.GetOptions{})
+ if err != nil {
+ logger.Error(errors.New("获取Service详情失败, " + err.Error()))
+ return nil, errors.New("获取Service详情失败, " + err.Error())
+ }
+
+ return service, nil
+}
+
+//创建service,,接收ServiceCreate对象
+func (s *servicev1) CreateService(data *ServiceCreate) (err error) {
+ //将data中的数据组装成corev1.Service对象
+ service := &corev1.Service{
+ //ObjectMeta中定义资源名、命名空间以及标签
+ ObjectMeta: metav1.ObjectMeta{
+ Name: data.Name,
+ Namespace: data.Namespace,
+ Labels: data.Label,
+ },
+ //Spec中定义类型,端口,选择器
+ Spec: corev1.ServiceSpec{
+ Type: corev1.ServiceType(data.Type),
+ Ports: []corev1.ServicePort{
+ {
+ Name: "http",
+ Port: data.Port,
+ Protocol: "TCP",
+ TargetPort: intstr.IntOrString{
+ Type: 0,
+ IntVal: data.ContainerPort,
+ },
+ },
+ },
+ Selector: data.Label,
+ },
+ }
+ //默认ClusterIP,这里是判断NodePort,添加配置
+ if data.NodePort != 0 && data.Type == "NodePort" {
+ service.Spec.Ports[0].NodePort = data.NodePort
+ }
+ //创建Service
+ _, err = K8s.ClientSet.CoreV1().Services(data.Namespace).Create(context.TODO(), service, metav1.CreateOptions{})
+ if err != nil {
+ logger.Error(errors.New("创建Service失败, " + err.Error()))
+ return errors.New("创建Service失败, " + err.Error())
+ }
+
+ return nil
+}
+
+//删除service
+func (s *servicev1) DeleteService(serviceName, namespace string) (err error) {
+ err = K8s.ClientSet.CoreV1().Services(namespace).Delete(context.TODO(), serviceName, metav1.DeleteOptions{})
+ if err != nil {
+ logger.Error(errors.New("删除Service失败, " + err.Error()))
+ return errors.New("删除Service失败, " + err.Error())
+ }
+
+ return nil
+}
+
+//更新service
+func (s *servicev1) UpdateService(namespace, content string) (err error) {
+ var service = &corev1.Service{}
+
+ err = json.Unmarshal([]byte(content), service)
+ if err != nil {
+ logger.Error(errors.New("反序列化失败, " + err.Error()))
+ return errors.New("反序列化失败, " + err.Error())
+ }
+
+ _, err = K8s.ClientSet.CoreV1().Services(namespace).Update(context.TODO(), service, metav1.UpdateOptions{})
+ if err != nil {
+ logger.Error(errors.New("更新service失败, " + err.Error()))
+ return errors.New("更新service失败, " + err.Error())
+ }
+ return nil
+}
+
+func (s *servicev1) toCells(std []corev1.Service) []DataCell {
+ cells := make([]DataCell, len(std))
+ for i := range std {
+ cells[i] = serviceCell(std[i])
+ }
+ return cells
+}
+
+func (s *servicev1) fromCells(cells []DataCell) []corev1.Service {
+ services := make([]corev1.Service, len(cells))
+ for i := range cells {
+ services[i] = corev1.Service(cells[i].(serviceCell))
+ }
+
+ return services
+}