├── .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 | [](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 |
--------------------------------------------------------------------------------