├── userdatabase ├── user.json └── userdatabase.go ├── conf └── app.ini ├── docker ├── setting.go ├── dockerlib.go ├── pramastruct.go └── docker.go ├── app └── response.go ├── jwt └── jwt.go ├── main.go ├── README.md ├── routers └── router.go ├── .github └── workflows │ └── go.yml ├── setting └── setting.go ├── LICENSE ├── go.mod └── go.sum /userdatabase/user.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /conf/app.ini: -------------------------------------------------------------------------------- 1 | #debug or release 2 | RUN_MODE = debug 3 | 4 | [app] 5 | APIKEY = 123456 6 | 7 | [server] 8 | HTTP_PORT = 8000 9 | READ_TIMEOUT = 60 10 | WRITE_TIMEOUT = 60 11 | -------------------------------------------------------------------------------- /docker/setting.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | import ( 4 | "context" 5 | "github.com/docker/docker/client" 6 | ) 7 | 8 | var cli *client.Client 9 | var clierr error 10 | var ctx context.Context 11 | 12 | func Setup() { 13 | ctx = context.Background() 14 | cli, clierr = client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) //cli客户端对象 15 | if clierr != nil { 16 | panic(clierr) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/response.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "net/http" 6 | ) 7 | 8 | type Gin struct { 9 | C *gin.Context 10 | } 11 | 12 | type Response struct { 13 | Code int `json:"code"` 14 | Msg string `json:"msg"` 15 | Data interface{} `json:"data"` 16 | } 17 | 18 | // Response setting gin.JSON 19 | func (g *Gin) Response(errCode int, msg string, data interface{}) { 20 | g.C.JSON(http.StatusOK, Response{ 21 | Code: errCode, 22 | Msg: msg, 23 | Data: data, 24 | }) 25 | return 26 | } 27 | -------------------------------------------------------------------------------- /jwt/jwt.go: -------------------------------------------------------------------------------- 1 | package jwt 2 | 3 | import ( 4 | "docker-api-service/setting" 5 | "github.com/gin-gonic/gin" 6 | "net/http" 7 | "strings" 8 | ) 9 | 10 | func JWT() gin.HandlerFunc { //返回JSON 11 | return func(ctx *gin.Context) { 12 | if strings.Trim(ctx.GetHeader("apikey"), "") != "" && strings.Trim(ctx.GetHeader("apikey"), "") == strings.Trim(setting.APIKEY, "") { 13 | ctx.Next() 14 | } else { 15 | ctx.JSON(http.StatusUnauthorized, gin.H{ 16 | "code": 0, 17 | "msg": "对接密匙错误", 18 | }) 19 | ctx.Abort() 20 | return 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /docker/dockerlib.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | import ( 4 | "github.com/docker/docker/api/types" 5 | "os/exec" 6 | ) 7 | 8 | func ContainerList() []types.Container { 9 | containers, clierr := cli.ContainerList(ctx, types.ContainerListOptions{All: true}) 10 | if clierr != nil { 11 | panic(clierr) 12 | } 13 | return containers 14 | } 15 | func iptablesexist(ip, porto, port, typee string) bool { 16 | cmd := exec.Command("bash", "-c", "cat /etc/sysconfig/iptables | grep "+ip+":"+port+" | grep \"dport "+porto+" -j DNAT\" | grep "+typee) 17 | err := cmd.Run() 18 | if err != nil { 19 | return false 20 | } 21 | return true 22 | } 23 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "docker-api-service/docker" 5 | "docker-api-service/routers" 6 | "docker-api-service/setting" 7 | "fmt" 8 | "log" 9 | "net/http" 10 | ) 11 | 12 | func init() { 13 | docker.Setup() 14 | } 15 | func main() { 16 | router := routers.InitRouter() 17 | s := &http.Server{ 18 | Addr: fmt.Sprint(":", setting.HttpPort), 19 | Handler: router, 20 | ReadHeaderTimeout: setting.ReadTimeout, 21 | WriteTimeout: setting.WriteTimeout, 22 | MaxHeaderBytes: 1 << 20, //最大1M 23 | } 24 | err := s.ListenAndServe() 25 | if err != nil { 26 | log.Fatal(err) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # docker-natcloud 2 | ##### docker共享容器云 3 | This a simple Docker REST API that providing a friendly API to manage Docker container. 4 | 这是一个简单的Docker容器的REST API服务,提供友好的API供操作容器。 5 | # 本地编译 6 | 1.下载安装最新 Go 环境 7 | 2.clone 并进入本项目,下载所需包 8 | ```bash 9 | git clone --depth=1 https://github.com/erwaorz/docker-natcloud.git 10 | cd docker-natcloud 11 | go version 12 | go env -w GOPROXY=https://goproxy.cn,direct 13 | go env -w GO111MODULE=auto 14 | go mod tidy 15 | ``` 16 | 3.编辑 main.go 文件,内容按需修改 17 | 4.按照平台输入命令编译,下面举了一些例子 18 | ```bash 19 | # 本机平台 20 | go build -ldflags "-s -w" -trimpath 21 | ``` 22 | ### 接口 23 | - [x] 开通容器 24 | - [x] 删除容器 25 | - [x] 启动容器 26 | - [x] 重启容器 27 | - [x] 停止容器 28 | - [x] 冻结容器 29 | - [x] 解冻容器 30 | - [x] 容器信息 31 | - [x] NAT端口绑定 32 | - [x] NAT端口解绑 33 | - [ ] 域名绑定 34 | - [ ] 域名解绑 35 | -------------------------------------------------------------------------------- /docker/pramastruct.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | type CreateStruct struct { 4 | Hostname string `json:"hostname" binding:"required""` 5 | CPUShares int64 `json:"cpushares" binding:"required"` 6 | CpusetCpus string `json:"cpusetcpus" binding:"required"` 7 | NanoCPUs int64 `json:"nanocpus" binding:"required"` 8 | Memory int64 `json:"memory" binding:"required"` 9 | MemorySwap int64 `json:"memoryswap" binding:"required"` 10 | MinNet string `json:"min_net" binding:"required"` 11 | MaxNet string `json:"max_net" binding:"required"` 12 | ProtNums int `json:"prot_nums" binding:"required"` 13 | DomainNums int `json:"domain_nums" binding:"required"` 14 | Image string `json:"image" binding:"required"` 15 | Ipprefix string `json:"Ipprefix" binding:"required"` 16 | } 17 | type InfoStruct struct { 18 | //Id string `json:"id" binding:"required"` 19 | Id string `json:"id"` 20 | } 21 | -------------------------------------------------------------------------------- /routers/router.go: -------------------------------------------------------------------------------- 1 | package routers 2 | 3 | import ( 4 | "docker-api-service/docker" 5 | "docker-api-service/jwt" 6 | "github.com/gin-gonic/gin" 7 | ) 8 | import setting "docker-api-service/setting" 9 | 10 | func InitRouter() *gin.Engine { 11 | r := gin.New() 12 | r.Use(gin.Logger()) 13 | r.Use(gin.Recovery()) 14 | gin.SetMode(setting.RunMode) 15 | r.Use(jwt.JWT()) 16 | r.GET("/test", func(c *gin.Context) { 17 | c.JSON(200, gin.H{ 18 | "code": 200, 19 | "msg": "连接成功", 20 | }) 21 | }) 22 | api := r.Group("/api") 23 | api.POST("/create", docker.CreateDocker) 24 | api.POST("/info", docker.Dockerinfo) 25 | api.POST("/delete", docker.DeleteDocker) 26 | api.POST("/addport", docker.AddPort) 27 | api.POST("/delport", docker.DelPort) 28 | api.POST("/pause", docker.PauseDocker) 29 | api.POST("/unpause", docker.UnPauseDocker) 30 | api.POST("/boot", docker.StartDocker) 31 | api.POST("/reboot", docker.RestarDocker) 32 | api.POST("/stop", docker.StopDocker) 33 | return r 34 | } 35 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: 正式版 2 | 3 | on: 4 | push: 5 | tags: 6 | - v* 7 | env: 8 | GITHUB_TOKEN: ${{ github.token }} 9 | 10 | jobs: 11 | my-job: 12 | name: Build docker-natcloud on Push Tag 🚀 13 | runs-on: ubuntu-latest 14 | steps: 15 | 16 | - name: Set up Go 17 | uses: actions/setup-go@v2 18 | with: 19 | go-version: 1.17 20 | 21 | - name: Check out code into the Go module directory 22 | uses: actions/checkout@v2 23 | - name: Tidy Go modules 24 | run: go mod tidy -go=1.16 && go mod tidy -go=1.17 25 | 26 | - name: Build linux-x64 27 | run: CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o artifacts/docker-natcloud-linux-x64 28 | - name: Upload binaries to release 29 | uses: svenstaro/upload-release-action@v2 30 | with: 31 | repo_token: ${{ secrets.GITHUB_TOKEN }} 32 | file: artifacts/docker-* 33 | tag: ${{ github.ref }} 34 | overwrite: true 35 | file_glob: true 36 | 37 | -------------------------------------------------------------------------------- /setting/setting.go: -------------------------------------------------------------------------------- 1 | package setting 2 | 3 | import ( 4 | "github.com/go-ini/ini" 5 | "log" 6 | "time" 7 | ) 8 | 9 | var ( 10 | cfg *ini.File 11 | RunMode string 12 | HttpPort int 13 | ReadTimeout time.Duration 14 | WriteTimeout time.Duration 15 | APIKEY string 16 | ) 17 | 18 | func init() { 19 | var err error 20 | cfg, err = ini.Load("conf/app.ini") 21 | if err != nil { 22 | log.Fatal(2, "无法载入app.ini配置文件") 23 | } 24 | LoadBase() 25 | LoadServer() 26 | } 27 | func LoadBase() { 28 | RunMode = cfg.Section("").Key("RUN_MODE").MustString("debug") 29 | APIKEY = cfg.Section("app").Key("APIKEY").MustString("") 30 | } 31 | func LoadServer() { 32 | sec, err := cfg.GetSection("server") 33 | if err != nil { 34 | log.Fatal(2, "无法载入app.ini配置文件里的Server") 35 | } 36 | RunMode = cfg.Section("").Key("RUN_MODE").MustString("debug") 37 | HttpPort = sec.Key("HTTP_PORT").MustInt(8000) 38 | ReadTimeout = time.Duration(sec.Key("READ_TIMEOUT").MustInt(60)) * time.Second 39 | WriteTimeout = time.Duration(sec.Key("WRITE_TIMEOUT").MustInt(60)) * time.Second 40 | } 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Senkin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module docker-api-service 2 | 3 | go 1.17 4 | 5 | require ( 6 | github.com/gin-gonic/gin v1.7.7 7 | github.com/go-ini/ini v1.66.4 8 | ) 9 | 10 | require ( 11 | github.com/Microsoft/go-winio v0.5.1 // indirect 12 | github.com/containerd/containerd v1.6.3 // indirect 13 | github.com/docker/distribution v2.8.1+incompatible // indirect 14 | github.com/docker/docker v20.10.14+incompatible // indirect 15 | github.com/docker/go-connections v0.4.0 // indirect 16 | github.com/docker/go-units v0.4.0 // indirect 17 | github.com/gin-contrib/sse v0.1.0 // indirect 18 | github.com/go-playground/locales v0.13.0 // indirect 19 | github.com/go-playground/universal-translator v0.17.0 // indirect 20 | github.com/go-playground/validator/v10 v10.4.1 // indirect 21 | github.com/gogo/protobuf v1.3.2 // indirect 22 | github.com/golang/protobuf v1.5.2 // indirect 23 | github.com/json-iterator/go v1.1.12 // indirect 24 | github.com/leodido/go-urn v1.2.0 // indirect 25 | github.com/mattn/go-isatty v0.0.12 // indirect 26 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 27 | github.com/modern-go/reflect2 v1.0.2 // indirect 28 | github.com/opencontainers/go-digest v1.0.0 // indirect 29 | github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect 30 | github.com/pkg/errors v0.9.1 // indirect 31 | github.com/sirupsen/logrus v1.8.1 // indirect 32 | github.com/ugorji/go/codec v1.1.7 // indirect 33 | golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect 34 | golang.org/x/net v0.0.0-20211216030914-fe4d6282115f // indirect 35 | golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect 36 | google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa // indirect 37 | google.golang.org/grpc v1.46.0 // indirect 38 | google.golang.org/protobuf v1.27.1 // indirect 39 | gopkg.in/yaml.v2 v2.4.0 // indirect 40 | ) 41 | -------------------------------------------------------------------------------- /userdatabase/userdatabase.go: -------------------------------------------------------------------------------- 1 | package userdatabase 2 | 3 | import ( 4 | "encoding/json" 5 | "io/ioutil" 6 | "log" 7 | "reflect" 8 | "strconv" 9 | ) 10 | 11 | type User struct { 12 | Id string `json:"id"` 13 | Portnums int `json:"portnums"` 14 | Domainnums int `json:"domainnums"` 15 | Ports []Port `json:"ports"` 16 | Domains []Domain `json:"domains"` 17 | Ip string `json:"ip"` 18 | } 19 | type Port struct { 20 | Porto string `json:"porto"` 21 | Port string `json:"port"` 22 | } 23 | type Domain struct { 24 | url string `json:"url"` 25 | } 26 | 27 | func findAll() []User { 28 | ps := make([]User, 0) 29 | data, err := ioutil.ReadFile("./user.json") 30 | if err != nil { 31 | log.Fatal(err) 32 | } 33 | json.Unmarshal(data, &ps) //反序列化 34 | return ps 35 | } 36 | func Create(id, natip string, portnums, domainnums int) { //保存容器ID和转发端口数量限制 37 | fields := make([]map[string]interface{}, 0) 38 | p1 := &User{ 39 | Id: id, 40 | Portnums: portnums, 41 | Domainnums: domainnums, 42 | Ports: []Port{}, 43 | Domains: []Domain{}, 44 | Ip: natip, 45 | } 46 | _, err := json.Marshal(p1) //序列化 47 | if err != nil { 48 | log.Fatal() 49 | } 50 | data, err := ioutil.ReadFile("./user.json") //讀取文件,存儲為map格式 51 | if err != nil { 52 | log.Fatal(err) 53 | } 54 | err = json.Unmarshal(data, &fields) 55 | if err != nil { 56 | log.Fatal(err) 57 | } 58 | //將結構體反射成map 59 | tp := reflect.TypeOf(p1).Elem() 60 | vp := reflect.ValueOf(p1).Elem() 61 | field := make(map[string]interface{}, 0) 62 | for i := 0; i < tp.NumField(); i++ { 63 | field1 := tp.Field(i) 64 | field2 := vp.Field(i) 65 | key := field1.Tag.Get("json") 66 | field[key] = field2.Interface() 67 | } 68 | fields = append(fields, field) 69 | out, err := json.Marshal(fields) 70 | if err != nil { 71 | log.Fatal(err) 72 | } 73 | ioutil.WriteFile("./user.json", out, 0775) 74 | } 75 | func DeleteUser(id string) bool { 76 | p1 := &User{ 77 | Id: id, 78 | } 79 | data, err := ioutil.ReadFile("./user.json") 80 | if err != nil { 81 | log.Fatal(err) 82 | } 83 | fields := make([]User, 0) 84 | err = json.Unmarshal(data, &fields) 85 | if err != nil { 86 | log.Fatal(err) 87 | } 88 | var p2 []User 89 | for _, v := range fields { 90 | if v.Id != p1.Id { 91 | p2 = append(p2, v) 92 | } 93 | } 94 | out, err := json.MarshalIndent(p2, "", " ") 95 | _ = ioutil.WriteFile("./user.json", out, 0755) 96 | return true 97 | } 98 | func SearchUser(id string) User { //取特定用戶信息 99 | data, err := ioutil.ReadFile("./user.json") 100 | if err != nil { 101 | log.Fatal(err) 102 | } 103 | fields := make([]User, 0) 104 | err = json.Unmarshal(data, &fields) 105 | if err != nil { 106 | log.Fatal(err) 107 | } 108 | for _, v := range fields { 109 | if v.Id == id { 110 | return v 111 | } 112 | } 113 | return User{} 114 | } 115 | func UpdateUserPortnums(id string, nums int) { 116 | p1 := &User{ 117 | Id: id, 118 | } 119 | tp := reflect.TypeOf(p1).Elem() 120 | vp := reflect.ValueOf(p1).Elem() 121 | field := make(map[string]interface{}, 0) 122 | for i := 0; i < tp.NumField(); i++ { 123 | field1 := tp.Field(i) 124 | field2 := vp.Field(i) 125 | key := field1.Tag.Get("json") 126 | switch field2.Kind() { 127 | case reflect.Int: 128 | field[key] = float64(field2.Interface().(int)) 129 | case reflect.Int8: 130 | field[key] = float64(field2.Interface().(int8)) 131 | case reflect.Int16: 132 | field[key] = float64(field2.Interface().(int16)) 133 | case reflect.Int32: 134 | field[key] = float64(field2.Interface().(int32)) 135 | case reflect.Int64: 136 | field[key] = float64(field2.Interface().(int64)) 137 | case reflect.Uint: 138 | field[key] = float64(field2.Interface().(uint)) 139 | case reflect.Uint8: 140 | field[key] = float64(field2.Interface().(uint8)) 141 | case reflect.Uint16: 142 | field[key] = float64(field2.Interface().(uint16)) 143 | case reflect.Uint32: 144 | field[key] = float64(field2.Interface().(uint32)) 145 | case reflect.Uint64: 146 | field[key] = float64(field2.Interface().(uint64)) 147 | case reflect.Float32: 148 | field[key] = float64(field2.Interface().(float32)) 149 | case reflect.Float64: 150 | field[key] = field2.Interface() 151 | default: 152 | field[key] = field2.Interface() 153 | } 154 | } 155 | _, err := json.Marshal(p1) 156 | if err != nil { 157 | log.Fatal(err) 158 | } 159 | data, err := ioutil.ReadFile("./user.json") 160 | if err != nil { 161 | log.Fatal(err) 162 | } 163 | fields := make([]map[string]interface{}, 0) 164 | err = json.Unmarshal(data, &fields) 165 | if err != nil { 166 | log.Fatal(err) 167 | } 168 | for _, v := range fields { 169 | if v["id"] == field["id"] { 170 | v["portnums"] = nums 171 | } 172 | } 173 | out, _ := json.MarshalIndent(fields, "", " ") 174 | _ = ioutil.WriteFile("./user.json", out, 0755) 175 | } 176 | 177 | func UpdateUserProt(id, proto, prot string) bool { 178 | p1 := &User{ 179 | Id: id, 180 | } 181 | data, err := ioutil.ReadFile("./user.json") 182 | if err != nil { 183 | log.Fatal(err) 184 | } 185 | fields := make([]User, 0) 186 | err = json.Unmarshal(data, &fields) 187 | if err != nil { 188 | log.Fatal(err) 189 | } 190 | var p2 []User 191 | for _, v := range fields { //检查对外端口 192 | for _, vv := range v.Ports { 193 | if vv.Porto == proto { 194 | return false 195 | } 196 | } 197 | } 198 | for _, v := range fields { 199 | if v.Id == p1.Id { 200 | if len(v.Ports) < v.Portnums { 201 | if len(v.Ports) == 0 { 202 | goto change 203 | } 204 | for _, vv := range v.Ports { 205 | if vv.Porto == proto { 206 | return false 207 | } 208 | } 209 | change: 210 | v.Ports = append(v.Ports, Port{proto, prot}) 211 | } 212 | } 213 | p2 = append(p2, v) 214 | } 215 | out, err := json.MarshalIndent(p2, "", " ") 216 | _ = ioutil.WriteFile("./user.json", out, 0755) 217 | return true 218 | } 219 | func DeleteUserProt(id, proto string) bool { 220 | p1 := &User{ 221 | Id: id, 222 | } 223 | data, err := ioutil.ReadFile("./user.json") 224 | if err != nil { 225 | log.Fatal(err) 226 | } 227 | fields := make([]User, 0) 228 | err = json.Unmarshal(data, &fields) 229 | if err != nil { 230 | log.Fatal(err) 231 | } 232 | var p2 []User 233 | for _, v := range fields { 234 | if v.Id == p1.Id { 235 | length := len(v.Ports) 236 | for index, vv := range v.Ports { 237 | if vv.Porto == proto { 238 | if index == length-1 { 239 | v.Ports = v.Ports[0:index] 240 | } else { 241 | v.Ports = append(v.Ports[0:index], v.Ports[index+1:]...) 242 | } 243 | } 244 | } 245 | } 246 | p2 = append(p2, v) 247 | } 248 | out, err := json.MarshalIndent(p2, "", " ") 249 | _ = ioutil.WriteFile("./user.json", out, 0755) 250 | return true 251 | } 252 | func UpdateUserDomain(id, url string) bool { 253 | p1 := &User{ 254 | Id: id, 255 | } 256 | data, err := ioutil.ReadFile("./user.json") 257 | if err != nil { 258 | log.Fatal(err) 259 | } 260 | fields := make([]User, 0) 261 | err = json.Unmarshal(data, &fields) 262 | if err != nil { 263 | log.Fatal(err) 264 | } 265 | var p2 []User 266 | for _, v := range fields { //检查对外端口 267 | for _, vv := range v.Domains { 268 | if vv.url == url { 269 | return false 270 | } 271 | } 272 | } 273 | for _, v := range fields { 274 | if v.Id == p1.Id { 275 | if len(v.Domains) < v.Domainnums { 276 | if len(v.Domains) == 0 { 277 | goto change 278 | } 279 | for _, vv := range v.Domains { 280 | if vv.url == url { 281 | return false 282 | } 283 | } 284 | change: 285 | v.Domains = append(v.Domains, Domain{url: url}) 286 | } 287 | } 288 | p2 = append(p2, v) 289 | } 290 | out, err := json.MarshalIndent(p2, "", " ") 291 | _ = ioutil.WriteFile("./user.json", out, 0755) 292 | return true 293 | } 294 | func DeleteUserDomain(id, url string) bool { 295 | p1 := &User{ 296 | Id: id, 297 | } 298 | data, err := ioutil.ReadFile("./user.json") 299 | if err != nil { 300 | log.Fatal(err) 301 | } 302 | fields := make([]User, 0) 303 | err = json.Unmarshal(data, &fields) 304 | if err != nil { 305 | log.Fatal(err) 306 | } 307 | var p2 []User 308 | for _, v := range fields { 309 | if v.Id == p1.Id { 310 | length := len(v.Domains) 311 | for index, vv := range v.Domains { 312 | if vv.url == url { 313 | if index == length-1 { 314 | v.Domains = v.Domains[0:index] 315 | } else { 316 | v.Domains = append(v.Domains[0:index], v.Domains[index+1:]...) 317 | } 318 | } 319 | } 320 | } 321 | p2 = append(p2, v) 322 | } 323 | out, err := json.MarshalIndent(p2, "", " ") 324 | _ = ioutil.WriteFile("./user.json", out, 0755) 325 | return true 326 | } 327 | func UserPort(id string) []Port { 328 | p1 := &User{ 329 | Id: id, 330 | } 331 | data, err := ioutil.ReadFile("./user.json") 332 | if err != nil { 333 | log.Fatal(err) 334 | } 335 | fields := make([]User, 0) 336 | err = json.Unmarshal(data, &fields) 337 | if err != nil { 338 | log.Fatal(err) 339 | } 340 | for _, v := range fields { 341 | if v.Id == p1.Id { 342 | return v.Ports 343 | } 344 | } 345 | return nil 346 | } 347 | func UserDoamin(id string) []Domain { 348 | p1 := &User{ 349 | Id: id, 350 | } 351 | data, err := ioutil.ReadFile("./user.json") 352 | if err != nil { 353 | log.Fatal(err) 354 | } 355 | fields := make([]User, 0) 356 | err = json.Unmarshal(data, &fields) 357 | if err != nil { 358 | log.Fatal(err) 359 | } 360 | for _, v := range fields { 361 | if v.Id == p1.Id { 362 | return v.Domains 363 | } 364 | } 365 | return nil 366 | } 367 | 368 | func Okip(ip_prefix string) string { //返回一个可用的ip 369 | data, err := ioutil.ReadFile("./user.json") 370 | if err != nil { 371 | log.Fatal(err) 372 | } 373 | fields := make([]User, 0) 374 | err = json.Unmarshal(data, &fields) 375 | if err != nil { 376 | log.Fatal(err) 377 | } 378 | for i := 2; i <= 254; i++ { 379 | if len(fields) == 0 { 380 | return ip_prefix + "." + strconv.Itoa(i) 381 | } 382 | for ii, v := range fields { 383 | if ip_prefix+"."+strconv.Itoa(i) == v.Ip { 384 | break 385 | } else if ii+1 == len(fields) { 386 | return ip_prefix + "." + strconv.Itoa(i) 387 | } 388 | } 389 | } 390 | return "" 391 | } 392 | -------------------------------------------------------------------------------- /docker/docker.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | import ( 4 | "docker-api-service/app" 5 | "docker-api-service/userdatabase" 6 | "github.com/docker/docker/api/types" 7 | "github.com/docker/docker/api/types/container" 8 | "github.com/docker/docker/api/types/network" 9 | "github.com/docker/docker/api/types/strslice" 10 | "github.com/gin-gonic/gin" 11 | "os/exec" 12 | ) 13 | 14 | func CreateDocker(ctx *gin.Context) { 15 | appG := app.Gin{C: ctx} 16 | form := CreateStruct{} 17 | if err := ctx.ShouldBindJSON(&form); err != nil { 18 | appG.Response(0, err.Error(), "") 19 | return 20 | } 21 | ip := userdatabase.Okip(form.Ipprefix) 22 | dockerconfig := &container.Config{ 23 | Hostname: form.Hostname, 24 | //ExposedPorts: nat.PortSet{"22/tcp": {}}, 25 | Image: form.Image, 26 | Labels: map[string]string{ 27 | "org.label-schema.tc.enabled": "1", 28 | "org.label-schema.tc.rate": form.MinNet, //最低宽带限制 29 | "org.label-schema.tc.ceil": form.MaxNet, //最大宽带限制 30 | }, 31 | Cmd: strslice.StrSlice{"sh", "-c", "/etc/init.d/ssh start && tail -f /dev/null"}, 32 | } 33 | dockerHostConfig := &container.HostConfig{ 34 | //PortBindings: nat.PortMap{"22/tcp": []nat.PortBinding{{HostIP: "0.0.0.0", HostPort: "41654"}}}, 35 | Resources: container.Resources{ 36 | CPUShares: form.CPUShares, //此容器相对于其他容器的相对CPU权重 37 | CpusetCpus: form.CpusetCpus, //运行的核心exp:0-3或0,1 38 | NanoCPUs: form.NanoCPUs, //相当于--cpus=2 39 | Memory: form.Memory * 1048576, //以字节为单位 40 | MemorySwap: form.MemorySwap, //总内存包含swap,-1未无限制使用swap 41 | }, 42 | StorageOpt: map[string]string{"size": "11G"}, 43 | } 44 | dockernetconfig := &network.NetworkingConfig{EndpointsConfig: map[string]*network.EndpointSettings{ 45 | "nat": { 46 | IPAMConfig: &network.EndpointIPAMConfig{IPv4Address: ip}}, 47 | }} 48 | _, err := cli.ContainerCreate(ctx, dockerconfig, dockerHostConfig, dockernetconfig, nil, form.Hostname) 49 | if err != nil { 50 | appG.Response(0, err.Error(), "") 51 | return 52 | } else { 53 | userdatabase.Create(form.Hostname, ip, form.ProtNums, form.DomainNums) 54 | appG.Response(200, "创建成功", ip) 55 | return 56 | } 57 | } 58 | func Dockerinfo(ctx *gin.Context) { 59 | appG := app.Gin{C: ctx} 60 | id := ctx.PostForm("id") 61 | if id == "" { 62 | appG.Response(0, "不是有效的Id标识", "") 63 | return 64 | } else { 65 | info, err := cli.ContainerInspect(ctx, id) 66 | if err != nil { 67 | appG.Response(0, err.Error(), "") 68 | return 69 | } else { 70 | if info.Name != "/"+id { 71 | appG.Response(0, "该用户不存在", "") 72 | return 73 | } else { 74 | appG.Response(200, "获取成功", info) 75 | return 76 | } 77 | } 78 | } 79 | } 80 | func DeleteDocker(ctx *gin.Context) { 81 | appG := app.Gin{C: ctx} 82 | id := ctx.PostForm("id") 83 | if id == "" { 84 | appG.Response(0, "不是有效的Id标识", "") 85 | return 86 | } else { 87 | info, err := cli.ContainerInspect(ctx, id) 88 | if err != nil { 89 | appG.Response(0, "该用户不存在", "") 90 | return 91 | } else { 92 | if info.Name != "/"+id { 93 | appG.Response(0, "该用户不存在", "") 94 | return 95 | } else { 96 | err = cli.ContainerRemove(ctx, info.ID, types.ContainerRemoveOptions{}) 97 | if err != nil { 98 | appG.Response(0, err.Error(), "") 99 | return 100 | } else { 101 | userdatabase.DeleteUser(id) 102 | appG.Response(200, "删除容器成功", "") 103 | return 104 | } 105 | } 106 | } 107 | } 108 | } 109 | func PauseDocker(ctx *gin.Context) { //暂停容器的所有进程 110 | appG := app.Gin{C: ctx} 111 | id := ctx.PostForm("id") 112 | if id == "" { 113 | appG.Response(0, "不是有效的Id标识", "") 114 | return 115 | } else { 116 | info, err := cli.ContainerInspect(ctx, id) 117 | if err != nil { 118 | appG.Response(0, "该用户不存在", "") 119 | return 120 | } else { 121 | if info.Name != "/"+id { 122 | appG.Response(0, "该用户不存在", "") 123 | return 124 | } else { 125 | err = cli.ContainerPause(ctx, info.ID) 126 | if err != nil { 127 | appG.Response(0, err.Error(), "") 128 | return 129 | } else { 130 | appG.Response(200, "冻结进程成功", "") 131 | return 132 | } 133 | } 134 | } 135 | } 136 | } 137 | func UnPauseDocker(ctx *gin.Context) { //恢复暂停容器的所有进程 138 | appG := app.Gin{C: ctx} 139 | id := ctx.PostForm("id") 140 | if id == "" { 141 | appG.Response(0, "不是有效的Id标识", "") 142 | return 143 | } else { 144 | info, err := cli.ContainerInspect(ctx, id) 145 | if err != nil { 146 | appG.Response(0, "该用户不存在", "") 147 | return 148 | } else { 149 | if info.Name != "/"+id { 150 | appG.Response(0, "该用户不存在", "") 151 | return 152 | } else { 153 | err = cli.ContainerUnpause(ctx, info.ID) 154 | if err != nil { 155 | appG.Response(0, err.Error(), "") 156 | return 157 | } else { 158 | appG.Response(200, "恢复冻结进程成功", "") 159 | return 160 | } 161 | } 162 | } 163 | } 164 | } 165 | func StartDocker(ctx *gin.Context) { 166 | appG := app.Gin{C: ctx} 167 | id := ctx.PostForm("id") 168 | if id == "" { 169 | appG.Response(0, "不是有效的Id标识", "") 170 | return 171 | } else { 172 | info, err := cli.ContainerInspect(ctx, id) 173 | if err != nil { 174 | appG.Response(0, "该用户不存在", "") 175 | return 176 | } else { 177 | if info.Name != "/"+id { 178 | appG.Response(0, "该用户不存在", "") 179 | return 180 | } else { 181 | err = cli.ContainerStart(ctx, info.ID, types.ContainerStartOptions{}) 182 | if err != nil { 183 | appG.Response(0, err.Error(), "") 184 | return 185 | } else { 186 | appG.Response(200, "启动容器成功", "") 187 | return 188 | } 189 | } 190 | } 191 | } 192 | } 193 | func StopDocker(ctx *gin.Context) { //恢复暂停容器的所有进程 194 | appG := app.Gin{C: ctx} 195 | id := ctx.PostForm("id") 196 | if id == "" { 197 | appG.Response(0, "不是有效的Id标识", "") 198 | return 199 | } else { 200 | info, err := cli.ContainerInspect(ctx, id) 201 | if err != nil { 202 | appG.Response(0, "该用户不存在", "") 203 | return 204 | } else { 205 | if info.Name != "/"+id { 206 | appG.Response(0, "该用户不存在", "") 207 | return 208 | } else { 209 | err = cli.ContainerStop(ctx, info.ID, nil) 210 | if err != nil { 211 | appG.Response(0, err.Error(), "") 212 | return 213 | } else { 214 | appG.Response(200, "停止容器成功", "") 215 | return 216 | } 217 | } 218 | } 219 | } 220 | } 221 | func RestarDocker(ctx *gin.Context) { 222 | appG := app.Gin{C: ctx} 223 | id := ctx.PostForm("id") 224 | if id == "" { 225 | appG.Response(0, "不是有效的Id标识", "") 226 | return 227 | } else { 228 | info, err := cli.ContainerInspect(ctx, id) 229 | if err != nil { 230 | appG.Response(0, "该用户不存在", "") 231 | return 232 | } else { 233 | if info.Name != "/"+id { 234 | appG.Response(0, "该用户不存在", "") 235 | return 236 | } else { 237 | err = cli.ContainerRestart(ctx, info.ID, nil) 238 | if err != nil { 239 | appG.Response(0, err.Error(), "") 240 | return 241 | } else { 242 | appG.Response(200, "重启容器成功", "") 243 | return 244 | } 245 | } 246 | } 247 | } 248 | } 249 | func AddPort(ctx *gin.Context) { //添加端口 250 | appG := app.Gin{C: ctx} 251 | id := ctx.PostForm("id") 252 | porto := ctx.PostForm("porto") 253 | port := ctx.PostForm("port") 254 | typee := ctx.PostForm("type") 255 | if id == "" { 256 | appG.Response(0, "不是有效的Id标识", "") 257 | return 258 | } else { 259 | info, err := cli.ContainerInspect(ctx, id) 260 | if err != nil { 261 | appG.Response(0, err.Error(), "") 262 | return 263 | } else { 264 | if info.Name != "/"+id { 265 | appG.Response(0, "该用户不存在", "") 266 | return 267 | } else { 268 | user := userdatabase.SearchUser(id) 269 | if iptablesexist(user.Ip, porto, port, typee) == true { 270 | appG.Response(0, "该规则已存在", "") 271 | return 272 | } else { 273 | if userdatabase.UpdateUserProt(id, porto, port) == true { 274 | cmd := exec.Command("iptables", "-t", "nat", "-A", "PREROUTING", "-i", "eth0", "-p", typee, "-m", typee, "--dport", porto, "-j", "DNAT", "--to-destination", user.Ip+":"+port) 275 | err := cmd.Run() 276 | if err != nil { 277 | appG.Response(0, "添加端口失败", "") 278 | return 279 | } else { 280 | appG.Response(200, "添加端口成功", "") 281 | cmd := exec.Command("/bin/bash", "-c", "service iptables save && service iptables restart") 282 | cmd.Run() 283 | return 284 | } 285 | } else { 286 | appG.Response(0, "添加端口失败", "") 287 | return 288 | } 289 | } 290 | } 291 | } 292 | } 293 | } 294 | func DelPort(ctx *gin.Context) { //删除端口 295 | appG := app.Gin{C: ctx} 296 | id := ctx.PostForm("id") 297 | porto := ctx.PostForm("porto") 298 | port := ctx.PostForm("port") 299 | typee := ctx.PostForm("type") 300 | if id == "" { 301 | appG.Response(0, "不是有效的Id标识", "") 302 | return 303 | } else { 304 | info, err := cli.ContainerInspect(ctx, id) 305 | if err != nil { 306 | appG.Response(0, err.Error(), "") 307 | return 308 | } else { 309 | if info.Name != "/"+id { 310 | appG.Response(0, "该用户不存在", "") 311 | return 312 | } else { 313 | user := userdatabase.SearchUser(id) 314 | cmd := exec.Command("iptables", "-t", "nat", "-D", "PREROUTING", "-i", "eth0", "-p", typee, "-m", typee, "--dport", porto, "-j", "DNAT", "--to-destination", user.Ip+":"+port) 315 | err := cmd.Run() 316 | if err != nil { 317 | appG.Response(0, "删除端口失败", "") 318 | return 319 | } else { 320 | appG.Response(200, "删除端口成功", "") 321 | userdatabase.DeleteUserProt(id, porto) 322 | cmd := exec.Command("/bin/bash", "-c", "service iptables save && service iptables restart") 323 | cmd.Run() 324 | return 325 | } 326 | } 327 | } 328 | } 329 | } 330 | func AddDomain(ctx *gin.Context) { //添加域名 331 | appG := app.Gin{C: ctx} 332 | id := ctx.PostForm("id") 333 | domain := ctx.PostForm("domain") 334 | if id == "" { 335 | appG.Response(0, "不是有效的Id标识", "") 336 | return 337 | } else { 338 | info, err := cli.ContainerInspect(ctx, id) 339 | if err != nil { 340 | appG.Response(0, err.Error(), "") 341 | return 342 | } else { 343 | if info.Name != "/"+id { 344 | appG.Response(0, "该用户不存在", "") 345 | return 346 | } else { 347 | if userdatabase.UpdateUserDomain(id, domain) == true { 348 | /* 349 | cmd := exec.Command("iptables", "-t", "nat", "-A", "PREROUTING", "-i", "eth0", "-p", typee, "-m", typee, "--dport", porto, "-j", "DNAT", "--to-destination", user.Ip+":"+port) 350 | err := cmd.Run() 351 | if err != nil { 352 | appG.Response(0, "添加域名失败", "") 353 | return 354 | } else { 355 | appG.Response(0, "添加域名成功", "") 356 | cmd := exec.Command("/bin/bash", "-c", "/etc/init.d/caddy restart") 357 | cmd.Run() 358 | return 359 | } 360 | */ 361 | appG.Response(200, "添加域名成功", "") 362 | return 363 | } else { 364 | appG.Response(0, "添加域名失败", "") 365 | return 366 | } 367 | } 368 | } 369 | } 370 | } 371 | func DelDomain(ctx *gin.Context) { //删除 372 | appG := app.Gin{C: ctx} 373 | id := ctx.PostForm("id") 374 | //domain := ctx.PostForm("domain") 375 | if id == "" { 376 | appG.Response(0, "不是有效的Id标识", "") 377 | return 378 | } else { 379 | info, err := cli.ContainerInspect(ctx, id) 380 | if err != nil { 381 | appG.Response(0, err.Error(), "") 382 | return 383 | } else { 384 | if info.Name != "/"+id { 385 | appG.Response(0, "该用户不存在", "") 386 | return 387 | } else { 388 | /* 389 | user := userdatabase.SearchUser(id) 390 | cmd := exec.Command("iptables", "-t", "nat", "-D", "PREROUTING", "-i", "eth0", "-p", typee, "-m", typee, "--dport", porto, "-j", "DNAT", "--to-destination", user.Ip+":"+port) 391 | err := cmd.Run() 392 | if err != nil { 393 | appG.Response(0, "删除端口失败", "") 394 | return 395 | } else { 396 | appG.Response(0, "删除端口成功", "") 397 | userdatabase.DeleteUserProt(id, porto) 398 | cmd := exec.Command("/bin/bash", "-c", "service iptables save && service iptables restart") 399 | cmd.Run() 400 | return 401 | } 402 | */ 403 | appG.Response(200, "删除域名成功", "") 404 | return 405 | } 406 | } 407 | } 408 | } 409 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 4 | github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= 5 | github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= 6 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 7 | github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= 8 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 9 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 10 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 11 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 12 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 13 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 14 | github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= 15 | github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 16 | github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 17 | github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 18 | github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 19 | github.com/containerd/containerd v1.6.3 h1:JfgUEIAH07xDWk6kqz0P3ArZt+KJ9YeihSC9uyFtSKg= 20 | github.com/containerd/containerd v1.6.3/go.mod h1:gCVGrYRYFm2E8GmuUIbj/NGD7DLZQLzSJQazjVKDOig= 21 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 22 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 23 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 24 | github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= 25 | github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= 26 | github.com/docker/docker v20.10.14+incompatible h1:+T9/PRYWNDo5SZl5qS1r9Mo/0Q8AwxKKPtu9S1yxM0w= 27 | github.com/docker/docker v20.10.14+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= 28 | github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= 29 | github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= 30 | github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= 31 | github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= 32 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 33 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 34 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 35 | github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 36 | github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= 37 | github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= 38 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 39 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 40 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 41 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 42 | github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs= 43 | github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= 44 | github.com/go-ini/ini v1.66.4 h1:dKjMqkcbkzfddhIhyglTPgMoJnkvmG+bSLrU9cTHc5M= 45 | github.com/go-ini/ini v1.66.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= 46 | github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= 47 | github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 48 | github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= 49 | github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= 50 | github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= 51 | github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= 52 | github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= 53 | github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= 54 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 55 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 56 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 57 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 58 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 59 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 60 | github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= 61 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 62 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 63 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 64 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 65 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 66 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 67 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 68 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 69 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 70 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 71 | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 72 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 73 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 74 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 75 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 76 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 77 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 78 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 79 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 80 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 81 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 82 | github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= 83 | github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= 84 | github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 85 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 86 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 87 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 88 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 89 | github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= 90 | github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= 91 | github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= 92 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 93 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= 94 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 95 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 96 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 97 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= 98 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 99 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 100 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 101 | github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= 102 | github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= 103 | github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 h1:rc3tiVYb5z54aKaDfakKn0dDjIyPpTtszkjuMzyt7ec= 104 | github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= 105 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 106 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 107 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 108 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 109 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 110 | github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= 111 | github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 112 | github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= 113 | github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 114 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 115 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 116 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 117 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 118 | github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= 119 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 120 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 121 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 122 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 123 | github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= 124 | github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= 125 | github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= 126 | github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= 127 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 128 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 129 | go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= 130 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 131 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 132 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= 133 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 134 | golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= 135 | golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 136 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 137 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 138 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 139 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 140 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 141 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 142 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 143 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 144 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 145 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 146 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 147 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 148 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 149 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 150 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 151 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 152 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 153 | golang.org/x/net v0.0.0-20211216030914-fe4d6282115f h1:hEYJvxw1lSnWIl8X9ofsYMklzaDs90JI2az5YMd4fPM= 154 | golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 155 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 156 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 157 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 158 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 159 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 160 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 161 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 162 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 163 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 164 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 165 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 166 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 167 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg= 168 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 169 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 170 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 171 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 172 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 173 | golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 174 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 175 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 176 | golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= 177 | golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 178 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 179 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 180 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 181 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 182 | golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 183 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 184 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 185 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 186 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 187 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 188 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 189 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 190 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 191 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 192 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 193 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 194 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 195 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 196 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 197 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 198 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 199 | google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 200 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 201 | google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa h1:I0YcKz0I7OAhddo7ya8kMnvprhcWM045PmkBdMO9zN0= 202 | google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= 203 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 204 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 205 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 206 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 207 | google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= 208 | google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 209 | google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= 210 | google.golang.org/grpc v1.46.0 h1:oCjezcn6g6A75TGoKYBPgKmVBLexhYLM6MebdrPApP8= 211 | google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= 212 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 213 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 214 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 215 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 216 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 217 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 218 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 219 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 220 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 221 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 222 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 223 | google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= 224 | google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 225 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 226 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 227 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 228 | gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 229 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 230 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 231 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 232 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 233 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 234 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 235 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 236 | --------------------------------------------------------------------------------