├── .semaphore └── semaphore.yml ├── LICENSE ├── README.md ├── core ├── auth.go ├── const.go ├── core.go ├── data.go ├── data_test.go ├── jobs.go ├── meta.go └── meta_test.go ├── go.mod ├── go.sum ├── rpc ├── config.toml ├── handler.go ├── middleware │ ├── cors.go │ ├── jwt.go │ └── metrics.go ├── server.go └── util │ └── util.go └── sdk ├── data.go ├── fanout.go ├── sdk.go └── sdk_test.go /.semaphore/semaphore.yml: -------------------------------------------------------------------------------- 1 | version: v1.0 2 | name: Go 3 | agent: 4 | machine: 5 | type: e1-standard-2 6 | os_image: ubuntu2004 7 | blocks: 8 | - name: Test 9 | task: 10 | jobs: 11 | - name: go test 12 | commands: 13 | - sem-version go 1.16 14 | - export GO111MODULE=on 15 | - export GOPATH=~/go 16 | - 'export PATH=/home/semaphore/go/bin:$PATH' 17 | - rm -fr /home/semaphore/go/pkg/mod/cache 18 | - checkout 19 | - go get ./... 20 | - go test -coverprofile=coverage.txt -covermode=atomic -v ./... 21 | - 'bash <(curl -s https://codecov.io/bash) -t 8c2bdd22-0aab-4cc1-a473-cd6db39aed9b || echo "Codecov did not collect coverage reports"' 22 | - du -hcs /tmp/test/* 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2022 Orca 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 |

6 | 7 |

OrcaS:一款开箱即用的轻量级对象存储

8 | 9 |

10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 31 |

32 | 33 | 主要特性: 34 | 35 | - ⏱ 对象级重删(秒传) 36 | - 📦 小对象打包 37 | - 🔪 大对象分片 38 | - 🗂 对象多版本 39 | - 🔐 零知识加密(端到端加密,国际标准算法) 40 | - 🗜 智能压缩 41 | - [ ] [差分同步](https://github.com/orcastor/xdelta) 42 | - [ ] 实时快照 43 | 44 | 45 | [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Forcastor%2Forcas.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Forcastor%2Forcas?ref=badge_large) -------------------------------------------------------------------------------- /core/auth.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/orca-zhang/ecache" 9 | ) 10 | 11 | const ( 12 | NA = 1 << iota 13 | DR // 数据读取 14 | DW // 数据写入 15 | DD // 数据删除 16 | MDR // 元数据读取 17 | MDW // 元数据写入 18 | MDD // 元数据删除 19 | 20 | DRW = DR | DW // 数据读写 21 | MDRW = MDR | MDW // 元数据读写 22 | ALL = DRW | DD | MDRW | MDD // 数据、元数据读写删 23 | ) 24 | 25 | const ( 26 | USER = iota // 普通用户 27 | ADMIN // 管理员 28 | ) 29 | 30 | type AccessCtrlMgr interface { 31 | SetAdapter(ma MetadataAdapter) 32 | 33 | CheckPermission(c Ctx, action int, bktID int64) error 34 | CheckRole(c Ctx, role uint32) error 35 | } 36 | 37 | var cache = ecache.NewLRUCache(16, 256, 30*time.Second) 38 | 39 | type DefaultAccessCtrlMgr struct { 40 | ma MetadataAdapter 41 | } 42 | 43 | func (dacm *DefaultAccessCtrlMgr) SetAdapter(ma MetadataAdapter) { 44 | dacm.ma = ma 45 | } 46 | 47 | func (dacm *DefaultAccessCtrlMgr) CheckPermission(c Ctx, action int, bktID int64) error { 48 | uid := getUID(c) 49 | if uid <= 0 { 50 | return ERR_NEED_LOGIN 51 | } 52 | bk := fmt.Sprintf("bkt:%d", bktID) 53 | if u, ok := cache.GetInt64(bk); ok { 54 | if u == uid { 55 | return nil 56 | } else { 57 | return ERR_NO_PERM 58 | } 59 | } 60 | b, err := dacm.ma.GetBkt(c, []int64{bktID}) 61 | if err != nil { 62 | return err 63 | } 64 | // check is owner of the bucket 65 | if len(b) > 0 { 66 | cache.PutInt64(bk, b[0].UID) 67 | if b[0].UID == uid { 68 | return nil 69 | } 70 | } 71 | return ERR_NO_PERM 72 | } 73 | 74 | func (dacm *DefaultAccessCtrlMgr) CheckRole(c Ctx, role uint32) error { 75 | uid := getUID(c) 76 | if uid <= 0 { 77 | return ERR_NEED_LOGIN 78 | } 79 | rk := fmt.Sprintf("role:%d", uid) 80 | if r, ok := cache.GetInt64(rk); ok { 81 | if uint32(r) == role { 82 | return nil 83 | } else { 84 | return ERR_NO_ROLE 85 | } 86 | } 87 | u, err := dacm.ma.GetUsr(c, []int64{uid}) 88 | if err != nil { 89 | return err 90 | } 91 | if len(u) > 0 { 92 | cache.PutInt64(rk, int64(u[0].Role)) 93 | if u[0].Role == role { 94 | return nil 95 | } 96 | } 97 | return ERR_NO_ROLE 98 | } 99 | 100 | func UserInfo2Ctx(c Ctx, u *UserInfo) Ctx { 101 | return context.WithValue(c, "o", map[string]interface{}{ 102 | "uid": u.ID, 103 | "key": u.Key, 104 | }) 105 | } 106 | 107 | func getUID(c Ctx) int64 { 108 | if v, ok := c.Value("o").(map[string]interface{}); ok { 109 | if uid, okk := v["uid"].(int64); okk { 110 | return uid 111 | } 112 | } 113 | return 0 114 | } 115 | 116 | func getKey(c Ctx) string { 117 | if v, ok := c.Value("o").(map[string]interface{}); ok { 118 | if key, okk := v["key"].(string); okk { 119 | return key 120 | } 121 | } 122 | return "" 123 | } 124 | -------------------------------------------------------------------------------- /core/const.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "context" 5 | "os" 6 | ) 7 | 8 | const ROOT_OID int64 = 0 9 | 10 | var ORCAS_BASE = os.Getenv("ORCAS_BASE") 11 | var ORCAS_DATA = os.Getenv("ORCAS_DATA") 12 | 13 | type Ctx context.Context 14 | 15 | type Error string 16 | 17 | func (e Error) Error() string { 18 | return string(e) 19 | } 20 | 21 | const ( 22 | ERR_AUTH_FAILED = Error("auth failed") 23 | ERR_NEED_LOGIN = Error("need login") 24 | ERR_INCORRECT_PWD = Error("incorrect username or password") 25 | 26 | ERR_NO_PERM = Error("no permission") 27 | ERR_NO_ROLE = Error("role mismatch") 28 | 29 | ERR_OPEN_FILE = Error("open file failed") 30 | ERR_READ_FILE = Error("read file failed") 31 | 32 | ERR_OPEN_DB = Error("open db failed") 33 | ERR_QUERY_DB = Error("query db failed") 34 | ERR_EXEC_DB = Error("exec db failed") 35 | ERR_DUP_KEY = Error("object with same name already exists") 36 | ) 37 | -------------------------------------------------------------------------------- /core/core.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "crypto/sha1" 5 | "encoding/base64" 6 | "strconv" 7 | "strings" 8 | 9 | "github.com/orca-zhang/idgen" 10 | "golang.org/x/crypto/pbkdf2" 11 | ) 12 | 13 | type ListOptions struct { 14 | Word string `json:"w,omitempty"` // 过滤词,支持通配符*和? 15 | Delim string `json:"d,omitempty"` // 分隔符,每次请求后返回,原样回传即可 16 | Type int `json:"t,omitempty"` // 对象类型,-1: malformed, 0: 不过滤(default), 1: dir, 2: file, 3: version, 4: preview(thumb/m3u8/pdf) 17 | Count int `json:"c,omitempty"` // 查询个数 18 | Order string `json:"o,omitempty"` // 排序方式,id/mtime/name/size/type 前缀 +: 升序(默认) -: 降序 19 | Brief int `json:"e,omitempty"` // 显示更少内容(只在网络传输层,节省流量时有效),0: FULL(default), 1: without EXT, 2:only ID 20 | // 可以考虑改用FieldMask实现 https://mp.weixin.qq.com/s/L7He7M4JWi84z1emuokjbQ 21 | } 22 | 23 | type Options struct { 24 | Sync bool 25 | } 26 | 27 | type Handler interface { 28 | // 传入underlying,返回当前的,构成链式调用 29 | New(h Handler) Handler 30 | Close() 31 | 32 | SetOptions(opt Options) 33 | // 设置自定义的存储适配器 34 | SetAdapter(ma MetadataAdapter, da DataAdapter) 35 | 36 | // 登录用户 37 | Login(c Ctx, usr, pwd string) (Ctx, *UserInfo, []*BucketInfo, error) 38 | 39 | NewID() int64 40 | // 只有文件长度、HdrCRC32是预Ref,如果成功返回新DataID,失败返回0 41 | // 有文件长度、CRC32、MD5,成功返回引用的DataID,失败返回0,客户端发现DataID有变化,说明不需要上传数据 42 | // 如果非预Ref DataID传0,说明跳过了预Ref 43 | Ref(c Ctx, bktID int64, d []*DataInfo) ([]int64, error) 44 | // sn从0开始,DataID不传默认创建一个新的 45 | PutData(c Ctx, bktID, dataID int64, sn int, buf []byte) (int64, error) 46 | // 只传一个参数说明是sn,传两个参数说明是sn+offset,传三个参数说明是sn+offset+size 47 | GetData(c Ctx, bktID, id int64, sn int, offset []int) ([]byte, error) 48 | // 上传元数据 49 | PutDataInfo(c Ctx, bktID int64, d []*DataInfo) ([]int64, error) 50 | // 获取数据信息 51 | GetDataInfo(c Ctx, bktID, id int64) (*DataInfo, error) 52 | 53 | // Name不传默认用ID字符串化后的值作为Name 54 | Put(c Ctx, bktID int64, o []*ObjectInfo) ([]int64, error) 55 | Get(c Ctx, bktID int64, ids []int64) ([]*ObjectInfo, error) 56 | List(c Ctx, bktID, pid int64, opt ListOptions) (o []*ObjectInfo, cnt int64, delim string, err error) 57 | 58 | Rename(c Ctx, bktID, id int64, name string) error 59 | MoveTo(c Ctx, bktID, id, pid int64) error 60 | 61 | // 垃圾回收时有数据没有元数据引用的为脏数据(需要留出窗口时间),有元数据没有数据的为损坏数据 62 | Recycle(c Ctx, bktID, id int64) error 63 | Delete(c Ctx, bktID, id int64) error 64 | } 65 | 66 | type Admin interface { 67 | // 传入underlying,返回当前的,构成链式调用 68 | New(a Admin) Admin 69 | Close() 70 | 71 | PutBkt(c Ctx, o []*BucketInfo) error 72 | } 73 | 74 | type LocalHandler struct { 75 | ma MetadataAdapter 76 | da DataAdapter 77 | acm AccessCtrlMgr 78 | ig *idgen.IDGen 79 | } 80 | 81 | func NewLocalHandler() Handler { 82 | dma := &DefaultMetadataAdapter{} 83 | return &LocalHandler{ 84 | ma: dma, 85 | da: &DefaultDataAdapter{}, 86 | acm: &DefaultAccessCtrlMgr{ma: dma}, 87 | ig: idgen.NewIDGen(nil, 0), // 需要改成配置 88 | } 89 | } 90 | 91 | // 传入underlying,返回当前的,构成链式调用 92 | func (lh *LocalHandler) New(Handler) Handler { 93 | // 忽略下层handler 94 | return lh 95 | } 96 | 97 | func (lh *LocalHandler) Close() { 98 | lh.da.Close() 99 | lh.ma.Close() 100 | } 101 | 102 | func (lh *LocalHandler) SetOptions(opt Options) { 103 | lh.da.SetOptions(opt) 104 | } 105 | 106 | func (lh *LocalHandler) SetAdapter(ma MetadataAdapter, da DataAdapter) { 107 | lh.ma = ma 108 | lh.da = da 109 | lh.acm.SetAdapter(ma) 110 | } 111 | 112 | func (lh *LocalHandler) Login(c Ctx, usr, pwd string) (Ctx, *UserInfo, []*BucketInfo, error) { 113 | // pwd from db or cache 114 | u, err := lh.ma.GetUsr2(c, usr) 115 | if err != nil { 116 | return c, nil, nil, err 117 | } 118 | 119 | if u.ID <= 0 { 120 | return c, nil, nil, ERR_INCORRECT_PWD 121 | } 122 | 123 | // pbkdf2 check 124 | vs := strings.Split(u.Pwd, ":") 125 | if len(vs) < 3 { 126 | return c, nil, nil, ERR_AUTH_FAILED 127 | } 128 | 129 | // iter/salt/hash 130 | iter, _ := strconv.Atoi(vs[0]) 131 | salt, _ := base64.URLEncoding.DecodeString(vs[1]) 132 | dk := pbkdf2.Key([]byte(pwd), salt, iter, 16, sha1.New) 133 | d := base64.URLEncoding.EncodeToString(dk) 134 | if d != vs[2] { 135 | return c, nil, nil, ERR_INCORRECT_PWD 136 | } 137 | 138 | // list buckets 139 | b, _ := lh.ma.ListBkt(c, u.ID) 140 | 141 | // set uid & key to ctx 142 | c, u.Pwd, u.Key = UserInfo2Ctx(c, u), "", "" 143 | return c, u, b, nil 144 | } 145 | 146 | func (lh *LocalHandler) NewID() int64 { 147 | id, _ := lh.ig.New() 148 | return id 149 | } 150 | 151 | // 只有文件长度、HdrCRC32是预Ref,如果成功返回新DataID,失败返回0 152 | // 有文件长度、CRC32、MD5,成功返回引用的DataID,失败返回0,客户端发现DataID有变化,说明不需要上传数据 153 | // 如果非预Ref DataID传0,说明跳过了预Ref 154 | func (lh *LocalHandler) Ref(c Ctx, bktID int64, d []*DataInfo) ([]int64, error) { 155 | if err := lh.acm.CheckPermission(c, MDRW, bktID); err != nil { 156 | return nil, err 157 | } 158 | return lh.ma.RefData(c, bktID, d) 159 | } 160 | 161 | // 打包上传或者小文件,sn传-1,大文件sn从0开始,DataID不传默认创建一个新的 162 | func (lh *LocalHandler) PutData(c Ctx, bktID, dataID int64, sn int, buf []byte) (int64, error) { 163 | if err := lh.acm.CheckPermission(c, DW, bktID); err != nil { 164 | return 0, err 165 | } 166 | 167 | if dataID == 0 { 168 | if len(buf) <= 0 { 169 | dataID = EmptyDataID 170 | } else { 171 | dataID, _ = lh.ig.New() 172 | } 173 | } 174 | return dataID, lh.da.Write(c, bktID, dataID, sn, buf) 175 | } 176 | 177 | // 上传完数据以后,再创建元数据 178 | func (lh *LocalHandler) PutDataInfo(c Ctx, bktID int64, d []*DataInfo) (ids []int64, err error) { 179 | if err := lh.acm.CheckPermission(c, MDW, bktID); err != nil { 180 | return nil, err 181 | } 182 | 183 | var n []*DataInfo 184 | for _, x := range d { 185 | if x.ID == 0 { 186 | x.ID, _ = lh.ig.New() 187 | } 188 | ids = append(ids, x.ID) 189 | if x.ID > 0 { 190 | n = append(n, x) 191 | } 192 | } 193 | // 设置为反码引用别的元素的数据 194 | for i := range ids { 195 | if ids[i] < 0 && ^ids[i] < int64(len(ids)) { 196 | ids[i] = ids[^ids[i]] 197 | } 198 | } 199 | return ids, lh.ma.PutData(c, bktID, n) 200 | } 201 | 202 | func (lh *LocalHandler) GetDataInfo(c Ctx, bktID, id int64) (*DataInfo, error) { 203 | if err := lh.acm.CheckPermission(c, MDR, bktID); err != nil { 204 | return nil, err 205 | } 206 | return lh.ma.GetData(c, bktID, id) 207 | } 208 | 209 | // 只传一个参数说明是sn,传两个参数说明是sn+offset,传三个参数说明是sn+offset+size 210 | func (lh *LocalHandler) GetData(c Ctx, bktID, id int64, sn int, offset []int) ([]byte, error) { 211 | if err := lh.acm.CheckPermission(c, DR, bktID); err != nil { 212 | return nil, err 213 | } 214 | 215 | switch len(offset) { 216 | case 0: 217 | return lh.da.Read(c, bktID, id, sn) 218 | case 1: 219 | return lh.da.ReadBytes(c, bktID, id, sn, offset[0], -1) 220 | } 221 | return lh.da.ReadBytes(c, bktID, id, sn, offset[0], offset[1]) 222 | } 223 | 224 | // 垃圾回收时有数据没有元数据引用的为脏数据(需要留出窗口时间),有元数据没有数据的为损坏数据 225 | // PID支持用补码来直接引用当次还未上传的对象的ID 226 | func (lh *LocalHandler) Put(c Ctx, bktID int64, o []*ObjectInfo) ([]int64, error) { 227 | if err := lh.acm.CheckPermission(c, MDW, bktID); err != nil { 228 | return nil, err 229 | } 230 | 231 | for _, x := range o { 232 | if x.ID == 0 { 233 | x.ID, _ = lh.ig.New() 234 | } 235 | if x.Name == "" { 236 | x.Name = strconv.FormatInt(x.ID, 10) 237 | } 238 | } 239 | for _, x := range o { 240 | if x.PID < 0 && int(^x.PID) <= len(o) { 241 | x.PID = o[^x.PID].ID 242 | } 243 | } 244 | return lh.ma.PutObj(c, bktID, o) 245 | } 246 | 247 | func (lh *LocalHandler) Get(c Ctx, bktID int64, ids []int64) ([]*ObjectInfo, error) { 248 | if err := lh.acm.CheckPermission(c, MDR, bktID); err != nil { 249 | return nil, err 250 | } 251 | return lh.ma.GetObj(c, bktID, ids) 252 | } 253 | 254 | func (lh *LocalHandler) List(c Ctx, bktID, pid int64, opt ListOptions) ([]*ObjectInfo, int64, string, error) { 255 | if err := lh.acm.CheckPermission(c, MDR, bktID); err != nil { 256 | return nil, 0, "", err 257 | } 258 | return lh.ma.ListObj(c, bktID, pid, opt.Word, opt.Delim, opt.Order, opt.Count) 259 | } 260 | 261 | func (lh *LocalHandler) Rename(c Ctx, bktID, id int64, name string) error { 262 | if err := lh.acm.CheckPermission(c, MDW, bktID); err != nil { 263 | return err 264 | } 265 | return lh.ma.SetObj(c, bktID, []string{"name"}, &ObjectInfo{ID: id, Name: name}) 266 | } 267 | 268 | func (lh *LocalHandler) MoveTo(c Ctx, bktID, id, pid int64) error { 269 | if err := lh.acm.CheckPermission(c, MDRW, bktID); err != nil { 270 | return err 271 | } 272 | return lh.ma.SetObj(c, bktID, []string{"pid"}, &ObjectInfo{ID: id, PID: pid}) 273 | } 274 | 275 | func (lh *LocalHandler) Recycle(c Ctx, bktID, id int64) error { 276 | if err := lh.acm.CheckPermission(c, MDRW, bktID); err != nil { 277 | return err 278 | } 279 | // TODO 280 | return nil 281 | } 282 | 283 | func (lh *LocalHandler) Delete(c Ctx, bktID, id int64) error { 284 | if err := lh.acm.CheckPermission(c, MDD, bktID); err != nil { 285 | return err 286 | } 287 | // TODO 288 | return nil 289 | } 290 | 291 | type LocalAdmin struct { 292 | ma MetadataAdapter 293 | acm AccessCtrlMgr 294 | ig *idgen.IDGen 295 | } 296 | 297 | func NewLocalAdmin() Admin { 298 | dma := &DefaultMetadataAdapter{} 299 | return &LocalAdmin{ 300 | ma: dma, 301 | acm: &DefaultAccessCtrlMgr{ma: dma}, 302 | ig: idgen.NewIDGen(nil, 0), // 需要改成配置 303 | } 304 | } 305 | 306 | // 传入underlying,返回当前的,构成链式调用 307 | func (la *LocalAdmin) New(Admin) Admin { 308 | // 忽略下层handler 309 | return la 310 | } 311 | 312 | func (la *LocalAdmin) Close() { 313 | } 314 | 315 | func (la *LocalAdmin) PutBkt(c Ctx, o []*BucketInfo) error { 316 | if err := la.acm.CheckRole(c, ADMIN); err != nil { 317 | return err 318 | } 319 | return la.ma.PutBkt(c, o) 320 | } 321 | -------------------------------------------------------------------------------- /core/data.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "bufio" 5 | "crypto/md5" 6 | "fmt" 7 | "io" 8 | "io/ioutil" 9 | "os" 10 | "path/filepath" 11 | "time" 12 | 13 | "github.com/orca-zhang/ecache" 14 | ) 15 | 16 | type DataAdapter interface { 17 | SetOptions(opt Options) 18 | Close() 19 | 20 | Write(c Ctx, bktID, dataID int64, sn int, buf []byte) error 21 | 22 | Read(c Ctx, bktID, dataID int64, sn int) ([]byte, error) 23 | ReadBytes(c Ctx, bktID, dataID int64, sn, offset, size int) ([]byte, error) 24 | } 25 | 26 | const interval = time.Second 27 | 28 | var queue = ecache.NewLRUCache(16, 1024, interval) 29 | 30 | func init() { 31 | queue.Inspect(func(action int, key string, iface *interface{}, bytes []byte, status int) { 32 | // evicted / updated / deleted 33 | if (action == ecache.PUT && status <= 0) || (action == ecache.DEL && status == 1) { 34 | (*iface).(*AsyncHandle).Close() 35 | } 36 | }) 37 | 38 | go func() { 39 | // manually evict expired items 40 | for { 41 | now := time.Now().UnixNano() 42 | keys := []string{} 43 | queue.Walk(func(key string, iface *interface{}, bytes []byte, expireAt int64) bool { 44 | if expireAt < now { 45 | keys = append(keys, key) 46 | } 47 | return true 48 | }) 49 | for _, k := range keys { 50 | queue.Del(k) 51 | } 52 | time.Sleep(interval) 53 | } 54 | }() 55 | } 56 | 57 | func HasInflight() (b bool) { 58 | queue.Walk(func(key string, iface *interface{}, bytes []byte, expireAt int64) bool { 59 | b = true 60 | return false 61 | }) 62 | return 63 | } 64 | 65 | type AsyncHandle struct { 66 | F *os.File 67 | B *bufio.Writer 68 | } 69 | 70 | func (ah AsyncHandle) Close() { 71 | ah.B.Flush() 72 | ah.F.Close() 73 | } 74 | 75 | type DefaultDataAdapter struct { 76 | opt Options 77 | } 78 | 79 | func (dda *DefaultDataAdapter) SetOptions(opt Options) { 80 | dda.opt = opt 81 | } 82 | 83 | func (dda *DefaultDataAdapter) Close() { 84 | for HasInflight() { 85 | time.Sleep(100 * time.Millisecond) 86 | } 87 | } 88 | 89 | // path/<文件名hash的最后三个字节>/hash/_ 90 | func toFilePath(path string, bcktID, dataID int64, sn int) string { 91 | fileName := fmt.Sprintf("%d_%d", dataID, sn) 92 | hash := fmt.Sprintf("%X", md5.Sum([]byte(fileName))) 93 | return filepath.Join(path, fmt.Sprint(bcktID), hash[21:24], hash[8:24], fileName) 94 | } 95 | 96 | func (dda *DefaultDataAdapter) Write(c Ctx, bktID, dataID int64, sn int, buf []byte) error { 97 | path := toFilePath(ORCAS_DATA, bktID, dataID, sn) 98 | // 不用判断是否存在,以及是否创建成功,如果失败,下面写入文件之前会报错 99 | os.MkdirAll(filepath.Dir(path), 0766) 100 | 101 | f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666) 102 | if err != nil { 103 | return ERR_OPEN_FILE 104 | } 105 | 106 | ah := &AsyncHandle{F: f, B: bufio.NewWriter(f)} 107 | _, err = ah.B.Write(buf) 108 | if dda.opt.Sync { 109 | ah.Close() 110 | } else { 111 | go ah.B.Flush() 112 | queue.Put(path, ah) 113 | } 114 | return err 115 | } 116 | 117 | func (dda *DefaultDataAdapter) Read(c Ctx, bktID, dataID int64, sn int) ([]byte, error) { 118 | return ioutil.ReadFile(toFilePath(ORCAS_DATA, bktID, dataID, sn)) 119 | } 120 | 121 | func (dda *DefaultDataAdapter) ReadBytes(c Ctx, bktID, dataID int64, sn, offset, size int) ([]byte, error) { 122 | if offset == 0 && size == -1 { 123 | return dda.Read(c, bktID, dataID, sn) 124 | } 125 | 126 | f, err := os.Open(toFilePath(ORCAS_DATA, bktID, dataID, sn)) 127 | if err != nil { 128 | return nil, ERR_OPEN_FILE 129 | } 130 | defer f.Close() 131 | 132 | if offset > 0 { 133 | f.Seek(int64(offset), io.SeekStart) 134 | } 135 | 136 | var buf []byte 137 | if size == -1 { 138 | fi, err := f.Stat() 139 | if err != nil || fi.Size() < int64(offset) { 140 | return nil, ERR_READ_FILE 141 | } 142 | buf = make([]byte, fi.Size()-int64(offset)) 143 | } else { 144 | buf = make([]byte, size) 145 | } 146 | 147 | n, err := bufio.NewReaderSize(f, cap(buf)).Read(buf) 148 | if err != nil { 149 | return nil, ERR_READ_FILE 150 | } 151 | if size > 0 && n < int(size) { 152 | return buf[:n], nil 153 | } 154 | return buf, nil 155 | } 156 | -------------------------------------------------------------------------------- /core/data_test.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "testing" 7 | "time" 8 | 9 | "github.com/orca-zhang/idgen" 10 | . "github.com/smartystreets/goconvey/convey" 11 | ) 12 | 13 | func init() { 14 | bktID, _ = idgen.NewIDGen(nil, 0).New() 15 | } 16 | 17 | var c = context.TODO() 18 | 19 | func TestWrite(t *testing.T) { 20 | Convey("normal", t, func() { 21 | Convey("sync write one file", func() { 22 | dda := &DefaultDataAdapter{} 23 | dda.SetOptions(Options{ 24 | Sync: true, 25 | }) 26 | So(dda.Write(c, bktID, 4701534814223, 0, []byte("xxxxx")), ShouldBeNil) 27 | }) 28 | Convey("async write one file", func() { 29 | dda := &DefaultDataAdapter{} 30 | So(dda.Write(c, bktID, 4701535862800, 0, []byte("yyyyy")), ShouldBeNil) 31 | for HasInflight() { 32 | time.Sleep(time.Second) 33 | } 34 | }) 35 | }) 36 | Convey("empty file", t, func() { 37 | Convey("write one empty file", func() { 38 | dda := &DefaultDataAdapter{} 39 | dda.SetOptions(Options{ 40 | Sync: true, 41 | }) 42 | So(dda.Write(c, bktID, 4701534814288, 0, nil), ShouldBeNil) 43 | }) 44 | }) 45 | } 46 | 47 | func TestRead(t *testing.T) { 48 | Convey("normal", t, func() { 49 | Convey("read one file", func() { 50 | dda := &DefaultDataAdapter{} 51 | dda.SetOptions(Options{ 52 | Sync: true, 53 | }) 54 | key, _ := idgen.NewIDGen(nil, 0).New() 55 | value := []byte("test_read") 56 | So(dda.Write(c, bktID, key, 0, []byte(value)), ShouldBeNil) 57 | 58 | bs, err := dda.Read(c, bktID, key, 0) 59 | So(err, ShouldBeNil) 60 | So(bs, ShouldResemble, value) 61 | }) 62 | }) 63 | } 64 | 65 | func TestReadBytes(t *testing.T) { 66 | Convey("normal", t, func() { 67 | dda := &DefaultDataAdapter{} 68 | dda.SetOptions(Options{ 69 | Sync: true, 70 | }) 71 | key, _ := idgen.NewIDGen(nil, 0).New() 72 | value := []byte("test_read") 73 | So(dda.Write(c, bktID, key, 0, []byte(value)), ShouldBeNil) 74 | 75 | Convey("offset - 0", func() { 76 | bs, err := dda.ReadBytes(c, bktID, key, 0, 0, -1) 77 | So(err, ShouldBeNil) 78 | So(bs, ShouldResemble, value) 79 | }) 80 | 81 | Convey("offset - valid x with all", func() { 82 | bs, err := dda.ReadBytes(c, bktID, key, 0, 2, -1) 83 | So(err, ShouldBeNil) 84 | So(bs, ShouldResemble, value[2:]) 85 | }) 86 | 87 | Convey("offset - invalid x with all", func() { 88 | bs, err := dda.ReadBytes(c, bktID, key, 0, 12, -1) 89 | So(err, ShouldEqual, ERR_READ_FILE) 90 | So(bs, ShouldBeNil) 91 | }) 92 | 93 | Convey("size - valid x with valid size", func() { 94 | bs, err := dda.ReadBytes(c, bktID, key, 0, 2, len(value)-2) 95 | So(err, ShouldBeNil) 96 | So(bs, ShouldResemble, value[2:]) 97 | }) 98 | 99 | Convey("size - valid x with bigger size", func() { 100 | bs, err := dda.ReadBytes(c, bktID, key, 0, 2, len(value)*2) 101 | So(err, ShouldBeNil) 102 | So(bs, ShouldResemble, value[2:]) 103 | }) 104 | }) 105 | } 106 | 107 | func TestWriteSyncConcurrent(t *testing.T) { 108 | Convey("normal", t, func() { 109 | Convey("sync write files", func() { 110 | ig := idgen.NewIDGen(nil, 0) 111 | dda := &DefaultDataAdapter{} 112 | dda.SetOptions(Options{ 113 | Sync: true, 114 | }) 115 | bid, _ := ig.New() 116 | for i := 0; i < 20000; i++ { 117 | id, _ := ig.New() 118 | dda.Write(c, bid, id, 0, []byte(fmt.Sprint(i))) 119 | } 120 | }) 121 | }) 122 | } 123 | 124 | func TestWriteAsyncConcurrent(t *testing.T) { 125 | Convey("normal", t, func() { 126 | Convey("async write files", func() { 127 | ig := idgen.NewIDGen(nil, 0) 128 | dda := &DefaultDataAdapter{} 129 | bid, _ := ig.New() 130 | for i := 0; i < 20000; i++ { 131 | id, _ := ig.New() 132 | dda.Write(c, bid, id, 0, []byte(fmt.Sprint(i))) 133 | } 134 | for HasInflight() { 135 | time.Sleep(time.Second) 136 | } 137 | }) 138 | }) 139 | } 140 | -------------------------------------------------------------------------------- /core/jobs.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | // 回收标记删除的文件 4 | // 机器断电、上传失败(扫描脏数据) 5 | // 审计数据完成性(scrub/audit) 6 | // 合并秒传重复数据 7 | // 碎片整理:小文件离线归并打包 8 | // 递归更新文件的最新版DataID、更新时间、大小和目录大小 9 | -------------------------------------------------------------------------------- /core/meta.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "os" 7 | "path/filepath" 8 | "strings" 9 | "time" 10 | 11 | _ "github.com/mattn/go-sqlite3" 12 | b "github.com/orca-zhang/borm" 13 | ) 14 | 15 | type BucketInfo struct { 16 | ID int64 `borm:"id" json:"i,omitempty"` // 桶ID 17 | Name string `borm:"name" json:"n,omitempty"` // 桶名称 18 | UID int64 `borm:"uid" json:"u,omitempty"` // 拥有者 19 | Type int `borm:"type" json:"t,omitempty"` // 桶类型,0: none, 1: normal ... 20 | Quota int64 `borm:"quota" json:"q,omitempty"` // 配额 21 | Usage int64 `borm:"usage" json:"s,omitempty"` // 使用量,统计所有版本的原始大小 22 | // SnapshotID int64 // 最新快照版本ID 23 | } 24 | 25 | type UserInfo struct { 26 | ID int64 `borm:"id" json:"i,omitempty"` // 用户ID 27 | Usr string `borm:"usr" json:"u,omitempty"` // 用户名 28 | Pwd string `borm:"pwd" json:"p,omitempty"` // 密码,加密方式PBKDF2-HMAC-SHA256 29 | Key string `borm:"key" json:"k,omitempty"` // 数据库key 30 | Role uint32 `borm:"role" json:"r,omitempty"` // 用户角色:普通用户 / 管理员 31 | Name string `borm:"name" json:"n,omitempty"` // 名称 32 | Avatar string `borm:"avatar" json:"a,omitempty"` // 头像 33 | } 34 | 35 | // 对象类型 36 | const ( 37 | OBJ_TYPE_MALFORMED = iota - 1 38 | OBJ_TYPE_NONE 39 | OBJ_TYPE_DIR 40 | OBJ_TYPE_FILE 41 | OBJ_TYPE_VERSION 42 | OBJ_TYPE_PREVIEW 43 | ) 44 | 45 | type ObjectInfo struct { 46 | ID int64 `borm:"id" json:"i,omitempty"` // 对象ID(idgen随机生成的id) 47 | PID int64 `borm:"pid" json:"p,omitempty"` // 父对象ID 48 | MTime int64 `borm:"mtime" json:"m,omitempty"` // 更新时间,秒级时间戳 49 | DataID int64 `borm:"did" json:"d,omitempty"` // 数据ID,如果为0,说明没有数据(新创建的文件,DataID就是对象ID,作为对象的首版本数据) 50 | Type int `borm:"type" json:"t,omitempty"` // 对象类型,-1: malformed, 0: none, 1: dir, 2: file, 3: version, 4: preview(thumb/m3u8/pdf) 51 | Name string `borm:"name" json:"n,omitempty"` // 对象名称 52 | Size int64 `borm:"size" json:"s,omitempty"` // 对象的大小,目录的大小是子对象数,文件的大小是最新版本的字节数 53 | Extra string `borm:"ext" json:"e,omitempty"` // 对象的扩展信息 54 | // BktID int64 // 桶ID,如果支持引用别的桶的数据,为0说明是本桶 55 | } 56 | 57 | // 数据状态 58 | const ( 59 | DATA_NORMAL = uint32(1 << iota) // 正常 60 | DATA_ENDEC_AES256 // 是否AES加密 61 | DATA_ENDEC_SM4 // 是否SM4加密 62 | DATA_ENDEC_RESERVED // 是否保留的加密 63 | DATA_CMPR_SNAPPY // 是否snappy压缩 64 | DATA_CMPR_ZSTD // 是否zstd压缩 65 | DATA_CMPR_GZIP // 是否gzip压缩 66 | DATA_CMPR_BR // 是否brotli压缩 67 | DATA_KIND_IMG // 图片类型 68 | DATA_KIND_VIDEO // 视频类型 69 | DATA_KIND_AUDIO // 音频类型 70 | DATA_KIND_ARCHIVE // 归档类型 71 | DATA_KIND_DOCS // 文档类型 72 | DATA_KIND_FONT // 字体类型 73 | DATA_KIND_APP // 应用类型 74 | DATA_KIND_RESERVED // 未知类型 75 | 76 | DATA_MALFORMED = 0 // 是否损坏 77 | DATA_ENDEC_MASK = DATA_ENDEC_AES256 | DATA_ENDEC_SM4 | DATA_ENDEC_RESERVED 78 | DATA_CMPR_MASK = DATA_CMPR_SNAPPY | DATA_CMPR_ZSTD | DATA_CMPR_GZIP | DATA_CMPR_BR 79 | DATA_KIND_MASK = DATA_KIND_IMG | DATA_KIND_VIDEO | DATA_KIND_AUDIO | DATA_KIND_ARCHIVE | DATA_KIND_DOCS | DATA_KIND_FONT | DATA_KIND_APP | DATA_KIND_RESERVED 80 | ) 81 | 82 | type DataInfo struct { 83 | ID int64 `borm:"id" json:"i,omitempty"` // 数据ID(idgen随机生成的id) 84 | Size int64 `borm:"size" json:"s,omitempty"` // 数据的大小 85 | OrigSize int64 `borm:"o_size" json:"r,omitempty"` // 数据的原始大小 86 | HdrCRC32 uint32 `borm:"h_crc32" json:"h,omitempty"` // 头部100KB的CRC32校验值 87 | CRC32 uint32 `borm:"crc32" json:"c,omitempty"` // 整个数据的CRC32校验值(最原始数据) 88 | MD5 int64 `borm:"md5" json:"m,omitempty"` // 整个数据的MD5值(最原始数据) 89 | Cksum uint32 `borm:"cksum" json:"u,omitempty"` // 整个数据的CRC32校验值(最终数据,用于一致性审计) 90 | Kind uint32 `borm:"kind" json:"k,omitempty"` // 数据状态,正常、损坏、加密、压缩、类型(用于预览等) 91 | PkgID int64 `borm:"pkg_id" json:"p,omitempty"` // 打包数据的ID(也是idgen生成的id) 92 | PkgOffset uint32 `borm:"pkg_off" json:"g,omitempty"` // 打包数据的偏移位置 93 | // PkgID不为0说明是打包数据 94 | // SnapshotID int64 // 快照版本ID 95 | } 96 | 97 | const EmptyDataID = 4708888888888 98 | 99 | func EmptyDataInfo() *DataInfo { 100 | return &DataInfo{ 101 | ID: EmptyDataID, 102 | MD5: -1081059644736014743, 103 | Kind: DATA_NORMAL, 104 | } 105 | } 106 | 107 | const ( 108 | BKT_TBL = "bkt" 109 | USR_TBL = "usr" 110 | 111 | OBJ_TBL = "obj" 112 | DATA_TBL = "data" 113 | ) 114 | 115 | type UserMetadataAdapter interface { 116 | PutUsr(c Ctx, u *UserInfo) error 117 | GetUsr(c Ctx, ids []int64) ([]*UserInfo, error) 118 | GetUsr2(c Ctx, usr string) (*UserInfo, error) 119 | SetUsr(c Ctx, fields []string, u *UserInfo) error 120 | } 121 | 122 | type BucketMetadataAdapter interface { 123 | PutBkt(c Ctx, o []*BucketInfo) error 124 | GetBkt(c Ctx, ids []int64) ([]*BucketInfo, error) 125 | ListBkt(c Ctx, uid int64) ([]*BucketInfo, error) 126 | } 127 | 128 | type DataMetadataAdapter interface { 129 | RefData(c Ctx, bktID int64, d []*DataInfo) ([]int64, error) 130 | PutData(c Ctx, bktID int64, d []*DataInfo) error 131 | GetData(c Ctx, bktID, id int64) (*DataInfo, error) 132 | } 133 | 134 | type ObjectMetadataAdapter interface { 135 | PutObj(c Ctx, bktID int64, o []*ObjectInfo) ([]int64, error) 136 | GetObj(c Ctx, bktID int64, ids []int64) ([]*ObjectInfo, error) 137 | SetObj(c Ctx, bktID int64, fields []string, o *ObjectInfo) error 138 | ListObj(c Ctx, bktID, pid int64, wd, delim, order string, count int) ([]*ObjectInfo, int64, string, error) 139 | } 140 | 141 | type MetadataAdapter interface { 142 | Close() 143 | 144 | BucketMetadataAdapter 145 | UserMetadataAdapter 146 | DataMetadataAdapter 147 | ObjectMetadataAdapter 148 | } 149 | 150 | func GetDB(c ...interface{}) (*sql.DB, error) { 151 | param := "?_journal=WAL&cache=shared&mode=rwc&nolock=1" 152 | dirPath := ORCAS_BASE 153 | if len(c) > 1 { 154 | dirPath = filepath.Join(ORCAS_DATA, fmt.Sprint(c[1])) 155 | if c, ok := c[0].(Ctx); ok { 156 | if key := getKey(c); key != "" { 157 | param += "&key=" + key 158 | } 159 | } 160 | } 161 | os.MkdirAll(dirPath, 0766) 162 | return sql.Open("sqlite3", filepath.Join(dirPath, "meta.db")+param) 163 | } 164 | 165 | func InitDB() error { 166 | db, err := GetDB() 167 | if err != nil { 168 | return ERR_OPEN_DB 169 | } 170 | defer db.Close() 171 | 172 | db.Exec(`CREATE TABLE bkt (id BIGINT PRIMARY KEY NOT NULL, 173 | uid BIGINT NOT NULL, 174 | quota BIGINT NOT NULL, 175 | usage BIGINT NOT NULL, 176 | type TINYINT NOT NULL, 177 | name TEXT NOT NULL 178 | )`) 179 | 180 | db.Exec(`CREATE TABLE usr (id BIGINT PRIMARY KEY NOT NULL, 181 | role TINYINT NOT NULL, 182 | usr TEXT NOT NULL, 183 | pwd TEXT NOT NULL, 184 | name TEXT NOT NULL, 185 | avatar TEXT NOT NULL, 186 | key TEXT NOT NULL 187 | )`) 188 | 189 | db.Exec(`CREATE INDEX ix_uid on bkt (uid)`) 190 | db.Exec(`CREATE UNIQUE INDEX uk_name on bkt (name)`) 191 | db.Exec(`CREATE UNIQUE INDEX uk_usr on bkt (usr)`) 192 | db.Exec(`INSERT OR IGNORE INTO usr VALUES (1, 1, 'orcas', '1000:Zd54dfEjoftaY8NiAINGag==:q1yB510yT5tGIGNewItVSg==', 'orcas', '', '')`) 193 | return nil 194 | } 195 | 196 | func InitBucketDB(c Ctx, bktID int64) error { 197 | db, err := GetDB(c, bktID) 198 | if err != nil { 199 | return ERR_OPEN_DB 200 | } 201 | defer db.Close() 202 | 203 | db.Exec(`CREATE TABLE obj (id BIGINT PRIMARY KEY NOT NULL, 204 | pid BIGINT NOT NULL, 205 | did BIGINT NOT NULL, 206 | size BIGINT NOT NULL, 207 | mtime BIGINT NOT NULL, 208 | type TINYINT NOT NULL, 209 | name TEXT NOT NULL, 210 | ext TEXT NOT NULL 211 | )`) 212 | 213 | db.Exec(`CREATE TABLE data (id BIGINT PRIMARY KEY NOT NULL, 214 | size BIGINT NOT NULL, 215 | o_size BIGINT NOT NULL, 216 | md5 BIGINT NOT NULL, 217 | pkg_id BIGINT NOT NULL, 218 | pkg_off UNSIGNED INT NOT NULL, 219 | h_crc32 UNSIGNED INT NOT NULL, 220 | crc32 UNSIGNED INT NOT NULL, 221 | cksum UNSIGNED INT NOT NULL, 222 | kind SMALLINT NOT NULL 223 | )`) 224 | 225 | db.Exec(`CREATE UNIQUE INDEX uk_pid_name on obj (pid, name)`) 226 | db.Exec(`CREATE INDEX ix_ref ON data (o_size, h_crc32, crc32, md5)`) 227 | db.Exec(`PRAGMA temp_store = MEMORY`) 228 | return nil 229 | } 230 | 231 | type DefaultMetadataAdapter struct { 232 | } 233 | 234 | func (dma *DefaultMetadataAdapter) Close() { 235 | } 236 | 237 | func (dma *DefaultMetadataAdapter) RefData(c Ctx, bktID int64, d []*DataInfo) ([]int64, error) { 238 | db, err := GetDB(c, bktID) 239 | if err != nil { 240 | return nil, ERR_OPEN_DB 241 | } 242 | defer db.Close() 243 | 244 | tbl := fmt.Sprintf("tmp_%x", time.Now().UnixNano()) 245 | // 创建临时表 246 | db.Exec(`CREATE TEMPORARY TABLE ` + tbl + ` (o_size BIGINT NOT NULL, 247 | h_crc32 UNSIGNED INT NOT NULL, 248 | crc32 UNSIGNED INT NOT NULL, 249 | md5 BIGINT NOT NULL 250 | )`) 251 | // 把待查询数据放到临时表 252 | if _, err = b.Table(db, tbl, c).Insert(&d, 253 | b.Fields("o_size", "h_crc32", "crc32", "md5")); err != nil { 254 | return nil, ERR_EXEC_DB 255 | } 256 | var refs []struct { 257 | ID int64 `borm:"max(a.id)"` 258 | OrigSize int64 `borm:"b.o_size"` 259 | HdrCRC32 uint32 `borm:"b.h_crc32"` 260 | CRC32 uint32 `borm:"b.crc32"` 261 | MD5 int64 `borm:"b.md5"` 262 | } 263 | // 联表查询 264 | if _, err = b.Table(db, `data a, `+tbl+` b`, c).Select(&refs, 265 | b.Join(`on a.o_size=b.o_size and a.h_crc32=b.h_crc32 and 266 | (b.crc32=0 or b.md5=0 or (a.crc32=b.crc32 and a.md5=b.md5))`), 267 | b.GroupBy("b.o_size", "b.h_crc32", "b.crc32", "b.md5")); err != nil { 268 | return nil, ERR_QUERY_DB 269 | } 270 | // 删除临时表 271 | db.Exec(`DROP TABLE ` + tbl) 272 | 273 | // 构造辅助查询map 274 | aux := make(map[string]int64, 0) 275 | for _, ref := range refs { 276 | aux[fmt.Sprintf("%d:%d:%d:%d", ref.OrigSize, ref.HdrCRC32, ref.CRC32, ref.MD5)] = ref.ID 277 | } 278 | 279 | res := make([]int64, len(d)) 280 | for i, x := range d { 281 | // 如果最基础的数据不完整,直接跳过 282 | if x.OrigSize == 0 || x.HdrCRC32 == 0 { 283 | continue 284 | } 285 | 286 | key := fmt.Sprintf("%d:%d:%d:%d", x.OrigSize, x.HdrCRC32, x.CRC32, x.MD5) 287 | if id, ok := aux[key]; ok { 288 | // 全文件的数据没有,说明是预Ref 289 | if x.CRC32 == 0 || x.MD5 == 0 { 290 | if id > 0 { 291 | res[i] = 1 // 非0代表预Ref成功,预Ref只看数据库 292 | } 293 | } else { 294 | res[i] = id 295 | } 296 | } else { 297 | // 没有秒传成功,但是当前批次可能有一样的数据 298 | aux[key] = int64(^i) 299 | } 300 | } 301 | return res, nil 302 | } 303 | 304 | func (dma *DefaultMetadataAdapter) PutData(c Ctx, bktID int64, d []*DataInfo) error { 305 | db, err := GetDB(c, bktID) 306 | if err != nil { 307 | return ERR_OPEN_DB 308 | } 309 | defer db.Close() 310 | 311 | if _, err = b.Table(db, DATA_TBL, c).ReplaceInto(&d); err != nil { 312 | return ERR_EXEC_DB 313 | } 314 | return nil 315 | } 316 | 317 | func (dma *DefaultMetadataAdapter) GetData(c Ctx, bktID, id int64) (d *DataInfo, err error) { 318 | db, err := GetDB(c, bktID) 319 | if err != nil { 320 | return nil, ERR_OPEN_DB 321 | } 322 | defer db.Close() 323 | 324 | d = &DataInfo{} 325 | if _, err = b.Table(db, DATA_TBL, c).Select(d, b.Where(b.Eq("id", id))); err != nil { 326 | return nil, ERR_QUERY_DB 327 | } 328 | return 329 | } 330 | 331 | func (dma *DefaultMetadataAdapter) PutObj(c Ctx, bktID int64, o []*ObjectInfo) (ids []int64, err error) { 332 | db, err := GetDB(c, bktID) 333 | if err != nil { 334 | return nil, ERR_OPEN_DB 335 | } 336 | defer db.Close() 337 | 338 | for _, x := range o { 339 | ids = append(ids, x.ID) 340 | } 341 | 342 | var n int 343 | if n, err = b.Table(db, OBJ_TBL, c).InsertIgnore(&o); err != nil { 344 | return nil, ERR_EXEC_DB 345 | } 346 | if n != len(o) { 347 | var inserted []int64 348 | if _, err = b.Table(db, OBJ_TBL, c).Select(&inserted, b.Fields("id"), b.Where(b.In("id", ids))); err != nil { 349 | return nil, ERR_QUERY_DB 350 | } 351 | // 处理有冲突的情况 352 | m := make(map[int64]struct{}, 0) 353 | for _, v := range inserted { 354 | m[v] = struct{}{} 355 | } 356 | // 擦除没有插入成功的id 357 | for i, id := range ids { 358 | if _, ok := m[id]; !ok { 359 | ids[i] = 0 360 | } 361 | } 362 | } 363 | return 364 | } 365 | 366 | func (dma *DefaultMetadataAdapter) GetObj(c Ctx, bktID int64, ids []int64) (o []*ObjectInfo, err error) { 367 | db, err := GetDB(c, bktID) 368 | if err != nil { 369 | return nil, ERR_OPEN_DB 370 | } 371 | defer db.Close() 372 | 373 | if _, err = b.Table(db, OBJ_TBL, c).Select(&o, b.Where(b.In("id", ids))); err != nil { 374 | return nil, ERR_QUERY_DB 375 | } 376 | return 377 | } 378 | 379 | func (dma *DefaultMetadataAdapter) SetObj(c Ctx, bktID int64, fields []string, o *ObjectInfo) error { 380 | db, err := GetDB(c, bktID) 381 | if err != nil { 382 | return ERR_OPEN_DB 383 | } 384 | defer db.Close() 385 | 386 | if _, err = b.Table(db, OBJ_TBL, c).Update(o, b.Fields(fields...), b.Where(b.Eq("id", o.ID))); err != nil { 387 | // 如果存在同名文件,会报错:Error: stepping, UNIQUE constraint failed: obj.name (19) 388 | if strings.Contains(err.Error(), "UNIQUE constraint failed") { 389 | return ERR_DUP_KEY 390 | } 391 | return ERR_EXEC_DB 392 | } 393 | return nil 394 | } 395 | 396 | func toDelim(field string, o *ObjectInfo) string { 397 | var d interface{} 398 | switch field { 399 | case "id": 400 | return fmt.Sprint(o.ID) 401 | case "name": 402 | return fmt.Sprint(o.Name) 403 | case "mtime": 404 | d = o.MTime 405 | case "size": 406 | d = o.Size 407 | case "type": 408 | d = o.Type 409 | } 410 | return fmt.Sprintf("%v:%d", d, o.ID) 411 | } 412 | 413 | func doOrder(delim, order string, conds *[]interface{}) (string, string) { 414 | // 处理order 415 | if order == "" { 416 | order = "id" 417 | } 418 | fn := b.Gt 419 | orderBy := order 420 | switch order[0] { 421 | case '-': 422 | fn = b.Lt 423 | order = order[1:] 424 | orderBy = order + " desc" 425 | case '+': 426 | order = order[1:] 427 | orderBy = order 428 | } 429 | if order != "id" && order != "name" { 430 | orderBy = orderBy + ", id" 431 | } 432 | 433 | // 处理边界条件 434 | ds := strings.Split(delim, ":") 435 | if len(ds) > 0 && ds[0] != "" { 436 | if order == "id" || order == "name" { 437 | *conds = append(*conds, fn(order, ds[0])) 438 | } else if len(ds) == 2 { 439 | *conds = append(*conds, b.Or(fn(order, ds[0]), 440 | b.And(b.Eq(order, ds[0]), b.Gt("id", ds[1])))) 441 | } 442 | } 443 | return orderBy, order 444 | } 445 | 446 | func (dma *DefaultMetadataAdapter) ListObj(c Ctx, bktID, pid int64, 447 | wd, delim, order string, count int) (o []*ObjectInfo, 448 | cnt int64, d string, err error) { 449 | conds := []interface{}{b.Eq("pid", pid)} 450 | if wd != "" { 451 | if strings.ContainsAny(wd, "*?") { 452 | conds = append(conds, b.GLOB("name", wd)) 453 | } else { 454 | conds = append(conds, b.Eq("name", wd)) 455 | } 456 | } 457 | 458 | db, err := GetDB(c, bktID) 459 | if err != nil { 460 | return nil, 0, "", ERR_OPEN_DB 461 | } 462 | defer db.Close() 463 | 464 | if _, err = b.Table(db, OBJ_TBL, c).Select(&cnt, 465 | b.Fields("count(1)"), 466 | b.Where(conds...)); err != nil { 467 | return nil, 0, "", ERR_QUERY_DB 468 | } 469 | 470 | if count > 0 { 471 | var orderBy string 472 | orderBy, order = doOrder(delim, order, &conds) 473 | if _, err = b.Table(db, OBJ_TBL, c).Select(&o, 474 | b.Where(conds...), 475 | b.OrderBy(orderBy), 476 | b.Limit(count)); err != nil { 477 | return nil, 0, "", ERR_QUERY_DB 478 | } 479 | 480 | if len(o) > 0 { 481 | d = toDelim(order, o[len(o)-1]) 482 | } 483 | } 484 | return 485 | } 486 | 487 | func (dma *DefaultMetadataAdapter) PutUsr(c Ctx, u *UserInfo) error { 488 | db, err := GetDB() 489 | if err != nil { 490 | return ERR_OPEN_DB 491 | } 492 | defer db.Close() 493 | 494 | if _, err = b.Table(db, USR_TBL, c).ReplaceInto(&u); err != nil { 495 | return ERR_EXEC_DB 496 | } 497 | return nil 498 | } 499 | 500 | func (dma *DefaultMetadataAdapter) GetUsr(c Ctx, ids []int64) (o []*UserInfo, err error) { 501 | db, err := GetDB() 502 | if err != nil { 503 | return nil, ERR_OPEN_DB 504 | } 505 | defer db.Close() 506 | 507 | if _, err = b.Table(db, USR_TBL, c).Select(&o, b.Where(b.In("id", ids))); err != nil { 508 | return nil, ERR_QUERY_DB 509 | } 510 | return 511 | } 512 | 513 | func (dma *DefaultMetadataAdapter) GetUsr2(c Ctx, usr string) (o *UserInfo, err error) { 514 | db, err := GetDB() 515 | if err != nil { 516 | return nil, ERR_OPEN_DB 517 | } 518 | defer db.Close() 519 | 520 | o = &UserInfo{} 521 | if _, err = b.Table(db, USR_TBL, c).Select(o, b.Where(b.Eq("usr", usr))); err != nil { 522 | return nil, ERR_QUERY_DB 523 | } 524 | return 525 | } 526 | 527 | func (dma *DefaultMetadataAdapter) SetUsr(c Ctx, fields []string, u *UserInfo) error { 528 | db, err := GetDB() 529 | if err != nil { 530 | return ERR_OPEN_DB 531 | } 532 | defer db.Close() 533 | 534 | if _, err = b.Table(db, USR_TBL, c).Update(&u, 535 | b.Fields(fields...), b.Where(b.Eq("id", u.ID))); err != nil { 536 | return ERR_EXEC_DB 537 | } 538 | return nil 539 | } 540 | 541 | func (dma *DefaultMetadataAdapter) PutBkt(c Ctx, o []*BucketInfo) error { 542 | db, err := GetDB() 543 | if err != nil { 544 | return ERR_OPEN_DB 545 | } 546 | defer db.Close() 547 | 548 | if _, err = b.Table(db, BKT_TBL, c).ReplaceInto(&o); err != nil { 549 | return ERR_EXEC_DB 550 | } 551 | for _, x := range o { 552 | InitBucketDB(c, x.ID) 553 | } 554 | return nil 555 | } 556 | 557 | func (dma *DefaultMetadataAdapter) GetBkt(c Ctx, ids []int64) (o []*BucketInfo, err error) { 558 | db, err := GetDB() 559 | if err != nil { 560 | return nil, ERR_OPEN_DB 561 | } 562 | defer db.Close() 563 | 564 | if _, err = b.Table(db, BKT_TBL, c).Select(&o, b.Where(b.In("id", ids))); err != nil { 565 | return nil, ERR_QUERY_DB 566 | } 567 | return 568 | } 569 | 570 | func (dma *DefaultMetadataAdapter) ListBkt(c Ctx, uid int64) (o []*BucketInfo, err error) { 571 | db, err := GetDB() 572 | if err != nil { 573 | return nil, ERR_OPEN_DB 574 | } 575 | defer db.Close() 576 | 577 | if _, err = b.Table(db, BKT_TBL, c).Select(&o, b.Where(b.Eq("uid", uid))); err != nil { 578 | return nil, ERR_QUERY_DB 579 | } 580 | return 581 | } 582 | -------------------------------------------------------------------------------- /core/meta_test.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "testing" 7 | "time" 8 | 9 | "github.com/orca-zhang/idgen" 10 | . "github.com/smartystreets/goconvey/convey" 11 | ) 12 | 13 | var bktID = int64(0) 14 | 15 | func init() { 16 | bktID, _ = idgen.NewIDGen(nil, 0).New() 17 | } 18 | 19 | func TestListBkt(t *testing.T) { 20 | Convey("normal", t, func() { 21 | Convey("put bkt", func() { 22 | InitDB() 23 | dma := &DefaultMetadataAdapter{} 24 | id1, _ := idgen.NewIDGen(nil, 0).New() 25 | id2, _ := idgen.NewIDGen(nil, 0).New() 26 | uid, _ := idgen.NewIDGen(nil, 0).New() 27 | b1 := &BucketInfo{ 28 | ID: id1, 29 | Name: "zhangwei", 30 | UID: uid, 31 | Type: 1, 32 | } 33 | b2 := &BucketInfo{ 34 | ID: id2, 35 | Name: "zhangwei2", 36 | UID: uid, 37 | Type: 1, 38 | } 39 | So(dma.PutBkt(c, []*BucketInfo{b1, b2}), ShouldBeNil) 40 | 41 | bs, err := dma.ListBkt(c, uid) 42 | So(err, ShouldBeNil) 43 | So(len(bs), ShouldEqual, 2) 44 | So(bs[0], ShouldResemble, b1) 45 | So(bs[1], ShouldResemble, b2) 46 | }) 47 | }) 48 | } 49 | 50 | func TestRefData(t *testing.T) { 51 | Convey("normal", t, func() { 52 | dma := &DefaultMetadataAdapter{} 53 | InitBucketDB(context.TODO(), bktID) 54 | 55 | id, _ := idgen.NewIDGen(nil, 0).New() 56 | So(dma.PutData(c, bktID, []*DataInfo{{ 57 | ID: id, 58 | OrigSize: 1, 59 | HdrCRC32: 222, 60 | CRC32: 333, 61 | MD5: -1081059644736014743, 62 | Kind: DATA_NORMAL, 63 | }}), ShouldBeNil) 64 | 65 | Convey("single try ref", func() { 66 | ids, err := dma.RefData(c, bktID, []*DataInfo{{ 67 | OrigSize: 1, 68 | HdrCRC32: 222, 69 | }}) 70 | So(err, ShouldBeNil) 71 | So(len(ids), ShouldEqual, 1) 72 | So(ids[0], ShouldNotEqual, 0) 73 | 74 | ids, err = dma.RefData(c, bktID, []*DataInfo{{ 75 | OrigSize: 0, 76 | HdrCRC32: 222, 77 | }}) 78 | So(err, ShouldBeNil) 79 | So(len(ids), ShouldEqual, 1) 80 | So(ids[0], ShouldEqual, 0) 81 | 82 | ids, err = dma.RefData(c, bktID, []*DataInfo{{ 83 | OrigSize: 1, 84 | HdrCRC32: 0, 85 | }}) 86 | So(err, ShouldBeNil) 87 | So(len(ids), ShouldEqual, 1) 88 | So(ids[0], ShouldEqual, 0) 89 | 90 | ids, err = dma.RefData(c, bktID, []*DataInfo{{ 91 | OrigSize: 1, 92 | HdrCRC32: 0, 93 | CRC32: 333, 94 | MD5: -1081059644736014743, 95 | }}) 96 | So(err, ShouldBeNil) 97 | So(len(ids), ShouldEqual, 1) 98 | So(ids[0], ShouldEqual, 0) 99 | }) 100 | Convey("multiple try ref", func() { 101 | ids, err := dma.RefData(c, bktID, []*DataInfo{{ 102 | OrigSize: 1, 103 | HdrCRC32: 222, 104 | }, { 105 | OrigSize: 1, 106 | HdrCRC32: 222, 107 | }}) 108 | So(err, ShouldBeNil) 109 | So(len(ids), ShouldEqual, 2) 110 | So(ids[0], ShouldNotEqual, 0) 111 | So(ids[1], ShouldNotEqual, 0) 112 | }) 113 | Convey("multiple try ref diff", func() { 114 | ids, err := dma.RefData(c, bktID, []*DataInfo{{ 115 | OrigSize: 1, 116 | HdrCRC32: 222, 117 | }, { 118 | OrigSize: 1, 119 | HdrCRC32: 111, 120 | }}) 121 | So(err, ShouldBeNil) 122 | So(len(ids), ShouldEqual, 2) 123 | So(ids[0], ShouldNotEqual, 0) 124 | So(ids[1], ShouldEqual, 0) 125 | }) 126 | 127 | Convey("single ref", func() { 128 | ids, err := dma.RefData(c, bktID, []*DataInfo{{ 129 | OrigSize: 1, 130 | HdrCRC32: 222, 131 | CRC32: 333, 132 | MD5: -1081059644736014743, 133 | }}) 134 | So(err, ShouldBeNil) 135 | So(len(ids), ShouldEqual, 1) 136 | So(ids[0], ShouldEqual, id) 137 | 138 | ids, err = dma.RefData(c, bktID, []*DataInfo{{ 139 | OrigSize: 1, 140 | HdrCRC32: 222, 141 | CRC32: 0, 142 | MD5: -1081059644736014743, 143 | }}) 144 | So(err, ShouldBeNil) 145 | So(len(ids), ShouldEqual, 1) 146 | So(ids[0], ShouldNotEqual, id) 147 | So(ids[0], ShouldNotEqual, 0) 148 | So(ids[0], ShouldEqual, 1) 149 | 150 | ids, err = dma.RefData(c, bktID, []*DataInfo{{ 151 | OrigSize: 1, 152 | HdrCRC32: 222, 153 | CRC32: 333, 154 | }}) 155 | So(err, ShouldBeNil) 156 | So(len(ids), ShouldEqual, 1) 157 | So(ids[0], ShouldNotEqual, id) 158 | So(ids[0], ShouldEqual, 1) 159 | }) 160 | 161 | Convey("multiple ref", func() { 162 | ids, err := dma.RefData(c, bktID, []*DataInfo{{ 163 | OrigSize: 1, 164 | HdrCRC32: 222, 165 | CRC32: 333, 166 | MD5: -1081059644736014743, 167 | }, { 168 | OrigSize: 1, 169 | HdrCRC32: 222, 170 | CRC32: 333, 171 | MD5: -1081059644736014743, 172 | }}) 173 | So(err, ShouldBeNil) 174 | So(len(ids), ShouldEqual, 2) 175 | So(ids[0], ShouldNotEqual, 0) 176 | So(ids[1], ShouldNotEqual, 0) 177 | }) 178 | Convey("multiple ref diff", func() { 179 | ids, err := dma.RefData(c, bktID, []*DataInfo{{ 180 | OrigSize: 1, 181 | HdrCRC32: 222, 182 | CRC32: 333, 183 | MD5: -1081059644736014743, 184 | }, { 185 | OrigSize: 1, 186 | HdrCRC32: 111, 187 | CRC32: 333, 188 | MD5: -1081059644736014743, 189 | }}) 190 | So(err, ShouldBeNil) 191 | So(len(ids), ShouldEqual, 2) 192 | So(ids[0], ShouldEqual, id) 193 | So(ids[1], ShouldEqual, 0) 194 | }) 195 | 196 | Convey("multiple ref same but do not exist", func() { 197 | ids, err := dma.RefData(c, bktID, []*DataInfo{{ 198 | OrigSize: 1, 199 | HdrCRC32: 222, 200 | CRC32: 333, 201 | MD5: -1081059644736014744, 202 | }, { 203 | OrigSize: 1, 204 | HdrCRC32: 222, 205 | CRC32: 333, 206 | MD5: -1081059644736014744, 207 | }}) 208 | So(err, ShouldBeNil) 209 | So(len(ids), ShouldEqual, 2) 210 | So(ids[0], ShouldEqual, 0) 211 | So(ids[1], ShouldEqual, ^0) 212 | }) 213 | }) 214 | } 215 | 216 | func TestGetData(t *testing.T) { 217 | Convey("normal", t, func() { 218 | Convey("get data info", func() { 219 | InitBucketDB(context.TODO(), bktID) 220 | dma := &DefaultMetadataAdapter{} 221 | id, _ := idgen.NewIDGen(nil, 0).New() 222 | d := &DataInfo{ 223 | ID: id, 224 | Size: 1, 225 | Kind: DATA_NORMAL, 226 | } 227 | So(dma.PutData(c, bktID, []*DataInfo{d}), ShouldBeNil) 228 | 229 | d1, err := dma.GetData(c, bktID, id) 230 | So(err, ShouldBeNil) 231 | So(d1, ShouldResemble, d) 232 | }) 233 | }) 234 | } 235 | 236 | func TestPutObj(t *testing.T) { 237 | Convey("normal", t, func() { 238 | Convey("put same name obj", func() { 239 | InitBucketDB(context.TODO(), bktID) 240 | 241 | dma := &DefaultMetadataAdapter{} 242 | ig := idgen.NewIDGen(nil, 0) 243 | id, _ := ig.New() 244 | pid, _ := ig.New() 245 | did, _ := ig.New() 246 | d := &ObjectInfo{ 247 | ID: id, 248 | PID: pid, 249 | MTime: time.Now().Unix(), 250 | DataID: did, 251 | Type: OBJ_TYPE_DIR, 252 | Name: "test", 253 | Size: 1, 254 | Extra: "{}", 255 | } 256 | ids, err := dma.PutObj(c, bktID, []*ObjectInfo{d}) 257 | So(err, ShouldBeNil) 258 | So(len(ids), ShouldEqual, 1) 259 | So(ids[0], ShouldEqual, id) 260 | 261 | id1, _ := ig.New() 262 | d1 := &ObjectInfo{ 263 | ID: id1, 264 | PID: pid, 265 | MTime: time.Now().Unix(), 266 | DataID: did, 267 | Type: OBJ_TYPE_DIR, 268 | Name: "test", 269 | Size: 1, 270 | Extra: "{}", 271 | } 272 | // push same name diff id obj 273 | ids, err = dma.PutObj(c, bktID, []*ObjectInfo{d1, d}) 274 | So(err, ShouldBeNil) 275 | So(len(ids), ShouldEqual, 2) 276 | So(ids[0], ShouldEqual, 0) 277 | // same name same id, like idempotent 278 | So(ids[1], ShouldEqual, id) 279 | }) 280 | }) 281 | } 282 | 283 | func TestGetObj(t *testing.T) { 284 | Convey("normal", t, func() { 285 | Convey("get obj info", func() { 286 | InitBucketDB(context.TODO(), bktID) 287 | 288 | dma := &DefaultMetadataAdapter{} 289 | ig := idgen.NewIDGen(nil, 0) 290 | id, _ := ig.New() 291 | pid, _ := ig.New() 292 | did, _ := ig.New() 293 | d := &ObjectInfo{ 294 | ID: id, 295 | PID: pid, 296 | MTime: time.Now().Unix(), 297 | DataID: did, 298 | Type: OBJ_TYPE_DIR, 299 | Name: "test", 300 | Size: 1, 301 | Extra: "{}", 302 | } 303 | ids, err := dma.PutObj(c, bktID, []*ObjectInfo{d}) 304 | So(err, ShouldBeNil) 305 | So(len(ids), ShouldEqual, 1) 306 | So(ids[0], ShouldNotEqual, 0) 307 | 308 | d1, err := dma.GetObj(c, bktID, ids) 309 | So(err, ShouldBeNil) 310 | So(len(d1), ShouldEqual, 1) 311 | So(d1[0], ShouldResemble, d) 312 | }) 313 | }) 314 | } 315 | 316 | func TestSetObj(t *testing.T) { 317 | Convey("normal", t, func() { 318 | InitBucketDB(context.TODO(), bktID) 319 | 320 | dma := &DefaultMetadataAdapter{} 321 | ig := idgen.NewIDGen(nil, 0) 322 | id, _ := ig.New() 323 | pid, _ := ig.New() 324 | did, _ := ig.New() 325 | d := &ObjectInfo{ 326 | ID: id, 327 | PID: pid, 328 | MTime: time.Now().Unix(), 329 | DataID: did, 330 | Type: OBJ_TYPE_DIR, 331 | Name: "test", 332 | Size: 1, 333 | Extra: "{}", 334 | } 335 | id1, _ := ig.New() 336 | d1 := &ObjectInfo{ 337 | ID: id1, 338 | PID: pid, 339 | MTime: time.Now().Unix(), 340 | DataID: did, 341 | Type: OBJ_TYPE_DIR, 342 | Name: "test2", 343 | Size: 1, 344 | Extra: "{}", 345 | } 346 | ids, err := dma.PutObj(c, bktID, []*ObjectInfo{d, d1}) 347 | So(err, ShouldBeNil) 348 | So(len(ids), ShouldEqual, 2) 349 | So(ids[0], ShouldNotEqual, 0) 350 | 351 | o, err := dma.GetObj(c, bktID, ids) 352 | So(err, ShouldBeNil) 353 | So(len(o), ShouldEqual, 2) 354 | So(o[0], ShouldResemble, d) 355 | So(o[1], ShouldResemble, d1) 356 | Convey("set obj name", func() { 357 | d.Name = "test1" 358 | dma.SetObj(c, bktID, []string{"name"}, &ObjectInfo{ID: id, Name: d.Name}) 359 | o, err = dma.GetObj(c, bktID, ids) 360 | So(err, ShouldBeNil) 361 | So(len(o), ShouldEqual, 2) 362 | So(o[0], ShouldResemble, d) 363 | So(o[1], ShouldResemble, d1) 364 | }) 365 | 366 | Convey("same obj name", func() { 367 | d.Name = "test2" 368 | err := dma.SetObj(c, bktID, []string{"name"}, &ObjectInfo{ID: id, Name: d.Name}) 369 | So(err, ShouldEqual, ERR_DUP_KEY) 370 | }) 371 | }) 372 | } 373 | 374 | func TestListObj(t *testing.T) { 375 | Convey("normal", t, func() { 376 | InitBucketDB(context.TODO(), bktID) 377 | 378 | dma := &DefaultMetadataAdapter{} 379 | ig := idgen.NewIDGen(nil, 0) 380 | pid, _ := ig.New() 381 | 382 | id1, _ := ig.New() 383 | id2, _ := ig.New() 384 | id3, _ := ig.New() 385 | id4, _ := ig.New() 386 | id5, _ := ig.New() 387 | 388 | did1, _ := ig.New() 389 | did2, _ := ig.New() 390 | did3, _ := ig.New() 391 | did4, _ := ig.New() 392 | did5, _ := ig.New() 393 | 394 | now := time.Now().Unix() 395 | d1 := &ObjectInfo{ 396 | ID: id1, 397 | PID: pid, 398 | MTime: now + 1, 399 | DataID: did1, 400 | Type: OBJ_TYPE_DIR, 401 | Name: "test1", 402 | Size: 0, 403 | Extra: "{}", 404 | } 405 | d2 := &ObjectInfo{ 406 | ID: id2, 407 | PID: pid, 408 | MTime: now + 2, 409 | DataID: did2, 410 | Type: OBJ_TYPE_FILE, 411 | Name: "test2", 412 | Size: 2, 413 | Extra: "{}", 414 | } 415 | d3 := &ObjectInfo{ 416 | ID: id3, 417 | PID: pid, 418 | MTime: now + 3, 419 | DataID: did3, 420 | Type: OBJ_TYPE_VERSION, 421 | Name: "test3", 422 | Size: 3, 423 | Extra: "{}", 424 | } 425 | d4 := &ObjectInfo{ 426 | ID: id4, 427 | PID: pid, 428 | MTime: now + 3, 429 | DataID: did4, 430 | Type: OBJ_TYPE_PREVIEW, 431 | Name: "test4", 432 | Size: 3, 433 | Extra: "{}", 434 | } 435 | d5 := &ObjectInfo{ 436 | ID: id5, 437 | PID: pid, 438 | MTime: now + 4, 439 | DataID: did5, 440 | Type: OBJ_TYPE_PREVIEW, 441 | Name: "test5", 442 | Size: 4, 443 | Extra: "{}", 444 | } 445 | ids, err := dma.PutObj(c, bktID, []*ObjectInfo{d1, d2, d3, d4, d5}) 446 | So(err, ShouldBeNil) 447 | So(len(ids), ShouldEqual, 5) 448 | 449 | Convey("list obj pagination", func() { 450 | o, cnt, d, err := dma.ListObj(c, bktID, pid, "", "", "", 2) 451 | So(err, ShouldBeNil) 452 | So(len(o), ShouldEqual, 2) 453 | So(cnt, ShouldEqual, 5) 454 | So(d, ShouldEqual, fmt.Sprint(id2)) 455 | 456 | o, cnt, d, err = dma.ListObj(c, bktID, pid, "", d, "", 2) 457 | So(err, ShouldBeNil) 458 | So(len(o), ShouldEqual, 2) 459 | So(cnt, ShouldEqual, 5) 460 | So(d, ShouldEqual, fmt.Sprint(id4)) 461 | 462 | o, cnt, d, err = dma.ListObj(c, bktID, pid, "", d, "", 2) 463 | So(err, ShouldBeNil) 464 | So(len(o), ShouldEqual, 1) 465 | So(cnt, ShouldEqual, 5) 466 | So(d, ShouldEqual, fmt.Sprint(id5)) 467 | 468 | o, cnt, d, err = dma.ListObj(c, bktID, pid, "", d, "", 2) 469 | So(err, ShouldBeNil) 470 | So(len(o), ShouldEqual, 0) 471 | So(cnt, ShouldEqual, 5) 472 | So(d, ShouldEqual, "") 473 | }) 474 | 475 | Convey("word", func() { 476 | o, cnt, d, err := dma.ListObj(c, bktID, pid, "xxx", "", "", 2) 477 | So(err, ShouldBeNil) 478 | So(len(o), ShouldEqual, 0) 479 | So(cnt, ShouldEqual, 0) 480 | So(d, ShouldEqual, "") 481 | 482 | o, cnt, d, err = dma.ListObj(c, bktID, pid, "test1", "", "", 2) 483 | So(err, ShouldBeNil) 484 | So(len(o), ShouldEqual, 1) 485 | So(cnt, ShouldEqual, 1) 486 | So(d, ShouldEqual, fmt.Sprint(id1)) 487 | 488 | o, cnt, d, err = dma.ListObj(c, bktID, pid, "?es*", "", "", 2) 489 | So(err, ShouldBeNil) 490 | So(len(o), ShouldEqual, 2) 491 | So(cnt, ShouldEqual, 5) 492 | So(d, ShouldEqual, fmt.Sprint(id2)) 493 | }) 494 | 495 | Convey("order", func() { 496 | o, cnt, d, err := dma.ListObj(c, bktID, pid, "", "", "+id", 5) 497 | So(err, ShouldBeNil) 498 | So(len(o), ShouldEqual, 5) 499 | So(cnt, ShouldEqual, 5) 500 | So(d, ShouldEqual, fmt.Sprint(id5)) 501 | So(o, ShouldResemble, []*ObjectInfo{d1, d2, d3, d4, d5}) 502 | 503 | o, cnt, d, err = dma.ListObj(c, bktID, pid, "", "", "-id", 5) 504 | So(err, ShouldBeNil) 505 | So(len(o), ShouldEqual, 5) 506 | So(cnt, ShouldEqual, 5) 507 | So(d, ShouldEqual, fmt.Sprint(id1)) 508 | So(o, ShouldResemble, []*ObjectInfo{d5, d4, d3, d2, d1}) 509 | 510 | o, cnt, d, err = dma.ListObj(c, bktID, pid, "", "", "-name", 5) 511 | So(err, ShouldBeNil) 512 | So(len(o), ShouldEqual, 5) 513 | So(cnt, ShouldEqual, 5) 514 | So(d, ShouldEqual, "test1") 515 | So(o, ShouldResemble, []*ObjectInfo{d5, d4, d3, d2, d1}) 516 | 517 | // 比较非id或者name的时候,相同值的排序是否稳定 518 | o, cnt, d, err = dma.ListObj(c, bktID, pid, "", "", "+mtime", 3) 519 | So(err, ShouldBeNil) 520 | So(len(o), ShouldEqual, 3) 521 | So(cnt, ShouldEqual, 5) 522 | So(d, ShouldEqual, fmt.Sprintf("%d:%d", now+3, id3)) 523 | So(o, ShouldResemble, []*ObjectInfo{d1, d2, d3}) 524 | 525 | o, cnt, d, err = dma.ListObj(c, bktID, pid, "", "", "-mtime", 2) 526 | So(err, ShouldBeNil) 527 | So(len(o), ShouldEqual, 2) 528 | So(cnt, ShouldEqual, 5) 529 | So(d, ShouldEqual, fmt.Sprintf("%d:%d", now+3, id3)) 530 | So(o, ShouldResemble, []*ObjectInfo{d5, d3}) 531 | 532 | o, cnt, d, err = dma.ListObj(c, bktID, pid, "", d, "-mtime", 2) 533 | So(err, ShouldBeNil) 534 | So(len(o), ShouldEqual, 2) 535 | So(cnt, ShouldEqual, 5) 536 | So(d, ShouldEqual, fmt.Sprintf("%d:%d", now+2, id2)) 537 | So(o, ShouldResemble, []*ObjectInfo{d4, d2}) 538 | 539 | o, cnt, d, err = dma.ListObj(c, bktID, pid, "", "", "+size", 3) 540 | So(err, ShouldBeNil) 541 | So(len(o), ShouldEqual, 3) 542 | So(cnt, ShouldEqual, 5) 543 | So(d, ShouldEqual, fmt.Sprintf("%d:%d", 3, id3)) 544 | So(o, ShouldResemble, []*ObjectInfo{d1, d2, d3}) 545 | 546 | o, cnt, d, err = dma.ListObj(c, bktID, pid, "", "", "-size", 2) 547 | So(err, ShouldBeNil) 548 | So(len(o), ShouldEqual, 2) 549 | So(cnt, ShouldEqual, 5) 550 | So(d, ShouldEqual, fmt.Sprintf("%d:%d", 3, id3)) 551 | So(o, ShouldResemble, []*ObjectInfo{d5, d3}) 552 | 553 | o, cnt, d, err = dma.ListObj(c, bktID, pid, "", d, "-size", 2) 554 | So(err, ShouldBeNil) 555 | So(len(o), ShouldEqual, 2) 556 | So(cnt, ShouldEqual, 5) 557 | So(d, ShouldEqual, fmt.Sprintf("%d:%d", 2, id2)) 558 | So(o, ShouldResemble, []*ObjectInfo{d4, d2}) 559 | 560 | o, cnt, d, err = dma.ListObj(c, bktID, pid, "", "", "+type", 3) 561 | So(err, ShouldBeNil) 562 | So(len(o), ShouldEqual, 3) 563 | So(cnt, ShouldEqual, 5) 564 | So(d, ShouldEqual, fmt.Sprintf("%d:%d", 3, id3)) 565 | So(o, ShouldResemble, []*ObjectInfo{d1, d2, d3}) 566 | 567 | o, cnt, d, err = dma.ListObj(c, bktID, pid, "", "", "-type", 1) 568 | So(err, ShouldBeNil) 569 | So(len(o), ShouldEqual, 1) 570 | So(cnt, ShouldEqual, 5) 571 | So(d, ShouldEqual, fmt.Sprintf("%d:%d", OBJ_TYPE_PREVIEW, id4)) 572 | So(o, ShouldResemble, []*ObjectInfo{d4}) 573 | 574 | o, cnt, d, err = dma.ListObj(c, bktID, pid, "", d, "-type", 1) 575 | So(err, ShouldBeNil) 576 | So(len(o), ShouldEqual, 1) 577 | So(cnt, ShouldEqual, 5) 578 | So(d, ShouldEqual, fmt.Sprintf("%d:%d", OBJ_TYPE_PREVIEW, id5)) 579 | So(o, ShouldResemble, []*ObjectInfo{d5}) 580 | }) 581 | 582 | Convey("list obj with 0 count", func() { 583 | o, cnt, d, err := dma.ListObj(c, bktID, pid, "", "", "", 0) 584 | So(err, ShouldBeNil) 585 | So(len(o), ShouldEqual, 0) 586 | So(cnt, ShouldEqual, 5) 587 | So(d, ShouldBeEmpty) 588 | }) 589 | }) 590 | } 591 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/orcastor/orcas 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/andybalholm/brotli v1.0.4 // indirect 7 | github.com/gin-gonic/gin v1.8.1 8 | github.com/go-kit/kit v0.10.0 9 | github.com/golang-jwt/jwt v3.2.2+incompatible 10 | github.com/gotomicro/ego v1.1.5 11 | github.com/h2non/filetype v1.1.4-0.20231228185113-6469358c2bcb 12 | github.com/klauspost/compress v1.11.4 13 | github.com/mattn/go-sqlite3 v1.14.6 14 | github.com/mholt/archiver/v3 v3.5.1 15 | github.com/mkmueller/aes256 v0.0.0-20171112015946-1ce9e9b3f91a 16 | github.com/orca-zhang/borm v0.0.0-20220921122740-135bb95b3a65 17 | github.com/orca-zhang/ecache v1.1.1 18 | github.com/orca-zhang/idgen v0.0.0-20220909041521-2662ff4c3c3e 19 | github.com/prometheus/client_golang v1.12.1 20 | github.com/smartystreets/goconvey v1.7.2 21 | github.com/tjfoc/gmsm v1.4.1 22 | golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0 23 | ) 24 | -------------------------------------------------------------------------------- /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 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 7 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 8 | cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= 9 | cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= 10 | cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= 11 | cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= 12 | cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= 13 | cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= 14 | cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= 15 | cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= 16 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 17 | cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= 18 | cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= 19 | cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= 20 | cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= 21 | cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= 22 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 23 | cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= 24 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 25 | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= 26 | cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= 27 | cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= 28 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 29 | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= 30 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= 31 | cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= 32 | cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= 33 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 34 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 35 | github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= 36 | github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= 37 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 38 | github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= 39 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 40 | github.com/RaMin0/gin-health-check v0.0.0-20180807004848-a677317b3f01 h1:GHwYgY6lZR2QKIuYH5k8DFK4e2h2oEEOtyI9E6qrpII= 41 | github.com/RaMin0/gin-health-check v0.0.0-20180807004848-a677317b3f01/go.mod h1:vZ/F780spvlix7Qg0/17Uj0SayI+CqtybQHtPEV9RTE= 42 | github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= 43 | github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= 44 | github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk= 45 | github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= 46 | github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= 47 | github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= 48 | github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= 49 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 50 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 51 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 52 | github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 53 | github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= 54 | github.com/alibaba/sentinel-golang v1.0.3 h1:x/04ZV3ONFsLaNYC/tOEEaZZQIJjhxDSxwZGxiWOQhY= 55 | github.com/alibaba/sentinel-golang v1.0.3/go.mod h1:Lag5rIYyJiPOylK8Kku2P+a23gdKMMqzQS7wTnjWEpk= 56 | github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= 57 | github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= 58 | github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= 59 | github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= 60 | github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20220418222510-f25a4f6275ed h1:ue9pVfIcP+QMEjfgo/Ez4ZjNZfonGgR6NgjMaJMu1Cg= 61 | github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20220418222510-f25a4f6275ed/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= 62 | github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= 63 | github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= 64 | github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= 65 | github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= 66 | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 67 | github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= 68 | github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= 69 | github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= 70 | github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= 71 | github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= 72 | github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= 73 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 74 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 75 | github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 76 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 77 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 78 | github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= 79 | github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= 80 | github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= 81 | github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= 82 | github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= 83 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 84 | github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= 85 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 86 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 87 | github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= 88 | github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 89 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 90 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 91 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 92 | github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= 93 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 94 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 95 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 96 | github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= 97 | github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 98 | github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 99 | github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 100 | github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 101 | github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 102 | github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= 103 | github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= 104 | github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 h1:sDMmm+q/3+BukdIpxwO365v/Rbspp2Nt5XntgQRXq8Q= 105 | github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= 106 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 107 | github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 108 | github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 109 | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 110 | github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= 111 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 112 | github.com/dave/dst v0.26.2/go.mod h1:UMDJuIRPfyUCC78eFuB+SV/WI8oDeyFDvM/JR6NI3IU= 113 | github.com/dave/gopackages v0.0.0-20170318123100-46e7023ec56e/go.mod h1:i00+b/gKdIDIxuLDFob7ustLAVqhsZRk2qVZrArELGQ= 114 | github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= 115 | github.com/dave/kerr v0.0.0-20170318121727-bc25dd6abe8e/go.mod h1:qZqlPyPvfsDJt+3wHJ1EvSXDuVjFTK0j2p/ca+gtsb8= 116 | github.com/dave/rebecca v0.9.1/go.mod h1:N6XYdMD/OKw3lkF3ywh8Z6wPGuwNFDNtWYEMFWEmXBA= 117 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 118 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 119 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 120 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 121 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= 122 | github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY= 123 | github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= 124 | github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= 125 | github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 126 | github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= 127 | github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= 128 | github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= 129 | github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= 130 | github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= 131 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 132 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 133 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 134 | github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 135 | github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= 136 | github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= 137 | github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= 138 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 139 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 140 | github.com/felixge/fgprof v0.9.2/go.mod h1:+VNi+ZXtHIQ6wIw6bUT8nXQRefQflWECoFyRealT5sg= 141 | github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= 142 | github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= 143 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 144 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 145 | github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= 146 | github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= 147 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 148 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 149 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 150 | github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= 151 | github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8= 152 | github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= 153 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 154 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 155 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 156 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 157 | github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 158 | github.com/go-kit/kit v0.10.0 h1:dXFJfIHVvUcpSgDOV+Ne6t7jXri8Tfv2uOLHUZ2XNuo= 159 | github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= 160 | github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= 161 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 162 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 163 | github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= 164 | github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 165 | github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= 166 | github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 167 | github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= 168 | github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= 169 | github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= 170 | github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= 171 | github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 172 | github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= 173 | github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 174 | github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= 175 | github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= 176 | github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= 177 | github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= 178 | github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= 179 | github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= 180 | github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= 181 | github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0= 182 | github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= 183 | github.com/go-redis/redis/v7 v7.4.1 h1:PASvf36gyUpr2zdOUS/9Zqc80GbM+9BDyiJSJDDOrTI= 184 | github.com/go-redis/redis/v7 v7.4.1/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg= 185 | github.com/go-redis/redis/v8 v8.11.4/go.mod h1:2Z2wHZXdQpCDXEGzqMockDpNyYvi2l4Pxt6RJr792+w= 186 | github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= 187 | github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= 188 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 189 | github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= 190 | github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM= 191 | github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 192 | github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= 193 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 194 | github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 195 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 196 | github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= 197 | github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= 198 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 199 | github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= 200 | github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= 201 | github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 202 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 203 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 204 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 205 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 206 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 207 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 208 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 209 | github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 210 | github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 211 | github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 212 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 213 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 214 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 215 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 216 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 217 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 218 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 219 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 220 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 221 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 222 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 223 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 224 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 225 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 226 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 227 | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 228 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 229 | github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 230 | github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw= 231 | github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 232 | github.com/gomodule/redigo v1.8.6/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= 233 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 234 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 235 | github.com/google/cel-go v0.11.3 h1:MnUpbcMtr/eA8vRTEYSru+fyCAgGUYLrY/49vUvphbI= 236 | github.com/google/cel-go v0.11.3/go.mod h1:Av7CU6r6X3YmcHR9GXqVDaEJYfEtSxl6wvIjUQTriCw= 237 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 238 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 239 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 240 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 241 | github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 242 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 243 | github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 244 | github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 245 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 246 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 247 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 248 | github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= 249 | github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= 250 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 251 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 252 | github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 253 | github.com/google/pprof v0.0.0-20181127221834-b4f47329b966/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 254 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 255 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 256 | github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 257 | github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 258 | github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 259 | github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 260 | github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 261 | github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= 262 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 263 | github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 264 | github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 265 | github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= 266 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 267 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 268 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 269 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= 270 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 271 | github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= 272 | github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= 273 | github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= 274 | github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 275 | github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= 276 | github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 277 | github.com/gotomicro/ego v1.1.5 h1:pmnJ/Raiff+BOfAHg49imEjqN9cLuOk54RU4Z7NjJKs= 278 | github.com/gotomicro/ego v1.1.5/go.mod h1:vHAfoVmngjJCC2eRfP7weWeLxs46QpWok2y7PmU0NF0= 279 | github.com/gotomicro/logrotate v0.0.0-20211108034117-46d53eedc960 h1:vp5ls3l11a1XCaU3pJUBV85PwRW47qybqdYEIWCGLIo= 280 | github.com/gotomicro/logrotate v0.0.0-20211108034117-46d53eedc960/go.mod h1:jKlh8i9m79fE8HAO28kYLN70l87bb7olTLuX/Blex/U= 281 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= 282 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 283 | github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= 284 | github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= 285 | github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= 286 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 h1:BZHcxBETFHIdVyhyEfOvn/RdU/QGdLI4y34qQGjGWO0= 287 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= 288 | github.com/h2non/filetype v1.1.4-0.20231228185113-6469358c2bcb h1:GlQyMv2C48qmfPItvAXFoyN341Swxp9JNVeUZxnmbJw= 289 | github.com/h2non/filetype v1.1.4-0.20231228185113-6469358c2bcb/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY= 290 | github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= 291 | github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= 292 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 293 | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 294 | github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 295 | github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= 296 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 297 | github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= 298 | github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= 299 | github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= 300 | github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 301 | github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 302 | github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 303 | github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= 304 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 305 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 306 | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= 307 | github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= 308 | github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= 309 | github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= 310 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 311 | github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= 312 | github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= 313 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 314 | github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= 315 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 316 | github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= 317 | github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= 318 | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 319 | github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= 320 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 321 | github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 322 | github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 323 | github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 324 | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 325 | github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 326 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 327 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 328 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 329 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 330 | github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= 331 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 332 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 333 | github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= 334 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 335 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 336 | github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= 337 | github.com/klauspost/compress v1.11.4 h1:kz40R/YWls3iqT9zX9AHN3WoVsrAWVyui5sxuLqiXqU= 338 | github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= 339 | github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= 340 | github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= 341 | github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= 342 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 343 | github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 344 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 345 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 346 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 347 | github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= 348 | github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= 349 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 350 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 351 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 352 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 353 | github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= 354 | github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= 355 | github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= 356 | github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= 357 | github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= 358 | github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= 359 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 360 | github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 361 | github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 362 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 363 | github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= 364 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 365 | github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= 366 | github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= 367 | github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= 368 | github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= 369 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 370 | github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo= 371 | github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4= 372 | github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 373 | github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= 374 | github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 375 | github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 376 | github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= 377 | github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= 378 | github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 379 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 380 | github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= 381 | github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 382 | github.com/mkmueller/aes256 v0.0.0-20171112015946-1ce9e9b3f91a h1:vOQuCMqFxk8aREFAOOedKDJMqx8fyG+//2lxdOkfSJo= 383 | github.com/mkmueller/aes256 v0.0.0-20171112015946-1ce9e9b3f91a/go.mod h1:tVcd4VUIMaGT8R9bI9fe9WqYU+fjAcdjElBoZ1X2vuQ= 384 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 385 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 386 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 387 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 388 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 389 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 390 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 391 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 392 | github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 393 | github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= 394 | github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= 395 | github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= 396 | github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= 397 | github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= 398 | github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= 399 | github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= 400 | github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ= 401 | github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= 402 | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= 403 | github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= 404 | github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= 405 | github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= 406 | github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= 407 | github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= 408 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 409 | github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 410 | github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 411 | github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= 412 | github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= 413 | github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= 414 | github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 415 | github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 416 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 417 | github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= 418 | github.com/onsi/gomega v1.16.0 h1:6gjqkI8iiRHMvdccRJM8rVKjCWk6ZIm6FTm3ddIe4/c= 419 | github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= 420 | github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= 421 | github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= 422 | github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= 423 | github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= 424 | github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= 425 | github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= 426 | github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= 427 | github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= 428 | github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= 429 | github.com/orca-zhang/borm v0.0.0-20220921122740-135bb95b3a65 h1:Tr5gVyPvloUm2q9HuUAczFCpOkTysK2r/1RjtQRnTuw= 430 | github.com/orca-zhang/borm v0.0.0-20220921122740-135bb95b3a65/go.mod h1:oPiCpRWKQyQGeQY4wmLUz5RBx2KTsexah1Dp6A31In4= 431 | github.com/orca-zhang/ecache v1.1.1 h1:krk4+/ihsLPg9/k2zYeRa7dNespTQsFnRcLWaJGFNds= 432 | github.com/orca-zhang/ecache v1.1.1/go.mod h1:1/6HCddx1iAbafDyRdddq5v/0rZf5sCU/fkLZ3BhciQ= 433 | github.com/orca-zhang/idgen v0.0.0-20220909041521-2662ff4c3c3e h1:Ae9G+JXfh/uot6qX0yJ9av+NqQaspOsuJ/tYYeNu6eU= 434 | github.com/orca-zhang/idgen v0.0.0-20220909041521-2662ff4c3c3e/go.mod h1:QgMA6kZTutcdAVcYrWX06UvJ8kI/+7Bx5RaREcN+3Qg= 435 | github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= 436 | github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 437 | github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= 438 | github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU= 439 | github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= 440 | github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= 441 | github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= 442 | github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= 443 | github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= 444 | github.com/pierrec/lz4/v4 v4.1.2 h1:qvY3YFXRQE/XB8MlLzJH7mSzBs74eA2gg52YTk6jUPM= 445 | github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= 446 | github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= 447 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 448 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 449 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 450 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 451 | github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= 452 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 453 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 454 | github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= 455 | github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= 456 | github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= 457 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 458 | github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= 459 | github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= 460 | github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= 461 | github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= 462 | github.com/prometheus/client_golang v1.9.0/go.mod h1:FqZLKOZnGdFAhOK4nqGHa7D66IdsO+O441Eve7ptJDU= 463 | github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= 464 | github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk= 465 | github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= 466 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 467 | github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 468 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 469 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 470 | github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 471 | github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= 472 | github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 473 | github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 474 | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 475 | github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= 476 | github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= 477 | github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= 478 | github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= 479 | github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= 480 | github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= 481 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 482 | github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 483 | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 484 | github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= 485 | github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= 486 | github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= 487 | github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= 488 | github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= 489 | github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= 490 | github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= 491 | github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= 492 | github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= 493 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= 494 | github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= 495 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 496 | github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= 497 | github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= 498 | github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= 499 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 500 | github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= 501 | github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= 502 | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= 503 | github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= 504 | github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= 505 | github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= 506 | github.com/shirou/gopsutil/v3 v3.21.6 h1:vU7jrp1Ic/2sHB7w6UNs7MIkn7ebVtTb5D9j45o9VYE= 507 | github.com/shirou/gopsutil/v3 v3.21.6/go.mod h1:JfVbDpIBLVzT8oKbvMg9P3wEIMDDpVn+LwHTKj0ST88= 508 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 509 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 510 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 511 | github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= 512 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 513 | github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= 514 | github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= 515 | github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 516 | github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= 517 | github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= 518 | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= 519 | github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= 520 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 521 | github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= 522 | github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 523 | github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= 524 | github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 525 | github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= 526 | github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= 527 | github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= 528 | github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= 529 | github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= 530 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 531 | github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= 532 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 533 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 534 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 535 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 536 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 537 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 538 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 539 | github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= 540 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 541 | github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= 542 | github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= 543 | github.com/tklauser/go-sysconf v0.3.6 h1:oc1sJWvKkmvIxhDHeKWvZS4f6AW+YcoguSfRF2/Hmo4= 544 | github.com/tklauser/go-sysconf v0.3.6/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= 545 | github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA= 546 | github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= 547 | github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 548 | github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= 549 | github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo= 550 | github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= 551 | github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= 552 | github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= 553 | github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= 554 | github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= 555 | github.com/ulikunitz/xz v0.5.9 h1:RsKRIA2MO8x56wkkcd3LbtcE/uMszhb6DpRf+3uwa3I= 556 | github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= 557 | github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= 558 | github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= 559 | github.com/wk8/go-ordered-map v1.0.0/go.mod h1:9ZIbRunKbuvfPKyBP1SIKLcXNlv74YCOZ3t3VTS6gRk= 560 | github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= 561 | github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= 562 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 563 | github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 564 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 565 | github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 566 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 567 | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 568 | github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 569 | github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= 570 | go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 571 | go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= 572 | go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= 573 | go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= 574 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 575 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 576 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 577 | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 578 | go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 579 | go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= 580 | go.opentelemetry.io/otel v1.7.0 h1:Z2lA3Tdch0iDcrhJXDIlC94XE+bxok1F9B+4Lz/lGsM= 581 | go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk= 582 | go.opentelemetry.io/otel/exporters/jaeger v1.7.0 h1:wXgjiRldljksZkZrldGVe6XrG9u3kYDyQmkZwmm5dI0= 583 | go.opentelemetry.io/otel/exporters/jaeger v1.7.0/go.mod h1:PwQAOqBgqbLQRKlj466DuD2qyMjbtcPpfPfj+AqbSBs= 584 | go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.7.0 h1:7Yxsak1q4XrJ5y7XBnNwqWx9amMZvoidCctv62XOQ6Y= 585 | go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.7.0/go.mod h1:M1hVZHNxcbkAlcvrOMlpQ4YOO3Awf+4N2dxkZL3xm04= 586 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.7.0 h1:cMDtmgJ5FpRvqx9x2Aq+Mm0O6K/zcUkH73SFz20TuBw= 587 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.7.0/go.mod h1:ceUgdyfNv4h4gLxHR0WNfDiiVmZFodZhZSbOLhpxqXE= 588 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.7.0 h1:MFAyzUPrTwLOwCi+cltN0ZVyy4phU41lwH+lyMyQTS4= 589 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.7.0/go.mod h1:E+/KKhwOSw8yoPxSSuUHG6vKppkvhN+S1Jc7Nib3k3o= 590 | go.opentelemetry.io/otel/sdk v1.7.0 h1:4OmStpcKVOfvDOgCt7UriAPtKolwIhxpnSNI/yK+1B0= 591 | go.opentelemetry.io/otel/sdk v1.7.0/go.mod h1:uTEOTwaqIVuTGiJN7ii13Ibp75wJmYUDe374q6cZwUU= 592 | go.opentelemetry.io/otel/trace v1.7.0 h1:O37Iogk1lEkMRXewVtZ1BBTVn5JEp8GrJvP92bJqC6o= 593 | go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU= 594 | go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= 595 | go.opentelemetry.io/proto/otlp v0.16.0 h1:WHzDWdXUvbc5bG2ObdrGfaNpQz7ft7QN9HHmJlbiB1E= 596 | go.opentelemetry.io/proto/otlp v0.16.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= 597 | go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 598 | go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 599 | go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 600 | go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= 601 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 602 | go.uber.org/automaxprocs v1.5.1 h1:e1YG66Lrk73dn4qhg8WFSvhF0JuFQF0ERIp4rpuV8Qk= 603 | go.uber.org/automaxprocs v1.5.1/go.mod h1:BF4eumQw0P9GtnuxxovUd06vwm1o18oMzFtK66vU6XU= 604 | go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= 605 | go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= 606 | go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= 607 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 608 | go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= 609 | go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= 610 | go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= 611 | go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= 612 | go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= 613 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 614 | go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= 615 | go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= 616 | go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= 617 | golang.org/x/arch v0.0.0-20180920145803-b19384d3c130/go.mod h1:cYlCBUl1MsqxdiKgmc4uh7TxZfWSFLOGSRR090WDxt8= 618 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 619 | golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 620 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 621 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 622 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 623 | golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 624 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 625 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 626 | golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 627 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 628 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 629 | golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0 h1:a5Yg6ylndHHYJqIPrdq0AhvR6KTvDTAvgBtaidhEevY= 630 | golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 631 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 632 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 633 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 634 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 635 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 636 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 637 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 638 | golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 639 | golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= 640 | golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= 641 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 642 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 643 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 644 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 645 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 646 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 647 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 648 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 649 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 650 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= 651 | golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 652 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 653 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 654 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 655 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 656 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 657 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 658 | golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 659 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 660 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 661 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 662 | golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= 663 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 664 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 665 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 666 | golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 667 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 668 | golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 669 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 670 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 671 | golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 672 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 673 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 674 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 675 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 676 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 677 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 678 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 679 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 680 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 681 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 682 | golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 683 | golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 684 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 685 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 686 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 687 | golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 688 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 689 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 690 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 691 | golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 692 | golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 693 | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 694 | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 695 | golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 696 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 697 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 698 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 699 | golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 700 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 701 | golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 702 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 703 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 704 | golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= 705 | golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 706 | golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 707 | golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 708 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 709 | golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA= 710 | golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 711 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 712 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 713 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 714 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 715 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 716 | golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 717 | golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 718 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 719 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 720 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 721 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 722 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 723 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 724 | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 725 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 726 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 727 | golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 728 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= 729 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 730 | golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 731 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 732 | golang.org/x/sys v0.0.0-20180903190138-2b024373dcd9/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 733 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 734 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 735 | golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 736 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 737 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 738 | golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 739 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 740 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 741 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 742 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 743 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 744 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 745 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 746 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 747 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 748 | golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 749 | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 750 | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 751 | golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 752 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 753 | golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 754 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 755 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 756 | golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 757 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 758 | golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 759 | golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 760 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 761 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 762 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 763 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 764 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 765 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 766 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 767 | golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 768 | golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 769 | golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 770 | golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 771 | golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 772 | golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 773 | golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 774 | golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 775 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 776 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 777 | golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 778 | golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 779 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 780 | golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 781 | golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 782 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 783 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 784 | golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 785 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 786 | golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 787 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 788 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 789 | golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 790 | golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 791 | golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 792 | golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 793 | golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 794 | golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0= 795 | golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 796 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 797 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 798 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 799 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 800 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 801 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 802 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 803 | golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 804 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 805 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= 806 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 807 | golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 808 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 809 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 810 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 811 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 812 | golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 813 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 814 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 815 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 816 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 817 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 818 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 819 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 820 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 821 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 822 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 823 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 824 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 825 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 826 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 827 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 828 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 829 | golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 830 | golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 831 | golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 832 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 833 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 834 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 835 | golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 836 | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 837 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 838 | golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 839 | golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 840 | golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 841 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 842 | golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 843 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 844 | golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 845 | golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 846 | golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 847 | golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 848 | golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 849 | golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= 850 | golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 851 | golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 852 | golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 853 | golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 854 | golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 855 | golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 856 | golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 857 | golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 858 | golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 859 | golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 860 | golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= 861 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 862 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 863 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 864 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= 865 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 866 | google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= 867 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 868 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 869 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 870 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 871 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 872 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 873 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 874 | google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 875 | google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 876 | google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 877 | google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 878 | google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 879 | google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 880 | google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 881 | google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= 882 | google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= 883 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 884 | google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 885 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 886 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 887 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 888 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 889 | google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 890 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 891 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 892 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 893 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 894 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 895 | google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= 896 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 897 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 898 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 899 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 900 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 901 | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 902 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 903 | google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 904 | google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 905 | google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= 906 | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 907 | google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 908 | google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 909 | google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 910 | google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 911 | google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 912 | google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 913 | google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 914 | google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 915 | google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= 916 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 917 | google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= 918 | google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 919 | google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 920 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 921 | google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= 922 | google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= 923 | google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3 h1:q1kiSVscqoDeqTF27eQ2NnLLDmqF0I373qQNXYMy0fo= 924 | google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= 925 | google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= 926 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 927 | google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= 928 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 929 | google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 930 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 931 | google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 932 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 933 | google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 934 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 935 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 936 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 937 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 938 | google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= 939 | google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= 940 | google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 941 | google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 942 | google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= 943 | google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= 944 | google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 945 | google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= 946 | google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= 947 | google.golang.org/grpc v1.46.0 h1:oCjezcn6g6A75TGoKYBPgKmVBLexhYLM6MebdrPApP8= 948 | google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= 949 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 950 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 951 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 952 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 953 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 954 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 955 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 956 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 957 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 958 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 959 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 960 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 961 | google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 962 | google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= 963 | google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 964 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 965 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 966 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 967 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 968 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 969 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 970 | gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= 971 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 972 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 973 | gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= 974 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= 975 | gopkg.in/src-d/go-billy.v4 v4.3.0/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk= 976 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 977 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 978 | gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= 979 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 980 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 981 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 982 | gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 983 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 984 | gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 985 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 986 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 987 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 988 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 989 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 990 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= 991 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 992 | honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 993 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 994 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 995 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 996 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 997 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 998 | honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 999 | honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 1000 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 1001 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= 1002 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 1003 | sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= 1004 | sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= 1005 | -------------------------------------------------------------------------------- /rpc/config.toml: -------------------------------------------------------------------------------- 1 | [server.http] 2 | port = 7982 3 | host = "0.0.0.0" -------------------------------------------------------------------------------- /rpc/handler.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/orcastor/orcas/core" 6 | "github.com/orcastor/orcas/rpc/middleware" 7 | "github.com/orcastor/orcas/rpc/util" 8 | ) 9 | 10 | var hanlder = core.NewLocalHandler() 11 | 12 | func login(ctx *gin.Context) { 13 | var req struct { 14 | UserName string `json:"u"` 15 | Password string `json:"p"` 16 | } 17 | ctx.BindJSON(&req) 18 | _, u, b, err := hanlder.Login(ctx.Request.Context(), req.UserName, req.Password) 19 | if err != nil { 20 | util.AbortResponse(ctx, 100, err.Error()) 21 | return 22 | } 23 | token, _, err := middleware.GenerateToken(u.Usr, u.ID, u.Role) 24 | if err != nil { 25 | util.AbortResponse(ctx, 100, err.Error()) 26 | return 27 | } 28 | util.Response(ctx, gin.H{ 29 | "u": u, 30 | "b": b, 31 | "access_token": token, 32 | }) 33 | } 34 | 35 | func list(ctx *gin.Context) { 36 | var req struct { 37 | BktID int64 `json:"b,omitempty"` 38 | PID int64 `json:"p,omitempty"` 39 | core.ListOptions 40 | } 41 | ctx.BindJSON(&req) 42 | if req.Count == 0 { 43 | req.Count = 1000 44 | } 45 | o, cnt, delimiter, err := hanlder.List(ctx.Request.Context(), req.BktID, req.PID, req.ListOptions) 46 | if err != nil { 47 | util.AbortResponse(ctx, 100, err.Error()) 48 | return 49 | } 50 | switch req.Brief { 51 | case 1: 52 | for i := range o { 53 | o[i].Extra = "" 54 | o[i].PID = 0 55 | o[i].DataID = 0 56 | } 57 | case 2: 58 | for i := range o { 59 | o[i] = &core.ObjectInfo{ID: o[i].ID} 60 | } 61 | } 62 | util.Response(ctx, gin.H{ 63 | "o": o, 64 | "c": cnt, 65 | "d": delimiter, 66 | }) 67 | } 68 | 69 | func get(ctx *gin.Context) { 70 | var req struct { 71 | BktID int64 `json:"b,omitempty"` 72 | ID int64 `json:"i,omitempty"` 73 | } 74 | ctx.BindJSON(&req) 75 | o, err := hanlder.Get(ctx.Request.Context(), req.BktID, []int64{req.ID}) 76 | if err != nil { 77 | util.AbortResponse(ctx, 100, err.Error()) 78 | return 79 | } 80 | if len(o) > 0 { 81 | util.Response(ctx, gin.H{ 82 | "o": o[0], 83 | }) 84 | return 85 | } 86 | util.Response(ctx, gin.H{}) 87 | } 88 | 89 | func token(ctx *gin.Context) { 90 | util.Response(ctx, gin.H{ 91 | "u": middleware.GetUID(ctx), 92 | }) 93 | } 94 | -------------------------------------------------------------------------------- /rpc/middleware/cors.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/gin-gonic/gin" 7 | ) 8 | 9 | func CORS() gin.HandlerFunc { 10 | return func(c *gin.Context) { 11 | origin := c.Request.Header.Get("Origin") 12 | if origin != "" { 13 | c.Header("Access-Control-Allow-Origin", origin) 14 | c.Header("Access-Control-Allow-Headers", "Content-Type, AccessToken, X-CSRF-Token, Authorization, Token") 15 | c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS") 16 | c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type") 17 | c.Header("Access-Control-Allow-Credentials", "true") 18 | } 19 | 20 | // 放行所有OPTIONS方法 21 | if c.Request.Method == "OPTIONS" { 22 | c.AbortWithStatus(http.StatusNoContent) 23 | return 24 | } 25 | 26 | c.Next() 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /rpc/middleware/jwt.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | "strconv" 7 | "time" 8 | 9 | "github.com/gin-gonic/gin" 10 | "github.com/golang-jwt/jwt" 11 | "github.com/gotomicro/ego/core/elog" 12 | "github.com/orcastor/orcas/core" 13 | "github.com/orcastor/orcas/rpc/util" 14 | ) 15 | 16 | var noAuthPath = map[string]bool{ 17 | "": true, 18 | "/": true, 19 | "/api/login": true, 20 | "/admin/api/login": true, 21 | } 22 | 23 | var ORCAS_SECRET = os.Getenv("ORCAS_SECRET") 24 | 25 | const ( 26 | TokenExpiredCode int32 = 599 27 | 28 | MOD_NAME = "orcas" 29 | ) 30 | 31 | var ( 32 | ErrTokenExpired = errors.New("token expired") 33 | ErrTokenMalformed = errors.New("not a token") 34 | ErrTokenInvalid = errors.New("token invalid") 35 | ) 36 | 37 | type Claims struct { 38 | User string `json:"u"` 39 | Role uint32 `json:"r"` 40 | jwt.StandardClaims 41 | } 42 | 43 | func GenerateToken(user string, uid int64, role uint32) (string, int64, error) { 44 | expireTime := time.Now().Add(24 * time.Hour).Unix() 45 | token, err := jwt.NewWithClaims(jwt.SigningMethodHS256, Claims{ 46 | user, 47 | role, 48 | jwt.StandardClaims{ 49 | Audience: strconv.FormatInt(uid, 10), 50 | ExpiresAt: expireTime, 51 | Issuer: MOD_NAME, 52 | }, 53 | }).SignedString([]byte(ORCAS_SECRET)) 54 | return token, expireTime, err 55 | } 56 | 57 | func ParseToken(token string) (*Claims, error) { 58 | tokenClaims, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (interface{}, error) { 59 | return []byte(ORCAS_SECRET), nil 60 | }) 61 | 62 | if err != nil { 63 | if ve, ok := err.(*jwt.ValidationError); ok { 64 | if ve.Errors&jwt.ValidationErrorMalformed != 0 { 65 | return nil, ErrTokenMalformed 66 | } else if ve.Errors&jwt.ValidationErrorExpired != 0 { 67 | return nil, ErrTokenExpired 68 | } 69 | return nil, ErrTokenInvalid 70 | } 71 | return nil, err 72 | } 73 | 74 | return tokenClaims.Claims.(*Claims), nil 75 | } 76 | 77 | func JWT() gin.HandlerFunc { 78 | return func(c *gin.Context) { 79 | if noAuthPath[c.FullPath()] { 80 | c.Next() 81 | return 82 | } 83 | 84 | token := GetToken(c) 85 | claims, err := ParseToken(token) 86 | if err != nil { 87 | elog.Errorf("%+v", err) 88 | if err == ErrTokenExpired { 89 | util.AbortResponse(c, int(TokenExpiredCode), "token expired") 90 | } else { 91 | util.AbortResponse(c, int(TokenExpiredCode), "token error") 92 | } 93 | return 94 | } else { 95 | elog.Debugf("login_info: %+v", claims) 96 | uid, _ := strconv.ParseInt(claims.StandardClaims.Audience, 10, 64) 97 | c.Request = c.Request.WithContext((core.UserInfo2Ctx(c.Request.Context(), &core.UserInfo{ 98 | ID: uid, 99 | }))) 100 | c.Set("uid", uid) 101 | } 102 | c.Next() 103 | } 104 | } 105 | 106 | func GetToken(c *gin.Context) (token string) { 107 | token = c.GetHeader("Authorization") 108 | if token != "" { 109 | return 110 | } 111 | // get 112 | token = c.Query("token") 113 | if token != "" { 114 | return 115 | } 116 | // postform 117 | token = c.PostForm("token") 118 | return 119 | } 120 | 121 | func GetUID(c *gin.Context) int64 { 122 | if uid, ok := c.Get("uid"); ok { 123 | return uid.(int64) 124 | } 125 | return 0 126 | } 127 | -------------------------------------------------------------------------------- /rpc/middleware/metrics.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "strconv" 5 | "time" 6 | 7 | "github.com/gin-gonic/gin" 8 | kitprometheus "github.com/go-kit/kit/metrics/prometheus" 9 | "github.com/prometheus/client_golang/prometheus" 10 | ) 11 | 12 | func Metrics() gin.HandlerFunc { 13 | return func(c *gin.Context) { 14 | start := time.Now() 15 | c.Next() 16 | 17 | var code int = -1 18 | resp := c.Request.Response 19 | if resp != nil { 20 | code = resp.StatusCode 21 | } 22 | 23 | RequestTime(c.Request.Method, c.Request.URL.Path, time.Since(start).Seconds()) 24 | RequestCount(c.Request.Method, c.Request.URL.Path, code) 25 | } 26 | } 27 | 28 | var ( 29 | requestTime *kitprometheus.Histogram 30 | requestCount *kitprometheus.Counter 31 | ) 32 | 33 | func init() { 34 | requestTime = kitprometheus.NewHistogramFrom(prometheus.HistogramOpts{ 35 | Namespace: "orcas", 36 | Subsystem: "rpc", 37 | Name: "community_request_time", 38 | Help: "community request time cost.", 39 | }, []string{"method", "path"}) 40 | 41 | requestCount = kitprometheus.NewCounterFrom(prometheus.CounterOpts{ 42 | Namespace: "orcas", 43 | Subsystem: "rpc", 44 | Name: "community_request_count", 45 | Help: "community request count.", 46 | }, []string{"method", "path", "code"}) 47 | } 48 | 49 | func RequestTime(method, path string, tm float64) { 50 | requestTime.With([]string{ 51 | "method", method, 52 | "path", path, 53 | }...).Observe(tm) 54 | } 55 | 56 | func RequestCount(method, path string, code int) { 57 | requestCount.With([]string{ 58 | "method", method, 59 | "path", path, 60 | "code", strconv.FormatInt(int64(code), 10), 61 | }...).Add(1) 62 | } 63 | -------------------------------------------------------------------------------- /rpc/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/gotomicro/ego" 5 | "github.com/gotomicro/ego/core/elog" 6 | "github.com/gotomicro/ego/server/egin" 7 | 8 | "github.com/orcastor/orcas/core" 9 | "github.com/orcastor/orcas/rpc/middleware" 10 | ) 11 | 12 | // bash <(curl -L https://raw.githubusercontent.com/gotomicro/egoctl/main/getlatest.sh) 13 | 14 | // EGO_DEBUG=true EGO_LOG_EXTRA_KEYS=uid ORCAS_BASE=/opt/orcas ORCAS_DATA=/opt/orcas_disk ORCAS_SECRET=xxxxxxxx egoctl run --runargs --config=config.toml 15 | // EGO_DEBUG=true EGO_LOG_EXTRA_KEYS=uid ORCAS_BASE=/opt/orcas ORCAS_DATA=/opt/orcas_disk ORCAS_SECRET=xxxxxxxx go run ./... --config=config.toml 16 | func main() { 17 | core.InitDB() 18 | if err := ego.New().Serve(func() *egin.Component { 19 | server := egin.Load("server.http").Build() 20 | 21 | server.Use(middleware.Metrics()) 22 | server.Use(middleware.CORS()) 23 | server.Use(middleware.JWT()) 24 | 25 | api := server.Group("/api") 26 | api.POST("/login", login) 27 | api.POST("/list", list) 28 | api.POST("/get", get) 29 | api.POST("/token", token) 30 | return server 31 | }()).Run(); err != nil { 32 | elog.Panic("startup", elog.Any("err", err)) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /rpc/util/util.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import "github.com/gin-gonic/gin" 4 | 5 | func AbortResponse(c *gin.Context, code int, msg string) { 6 | c.AbortWithStatusJSON(200, gin.H{ 7 | "code": code, 8 | "msg": msg, 9 | }) 10 | } 11 | 12 | func Response(c *gin.Context, data gin.H) { 13 | c.AbortWithStatusJSON(200, gin.H{ 14 | "code": 0, 15 | "data": data, 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /sdk/data.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "crypto/md5" 7 | "encoding/binary" 8 | "fmt" 9 | "hash" 10 | "hash/crc32" 11 | "io" 12 | "os" 13 | "path/filepath" 14 | "runtime" 15 | "sort" 16 | 17 | "github.com/h2non/filetype" 18 | "github.com/klauspost/compress/zstd" 19 | "github.com/mholt/archiver/v3" 20 | "github.com/mkmueller/aes256" 21 | "github.com/orcastor/orcas/core" 22 | "github.com/tjfoc/gmsm/sm4" 23 | ) 24 | 25 | const ( 26 | UPLOAD_DATA = 1 << iota 27 | CRC32_MD5 28 | HDR_CRC32 29 | ) 30 | 31 | const ( 32 | PKG_ALIGN = 4096 33 | PKG_SIZE = 4194304 34 | HDR_SIZE = 102400 35 | ) 36 | 37 | type listener struct { 38 | bktID int64 39 | d *core.DataInfo 40 | cfg Config 41 | action uint32 42 | md5Hash hash.Hash 43 | once bool 44 | sn, cnt int 45 | cmprBuf bytes.Buffer 46 | cmpr archiver.Compressor 47 | } 48 | 49 | func newListener(bktID int64, d *core.DataInfo, cfg Config, action uint32) *listener { 50 | l := &listener{bktID: bktID, d: d, cfg: cfg, action: action} 51 | if action&CRC32_MD5 != 0 { 52 | l.md5Hash = md5.New() 53 | } 54 | return l 55 | } 56 | 57 | func (l *listener) Once() *listener { 58 | l.once = true 59 | return l 60 | } 61 | 62 | func (l *listener) encode(src []byte) (dst []byte, err error) { 63 | // 加密 64 | if l.d.Kind&core.DATA_ENDEC_AES256 != 0 { 65 | // AES256加密 66 | dst, err = aes256.Encrypt(l.cfg.EndecKey, src) 67 | } else if l.d.Kind&core.DATA_ENDEC_SM4 != 0 { 68 | // SM4加密 69 | dst, err = sm4.Sm4Cbc([]byte(l.cfg.EndecKey), src, true) //sm4Cbc模式PKSC7填充加密 70 | } else { 71 | dst = src 72 | } 73 | if err != nil { 74 | dst = src 75 | } 76 | return 77 | } 78 | 79 | func (l *listener) OnData(c core.Ctx, h core.Handler, dp *dataPkger, buf []byte) (once bool, err error) { 80 | if l.cnt == 0 { 81 | if l.action&HDR_CRC32 != 0 { 82 | if len(buf) > HDR_SIZE { 83 | l.d.HdrCRC32 = crc32.ChecksumIEEE(buf[0:HDR_SIZE]) 84 | } else { 85 | l.d.HdrCRC32 = crc32.ChecksumIEEE(buf) 86 | } 87 | } 88 | // 如果开启智能压缩的,检查文件类型确定是否要压缩 89 | if l.cfg.WiseCmpr > 0 { 90 | kind, _ := filetype.Match(buf) 91 | if kind == filetype.Unknown { // 多媒体、存档、应用 92 | l.d.Kind |= l.cfg.WiseCmpr 93 | if l.cfg.WiseCmpr&core.DATA_CMPR_SNAPPY != 0 { 94 | l.cmpr = &archiver.Snappy{} 95 | } else if l.cfg.WiseCmpr&core.DATA_CMPR_ZSTD != 0 { 96 | l.cmpr = &archiver.Zstd{EncoderOptions: []zstd.EOption{zstd.WithEncoderLevel(zstd.EncoderLevelFromZstd(int(l.cfg.CmprQlty)))}} 97 | } else if l.cfg.WiseCmpr&core.DATA_CMPR_GZIP != 0 { 98 | l.cmpr = &archiver.Gz{CompressionLevel: int(l.cfg.CmprQlty)} 99 | } else if l.cfg.WiseCmpr&core.DATA_CMPR_BR != 0 { 100 | l.cmpr = &archiver.Brotli{Quality: int(l.cfg.CmprQlty)} 101 | } 102 | } 103 | // 如果是黑名单类型,不压缩 104 | // fmt.Println(kind.MIME.Value) 105 | } 106 | if l.cfg.EndecWay > 0 { 107 | l.d.Kind |= l.cfg.EndecWay 108 | } 109 | } 110 | l.cnt++ 111 | 112 | if l.action&CRC32_MD5 != 0 { 113 | l.d.CRC32 = crc32.Update(l.d.CRC32, crc32.IEEETable, buf) 114 | l.md5Hash.Write(buf) 115 | } 116 | 117 | // 上传数据 118 | if l.action&UPLOAD_DATA != 0 { 119 | var cmprBuf []byte 120 | if l.d.Kind&core.DATA_CMPR_MASK == 0 { 121 | cmprBuf = buf 122 | } else { 123 | l.cmpr.Compress(bytes.NewBuffer(buf), &l.cmprBuf) 124 | // 如果压缩后更大了,恢复原始的 125 | if l.d.OrigSize < PKG_SIZE { 126 | if l.cmprBuf.Len() >= len(buf) { 127 | l.d.Kind &= ^core.DATA_CMPR_MASK 128 | cmprBuf = buf 129 | } else { 130 | cmprBuf = l.cmprBuf.Bytes() 131 | } 132 | l.cmprBuf.Reset() 133 | } else { 134 | if l.cmprBuf.Len() >= PKG_SIZE { 135 | cmprBuf = l.cmprBuf.Next(PKG_SIZE) 136 | } 137 | } 138 | } 139 | 140 | if cmprBuf != nil { 141 | // 加密 142 | encodedBuf, err := l.encode(cmprBuf) 143 | if err != nil { 144 | return l.once, err 145 | } 146 | 147 | if l.d.Kind&core.DATA_CMPR_MASK != 0 || l.d.Kind&core.DATA_ENDEC_MASK != 0 { 148 | l.d.Cksum = crc32.Update(l.d.Cksum, crc32.IEEETable, encodedBuf) 149 | l.d.Size += int64(len(encodedBuf)) 150 | } 151 | 152 | // 需要上传 153 | if l.d.OrigSize < PKG_SIZE { 154 | // 7. 检查是否要打包 155 | if ok, err := dp.Push(c, h, l.bktID, encodedBuf, l.d); err == nil { 156 | if ok { 157 | encodedBuf = nil // 打包成功,不再上传 158 | } 159 | } else { 160 | return l.once, err 161 | } 162 | } 163 | 164 | if encodedBuf != nil { 165 | if l.d.ID, err = h.PutData(c, l.bktID, l.d.ID, l.sn, encodedBuf); err != nil { 166 | return l.once, err 167 | } 168 | l.sn++ 169 | } 170 | } 171 | } 172 | return l.once, nil 173 | } 174 | 175 | func (l *listener) OnFinish(c core.Ctx, h core.Handler) error { 176 | if l.action&CRC32_MD5 != 0 { 177 | l.d.MD5 = int64(binary.BigEndian.Uint64(l.md5Hash.Sum(nil)[4:12])) 178 | } 179 | if l.cmprBuf.Len() > 0 { 180 | encodedBuf, err := l.encode(l.cmprBuf.Bytes()) 181 | if err != nil { 182 | return err 183 | } 184 | if l.d.ID, err = h.PutData(c, l.bktID, l.d.ID, l.sn, encodedBuf); err != nil { 185 | fmt.Println(runtime.Caller(0)) 186 | fmt.Println(err) 187 | return err 188 | } 189 | l.d.Cksum = crc32.Update(l.d.Cksum, crc32.IEEETable, encodedBuf) 190 | l.d.Size += int64(l.cmprBuf.Len()) 191 | } 192 | if l.d.Kind&core.DATA_CMPR_MASK == 0 && l.d.Kind&core.DATA_ENDEC_MASK == 0 { 193 | l.d.Cksum = l.d.CRC32 194 | l.d.Size = l.d.OrigSize 195 | } 196 | return nil 197 | } 198 | 199 | func (osi *OrcasSDKImpl) readFile(c core.Ctx, path string, dp *dataPkger, l *listener) error { 200 | f, err := os.Open(path) 201 | if err != nil { 202 | return err 203 | } 204 | defer f.Close() 205 | 206 | var n int 207 | var once bool 208 | buf := make([]byte, PKG_SIZE) 209 | for { 210 | if n, err = f.Read(buf); n <= 0 || err != nil { 211 | break 212 | } 213 | if once, err = l.OnData(c, osi.h, dp, buf[0:n]); once || err != nil { 214 | break 215 | } 216 | } 217 | if err != io.EOF { 218 | return err 219 | } 220 | err = l.OnFinish(c, osi.h) 221 | return err 222 | } 223 | 224 | func (osi *OrcasSDKImpl) putOne(c core.Ctx, bktID int64, o *core.ObjectInfo) (int64, error) { 225 | ids, err := osi.h.Put(c, bktID, []*core.ObjectInfo{o}) 226 | if err != nil { 227 | return 0, err 228 | } 229 | if len(ids) > 0 && ids[0] > 0 { 230 | return ids[0], nil 231 | } 232 | return 0, fmt.Errorf("remote object exists, pid:%d, name:%s", o.PID, o.Name) 233 | } 234 | 235 | func (osi *OrcasSDKImpl) getRename(o *core.ObjectInfo, i int) *core.ObjectInfo { 236 | x := *o 237 | if i <= 0 { 238 | x.Name = fmt.Sprintf(osi.cfg.NameTmpl, x.Name) 239 | } else { 240 | x.Name = fmt.Sprintf(osi.cfg.NameTmpl+"%d", x.Name, i+1) 241 | } 242 | return &x 243 | } 244 | 245 | func (osi *OrcasSDKImpl) putObjects(c core.Ctx, bktID int64, o []*core.ObjectInfo) ([]int64, error) { 246 | if len(o) <= 0 { 247 | return nil, nil 248 | } 249 | 250 | ids, err := osi.h.Put(c, bktID, o) 251 | if err != nil { 252 | fmt.Println(runtime.Caller(0)) 253 | fmt.Println(err) 254 | return nil, err 255 | } 256 | 257 | switch osi.cfg.Conflict { 258 | case COVER: // 合并或覆盖 259 | var vers []*core.ObjectInfo 260 | for i := range ids { 261 | if ids[i] <= 0 { 262 | ids[i], err = osi.Path2ID(c, bktID, o[i].PID, o[i].Name) 263 | // 如果是文件,需要新建一个版本,版本更新的逻辑需要jobs来完成 264 | if o[i].Type == core.OBJ_TYPE_FILE { 265 | vers = append(vers, &core.ObjectInfo{ 266 | PID: ids[i], 267 | MTime: o[i].MTime, 268 | Type: core.OBJ_TYPE_VERSION, 269 | Size: o[i].Size, 270 | }) 271 | } 272 | } 273 | } 274 | if len(vers) > 0 { 275 | _, err = osi.h.Put(c, bktID, vers) 276 | if err != nil { 277 | fmt.Println(runtime.Caller(0)) 278 | fmt.Println(err) 279 | return nil, err 280 | } 281 | // 需要定时任务把首版本的DataID更新掉 282 | } 283 | case RENAME: // 重命名 284 | m := make(map[int]int) 285 | var rename []*core.ObjectInfo 286 | for i := range ids { 287 | // 需要重命名重新创建 288 | if ids[i] <= 0 { 289 | // 先直接用NameTmpl创建,覆盖大部分场景 290 | m[len(rename)] = i 291 | rename = append(rename, osi.getRename(o[i], 0)) 292 | } 293 | } 294 | if len(rename) > 0 { 295 | ids2, err2 := osi.h.Put(c, bktID, rename) 296 | if err2 != nil { 297 | return ids, err2 298 | } 299 | for i := range ids2 { 300 | if ids2[i] > 0 { 301 | ids[m[i]] = ids2[i] 302 | continue 303 | } 304 | // 还是失败,用NameTmpl找有多少个目录,然后往后一个一个尝试 305 | _, cnt, _, err3 := osi.h.List(c, bktID, rename[i].PID, core.ListOptions{ 306 | Word: rename[i].Name + "*", 307 | }) 308 | if err3 != nil { 309 | return ids, err3 310 | } 311 | // 假设有 test、test的副本、test的副本2,cnt为2 312 | for j := 0; j <= int(cnt/2)+1; j++ { 313 | // 先试试个数后面一个,正常顺序查找,最大概率命中的分支 314 | if ids[m[i]], err3 = osi.putOne(c, bktID, 315 | osi.getRename(o[i], int(cnt)+j)); err3 == nil { 316 | break 317 | } 318 | // 从最前面往后找 319 | if ids[m[i]], err3 = osi.putOne(c, bktID, 320 | osi.getRename(o[i], j)); err3 == nil { 321 | break 322 | } 323 | // 从cnt个开始往前找 324 | if ids[m[i]], err3 = osi.putOne(c, bktID, 325 | osi.getRename(o[i], int(cnt)-1-j)); err3 == nil { 326 | break 327 | } 328 | } 329 | } 330 | } 331 | case THROW: // 报错 332 | for i := range ids { 333 | if ids[i] <= 0 { 334 | return ids, fmt.Errorf("remote object exists, pid:%d, name:%s", o[i].PID, o[i].Name) 335 | } 336 | } 337 | case SKIP: // 跳过 338 | break 339 | } 340 | return ids, nil 341 | } 342 | 343 | type sizeSort []uploadInfo 344 | 345 | func (u sizeSort) Len() int { return len(u) } 346 | func (u sizeSort) Less(i, j int) bool { return u[i].o.Size < u[j].o.Size || u[i].o.Name < u[j].o.Name } 347 | func (u sizeSort) Swap(i, j int) { u[i], u[j] = u[j], u[i] } 348 | 349 | func (osi *OrcasSDKImpl) uploadFiles(c core.Ctx, bktID int64, u []uploadInfo, 350 | d []*core.DataInfo, dp *dataPkger, level, doneAction uint32) error { 351 | if len(u) <= 0 { 352 | return nil 353 | } 354 | 355 | if len(d) <= 0 { 356 | // 先按文件大小排序一下,尽量让它们可以打包 357 | sort.Sort(sizeSort(u)) 358 | d = make([]*core.DataInfo, len(u)) 359 | for i := range u { 360 | d[i] = &core.DataInfo{ 361 | Kind: core.DATA_NORMAL, 362 | OrigSize: u[i].o.Size, 363 | } 364 | } 365 | } 366 | 367 | if dp == nil { 368 | dp = newDataPkger(osi.cfg.PkgThres) 369 | } 370 | 371 | var u1, u2 []uploadInfo 372 | var d1, d2 []*core.DataInfo 373 | 374 | // 如果是文件,先看是否要秒传 375 | switch level { 376 | case FAST: 377 | // 如果要预先秒传的,先读取hdrCRC32,排队检查 378 | for i, fi := range u { 379 | if fi.o.Size > PKG_SIZE { 380 | u1 = append(u1, fi) 381 | d1 = append(d1, d[i]) 382 | } else { 383 | u2 = append(u2, fi) 384 | d2 = append(d2, d[i]) 385 | } 386 | } 387 | if len(u1) > 0 { 388 | u, d, u1, d1 = u1, d1, nil, nil 389 | for i, fi := range u { 390 | if err := osi.readFile(c, filepath.Join(fi.path, fi.o.Name), dp, 391 | newListener(bktID, d[i], osi.cfg, HDR_CRC32&^doneAction).Once()); err != nil { 392 | fmt.Println(runtime.Caller(0)) 393 | fmt.Println(err) 394 | } 395 | } 396 | ids, err := osi.h.Ref(c, bktID, d) 397 | if err != nil { 398 | fmt.Println(runtime.Caller(0)) 399 | fmt.Println(err) 400 | } 401 | for i, id := range ids { 402 | if id > 0 { 403 | u2 = append(u2, u[i]) 404 | d2 = append(d2, d[i]) 405 | } else { 406 | u1 = append(u1, u[i]) 407 | d1 = append(d1, d[i]) 408 | } 409 | } 410 | if err := osi.uploadFiles(c, bktID, u1, d1, dp, OFF, doneAction|HDR_CRC32); err != nil { 411 | fmt.Println(runtime.Caller(0)) 412 | fmt.Println(err) 413 | return err 414 | } 415 | } 416 | if err := osi.uploadFiles(c, bktID, u2, d2, dp, FULL, doneAction); err != nil { 417 | fmt.Println(runtime.Caller(0)) 418 | fmt.Println(err) 419 | return err 420 | } 421 | case FULL: 422 | // 如果不需要预先秒传或者预先秒传失败的,整个读取crc32和md5以后尝试秒传 423 | for i, fi := range u { 424 | if err := osi.readFile(c, filepath.Join(fi.path, fi.o.Name), dp, 425 | newListener(bktID, d[i], osi.cfg, (HDR_CRC32|CRC32_MD5)&^doneAction)); err != nil { 426 | fmt.Println(runtime.Caller(0)) 427 | fmt.Println(err) 428 | } 429 | } 430 | ids, err := osi.h.Ref(c, bktID, d) 431 | if err != nil { 432 | fmt.Println(runtime.Caller(0)) 433 | fmt.Println(err) 434 | } 435 | var f []*core.ObjectInfo 436 | var gap int64 437 | for i, id := range ids { 438 | // 设置DataID,如果是有的,说明秒传成功,不再需要上传数据了 439 | if id > 0 { 440 | u[i].o.DataID = id 441 | f = append(f, u[i].o) 442 | gap++ 443 | } else { 444 | if id < 0 { // 小于0说明是引用的数据 445 | d[i].ID = id + gap // 有人走了,那需要补充空隙 446 | } 447 | u1 = append(u1, u[i]) 448 | d1 = append(d1, d[i]) 449 | } 450 | } 451 | if _, err := osi.putObjects(c, bktID, f); err != nil { 452 | fmt.Println(runtime.Caller(0)) 453 | fmt.Println(err) 454 | return err 455 | } 456 | // 秒传失败的(包括超过大小或者预先秒传失败),普通上传 457 | if err := osi.uploadFiles(c, bktID, u1, d1, dp, OFF, doneAction|HDR_CRC32|CRC32_MD5); err != nil { 458 | fmt.Println(runtime.Caller(0)) 459 | fmt.Println(err) 460 | return err 461 | } 462 | case OFF: 463 | // 直接上传的对象 464 | for i, fi := range u { 465 | // 如果是引用别人的,也就是<0的,不用再传了就 466 | if d[i].ID >= 0 { 467 | if err := osi.readFile(c, filepath.Join(fi.path, fi.o.Name), dp, 468 | newListener(bktID, d[i], osi.cfg, (UPLOAD_DATA|HDR_CRC32|CRC32_MD5)&^doneAction)); err != nil { 469 | fmt.Println(runtime.Caller(0)) 470 | fmt.Println(err) 471 | } 472 | } 473 | } 474 | // 刷新一下打包数据 475 | if err := dp.Flush(c, osi.h, bktID); err != nil { 476 | return err 477 | } 478 | ids, err := osi.h.PutDataInfo(c, bktID, d) 479 | if err != nil { 480 | fmt.Println(runtime.Caller(0)) 481 | fmt.Println(err) 482 | return err 483 | } 484 | var f []*core.ObjectInfo 485 | // 处理打包上传的对象 486 | for i, id := range ids { 487 | if u[i].o.DataID != id { // 包括小于0的 488 | u[i].o.DataID = id 489 | } 490 | f = append(f, u[i].o) 491 | } 492 | if _, err := osi.putObjects(c, bktID, f); err != nil { 493 | fmt.Println(runtime.Caller(0)) 494 | fmt.Println(err) 495 | return err 496 | } 497 | } 498 | return nil 499 | } 500 | 501 | type DummyArchiver struct{} 502 | 503 | func (DummyArchiver) CheckExt(string) error { return nil } 504 | func (DummyArchiver) Compress(in io.Reader, out io.Writer) error { 505 | _, err := io.Copy(out, in) 506 | return err 507 | } 508 | func (DummyArchiver) Decompress(in io.Reader, out io.Writer) error { 509 | _, err := io.Copy(out, in) 510 | return err 511 | } 512 | 513 | type dataReader struct { 514 | c core.Ctx 515 | h core.Handler 516 | buf bytes.Buffer 517 | bktID int64 518 | dataID int64 519 | size, sn int 520 | getSize, remain int 521 | offset, kind uint32 522 | endecKey string 523 | } 524 | 525 | func NewDataReader(c core.Ctx, h core.Handler, bktID int64, d *core.DataInfo, endecKey string) *dataReader { 526 | dr := &dataReader{c: c, h: h, bktID: bktID, remain: int(d.Size), kind: d.Kind, endecKey: endecKey} 527 | if d.PkgID > 0 { 528 | dr.dataID = d.PkgID 529 | dr.offset = d.PkgOffset 530 | dr.getSize = int(d.Size) 531 | } else { 532 | dr.dataID = d.ID 533 | dr.getSize = -1 534 | } 535 | return dr 536 | } 537 | 538 | func (dr *dataReader) Read(p []byte) (n int, err error) { 539 | if len(p) <= dr.buf.Len() { 540 | return dr.buf.Read(p) 541 | } 542 | if dr.remain > 0 { 543 | buf, err := dr.h.GetData(dr.c, dr.bktID, dr.dataID, dr.sn, []int{int(dr.offset), dr.getSize}) 544 | if err != nil { 545 | fmt.Println(runtime.Caller(0)) 546 | fmt.Println(err) 547 | return 0, err 548 | } 549 | dr.remain -= len(buf) 550 | dr.sn++ 551 | 552 | decodeBuf := buf 553 | if dr.kind&core.DATA_ENDEC_AES256 != 0 { 554 | // AES256解密 555 | decodeBuf, err = aes256.Decrypt(dr.endecKey, buf) 556 | } else if dr.kind&core.DATA_ENDEC_SM4 != 0 { 557 | // SM4解密 558 | decodeBuf, err = sm4.Sm4Cbc([]byte(dr.endecKey), buf, false) //sm4Cbc模式pksc7填充加密 559 | } 560 | if err != nil { 561 | fmt.Println(runtime.Caller(0)) 562 | fmt.Println(err) 563 | decodeBuf = buf 564 | } 565 | dr.buf.Write(decodeBuf) 566 | } 567 | return dr.buf.Read(p) 568 | } 569 | 570 | func (osi *OrcasSDKImpl) downloadFile(c core.Ctx, bktID int64, o *core.ObjectInfo, lpath string) (err error) { 571 | if o.Type != core.OBJ_TYPE_FILE { 572 | return nil 573 | } 574 | 575 | dataID := o.DataID 576 | // 如果不是首版本 577 | if dataID == 0 { 578 | os, _, _, err := osi.h.List(c, bktID, o.ID, core.ListOptions{ 579 | Type: core.OBJ_TYPE_VERSION, 580 | Count: 1, 581 | Order: "-id", 582 | }) 583 | if err != nil || len(os) < 1 { 584 | fmt.Println(runtime.Caller(0)) 585 | fmt.Println(err) 586 | return err 587 | } 588 | dataID = os[0].DataID 589 | } 590 | 591 | var d *core.DataInfo 592 | if dataID == core.EmptyDataID { 593 | d = core.EmptyDataInfo() 594 | } else { 595 | d, err = osi.h.GetDataInfo(c, bktID, dataID) 596 | if err != nil { 597 | fmt.Println(runtime.Caller(0)) 598 | fmt.Println(err) 599 | return err 600 | } 601 | } 602 | 603 | os.MkdirAll(filepath.Dir(lpath), 0766) 604 | 605 | f, err := os.OpenFile(lpath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666) 606 | if err != nil { 607 | return err 608 | } 609 | defer f.Close() 610 | b := bufio.NewWriter(f) 611 | defer b.Flush() 612 | 613 | var decmpr archiver.Decompressor 614 | if d.Kind&core.DATA_CMPR_MASK != 0 { 615 | if d.Kind&core.DATA_CMPR_SNAPPY != 0 { 616 | decmpr = &archiver.Snappy{} 617 | } else if d.Kind&core.DATA_CMPR_ZSTD != 0 { 618 | decmpr = &archiver.Zstd{} 619 | } else if d.Kind&core.DATA_CMPR_GZIP != 0 { 620 | decmpr = &archiver.Gz{} 621 | } else if d.Kind&core.DATA_CMPR_BR != 0 { 622 | decmpr = &archiver.Brotli{} 623 | } 624 | } else { 625 | decmpr = &DummyArchiver{} 626 | } 627 | 628 | if err = decmpr.Decompress(NewDataReader(c, osi.h, bktID, d, osi.cfg.EndecKey), b); err != nil { 629 | fmt.Println(runtime.Caller(0)) 630 | fmt.Println(err) 631 | return err 632 | } 633 | return nil 634 | } 635 | 636 | type dataPkger struct { 637 | buf *bytes.Buffer 638 | infos []*core.DataInfo 639 | thres uint32 640 | } 641 | 642 | func newDataPkger(thres uint32) *dataPkger { 643 | return &dataPkger{ 644 | buf: bytes.NewBuffer(make([]byte, 0, PKG_SIZE)), 645 | thres: thres, 646 | } 647 | } 648 | 649 | func (dp *dataPkger) SetThres(thres uint32) { 650 | dp.thres = thres 651 | } 652 | 653 | func (dp *dataPkger) Push(c core.Ctx, h core.Handler, bktID int64, b []byte, d *core.DataInfo) (bool, error) { 654 | offset := dp.buf.Len() 655 | if offset+ /*offset%PKG_ALIGN+*/ len(b) > PKG_SIZE || len(dp.infos) >= int(dp.thres) || len(b) >= PKG_SIZE { 656 | return false, dp.Flush(c, h, bktID) 657 | } 658 | // 写入前再处理对齐,最后一块就不用补齐了,PS:需要测试一下读性能差多少 659 | /*if offset%PKG_ALIGN > 0 { 660 | if padding := PKG_ALIGN - offset%PKG_ALIGN; padding > 0 { 661 | dp.buf = append(dp.buf, make([]byte, padding)...) 662 | offset = len(dp.buf) 663 | } 664 | }*/ 665 | // 填充内容 666 | dp.buf.Write(b) 667 | // 记录偏移 668 | d.PkgOffset = uint32(offset) 669 | // 记录下来要设置打包数据的数据信息 670 | dp.infos = append(dp.infos, d) 671 | return true, nil 672 | } 673 | 674 | func (dp *dataPkger) Flush(c core.Ctx, h core.Handler, bktID int64) error { 675 | if dp.buf.Len() <= 0 { 676 | return nil 677 | } 678 | // 上传打包的数据包 679 | pkgID, err := h.PutData(c, bktID, 0, 0, dp.buf.Bytes()) 680 | if err != nil { 681 | fmt.Println(runtime.Caller(0)) 682 | fmt.Println(err) 683 | return err 684 | } 685 | if len(dp.infos) == 1 { 686 | dp.infos[0].ID = pkgID 687 | } else { 688 | for i := range dp.infos { 689 | dp.infos[i].PkgID = pkgID 690 | } 691 | } 692 | dp.buf.Reset() 693 | dp.infos = nil 694 | return nil 695 | } 696 | -------------------------------------------------------------------------------- /sdk/fanout.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "runtime" 8 | "sync" 9 | "sync/atomic" 10 | "time" 11 | 12 | "github.com/orcastor/orcas/core" 13 | ) 14 | 15 | var ( 16 | // ErrFull chan full. 17 | ErrFull = errors.New("fanout: chan full") 18 | ErrTimeout = errors.New("fanout: chan full, wait timeout") 19 | ) 20 | 21 | type options struct { 22 | worker int32 23 | buffer int32 24 | } 25 | 26 | // Option fanout option 27 | type Option func(*options) 28 | 29 | // Worker specifies the worker of fanout 30 | func Worker(n int) Option { 31 | if n <= 0 { 32 | panic("fanout: worker should > 0") 33 | } 34 | return func(o *options) { 35 | o.worker = int32(n) 36 | } 37 | } 38 | 39 | // Buffer specifies the buffer of fanout 40 | func Buffer(n int) Option { 41 | if n <= 0 { 42 | panic("fanout: buffer should > 0") 43 | } 44 | return func(o *options) { 45 | o.buffer = int32(n) 46 | } 47 | } 48 | 49 | type item struct { 50 | f func(c core.Ctx) 51 | ctx core.Ctx 52 | } 53 | 54 | // Fanout async consume data from chan. 55 | type Fanout struct { 56 | taskNum int32 57 | ch chan item 58 | options *options 59 | waiter sync.WaitGroup 60 | ctx core.Ctx 61 | cancel func() 62 | state int32 63 | closeWorker chan struct{} 64 | closeChan chan struct{} 65 | once sync.Once 66 | } 67 | 68 | // NewFanout new a fanout struct. 69 | func NewFanout(opts ...Option) *Fanout { 70 | o := &options{ 71 | worker: 16, 72 | buffer: 1024, 73 | } 74 | for _, op := range opts { 75 | op(o) 76 | } 77 | c := &Fanout{ 78 | taskNum: 0, 79 | ch: make(chan item, o.buffer), 80 | options: o, 81 | closeChan: make(chan struct{}), 82 | once: sync.Once{}, 83 | closeWorker: make(chan struct{}), 84 | } 85 | atomic.StoreInt32(&c.state, 1) 86 | c.ctx, c.cancel = context.WithCancel(context.Background()) 87 | c.waiter.Add(int(o.worker)) 88 | for i := 0; i < int(o.worker); i++ { 89 | go c.proc() 90 | } 91 | return c 92 | } 93 | 94 | // TuneWorker Dynamically adjust the worker number 95 | func (c *Fanout) TuneWorker(n int) { 96 | if n > int(c.options.worker) { 97 | needAddNum := n - int(c.options.worker) 98 | for i := 0; i < needAddNum; i++ { 99 | c.waiter.Add(1) 100 | go c.proc() 101 | atomic.StoreInt32(&c.options.worker, atomic.LoadInt32(&c.options.worker)+1) 102 | } 103 | } else { 104 | needAddNum := int(c.options.worker) - n 105 | for i := 0; i < needAddNum; i++ { 106 | go func() { 107 | c.closeChan <- struct{}{} 108 | }() 109 | } 110 | } 111 | } 112 | 113 | // TunePool Dynamically adjust the gorutine pool size 114 | func (c *Fanout) TunePool(n int) { 115 | if n == int(c.options.buffer) { 116 | return 117 | } 118 | dirtyCh := make(chan item, n) 119 | for { 120 | select { 121 | case task := <-c.ch: 122 | dirtyCh <- task 123 | default: 124 | c.ch = dirtyCh 125 | atomic.StoreInt32(&c.options.buffer, int32(n)) 126 | return 127 | } 128 | } 129 | } 130 | 131 | func (c *Fanout) proc() { 132 | defer func() { 133 | atomic.StoreInt32(&c.options.worker, atomic.LoadInt32(&c.options.worker)-1) 134 | c.waiter.Done() 135 | }() 136 | for { 137 | select { 138 | case t := <-c.ch: 139 | c.wrapFunc(t.f)(t.ctx) 140 | atomic.StoreInt32(&c.taskNum, atomic.LoadInt32(&c.taskNum)-1) 141 | if atomic.LoadInt32(&c.state) == 0 && len(c.ch) == 0 { 142 | c.once.Do( 143 | func() { close(c.closeChan) }) 144 | return 145 | } 146 | case <-c.ctx.Done(): 147 | return 148 | case <-c.closeWorker: 149 | return 150 | case <-c.closeChan: 151 | return 152 | } 153 | } 154 | } 155 | 156 | func (c *Fanout) wrapFunc(f func(c core.Ctx)) (res func(core.Ctx)) { 157 | res = func(ctx core.Ctx) { 158 | defer func() { 159 | if r := recover(); r != nil { 160 | buf := make([]byte, 64*1024) 161 | buf = buf[:runtime.Stack(buf, false)] 162 | fmt.Printf("panic in fanout proc, err: %s, stack: %s", r, buf) 163 | } 164 | }() 165 | f(ctx) 166 | } 167 | return 168 | } 169 | 170 | // MustDo save a callback func. 171 | func (c *Fanout) MustDo(ctx core.Ctx, f func(ctx core.Ctx)) (err error) { 172 | if err = c.Do(ctx, f); err != nil { 173 | f(ctx) 174 | } 175 | return err 176 | } 177 | 178 | // Do save a callback func. 179 | func (c *Fanout) Do(ctx core.Ctx, f func(ctx core.Ctx)) (err error) { 180 | if f == nil || c.ctx.Err() != nil { 181 | return c.ctx.Err() 182 | } 183 | atomic.StoreInt32(&c.taskNum, atomic.LoadInt32(&c.taskNum)+1) 184 | select { 185 | case c.ch <- item{f: f, ctx: ctx}: 186 | default: 187 | err = ErrFull 188 | } 189 | return 190 | } 191 | 192 | func (c *Fanout) DoWhen(ctx core.Ctx, f func(ctx core.Ctx), cond bool) (err error) { 193 | if cond { 194 | return c.Do(ctx, f) 195 | } 196 | return nil 197 | } 198 | 199 | // Do save a callback func, wait for timeout if the channel is full. 200 | func (c *Fanout) DoWait(ctx core.Ctx, f func(ctx core.Ctx), timeout time.Duration) (err error) { 201 | if f == nil || c.ctx.Err() != nil { 202 | return c.ctx.Err() 203 | } 204 | cancelCtx, cancel := context.WithTimeout(ctx, timeout) 205 | defer cancel() 206 | for { 207 | select { 208 | case c.ch <- item{f: f, ctx: ctx}: 209 | return 210 | case <-cancelCtx.Done(): 211 | return ErrTimeout 212 | } 213 | } 214 | } 215 | 216 | // DoRetry 217 | // Auto retry call func with back off, if return err 218 | // Notice: no retry if panic 219 | func (c *Fanout) DoRetry(ctx core.Ctx, f func(ctx core.Ctx) error, maxRetry int) (err error) { 220 | return c.Do(ctx, func(ctx core.Ctx) { 221 | for i := maxRetry; i > 0; i-- { 222 | if f(ctx) == nil { 223 | break 224 | } 225 | time.Sleep(time.Duration((maxRetry-i)*(maxRetry-i)+1) * time.Millisecond) 226 | } 227 | }) 228 | } 229 | 230 | // Close close fanout. 231 | func (c *Fanout) Close() error { 232 | if err := c.ctx.Err(); err != nil { 233 | return err 234 | } 235 | c.cancel() 236 | c.waiter.Wait() 237 | return nil 238 | } 239 | 240 | // Wait wait fanout. 241 | func (c *Fanout) Wait() error { 242 | if err := c.ctx.Err(); err != nil { 243 | return err 244 | } 245 | atomic.StoreInt32(&c.state, 0) 246 | c.waiter.Wait() 247 | c.cancel() // in case of ctx leak 248 | return nil 249 | } 250 | 251 | // Shutdown wait fanout with given timeout. 252 | func (c *Fanout) Shutdown(timeout time.Duration) { 253 | if len(c.ch) == 0 { 254 | fmt.Println("[fanout] no task remaining, wait running task to be finished") 255 | c.cancel() 256 | } 257 | closeDone := make(chan struct{}) 258 | pctx, cancel := context.WithTimeout(context.Background(), timeout) 259 | defer cancel() 260 | go func(ctx core.Ctx) { 261 | defer close(closeDone) 262 | _ = c.Wait() 263 | }(pctx) 264 | for { 265 | select { 266 | case <-pctx.Done(): 267 | fmt.Printf("[fanout] close timeout(%s), force exit\n", timeout) 268 | c.cancel() 269 | return 270 | case <-closeDone: 271 | fmt.Println("[fanout] closed gracefully") 272 | return 273 | } 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /sdk/sdk.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "io/ioutil" 8 | "os" 9 | "path/filepath" 10 | "runtime" 11 | "strings" 12 | "sync" 13 | 14 | "github.com/orcastor/orcas/core" 15 | ) 16 | 17 | // 秒传级别设置 对应 Config.RefLevel 18 | const ( 19 | OFF = iota // OFF 20 | FULL // 整个文件读取 21 | FAST // 头部检查成功再整个文件读取 22 | ) 23 | 24 | // 同名冲突解决方式 25 | const ( 26 | COVER = iota // 合并或覆盖 27 | RENAME // 重命名 28 | THROW // 报错 29 | SKIP // 跳过 30 | ) 31 | 32 | type Config struct { 33 | UserName string // 用户名 34 | Password string // 密码 35 | DataSync bool // 断电保护策略(Power-off Protection Policy),强制每次写入数据后刷到磁盘 36 | RefLevel uint32 // 秒传级别设置:OFF(默认) / FULL: Ref / FAST: TryRef+Ref 37 | PkgThres uint32 // 打包个数限制,不设置默认1000个 38 | WiseCmpr uint32 // 智能压缩,根据文件类型决定是否压缩,取值见core.DATA_CMPR_MASK 39 | CmprQlty uint32 // 压缩级别,br:[0,11],gzip:[-3,9],zstd:[0,10] 40 | EndecWay uint32 // 加密方式,取值见core.DATA_ENDEC_MASK 41 | EndecKey string // 加密KEY,SM4需要固定为16个字符,AES256需要大于16个字符 42 | DontSync string // 不同步的文件名通配符(https://pkg.go.dev/path/filepath#Match),用分号分隔 43 | Conflict uint32 // 同名冲突解决方式,COVER:合并或覆盖 / RENAME:重命名 / THROW:报错 / SKIP:跳过 44 | NameTmpl string // 重命名尾巴,"%s的副本" 45 | WorkersN uint32 // 并发池大小,不小于16 46 | // ChkPtDir string // 断点续传记录目录,不设置路径默认不开启 47 | } 48 | 49 | type OrcasSDK interface { 50 | H() core.Handler 51 | 52 | Close() 53 | 54 | Login(cfg Config) (core.Ctx, *core.UserInfo, []*core.BucketInfo, error) 55 | 56 | Path2ID(c core.Ctx, bktID, pid int64, rpath string) (id int64, err error) 57 | ID2Path(c core.Ctx, bktID, id int64) (rpath string, err error) 58 | 59 | Upload(c core.Ctx, bktID, pid int64, lpath string) error 60 | Download(c core.Ctx, bktID, pid int64, lpath string) error 61 | } 62 | 63 | type OrcasSDKImpl struct { 64 | h core.Handler 65 | cfg Config 66 | bl []string 67 | f *Fanout 68 | } 69 | 70 | func New(h core.Handler) OrcasSDK { 71 | return &OrcasSDKImpl{h: h, f: NewFanout()} 72 | } 73 | 74 | func (osi *OrcasSDKImpl) Close() { 75 | osi.h.Close() 76 | } 77 | 78 | func (osi *OrcasSDKImpl) H() core.Handler { 79 | return osi.h 80 | } 81 | 82 | func (osi *OrcasSDKImpl) Login(cfg Config) (core.Ctx, *core.UserInfo, []*core.BucketInfo, error) { 83 | if cfg.UserName == "" { 84 | return nil, nil, nil, errors.New(`UserName is empty.`) 85 | } 86 | if cfg.Password == "" { 87 | return nil, nil, nil, errors.New(`Password is empty.`) 88 | } 89 | if cfg.PkgThres <= 0 { 90 | cfg.PkgThres = 1000 91 | } 92 | osi.h.SetOptions(core.Options{Sync: cfg.DataSync}) 93 | osi.bl = strings.Split(cfg.DontSync, ";") 94 | if cfg.WorkersN < 16 { 95 | cfg.WorkersN = 16 96 | } 97 | if cfg.WorkersN > 0 { 98 | osi.f.TuneWorker(int(cfg.WorkersN)) 99 | } 100 | if cfg.Conflict == RENAME { 101 | if !strings.Contains(cfg.NameTmpl, "%s") { 102 | return nil, nil, nil, errors.New(`cfg.NameTmp should contains "%s".`) 103 | } 104 | } 105 | switch cfg.EndecWay { 106 | case core.DATA_ENDEC_SM4: 107 | if len(cfg.EndecKey) != 16 { 108 | return nil, nil, nil, errors.New(`The length of EndecKey for DATA_ENDEC_SM4 should be 16.`) 109 | } 110 | case core.DATA_ENDEC_AES256: 111 | if len(cfg.EndecKey) <= 16 { 112 | return nil, nil, nil, errors.New(`The length of EndecKey for DATA_ENDEC_AES256 should be greater than 16.`) 113 | } 114 | } 115 | osi.cfg = cfg 116 | return osi.h.Login(context.TODO(), cfg.UserName, cfg.Password) 117 | } 118 | 119 | func (osi *OrcasSDKImpl) skip(name string) bool { 120 | for _, v := range osi.bl { 121 | if ok, _ := filepath.Match(v, name); ok { 122 | return true 123 | } 124 | } 125 | return false 126 | } 127 | 128 | const PathSeparator = "/" 129 | 130 | func (osi *OrcasSDKImpl) Path2ID(c core.Ctx, bktID, pid int64, rpath string) (int64, error) { 131 | for _, child := range strings.Split(rpath, PathSeparator) { 132 | if child == "" { 133 | continue 134 | } 135 | os, _, _, err := osi.h.List(c, bktID, pid, core.ListOptions{ 136 | Word: child, 137 | Count: 1, 138 | Brief: 2, 139 | }) 140 | if err != nil { 141 | return 0, fmt.Errorf("open remote path error(path:%s): %+v", rpath, err) 142 | } 143 | if len(os) <= 0 { 144 | return 0, errors.New("remote path does not exist, path:" + rpath) 145 | } 146 | pid = os[0].ID 147 | } 148 | return pid, nil 149 | } 150 | 151 | func (osi *OrcasSDKImpl) ID2Path(c core.Ctx, bktID, id int64) (rpath string, err error) { 152 | for id != core.ROOT_OID { 153 | os, err := osi.h.Get(c, bktID, []int64{id}) 154 | if err != nil { 155 | return "", fmt.Errorf("open remote object error(id:%d): %+v", id, err) 156 | } 157 | if len(os) <= 0 { 158 | return "", fmt.Errorf("remote object does not exist, id:%d", id) 159 | } 160 | rpath = filepath.Join(os[0].Name, rpath) 161 | id = os[0].PID 162 | } 163 | return rpath, nil 164 | } 165 | 166 | // 层序遍历 167 | type elem struct { 168 | id int64 169 | path string 170 | } 171 | 172 | type uploadInfo struct { 173 | path string 174 | o *core.ObjectInfo 175 | } 176 | 177 | func (osi *OrcasSDKImpl) Upload(c core.Ctx, bktID, pid int64, lpath string) error { 178 | f, err := os.Open(lpath) 179 | if err != nil { 180 | return err 181 | } 182 | 183 | fi, err := f.Stat() 184 | if err != nil { 185 | return err 186 | } 187 | f.Close() 188 | 189 | o := &core.ObjectInfo{ 190 | ID: osi.h.NewID(), 191 | PID: pid, 192 | MTime: fi.ModTime().Unix(), 193 | Type: core.OBJ_TYPE_FILE, 194 | Name: fi.Name(), 195 | Size: fi.Size(), 196 | } 197 | if !fi.IsDir() { 198 | if osi.skip(o.Name) { 199 | return nil 200 | } 201 | if o.Size > 0 { 202 | return osi.uploadFiles(c, 203 | bktID, 204 | []uploadInfo{{path: filepath.Dir(lpath), o: o}}, 205 | nil, nil, osi.cfg.RefLevel, 0) 206 | } 207 | 208 | o.DataID = core.EmptyDataID 209 | if _, err = osi.putObjects(c, bktID, []*core.ObjectInfo{o}); err != nil { 210 | fmt.Println(runtime.Caller(0)) 211 | fmt.Println(err) 212 | } 213 | return err 214 | } 215 | 216 | // 上传目录 217 | o.Type = core.OBJ_TYPE_DIR 218 | dirIDs, err := osi.putObjects(c, bktID, []*core.ObjectInfo{o}) 219 | if err != nil || len(dirIDs) <= 0 || dirIDs[0] <= 0 { 220 | fmt.Println(runtime.Caller(0)) 221 | fmt.Println(err) 222 | return err 223 | } 224 | 225 | q := []elem{{id: dirIDs[0], path: lpath}} 226 | var emptyFiles []*core.ObjectInfo 227 | var u []uploadInfo 228 | 229 | // 遍历本地目录 230 | for len(q) > 0 { 231 | if q[0].path == "" { 232 | // 路径为空,直接弹出 233 | q = q[1:] 234 | continue 235 | } 236 | 237 | rawFiles, err := ioutil.ReadDir(q[0].path) 238 | if err != nil { 239 | fmt.Println(runtime.Caller(0)) 240 | fmt.Println(err) 241 | return err 242 | } 243 | 244 | if len(rawFiles) <= 0 { 245 | // 目录为空,直接弹出 246 | q = q[1:] 247 | continue 248 | } 249 | 250 | var dirs []*core.ObjectInfo 251 | for _, fi := range rawFiles { 252 | if osi.skip(fi.Name()) { 253 | continue 254 | } 255 | if fi.IsDir() { 256 | dirs = append(dirs, &core.ObjectInfo{ 257 | ID: osi.h.NewID(), 258 | PID: q[0].id, 259 | MTime: fi.ModTime().Unix(), 260 | Type: core.OBJ_TYPE_DIR, 261 | Name: fi.Name(), 262 | }) 263 | continue 264 | } else { 265 | file := &core.ObjectInfo{ 266 | ID: osi.h.NewID(), 267 | PID: q[0].id, 268 | MTime: fi.ModTime().Unix(), 269 | Type: core.OBJ_TYPE_FILE, 270 | Name: fi.Name(), 271 | Size: fi.Size(), 272 | } 273 | if file.Size > 0 { 274 | u = append(u, uploadInfo{ 275 | path: q[0].path, 276 | o: file, 277 | }) 278 | if len(u) >= int(osi.cfg.PkgThres) { 279 | var tmpu []uploadInfo 280 | tmpu, u = u, nil 281 | osi.f.MustDo(c, func(c core.Ctx) { 282 | osi.uploadFiles(c, bktID, tmpu, nil, nil, osi.cfg.RefLevel, 0) 283 | }) 284 | } 285 | } else { 286 | file.DataID = core.EmptyDataID 287 | emptyFiles = append(emptyFiles, file) 288 | } 289 | } 290 | } 291 | 292 | // 异步获取上一级目录的id 293 | wg := &sync.WaitGroup{} 294 | dirElems := make([]elem, len(dirs)) 295 | if len(dirs) > 0 { // FIXME:处理目录过多问题 296 | // 1. 如果是目录, 直接上传 297 | wg.Add(1) 298 | go func() { 299 | defer wg.Done() 300 | // 上传目录 301 | ids, err := osi.putObjects(c, bktID, dirs) 302 | if err != nil { 303 | fmt.Println(runtime.Caller(0)) 304 | fmt.Println(err) 305 | return 306 | } 307 | for i, id := range ids { 308 | if id > 0 { 309 | dirElems[i].id = id 310 | dirElems[i].path = filepath.Join(q[0].path, dirs[i].Name) 311 | } 312 | } 313 | }() 314 | } 315 | 316 | wg.Wait() 317 | q = append(q[1:], dirElems...) 318 | } 319 | 320 | if len(u) > 0 { 321 | var tmpu []uploadInfo 322 | tmpu, u = u, nil 323 | osi.f.MustDo(c, func(c core.Ctx) { 324 | osi.uploadFiles(c, bktID, tmpu, nil, nil, osi.cfg.RefLevel, 0) 325 | }) 326 | } 327 | 328 | if len(emptyFiles) > 0 { 329 | osi.f.MustDo(c, func(c core.Ctx) { 330 | if _, err := osi.putObjects(c, bktID, emptyFiles); err != nil { 331 | fmt.Println(runtime.Caller(0)) 332 | fmt.Println(err) 333 | } 334 | }) 335 | } 336 | osi.f.Wait() 337 | return nil 338 | } 339 | 340 | func (osi *OrcasSDKImpl) Download(c core.Ctx, bktID, id int64, lpath string) error { 341 | o, err := osi.h.Get(c, bktID, []int64{id}) 342 | if err != nil { 343 | return fmt.Errorf("open remote object error(id:%d): %+v", id, err) 344 | } 345 | if len(o) <= 0 { 346 | return fmt.Errorf("remote object does not exist, id:%d", id) 347 | } 348 | 349 | if o[0].Type == core.OBJ_TYPE_FILE { 350 | return osi.downloadFile(c, bktID, o[0], lpath) 351 | } else if o[0].Type != core.OBJ_TYPE_DIR { 352 | return fmt.Errorf("remote object type error, id:%d type:%d", id, o[0].Type) 353 | } 354 | 355 | // 遍历远端目录 356 | q := []elem{{id: id, path: filepath.Join(lpath, o[0].Name)}} 357 | var delim string 358 | for len(q) > 0 { 359 | os.MkdirAll(q[0].path, 0766) 360 | o, _, d, err := osi.h.List(c, bktID, q[0].id, core.ListOptions{ 361 | Delim: delim, 362 | Count: 1000, 363 | Order: "type", 364 | }) 365 | if err != nil { 366 | fmt.Println(runtime.Caller(0)) 367 | fmt.Println(err) 368 | return err 369 | } 370 | 371 | for _, x := range o { 372 | if osi.skip(x.Name) { 373 | continue 374 | } 375 | path := filepath.Join(q[0].path, x.Name) 376 | switch x.Type { 377 | case core.OBJ_TYPE_DIR: 378 | q = append(q, elem{id: x.ID, path: path}) 379 | case core.OBJ_TYPE_FILE: 380 | f := x 381 | osi.f.MustDo(c, func(c core.Ctx) { 382 | osi.downloadFile(c, bktID, f, path) 383 | }) 384 | } 385 | } 386 | 387 | if len(o) <= 0 { 388 | q = q[1:] 389 | delim = "" 390 | } else { 391 | delim = d 392 | } 393 | } 394 | osi.f.Wait() 395 | return nil 396 | } 397 | -------------------------------------------------------------------------------- /sdk/sdk_test.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "os/exec" 7 | "path/filepath" 8 | "testing" 9 | 10 | "github.com/orca-zhang/idgen" 11 | "github.com/orcastor/orcas/core" 12 | . "github.com/smartystreets/goconvey/convey" 13 | ) 14 | 15 | // ORCAS_BASE=/tmp/test ORCAS_DATA=/tmp/test ORCAS_SECRET=xxxxxxxx go test . -run=TestUpload -v 16 | var mntPath = "/tmp/test/" 17 | var path = "/home/semaphore/go/" 18 | var cfg = Config{ 19 | UserName: "orcas", 20 | Password: "orcas", 21 | DataSync: true, 22 | RefLevel: FULL, 23 | EndecWay: core.DATA_ENDEC_AES256, 24 | EndecKey: "1234567890abcdef12345678", 25 | WiseCmpr: core.DATA_CMPR_GZIP, 26 | CmprQlty: 5, 27 | DontSync: ".*", 28 | } 29 | 30 | func init() { 31 | core.InitDB() 32 | 33 | sdk := New(core.NewLocalHandler()) 34 | defer sdk.Close() 35 | 36 | c, _, b, _ := sdk.Login(cfg) 37 | if len(b) <= 0 { 38 | bktID, _ := idgen.NewIDGen(nil, 0).New() 39 | core.InitBucketDB(c, bktID) 40 | core.NewLocalAdmin().PutBkt(c, []*core.BucketInfo{{ID: bktID, Name: "备份测试", UID: 1, Type: 1}}) 41 | } 42 | } 43 | 44 | func TestUpload(t *testing.T) { 45 | Convey("upload dir", t, func() { 46 | sdk := New(core.NewLocalHandler()) 47 | defer sdk.Close() 48 | 49 | c, _, b, err := sdk.Login(cfg) 50 | So(err, ShouldBeNil) 51 | 52 | So(sdk.Upload(c, b[0].ID, core.ROOT_OID, path), ShouldBeNil) 53 | }) 54 | } 55 | 56 | func TestDownload(t *testing.T) { 57 | Convey("download dir", t, func() { 58 | sdk := New(core.NewLocalHandler()) 59 | defer sdk.Close() 60 | 61 | c, _, b, err := sdk.Login(cfg) 62 | So(err, ShouldBeNil) 63 | 64 | id, _ := sdk.Path2ID(c, b[0].ID, core.ROOT_OID, filepath.Base(path)) 65 | So(sdk.Download(c, b[0].ID, id, mntPath), ShouldBeNil) 66 | 67 | fmt.Println(sdk.ID2Path(c, b[0].ID, id)) 68 | }) 69 | } 70 | 71 | func TestCheck(t *testing.T) { 72 | Convey("normal", t, func() { 73 | var out bytes.Buffer 74 | cmd := exec.Command("diff", "-urNa", "-x.*", path, filepath.Join(mntPath, filepath.Base(path))) 75 | cmd.Stdout = &out 76 | err := cmd.Run() 77 | So(out.String(), ShouldBeEmpty) 78 | So(err, ShouldBeNil) 79 | }) 80 | } 81 | --------------------------------------------------------------------------------