diff --git a/.test.env b/.test.env index 569bfda..995d4a3 100644 --- a/.test.env +++ b/.test.env @@ -1,5 +1,5 @@ HTTP_GW_VERBOSE=--true -HTTP_GW_KEY=generated +HTTP_GW_KEY=/home/pk/devbox/work/neo/user.key HTTP_GW_LISTEN_ADDRESS=0.0.0.0:8087 HTTP_GW_LOGGER_LEVEL=debug @@ -10,10 +10,4 @@ HTTP_GW_KEEPALIVE_TIME=120s HTTP_GW_KEEPALIVE_PERMIT_WITHOUT_STREAM=True HTTP_GW_CONN_TTL=1h HTTP_GW_PEERS_0_WEIGHT=1.0 -HTTP_GW_PEERS_1_WEIGHT=0.125 -HTTP_GW_PEERS_2_WEIGHT=0.125 -HTTP_GW_PEERS_3_WEIGHT=0.125 -HTTP_GW_PEERS_4_WEIGHT=0.125 -HTTP_GW_PEERS_5_WEIGHT=0.125 -HTTP_GW_PEERS_6_WEIGHT=0.125 -HTTP_GW_PEERS_7_WEIGHT=0.125 \ No newline at end of file +HTTP_GW_PEERS_0_ADDRESS=s01.neofs.devenv:8080 \ No newline at end of file diff --git a/README.md b/README.md index 73aaeda..c28063a 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,8 @@ NeoFS HTTP Gate is example of tool that provides basic interactions with NeoFS. ## Configuration ``` -# Flags +# Flags: + --pprof enable pprof --metrics enable prometheus -h, --help show help @@ -43,35 +44,36 @@ NeoFS HTTP Gate is example of tool that provides basic interactions with NeoFS. --verbose debug gRPC connections --request_timeout duration gRPC request timeout (default 5s) --connect_timeout duration gRPC connect timeout (default 30s) - --listen_address string HTTP Gate listen address (default "0.0.0.0:8082") + --listen_address string HTTP gate's listen address (default "0.0.0.0:8082") -p, --peers stringArray NeoFS nodes # Environments: -HTTP_GW_KEY=string - "generated" to generate key, path to private key file, hex string or wif (default "generated") -HTTP_GW_CONNECT_TIMEOUT=Duration - timeout for connection -HTTP_GW_REQUEST_TIMEOUT=Duration - timeout for request -HTTP_GW_REBALANCE_TIMER=Duration - time between connections checks -HTTP_GW_LISTEN_ADDRESS=host:port - address to listen connections -HTTP_GW_PEERS__ADDRESS=host:port - address of NeoFS Node -HTTP_GW_PEERS__WEIGHT=float - weight of NeoFS Node -HTTP_GW_PPROF=bool - enable/disable pprof (/debug/pprof) -HTTP_GW_METRICS=bool - enable/disable prometheus metrics endpoint (/metrics) -HTTP_GW_LOGGER_FORMAT=string - logger format -HTTP_GW_LOGGER_LEVEL=string - logger level -HTTP_GW_LOGGER_NO_CALLER=bool - logger don't show caller -HTTP_GW_LOGGER_NO_DISCLAIMER=bool - logger don't show application name/version -HTTP_GW_LOGGER_SAMPLING_INITIAL=int - logger sampling initial -HTTP_GW_LOGGER_SAMPLING_THEREAFTER=int - logger sampling thereafter -HTTP_GW_LOGGER_TRACE_LEVEL=string - logger show trace on level -HTTP_GW_KEEPALIVE_TIME=Duration - аfter a duration of this time if the client doesn't see any activity -it pings the server to see if the transport is still alive. -HTTP_GW_KEEPALIVE_TIMEOUT=Duration - after having pinged for keepalive check, the client waits for a duration -of Timeout and if no activity is seen even after that the connection is closed -HTTP_GW_KEEPALIVE_PERMIT_WITHOUT_STREAM=Bool - if true, client sends keepalive pings even with no active RPCs. -If false, when there are no active RPCs, Time and Timeout will be ignored and no keepalive pings will be sent. - -HTTP_GW_UPLOAD_HEADER_USE_DEFAULT_TIMESTAMP=bool - enable/disable adding current timestamp attribute when object uploads +HTTP_GW_KEY=string - Path to private key file, hex string or wif string +HTTP_GW_CONNECT_TIMEOUT=duration - Timeout for connection +HTTP_GW_REQUEST_TIMEOUT=duration - Timeout for request +HTTP_GW_REBALANCE_TIMER=duration - Time between connections checks +HTTP_GW_LISTEN_ADDRESS=host:port - Address to listen connections +HTTP_GW_PEERS__ADDRESS=host:port - Address of NeoFS Node +HTTP_GW_PEERS__WEIGHT=float - Weight of NeoFS Node +HTTP_GW_PPROF=bool - Enable/disable pprof (/debug/pprof) +HTTP_GW_METRICS=bool - Enable/disable prometheus metrics endpoint (/metrics) +HTTP_GW_LOGGER_FORMAT=string - Logger format +HTTP_GW_LOGGER_LEVEL=string - Logger level +HTTP_GW_LOGGER_NO_CALLER=bool - Logger don't show caller +HTTP_GW_LOGGER_NO_DISCLAIMER=bool - Logger don't show application name/version +HTTP_GW_LOGGER_SAMPLING_INITIAL=int - Logger sampling initial +HTTP_GW_LOGGER_SAMPLING_THEREAFTER=int - Logger sampling thereafter +HTTP_GW_LOGGER_TRACE_LEVEL=string - Logger show trace on level +HTTP_GW_KEEPALIVE_TIME=duration - After a duration of this time if the client sees no activity + it pings the server to see if the transport is still alive +HTTP_GW_KEEPALIVE_TIMEOUT=duration - After having pinged for keepalive check, the client waits for a duration + of Timeout and if no activity is seen even after that the connection + is closed +HTTP_GW_KEEPALIVE_PERMIT_WITHOUT_STREAM=bool - If true, client sends keepalive pings even with no active RPCs. + If false, when there are no active RPCs, Time and Timeout will be + ignored and no keepalive pings will be sent +HTTP_GW_UPLOAD_HEADER_USE_DEFAULT_TIMESTAMP=bool - Enable/disable adding current timestamp attribute when object uploads HTTP_GW_WEB_READ_BUFFER_SIZE=4096 - per-connection buffer size for requests' reading HTTP_GW_WEB_READ_TIMEOUT=15s - an amount of time allowed to read the full request including body @@ -84,6 +86,6 @@ HTTP_GW_WEB_MAX_REQUEST_BODY_SIZE=4194304 - maximum request body size, server r Peers preset: -HTTP_GW_PEERS_[N]_ADDRESS = string -HTTP_GW_PEERS_[N]_WEIGHT = 0..1 (float) -``` \ No newline at end of file +HTTP_GW_PEERS__ADDRESS = string +HTTP_GW_PEERS__WEIGHT = 0..1 (float) +``` diff --git a/app.go b/app.go index 6c70b90..8fe8960 100644 --- a/app.go +++ b/app.go @@ -2,36 +2,33 @@ package main import ( "context" - "crypto/ecdsa" + "sort" "strconv" "github.com/fasthttp/router" - sdk "github.com/nspcc-dev/cdn-sdk" - "github.com/nspcc-dev/cdn-sdk/creds/neofs" - "github.com/nspcc-dev/cdn-sdk/logger" - "github.com/nspcc-dev/cdn-sdk/pool" + "github.com/nspcc-dev/neofs-api-go/pkg/client" + "github.com/nspcc-dev/neofs-api-go/pkg/token" + "github.com/nspcc-dev/neofs-http-gate/logger" + "github.com/nspcc-dev/neofs-http-gate/neofs" "github.com/spf13/viper" "github.com/valyala/fasthttp" "go.uber.org/zap" - "google.golang.org/grpc" "google.golang.org/grpc/grpclog" - "google.golang.org/grpc/keepalive" ) type ( app struct { - cli sdk.Client - pool pool.Client - log *zap.Logger - cfg *viper.Viper - key *ecdsa.PrivateKey - - wlog logger.Logger - web *fasthttp.Server - - jobDone chan struct{} - webDone chan struct{} - + plant neofs.ClientPlant + getOperations struct { + client client.Client + sessionToken *token.SessionToken + } + log *zap.Logger + cfg *viper.Viper + wlog logger.Logger + web *fasthttp.Server + jobDone chan struct{} + webDone chan struct{} enableDefaultTimestamp bool } @@ -84,11 +81,11 @@ func newApp(ctx context.Context, opt ...Option) App { grpclog.SetLoggerV2(a.wlog) } - conTimeout := a.cfg.GetDuration(cfgConTimeout) - reqTimeout := a.cfg.GetDuration(cfgReqTimeout) - tckTimeout := a.cfg.GetDuration(cfgRebalance) + // conTimeout := a.cfg.GetDuration(cfgConTimeout) + // reqTimeout := a.cfg.GetDuration(cfgReqTimeout) + // tckTimeout := a.cfg.GetDuration(cfgRebalance) - // -- setup FastHTTP server: -- + // -- setup FastHTTP server -- a.web.Name = "neofs-http-gate" a.web.ReadBufferSize = a.cfg.GetInt(cfgWebReadBufferSize) a.web.WriteBufferSize = a.cfg.GetInt(cfgWebWriteBufferSize) @@ -99,68 +96,42 @@ func newApp(ctx context.Context, opt ...Option) App { a.web.NoDefaultContentType = true a.web.MaxRequestBodySize = a.cfg.GetInt(cfgWebMaxRequestBodySize) - // FIXME don't work with StreamRequestBody, - // some bugs with readMultipartForm + // -- -- -- -- -- -- FIXME -- -- -- -- -- -- + // Does not work with StreamRequestBody, + // some bugs with readMultipartForm // https://github.com/valyala/fasthttp/issues/968 a.web.DisablePreParseMultipartForm = true - a.web.StreamRequestBody = a.cfg.GetBool(cfgWebStreamRequestBody) - // -- -- -- -- -- -- -- -- -- -- + // -- -- -- -- -- -- -- -- -- -- -- -- -- -- - connections := make(map[string]float64) + var cl connectionList for i := 0; ; i++ { address := a.cfg.GetString(cfgPeers + "." + strconv.Itoa(i) + ".address") weight := a.cfg.GetFloat64(cfgPeers + "." + strconv.Itoa(i) + ".weight") if address == "" { break } - - connections[address] = weight - a.log.Info("add connection peer", - zap.String("address", address), - zap.Float64("weight", weight)) + cl = append(cl, connection{address: address, weight: weight}) + a.log.Info("add connection peer", zap.String("address", address), zap.Float64("weight", weight)) } - - cred, err := neofs.New(a.cfg.GetString(cmdNeoFSKey)) + sort.Sort(sort.Reverse(cl)) + cred, err := neofs.NewCredentials(a.cfg.GetString(cmdNeoFSKey)) if err != nil { - a.log.Fatal("could not prepare credentials", zap.Error(err)) + a.log.Fatal("could not get credentials", zap.Error(err)) } - - a.pool, err = pool.New(ctx, - pool.WithLogger(a.log), - pool.WithCredentials(cred), - pool.WithWeightPool(connections), - pool.WithTickerTimeout(tckTimeout), - pool.WithConnectTimeout(conTimeout), - pool.WithRequestTimeout(reqTimeout), - pool.WithAPIPreparer(sdk.APIPreparer), - pool.WithGRPCOptions( - grpc.WithBlock(), - grpc.WithInsecure(), - grpc.WithKeepaliveParams(keepalive.ClientParameters{ - Time: a.cfg.GetDuration(cfgKeepaliveTime), - Timeout: a.cfg.GetDuration(cfgKeepaliveTimeout), - PermitWithoutStream: a.cfg.GetBool(cfgKeepalivePermitWithoutStream), - }))) - + a.plant, err = neofs.NewClientPlant(ctx, cl[0].address, cred) if err != nil { - a.log.Fatal("could not prepare connection pool", zap.Error(err)) + a.log.Fatal("failed to create neofs client") } - - a.cli, err = sdk.New(ctx, - sdk.WithLogger(a.log), - sdk.WithCredentials(cred), - sdk.WithConnectionPool(a.pool), - sdk.WithAPIPreparer(sdk.APIPreparer)) + a.getOperations.client, a.getOperations.sessionToken, err = a.plant.GetReusableArtifacts(ctx) if err != nil { - a.log.Fatal("could not prepare sdk client", zap.Error(err)) + a.log.Fatal("failed to get neofs client's reusable artifacts") } - return a } func (a *app) Wait() { - a.log.Info("application started") + a.log.Info("starting application") select { case <-a.jobDone: // wait for job is stopped @@ -171,50 +142,51 @@ func (a *app) Wait() { } func (a *app) Worker(ctx context.Context) { - a.pool.Worker(ctx) close(a.jobDone) } func (a *app) Serve(ctx context.Context) { go func() { <-ctx.Done() - a.log.Info("stop web-server", zap.Error(a.web.Shutdown())) + a.log.Info("shutting down web server", zap.Error(a.web.Shutdown())) close(a.webDone) }() - + // Configure router. r := router.New() r.RedirectTrailingSlash = true - - a.log.Info("enabled /upload/{cid}") r.POST("/upload/{cid}", a.upload) - - a.log.Info("enabled /get/{cid}/{oid}") + a.log.Info("added path /upload/{cid}") r.GET("/get/{cid}/{oid}", a.byAddress) - - a.log.Info("enabled /get_by_attribute/{cid}/{attr_key}/{attr_val:*}") + a.log.Info("added path /get/{cid}/{oid}") r.GET("/get_by_attribute/{cid}/{attr_key}/{attr_val:*}", a.byAttribute) - + a.log.Info("added path /get_by_attribute/{cid}/{attr_key}/{attr_val:*}") // attaching /-/(ready,healthy) - attachHealthy(r, a.pool.Status) - + // attachHealthy(r, a.pool.Status) // enable metrics if a.cfg.GetBool(cmdMetrics) { - a.log.Info("enabled /metrics/") + a.log.Info("added path /metrics/") attachMetrics(r, a.wlog) } - // enable pprof if a.cfg.GetBool(cmdPprof) { - a.log.Info("enabled /debug/pprof/") + a.log.Info("added path /debug/pprof/") attachProfiler(r) } - bind := a.cfg.GetString(cfgListenAddress) - a.log.Info("run gateway server", - zap.String("address", bind)) - + a.log.Info("running web server", zap.String("address", bind)) a.web.Handler = r.Handler if err := a.web.ListenAndServe(bind); err != nil { a.log.Fatal("could not start server", zap.Error(err)) } } + +type connection struct { + address string + weight float64 +} + +type connectionList []connection + +func (p connectionList) Len() int { return len(p) } +func (p connectionList) Less(i, j int) bool { return p[i].weight < p[j].weight } +func (p connectionList) Swap(i, j int) { p[i], p[j] = p[j], p[i] } diff --git a/bearer.go b/bearer.go index ee01fee..e14c35c 100644 --- a/bearer.go +++ b/bearer.go @@ -2,6 +2,7 @@ package main import ( "bytes" + "context" "encoding/base64" "github.com/nspcc-dev/neofs-api-go/pkg/token" @@ -18,7 +19,7 @@ const ( // BearerToken usage: // -// if err = checkAndPropagateBearerToken(ctx); err != nil { +// if err = storeBearerToken(ctx); err != nil { // log.Error("could not fetch bearer token", zap.Error(err)) // c.Error("could not fetch bearer token", fasthttp.StatusBadRequest) // return @@ -46,15 +47,13 @@ func fromCookie(h *fasthttp.RequestHeader) []byte { return auth } -func checkAndPropagateBearerToken(ctx *fasthttp.RequestCtx) error { +func storeBearerToken(ctx *fasthttp.RequestCtx) error { tkn, err := fetchBearerToken(ctx) if err != nil { return err } - // This is an analog of context.WithValue. ctx.SetUserValue(bearerTokenKey, tkn) - return nil } @@ -63,14 +62,12 @@ func fetchBearerToken(ctx *fasthttp.RequestCtx) (*token.BearerToken, error) { if ctx == nil { return nil, nil } - var ( lastErr error buf []byte tkn = new(token.BearerToken) ) - for _, parse := range []fromHandler{fromHeader, fromCookie} { if buf = parse(&ctx.Request.Header); buf == nil { continue @@ -89,3 +86,10 @@ func fetchBearerToken(ctx *fasthttp.RequestCtx) (*token.BearerToken, error) { return nil, lastErr } + +func loadBearerToken(ctx context.Context) (*token.BearerToken, error) { + if tkn, ok := ctx.Value(bearerTokenKey).(*token.BearerToken); ok && tkn != nil { + return tkn, nil + } + return nil, errors.New("found empty bearer token") +} diff --git a/bearer_test.go b/bearer_test.go index 4ae7b20..4cfa31f 100644 --- a/bearer_test.go +++ b/bearer_test.go @@ -4,7 +4,6 @@ import ( "encoding/base64" "testing" - sdk "github.com/nspcc-dev/cdn-sdk" "github.com/nspcc-dev/neofs-api-go/pkg/owner" "github.com/nspcc-dev/neofs-api-go/pkg/token" "github.com/stretchr/testify/require" @@ -152,10 +151,10 @@ func Test_checkAndPropagateBearerToken(t *testing.T) { ctx := makeTestRequest(t64, "") // Expect to see the token within the context. - require.NoError(t, checkAndPropagateBearerToken(ctx)) + require.NoError(t, storeBearerToken(ctx)) // Expect to see the same token without errors. - actual, err := sdk.BearerToken(ctx) + actual, err := loadBearerToken(ctx) require.NoError(t, err) require.Equal(t, tkn, actual) } diff --git a/global/context.go b/global/context.go index 6aa6754..19b3c53 100644 --- a/global/context.go +++ b/global/context.go @@ -8,13 +8,16 @@ import ( ) var ( - globalContext context.Context - globalContexOnce sync.Once + globalContext context.Context + globalContextOnce sync.Once + globalContextBarrier = make(chan struct{}) ) func Context() context.Context { - globalContexOnce.Do(func() { + globalContextOnce.Do(func() { globalContext, _ = signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP) + close(globalContextBarrier) }) + <-globalContextBarrier return globalContext } diff --git a/go.mod b/go.mod index 6e7828b..016df60 100644 --- a/go.mod +++ b/go.mod @@ -1,18 +1,21 @@ module github.com/nspcc-dev/neofs-http-gate -go 1.15 +go 1.16 require ( github.com/fasthttp/router v1.3.5 - github.com/nspcc-dev/cdn-sdk v0.3.4 - github.com/nspcc-dev/neofs-api-go v1.23.0 + github.com/nspcc-dev/neofs-api-go v1.25.0 + github.com/nspcc-dev/neofs-crypto v0.3.0 + github.com/nspcc-dev/neofs-node v0.17.0 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.9.0 github.com/prometheus/common v0.15.0 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.7.1 github.com/stretchr/testify v1.7.0 - github.com/valyala/fasthttp v1.20.0 + github.com/valyala/fasthttp v1.22.0 + go.uber.org/atomic v1.7.0 // indirect go.uber.org/zap v1.16.0 - google.golang.org/grpc v1.35.0 + golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 // indirect + google.golang.org/grpc v1.36.1 ) diff --git a/go.sum b/go.sum index b4818b2..439631f 100644 --- a/go.sum +++ b/go.sum @@ -10,6 +10,7 @@ cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7 cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +code.cloudfoundry.org/bytefmt v0.0.0-20200131002437-cf55d5288a48/go.mod h1:wN/zk7mhREp/oviagqUXY3EwuHhWyOvAdsn5Y4CzOrc= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -21,6 +22,9 @@ github.com/CityOfZion/neo-go v0.71.1-pre.0.20200129171427-f773ec69fb84/go.mod h1 github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/PaesslerAG/gval v1.0.0/go.mod h1:y/nm5yEyTeX6av0OfKJNp9rBNj2XrGhAf5+v24IBN1I= +github.com/PaesslerAG/jsonpath v0.1.0/go.mod h1:4BzmtoM/PI8fPO4aQGIusjGxGir2BzcV0grWtFzq1Y8= +github.com/PaesslerAG/jsonpath v0.1.1/go.mod h1:lVboNxFGal/VwW6d9JzIy56bUsYAP6tH/x80vjnCseY= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= @@ -29,6 +33,8 @@ github.com/abiosoft/ishell v2.0.0+incompatible/go.mod h1:HQR9AqF2R3P4XXpMpI0NAzg github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db/go.mod h1:rB3B4rKii8V21ydCbIzH5hZiCQE7f5E9SzUb/ZZx530= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/alecthomas/participle v0.6.0/go.mod h1:HfdmEuwvr12HXQN44HPWXR0lHmVolVYe4dyL6lQ3duY= +github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -38,6 +44,8 @@ github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGn github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk= github.com/andybalholm/brotli v1.0.0 h1:7UCwP93aiSfvWpapti8g88vVVGp2qqtGyePsSuDafo4= github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= +github.com/andybalholm/brotli v1.0.1 h1:KqhlKozYbRtJvsPrrEeXcO+N2l6NYT5A2QAFmSULpEc= +github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= @@ -92,6 +100,7 @@ github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfc github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -140,6 +149,7 @@ github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFG github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -190,6 +200,7 @@ github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51 github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= @@ -214,6 +225,8 @@ github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09 github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= @@ -232,6 +245,7 @@ github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlT github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= @@ -239,10 +253,14 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/klauspost/compress v1.10.7 h1:7rix8v8GpI3ZBb0nSozFRgbtXKv+hOe+qfEpZqybrAg= github.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.8 h1:difgzQsp5mdAz9v8lm3P/I+EpDKMU/6uTMw1y1FObuo= +github.com/klauspost/compress v1.11.8/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -270,6 +288,8 @@ github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= +github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -285,6 +305,14 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mr-tron/base58 v1.1.2 h1:ZEw4I2EgPKDJ2iEw0cNmLB3ROrEmkOtXIkaG7wZg+78= github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/mr-tron/base58 v1.1.3 h1:v+sk57XuaCKGXpWtVBX8YJzO7hMGx4Aajh4TQbdEFdc= +github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4= +github.com/multiformats/go-multiaddr-net v0.1.2/go.mod h1:QsWt3XK/3hwvNxZJp92iMQKME1qHfpYmyIjFVsSOY6Y= +github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= +github.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= +github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= +github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= @@ -294,27 +322,37 @@ github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzE github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= -github.com/nspcc-dev/cdn-sdk v0.3.4 h1:RtYWuF9xDWrkVwu6sFRWlyZ+ToYM7Y9h8B93Fg4CPEA= -github.com/nspcc-dev/cdn-sdk v0.3.4/go.mod h1:JC4dT16H5HilyZcb8sTxL/TMC1FSEKMuFAqRsmAPoAk= github.com/nspcc-dev/dbft v0.0.0-20191205084618-dacb1a30c254/go.mod h1:w1Ln2aT+dBlPhLnuZhBV+DfPEdS2CHWWLp5JTScY3bw= github.com/nspcc-dev/dbft v0.0.0-20191209120240-0d6b7568d9ae/go.mod h1:3FjXOoHmA51EGfb5GS/HOv7VdmngNRTssSeQ729dvGY= github.com/nspcc-dev/dbft v0.0.0-20200117124306-478e5cfbf03a/go.mod h1:/YFK+XOxxg0Bfm6P92lY5eDSLYfp06XOdL8KAVgXjVk= github.com/nspcc-dev/dbft v0.0.0-20200219114139-199d286ed6c1/go.mod h1:O0qtn62prQSqizzoagHmuuKoz8QMkU3SzBoKdEvm3aQ= github.com/nspcc-dev/dbft v0.0.0-20200711144034-c526ccc6f570/go.mod h1:1FYQXSbb6/9HQIkoF8XO7W/S8N7AZRkBsgwbcXRvk0E= +github.com/nspcc-dev/dbft v0.0.0-20210302103605-cc75991b7cfb/go.mod h1:U8MSnEShH+o5hexfWJdze6uMFJteP0ko7J2frO7Yu1Y= github.com/nspcc-dev/hrw v1.0.9 h1:17VcAuTtrstmFppBjfRiia4K2wA/ukXZhLFS8Y8rz5Y= github.com/nspcc-dev/hrw v1.0.9/go.mod h1:l/W2vx83vMQo6aStyx2AuZrJ+07lGv2JQGlVkPG06MU= github.com/nspcc-dev/neo-go v0.73.1-pre.0.20200303142215-f5a1b928ce09/go.mod h1:pPYwPZ2ks+uMnlRLUyXOpLieaDQSEaf4NM3zHVbRjmg= github.com/nspcc-dev/neo-go v0.91.0 h1:KKOPMKs0fm8JIau1SuwxiLdrZ+1kDPBiVRlWwzfebWE= github.com/nspcc-dev/neo-go v0.91.0/go.mod h1:G6HdOWvzQ6tlvFdvFSN/PgCzLPN/X/X4d5hTjFRUDcc= +github.com/nspcc-dev/neo-go v0.94.0 h1:2eafoyEnueqEMGDZF06HZJQN0MHgYxFzlcre5YUOP1M= +github.com/nspcc-dev/neo-go v0.94.0/go.mod h1:IrBT/UG3/Slhqgjge8r6zni5tNRsWmwwG9OnVdASheI= github.com/nspcc-dev/neofs-api-go v1.23.0 h1:t4FB5uVY99UkYR0Hiyi1SHjZuqzf4qicw7tf7BBnkHk= github.com/nspcc-dev/neofs-api-go v1.23.0/go.mod h1:G7dqincfdjBrAbL5nxVp82emF05fSVEqe59ICsoRDI8= +github.com/nspcc-dev/neofs-api-go v1.24.0/go.mod h1:G7dqincfdjBrAbL5nxVp82emF05fSVEqe59ICsoRDI8= +github.com/nspcc-dev/neofs-api-go v1.25.0 h1:hw1TTi3/3wCBB3KN6cycuM8Sn/MFnapSQptBx8KgDIY= +github.com/nspcc-dev/neofs-api-go v1.25.0/go.mod h1:G7dqincfdjBrAbL5nxVp82emF05fSVEqe59ICsoRDI8= github.com/nspcc-dev/neofs-crypto v0.2.0/go.mod h1:F/96fUzPM3wR+UGsPi3faVNmFlA9KAEAUQR7dMxZmNA= github.com/nspcc-dev/neofs-crypto v0.2.3/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw= github.com/nspcc-dev/neofs-crypto v0.3.0 h1:zlr3pgoxuzrmGCxc5W8dGVfA9Rro8diFvVnBg0L4ifM= github.com/nspcc-dev/neofs-crypto v0.3.0/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw= +github.com/nspcc-dev/neofs-http-gate/sdk v0.3.4 h1:RtYWuF9xDWrkVwu6sFRWlyZ+ToYM7Y9h8B93Fg4CPEA= +github.com/nspcc-dev/neofs-http-gate/sdk v0.3.4/go.mod h1:JC4dT16H5HilyZcb8sTxL/TMC1FSEKMuFAqRsmAPoAk= +github.com/nspcc-dev/neofs-node v0.17.0 h1:Xh01n8AKC1XmSRBN7yBKSO7Vz2rchqZfAnofcq1lis0= +github.com/nspcc-dev/neofs-node v0.17.0/go.mod h1:nC4+0Zqxv7KZMOv59g5TvcCg0kV6m+1ySiDEuzlAuLc= github.com/nspcc-dev/rfc6979 v0.1.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso= github.com/nspcc-dev/rfc6979 v0.2.0 h1:3e1WNxrN60/6N0DW7+UYisLeZJyfqZTNOjeV/toYvOE= github.com/nspcc-dev/rfc6979 v0.2.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso= +github.com/nspcc-dev/tzhash v1.4.0 h1:RVIR+mxOBHl58CE99+DXtE31ylD5PEkZSoWqoj4fVjg= +github.com/nspcc-dev/tzhash v1.4.0/go.mod h1:Z8gp/VZbyWgPhaMp/KTmeoW5UTynp/N60g0jTtSzBws= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= @@ -335,7 +373,10 @@ github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJ github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= +github.com/panjf2000/ants/v2 v2.3.0/go.mod h1:LtwNaBX6OeF5qRtQlaeGndalVwJlS2ueur7uwoAHbPA= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/paulmach/orb v0.2.1/go.mod h1:91bG5A8qKNOiZtlKc0BqKMB3O5kWfRQorTwo8BZ2B/0= +github.com/paulmach/protoscan v0.2.0/go.mod h1:2c55sl1Hu6/tgRfc8Y8zADsxuSCYC2IrPh0JCqP/yrw= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= @@ -357,6 +398,7 @@ github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDf github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.2.1/go.mod h1:XMU6Z2MjaRKVu/dC1qupJI9SiNkDYzz3xecMgSW/F+U= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= +github.com/prometheus/client_golang v1.6.0/go.mod h1:ZLOG9ck3JLRdB5MgO8f+lLTe83AXG6ro35rLTxvnIl4= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.9.0 h1:Rrch9mh17XcxvEu9D9DEpb4isxjGBtcevQjKvxPRQIU= github.com/prometheus/client_golang v1.9.0/go.mod h1:FqZLKOZnGdFAhOK4nqGHa7D66IdsO+O441Eve7ptJDU= @@ -372,6 +414,7 @@ github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.15.0 h1:4fgOnadei3EZvgRwxJ7RMpG1k1pOZth5Pc13tyspaKM= @@ -382,6 +425,7 @@ github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7z github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4= github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= @@ -415,6 +459,7 @@ github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -422,6 +467,8 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk= github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= @@ -442,6 +489,7 @@ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69 github.com/syndtr/goleveldb v0.0.0-20180307113352-169b1b37be73/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= @@ -451,6 +499,8 @@ github.com/valyala/fasthttp v1.19.0 h1:PfTS4PeH3xDr3WomrDS2ID8lU2GskK1xS3YG6gIpi github.com/valyala/fasthttp v1.19.0/go.mod h1:jjraHZVbKOXftJfsOYoAjaeygpj5hr8ermTRJNroD7A= github.com/valyala/fasthttp v1.20.0 h1:olTmcnLQeZrkBc4TVgE/BatTo1NE/IvW050AuD8SW+U= github.com/valyala/fasthttp v1.20.0/go.mod h1:jjraHZVbKOXftJfsOYoAjaeygpj5hr8ermTRJNroD7A= +github.com/valyala/fasthttp v1.22.0 h1:OpwH5KDOJ9cS2bq8fD+KfT4IrksK0llvkHf4MZx42jQ= +github.com/valyala/fasthttp v1.22.0/go.mod h1:0mw2RjXGOzxf4NL2jni3gUQ7LfjjUSiG5sskOUUSEpU= github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= @@ -459,6 +509,7 @@ github.com/yuin/gopher-lua v0.0.0-20191128022950-c6266f4fe8d7/go.mod h1:gqRgreBU go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= +go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= @@ -468,12 +519,14 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.5.1/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= @@ -489,11 +542,17 @@ golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200117160349-530e935923ad/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 h1:hb9wdF1z5waM+dSIICn1l0DkLVDT3hqhhQsDNUmHPRE= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g= +golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -509,6 +568,7 @@ golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -528,15 +588,21 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191105084925-a882066a44e0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYcn15TUbkhwuCO3foqqM= golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201016165138-7b1cca2348c0 h1:5kGOVHlq0euqwzgTC9Vu15p6fV1Wi0ArVi8da2urnVg= golang.org/x/net v0.0.0-20201016165138-7b1cca2348c0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226101413-39120d07d75e h1:jIQURUJ9mlLvYwTBtRHm9h58rYhSonLvRvgAnP8Nr7I= +golang.org/x/net v0.0.0-20210226101413-39120d07d75e/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -571,23 +637,34 @@ golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980 h1:OjiUf46hAmXblsZdnoSXsEUSKU8r1UEzcL5RVZ4gO9Y= golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e h1:AyodaIpKjppX+cBfTASF2E1US3H2JFBj920Ot3rtDjs= golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073 h1:8qxJSnu+7dRq6upnbntrmriWByIakBuct5OM/MdQC1M= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -596,6 +673,7 @@ golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20180318012157-96caea41033d/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -614,6 +692,7 @@ golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200123022218-593de606220b h1:ztSlcncMErSAUzXwnVO1iTPxHwtvOHBB26SGiyYXIEE= golang.org/x/tools v0.0.0-20200123022218-593de606220b/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -660,6 +739,10 @@ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8 google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.35.0 h1:TwIQcH3es+MojMVojxxfQ3l3OF2KzlRxML2xZq0kRo8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0 h1:o1bcQ6imQMIOpdrO3SWf2z5RV72WbDwdXuK0MDlc8As= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1 h1:cmUfbeGKnz9+2DD/UYsMQXeqbHZqZDs4eQwW0sFOpBY= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -692,6 +775,7 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= diff --git a/go_dev.mod b/go_dev.mod index 8077c1d..f45f6ae 100644 --- a/go_dev.mod +++ b/go_dev.mod @@ -1,6 +1,6 @@ module github.com/nspcc-dev/neofs-http-gate -go 1.13 +go 1.16 require ( github.com/fasthttp/router v0.6.1 diff --git a/health.go b/health.go index 4483e70..719667b 100644 --- a/health.go +++ b/health.go @@ -17,7 +17,6 @@ func attachHealthy(r *router.Router, e stater) { ctx.SetStatusCode(fasthttp.StatusOK) ctx.SetBodyString(healthyState + "ready") }) - r.GET("/-/healthy/", func(c *fasthttp.RequestCtx) { code := fasthttp.StatusOK msg := "healthy" @@ -26,7 +25,6 @@ func attachHealthy(r *router.Router, e stater) { msg = "unhealthy: " + err.Error() code = fasthttp.StatusBadRequest } - c.Response.Reset() c.SetStatusCode(code) c.SetContentType(defaultContentType) diff --git a/logger/grpc.go b/logger/grpc.go new file mode 100644 index 0000000..b163e7c --- /dev/null +++ b/logger/grpc.go @@ -0,0 +1,60 @@ +package logger + +import ( + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + "google.golang.org/grpc/grpclog" +) + +type ( + zapLogger struct { + zapcore.Core + log *zap.SugaredLogger + } + + Logger interface { + grpclog.LoggerV2 + Println(v ...interface{}) + } +) + +func GRPC(l *zap.Logger) Logger { + log := l.WithOptions( + // skip gRPCLog + zapLogger in caller + zap.AddCallerSkip(2)) + + return &zapLogger{ + Core: log.Core(), + log: log.Sugar(), + } +} + +func (z *zapLogger) Info(args ...interface{}) { z.log.Info(args...) } + +func (z *zapLogger) Infoln(args ...interface{}) { z.log.Info(args...) } + +func (z *zapLogger) Infof(format string, args ...interface{}) { z.log.Infof(format, args...) } + +func (z *zapLogger) Println(args ...interface{}) { z.log.Info(args...) } + +func (z *zapLogger) Printf(format string, args ...interface{}) { z.log.Infof(format, args...) } + +func (z *zapLogger) Warning(args ...interface{}) { z.log.Warn(args...) } + +func (z *zapLogger) Warningln(args ...interface{}) { z.log.Warn(args...) } + +func (z *zapLogger) Warningf(format string, args ...interface{}) { z.log.Warnf(format, args...) } + +func (z *zapLogger) Error(args ...interface{}) { z.log.Error(args...) } + +func (z *zapLogger) Errorln(args ...interface{}) { z.log.Error(args...) } + +func (z *zapLogger) Errorf(format string, args ...interface{}) { z.log.Errorf(format, args...) } + +func (z *zapLogger) Fatal(args ...interface{}) { z.log.Fatal(args...) } + +func (z *zapLogger) Fatalln(args ...interface{}) { z.log.Fatal(args...) } + +func (z *zapLogger) Fatalf(format string, args ...interface{}) { z.Fatalf(format, args...) } + +func (z *zapLogger) V(int) bool { return z.Enabled(zapcore.DebugLevel) } diff --git a/logger/option.go b/logger/option.go new file mode 100644 index 0000000..23d1277 --- /dev/null +++ b/logger/option.go @@ -0,0 +1,23 @@ +package logger + +import "go.uber.org/zap" + +func WithSamplingInitial(v int) Option { return func(o *options) { o.SamplingInitial = v } } + +func WithSamplingThereafter(v int) Option { return func(o *options) { o.SamplingThereafter = v } } + +func WithFormat(v string) Option { return func(o *options) { o.Format = v } } + +func WithLevel(v string) Option { return func(o *options) { o.Level = v } } + +func WithTraceLevel(v string) Option { return func(o *options) { o.TraceLevel = v } } + +func WithoutDisclaimer() Option { return func(o *options) { o.NoDisclaimer = true } } + +func WithoutCaller() Option { return func(o *options) { o.NoCaller = true } } + +func WithAppName(v string) Option { return func(o *options) { o.AppName = v } } + +func WithAppVersion(v string) Option { return func(o *options) { o.AppVersion = v } } + +func WithZapOptions(opts ...zap.Option) Option { return func(o *options) { o.Options = opts } } diff --git a/logger/zap.go b/logger/zap.go new file mode 100644 index 0000000..6228af8 --- /dev/null +++ b/logger/zap.go @@ -0,0 +1,131 @@ +package logger + +import ( + "strings" + + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +type ( + Option func(o *options) + + options struct { + Options []zap.Option + + SamplingInitial int + SamplingThereafter int + + Format string + Level string + TraceLevel string + + NoCaller bool + NoDisclaimer bool + + AppName string + AppVersion string + } +) + +const ( + formatJSON = "json" + formatConsole = "console" + + defaultSamplingInitial = 100 + defaultSamplingThereafter = 100 + + lvlInfo = "info" + lvlWarn = "warn" + lvlDebug = "debug" + lvlError = "error" + lvlFatal = "fatal" + lvlPanic = "panic" +) + +func safeLevel(lvl string) zap.AtomicLevel { + switch strings.ToLower(lvl) { + case lvlDebug: + return zap.NewAtomicLevelAt(zap.DebugLevel) + case lvlWarn: + return zap.NewAtomicLevelAt(zap.WarnLevel) + case lvlError: + return zap.NewAtomicLevelAt(zap.ErrorLevel) + case lvlFatal: + return zap.NewAtomicLevelAt(zap.FatalLevel) + case lvlPanic: + return zap.NewAtomicLevelAt(zap.PanicLevel) + default: + return zap.NewAtomicLevelAt(zap.InfoLevel) + } +} + +func defaults() *options { + return &options{ + SamplingInitial: defaultSamplingInitial, + SamplingThereafter: defaultSamplingThereafter, + + Format: formatConsole, + Level: lvlDebug, + TraceLevel: lvlInfo, + + NoCaller: false, + NoDisclaimer: false, + + AppName: "", + AppVersion: "", + } +} + +func New(opts ...Option) (*zap.Logger, error) { + o := defaults() + c := zap.NewProductionConfig() + + c.OutputPaths = []string{"stdout"} + c.ErrorOutputPaths = []string{"stdout"} + + for _, opt := range opts { + opt(o) + } + + // set sampling + c.Sampling = &zap.SamplingConfig{ + Initial: o.SamplingInitial, + Thereafter: o.SamplingThereafter, + } + + // logger level + c.Level = safeLevel(o.Level) + traceLvl := safeLevel(o.TraceLevel) + + // logger format + switch f := o.Format; strings.ToLower(f) { + case formatConsole: + c.Encoding = formatConsole + default: + c.Encoding = formatJSON + } + + // logger time + c.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder + + if o.NoCaller { + c.EncoderConfig.EncodeCaller = nil + } + + // enable trace only for current log-level + o.Options = append(o.Options, zap.AddStacktrace(traceLvl)) + + l, err := c.Build(o.Options...) + if err != nil { + return nil, err + } + + if o.NoDisclaimer { + return l, nil + } + + return l.With( + zap.String("app_name", o.AppName), + zap.String("app_version", o.AppVersion)), nil +} diff --git a/main.go b/main.go index 3fb5c6c..9e07835 100644 --- a/main.go +++ b/main.go @@ -1,50 +1,43 @@ package main import ( - "github.com/nspcc-dev/cdn-sdk/logger" "github.com/nspcc-dev/neofs-http-gate/global" + "github.com/nspcc-dev/neofs-http-gate/logger" "github.com/spf13/viper" "go.uber.org/zap" ) -func newLogger(v *viper.Viper) *zap.Logger { - options := []logger.Option{ - logger.WithLevel(v.GetString(cfgLoggerLevel)), - logger.WithTraceLevel(v.GetString(cfgLoggerTraceLevel)), - - logger.WithFormat(v.GetString(cfgLoggerFormat)), - - logger.WithSamplingInitial(v.GetInt(cfgLoggerSamplingInitial)), - logger.WithSamplingThereafter(v.GetInt(cfgLoggerSamplingThereafter)), - - logger.WithAppName(v.GetString(cfgApplicationName)), - logger.WithAppVersion(v.GetString(cfgApplicationVersion)), - } - - if v.GetBool(cfgLoggerNoCaller) { - options = append(options, logger.WithoutCaller()) - } - - if v.GetBool(cfgLoggerNoDisclaimer) { - options = append(options, logger.WithoutDisclaimer()) - } - - l, err := logger.New(options...) - if err != nil { - panic(err) - } - - return l -} - func main() { var ( v = settings() l = newLogger(v) - g = global.Context() - a = newApp(g, WithLogger(l), WithConfig(v)) ) - go a.Serve(g) - go a.Worker(g) - a.Wait() + globalContext := global.Context() + app := newApp(globalContext, WithLogger(l), WithConfig(v)) + go app.Serve(globalContext) + go app.Worker(globalContext) + app.Wait() +} + +func newLogger(v *viper.Viper) *zap.Logger { + options := []logger.Option{ + logger.WithLevel(v.GetString(cfgLoggerLevel)), + logger.WithTraceLevel(v.GetString(cfgLoggerTraceLevel)), + logger.WithFormat(v.GetString(cfgLoggerFormat)), + logger.WithSamplingInitial(v.GetInt(cfgLoggerSamplingInitial)), + logger.WithSamplingThereafter(v.GetInt(cfgLoggerSamplingThereafter)), + logger.WithAppName(v.GetString(cfgApplicationName)), + logger.WithAppVersion(v.GetString(cfgApplicationVersion)), + } + if v.GetBool(cfgLoggerNoCaller) { + options = append(options, logger.WithoutCaller()) + } + if v.GetBool(cfgLoggerNoDisclaimer) { + options = append(options, logger.WithoutDisclaimer()) + } + l, err := logger.New(options...) + if err != nil { + panic(err) + } + return l } diff --git a/neofs/client-plant.go b/neofs/client-plant.go new file mode 100644 index 0000000..5102c29 --- /dev/null +++ b/neofs/client-plant.go @@ -0,0 +1,286 @@ +package neofs + +import ( + "bytes" + "context" + "crypto/ecdsa" + "io" + "math" + "time" + + "github.com/nspcc-dev/neofs-api-go/pkg/client" + "github.com/nspcc-dev/neofs-api-go/pkg/container" + "github.com/nspcc-dev/neofs-api-go/pkg/object" + "github.com/nspcc-dev/neofs-api-go/pkg/owner" + "github.com/nspcc-dev/neofs-api-go/pkg/token" + objectCore "github.com/nspcc-dev/neofs-node/pkg/core/object" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transformer" + "github.com/pkg/errors" + "google.golang.org/grpc" +) + +const ( + nodeConnectionTimeout = 10 * time.Second + maxObjectSize = uint64(1 << (20 + 6)) // 64MB +) + +type PutOptions struct { + Client client.Client + SessionToken *token.SessionToken + BearerToken *token.BearerToken + // ... + ContainerID *container.ID + OwnerID *owner.ID + PrepareObjectOnsite bool + Reader io.Reader +} + +type GetOptions struct { + Client client.Client + SessionToken *token.SessionToken + BearerToken *token.BearerToken + // ... + ObjectAddress *object.Address + Writer io.Writer +} + +type SearchOptions struct { + Client client.Client + SessionToken *token.SessionToken + BearerToken *token.BearerToken + // ... + ContainerID *container.ID + Attribute struct { + Key string + Value string + } +} + +type DeleteOptions struct { + Client client.Client + SessionToken *token.SessionToken + BearerToken *token.BearerToken + // ... + ObjectAddress *object.Address +} + +type ObjectClient interface { + Put(context.Context, *PutOptions) (*object.Address, error) + Get(context.Context, *GetOptions) (*object.Object, error) + Search(context.Context, *SearchOptions) ([]*object.ID, error) + Delete(context.Context, *DeleteOptions) error +} + +type ClientPlant interface { + GetReusableArtifacts(ctx context.Context) (client.Client, *token.SessionToken, error) + Object() ObjectClient + OwnerID() *owner.ID +} + +type objectClient struct { + key *ecdsa.PrivateKey + conn *grpc.ClientConn +} + +type neofsClient struct { + key *ecdsa.PrivateKey + ownerID *owner.ID + conn *grpc.ClientConn +} + +func (cc *neofsClient) GetReusableArtifacts(ctx context.Context) (client.Client, *token.SessionToken, error) { + c, err := client.New(client.WithDefaultPrivateKey(cc.key), client.WithGRPCConnection(cc.conn)) + if err != nil { + return nil, nil, errors.Wrap(err, "failed to create reusable neofs client") + } + st, err := c.CreateSession(ctx, math.MaxUint64) + if err != nil { + return nil, nil, errors.Wrap(err, "failed to create reusable neofs session token") + } + return c, st, nil +} + +func (cc *neofsClient) Object() ObjectClient { + return &objectClient{key: cc.key, conn: cc.conn} +} + +func (cc *neofsClient) OwnerID() *owner.ID { + return cc.ownerID +} + +func NewClientPlant(ctx context.Context, address string, creds Credentials) (ClientPlant, error) { + toctx, c := context.WithTimeout(ctx, nodeConnectionTimeout) + defer c() + conn, err := grpc.DialContext(toctx, address, grpc.WithInsecure(), grpc.WithBlock()) + if err != nil { + if err == context.DeadlineExceeded { + err = errors.New("failed to connect to neofs node") + } + return nil, err + } + return &neofsClient{ + key: creds.PrivateKey(), + ownerID: creds.Owner(), + conn: conn, + }, nil +} + +func (oc *objectClient) Put(ctx context.Context, options *PutOptions) (*object.Address, error) { + var ( + err error + objectID *object.ID + ) + address := object.NewAddress() + if options.PrepareObjectOnsite { + rawObject := objectCore.NewRaw() + rawObject.SetContainerID(options.ContainerID) + rawObject.SetOwnerID(options.OwnerID) + ns := newNetworkState(ctx, options.Client) + objectTarget := transformer.NewPayloadSizeLimiter(maxObjectSize, func() transformer.ObjectTarget { + return transformer.NewFormatTarget(&transformer.FormatterParams{ + Key: oc.key, + NextTarget: &remoteClientTarget{ + ctx: ctx, + client: options.Client, + }, + NetworkState: ns, + }) + }) + if err = ns.LastError(); err != nil { + return nil, err + } + err = objectTarget.WriteHeader(rawObject) + if err != nil { + return nil, err + } + _, err = io.Copy(objectTarget, options.Reader) + if err != nil { + return nil, err + } + var ids *transformer.AccessIdentifiers + ids, err = objectTarget.Close() + if err != nil { + return nil, err + } + address.SetObjectID(ids.SelfID()) + } else { + rawObject := object.NewRaw() + rawObject.SetContainerID(options.ContainerID) + rawObject.SetOwnerID(options.OwnerID) + ops := new(client.PutObjectParams). + WithObject(rawObject.Object()). + WithPayloadReader(options.Reader) + objectID, err = options.Client.PutObject( + ctx, + ops, + client.WithSession(options.SessionToken), + client.WithBearer(options.BearerToken), + ) + if err != nil { + return nil, err + } + address.SetObjectID(objectID) + } + address.SetContainerID(options.ContainerID) + return address, nil +} + +func (oc *objectClient) Get(ctx context.Context, options *GetOptions) (*object.Object, error) { + var ( + err error + obj *object.Object + ) + ops := new(client.GetObjectParams). + WithAddress(options.ObjectAddress). + WithPayloadWriter(options.Writer) + obj, err = options.Client.GetObject( + ctx, + ops, + client.WithSession(options.SessionToken), + client.WithBearer(options.BearerToken), + ) + return obj, err +} + +func (oc *objectClient) Search(ctx context.Context, options *SearchOptions) ([]*object.ID, error) { + sfs := object.NewSearchFilters() + sfs.AddRootFilter() + sfs.AddFilter(options.Attribute.Key, options.Attribute.Value, object.MatchStringEqual) + sops := new(client.SearchObjectParams) + sops.WithContainerID(options.ContainerID) + sops.WithSearchFilters(sfs) + return options.Client.SearchObject( + ctx, + sops, + client.WithSession(options.SessionToken), + client.WithBearer(options.BearerToken), + ) +} + +func (oc *objectClient) Delete(ctx context.Context, options *DeleteOptions) error { + ops := new(client.DeleteObjectParams).WithAddress(options.ObjectAddress) + err := options.Client.DeleteObject( + ctx, + ops, + client.WithSession(options.SessionToken), + client.WithBearer(options.BearerToken), + ) + return err +} + +type remoteClientTarget struct { + ctx context.Context + client client.Client + object *object.Object + payload []byte +} + +func (rct *remoteClientTarget) WriteHeader(raw *objectCore.RawObject) error { + rct.object = raw.Object().SDK() + return nil +} + +func (rct *remoteClientTarget) Write(p []byte) (n int, err error) { + rct.payload = append(rct.payload, p...) + return len(p), nil +} + +func (rct *remoteClientTarget) Close() (*transformer.AccessIdentifiers, error) { + id, err := rct.client.PutObject( + rct.ctx, new(client.PutObjectParams). + WithObject(rct.object). + WithPayloadReader(bytes.NewReader(rct.payload)), + ) + if err != nil { + return nil, err + } + return new(transformer.AccessIdentifiers).WithSelfID(id), nil +} + +type networkState struct { + ctx context.Context + client client.Client + lastError error + onError func(error) +} + +func newNetworkState(ctx context.Context, client client.Client) *networkState { + ns := &networkState{ + ctx: ctx, + client: client, + } + ns.onError = func(err error) { ns.lastError = err } + return ns +} + +func (ns *networkState) LastError() error { + return ns.lastError +} + +func (ns *networkState) CurrentEpoch() uint64 { + ce, err := ns.client.NetworkInfo(ns.ctx) + if err != nil { + ns.onError(err) + } + return ce.CurrentEpoch() +} diff --git a/neofs/credentials.go b/neofs/credentials.go new file mode 100644 index 0000000..48faf64 --- /dev/null +++ b/neofs/credentials.go @@ -0,0 +1,67 @@ +package neofs + +import ( + "crypto/ecdsa" + + "github.com/nspcc-dev/neofs-api-go/pkg/owner" + crypto "github.com/nspcc-dev/neofs-crypto" +) + +type ( + // Credentials contains methods that needed to work with NeoFS. + Credentials interface { + WIF() string + Owner() *owner.ID + PublicKey() *ecdsa.PublicKey + PrivateKey() *ecdsa.PrivateKey + } + + cred struct { + key *ecdsa.PrivateKey + owner *owner.ID + wif string + } +) + +// New creates an instance of Credentials through string representation of secret. +// It allows passing WIF, path, hex-encoded and others. +func NewCredentials(secret string) (Credentials, error) { + key, err := crypto.LoadPrivateKey(secret) + if err != nil { + return nil, err + } + return setFromPrivateKey(key) +} + +// PrivateKey returns ecdsa.PrivateKey. +func (c *cred) PrivateKey() *ecdsa.PrivateKey { + return c.key +} + +// PublicKey returns ecdsa.PublicKey. +func (c *cred) PublicKey() *ecdsa.PublicKey { + return &c.key.PublicKey +} + +// Owner returns owner.ID. +func (c *cred) Owner() *owner.ID { + return c.owner +} + +// WIF returns string representation of WIF. +func (c *cred) WIF() string { + return c.wif +} + +func setFromPrivateKey(key *ecdsa.PrivateKey) (*cred, error) { + wallet, err := owner.NEO3WalletFromPublicKey(&key.PublicKey) + if err != nil { + return nil, err + } + ownerID := owner.NewIDFromNeo3Wallet(wallet) + wif, err := crypto.WIFEncode(key) + if err != nil { + return nil, err + } + return &cred{key: key, owner: ownerID, wif: wif}, nil +} diff --git a/pool/pool.go b/pool/pool.go new file mode 100644 index 0000000..94874ec --- /dev/null +++ b/pool/pool.go @@ -0,0 +1,39 @@ +package pool + +import ( + "context" + + "github.com/nspcc-dev/neofs-api-go/pkg/token" + "google.golang.org/grpc" +) + +type Client interface { + // receive status of connection pool + Status() error + // worker should be run in goroutine to re-balancing + Worker(context.Context) + Connection(context.Context) (*grpc.ClientConn, error) + Session(context.Context, *grpc.ClientConn) (*token.SessionToken, error) +} + +type pool struct{} + +func (p *pool) Status() error { + return nil +} + +func (p *pool) Worker(ctx context.Context) { + panic("not implemented") +} + +func (p *pool) Connection(ctx context.Context) (*grpc.ClientConn, error) { + panic("not implemented") +} + +func (p *pool) Session(ctx context.Context, conn *grpc.ClientConn) (*token.SessionToken, error) { + panic("not implemented") +} + +func New() Client { + return &pool{} +} diff --git a/receive.go b/receive.go index ae1b555..9728e7b 100644 --- a/receive.go +++ b/receive.go @@ -9,9 +9,9 @@ import ( "sync" "time" - sdk "github.com/nspcc-dev/cdn-sdk" "github.com/nspcc-dev/neofs-api-go/pkg/container" "github.com/nspcc-dev/neofs-api-go/pkg/object" + "github.com/nspcc-dev/neofs-http-gate/neofs" "github.com/pkg/errors" "github.com/valyala/fasthttp" "go.uber.org/zap" @@ -31,7 +31,7 @@ type ( *fasthttp.RequestCtx log *zap.Logger - obj sdk.ObjectClient + obj neofs.ObjectClient } objectIDs []*object.ID @@ -49,40 +49,38 @@ func (d *detector) Write(data []byte) (int, error) { return d.Writer.Write(data) } -func (r *request) receiveFile(address *object.Address) { +func (r *request) receiveFile(options *neofs.GetOptions) { var ( err error dis = "inline" start = time.Now() filename string ) - - if err = checkAndPropagateBearerToken(r.RequestCtx); err != nil { - r.log.Error("could not fetch bearer token", zap.Error(err)) - r.Error("could not fetch bearer token", fasthttp.StatusBadRequest) + if err = storeBearerToken(r.RequestCtx); err != nil { + r.log.Error("could not fetch and store bearer token", zap.Error(err)) + r.Error("could not fetch and store bearer token", fasthttp.StatusBadRequest) return } - writer := newDetector(r.Response.BodyWriter()) - obj, err := r.obj.Get(r, address, sdk.WithGetWriter(writer)) + // obj, err := r.obj.Get(r, address, sdk.WithGetWriter(writer)) + options.Writer = writer + obj, err := r.obj.Get(r.RequestCtx, options) if err != nil { - r.log.Error("could not receive object", + r.log.Error( + "could not receive object", zap.Stringer("elapsed", time.Since(start)), - zap.Error(err)) - + zap.Error(err), + ) var ( msg = errors.Wrap(err, "could not receive object").Error() code = fasthttp.StatusBadRequest ) - if st, ok := status.FromError(errors.Cause(err)); ok && st != nil { if st.Code() == codes.NotFound { code = fasthttp.StatusNotFound } - msg = st.Message() } - r.Error(msg, code) return } @@ -139,68 +137,79 @@ func (a *app) request(ctx *fasthttp.RequestCtx, log *zap.Logger) *request { RequestCtx: ctx, log: log, - obj: a.cli.Object(), + obj: a.plant.Object(), } } func (a *app) byAddress(c *fasthttp.RequestCtx) { var ( - err error - adr = object.NewAddress() - cid, _ = c.UserValue("cid").(string) - oid, _ = c.UserValue("oid").(string) - val = strings.Join([]string{cid, oid}, "/") - log = a.log.With( - zap.String("cid", cid), - zap.String("oid", oid)) + err error + address = object.NewAddress() + cid, _ = c.UserValue("cid").(string) + oid, _ = c.UserValue("oid").(string) + val = strings.Join([]string{cid, oid}, "/") + log = a.log.With(zap.String("cid", cid), zap.String("oid", oid)) ) - - if err = adr.Parse(val); err != nil { + if err = address.Parse(val); err != nil { log.Error("wrong object address", zap.Error(err)) c.Error("wrong object address", fasthttp.StatusBadRequest) return } - - a.request(c, log).receiveFile(adr) + // TODO: Take this from a sync-pool. + getOpts := new(neofs.GetOptions) + getOpts.Client = a.getOperations.client + getOpts.SessionToken = a.getOperations.sessionToken + getOpts.ObjectAddress = address + getOpts.Writer = nil + a.request(c, log).receiveFile(getOpts) } func (a *app) byAttribute(c *fasthttp.RequestCtx) { var ( err error - ids []*object.ID - cid = container.NewID() - adr = object.NewAddress() - sCID, _ = c.UserValue("cid").(string) + scid, _ = c.UserValue("cid").(string) key, _ = c.UserValue("attr_key").(string) val, _ = c.UserValue("attr_val").(string) - - log = a.log.With( - zap.String("cid", sCID), - zap.String("attr_key", key), - zap.String("attr_val", val)) + log = a.log.With(zap.String("cid", scid), zap.String("attr_key", key), zap.String("attr_val", val)) ) - - if err = cid.Parse(sCID); err != nil { + cid := container.NewID() + if err = cid.Parse(scid); err != nil { log.Error("wrong container id", zap.Error(err)) c.Error("wrong container id", fasthttp.StatusBadRequest) return - } else if ids, err = a.cli.Object().Search(c, cid, sdk.SearchRootObjects(), sdk.SearchByAttribute(key, val)); err != nil { + // } else if ids, err = a.cli.Object().Search(c, cid, sdk.SearchRootObjects(), sdk.SearchByAttribute(key, val)); err != nil { + } + // TODO: Take this from a sync-pool. + searchOpts := new(neofs.SearchOptions) + searchOpts.Client = a.getOperations.client + searchOpts.SessionToken = a.getOperations.sessionToken + searchOpts.BearerToken = nil + searchOpts.ContainerID = cid + searchOpts.Attribute.Key = key + searchOpts.Attribute.Value = val + var ids []*object.ID + if ids, err = a.plant.Object().Search(c, searchOpts); err != nil { log.Error("something went wrong", zap.Error(err)) c.Error("something went wrong", fasthttp.StatusBadRequest) return } else if len(ids) == 0 { log.Debug("object not found") - c.Error("not found", fasthttp.StatusNotFound) + c.Error("object not found", fasthttp.StatusNotFound) return } - if len(ids) > 1 { log.Debug("found multiple objects", zap.Strings("object_ids", objectIDs(ids).Slice()), zap.Stringer("show_object_id", ids[0])) } - - adr.SetContainerID(cid) - adr.SetObjectID(ids[0]) - a.request(c, log).receiveFile(adr) + address := object.NewAddress() + address.SetContainerID(cid) + address.SetObjectID(ids[0]) + // TODO: Take this from a sync-pool. + getOpts := new(neofs.GetOptions) + getOpts.Client = a.getOperations.client + getOpts.SessionToken = a.getOperations.sessionToken + getOpts.ObjectAddress = address + getOpts.Writer = nil + a.request(c, log).receiveFile(getOpts) } diff --git a/settings.go b/settings.go index 206fd0a..0e5f79f 100644 --- a/settings.go +++ b/settings.go @@ -79,11 +79,9 @@ var ignore = map[string]struct{}{ cfgApplicationName: {}, cfgApplicationVersion: {}, cfgApplicationBuildTime: {}, - - cfgPeers: {}, - - cmdHelp: {}, - cmdVersion: {}, + cfgPeers: {}, + cmdHelp: {}, + cmdVersion: {}, } func (empty) Read([]byte) (int, error) { return 0, io.EOF } @@ -106,14 +104,14 @@ func settings() *viper.Viper { help := flags.BoolP(cmdHelp, "h", false, "show help") version := flags.BoolP(cmdVersion, "v", false, "show version") - flags.String(cmdNeoFSKey, "", `"Path to private key file, hex string or wif`) + flags.String(cmdNeoFSKey, "", `path to private key file, hex string or wif`) flags.Bool(cmdVerbose, false, "debug gRPC connections") flags.Duration(cfgConTimeout, defaultConnectTimeout, "gRPC connect timeout") flags.Duration(cfgReqTimeout, defaultRequestTimeout, "gRPC request timeout") flags.Duration(cfgRebalance, defaultRebalanceTimer, "gRPC connection rebalance timer") - flags.String(cfgListenAddress, "0.0.0.0:8082", "HTTP Gateway listen address") + flags.String(cfgListenAddress, "0.0.0.0:8082", "address to listen") peers := flags.StringArrayP(cfgPeers, "p", nil, "NeoFS nodes") // set prefers: diff --git a/upload.go b/upload.go index 1360ec5..20a65f5 100644 --- a/upload.go +++ b/upload.go @@ -7,10 +7,11 @@ import ( "strconv" "time" - sdk "github.com/nspcc-dev/cdn-sdk" "github.com/nspcc-dev/neofs-api-go/pkg/container" "github.com/nspcc-dev/neofs-api-go/pkg/object" "github.com/nspcc-dev/neofs-api-go/pkg/owner" + "github.com/nspcc-dev/neofs-api-go/pkg/token" + "github.com/nspcc-dev/neofs-http-gate/neofs" "github.com/valyala/fasthttp" "go.uber.org/zap" ) @@ -29,18 +30,17 @@ func newPutResponse(addr *object.Address) *putResponse { } } -func (pr *putResponse) Encode(w io.Writer) error { +func (pr *putResponse) encode(w io.Writer) error { enc := json.NewEncoder(w) enc.SetIndent("", "\t") return enc.Encode(pr) } -func (a *app) fetchOwner(ctx context.Context) *owner.ID { - if tkn, err := sdk.BearerToken(ctx); err == nil && tkn != nil { - return tkn.Issuer() +func (a *app) fetchOwnerAndBearerToken(ctx context.Context) (*owner.ID, *token.BearerToken) { + if token, err := loadBearerToken(ctx); err == nil && token != nil { + return token.Issuer(), token } - - return a.cli.Owner() + return a.plant.OwnerID(), nil } func (a *app) upload(c *fasthttp.RequestCtx) { @@ -50,94 +50,93 @@ func (a *app) upload(c *fasthttp.RequestCtx) { addr *object.Address cid = container.NewID() sCID, _ = c.UserValue("cid").(string) - - log = a.log.With(zap.String("cid", sCID)) + log = a.log.With(zap.String("cid", sCID)) ) - - if err = checkAndPropagateBearerToken(c); err != nil { + if err = storeBearerToken(c); err != nil { log.Error("could not fetch bearer token", zap.Error(err)) c.Error("could not fetch bearer token", fasthttp.StatusBadRequest) return } - if err = cid.Parse(sCID); err != nil { log.Error("wrong container id", zap.Error(err)) c.Error("wrong container id", fasthttp.StatusBadRequest) return } - defer func() { // if temporary reader can be closed - close it if file == nil { return } - - log.Debug("close temporary multipart/form file", + err := file.Close() + log.Debug( + "close temporary multipart/form file", zap.Stringer("address", addr), zap.String("filename", file.FileName()), - zap.Error(file.Close())) + zap.Error(err), + ) }() - boundary := string(c.Request.Header.MultipartFormBoundary()) if file, err = fetchMultipartFile(a.log, c.RequestBodyStream(), boundary); err != nil { log.Error("could not receive multipart/form", zap.Error(err)) c.Error("could not receive multipart/form: "+err.Error(), fasthttp.StatusBadRequest) - return } - filtered := filterHeaders(a.log, &c.Request.Header) attributes := make([]*object.Attribute, 0, len(filtered)) - // prepares attributes from filtered headers for key, val := range filtered { attribute := object.NewAttribute() attribute.SetKey(key) attribute.SetValue(val) - attributes = append(attributes, attribute) } - // sets FileName attribute if it wasn't set from header if _, ok := filtered[object.AttributeFileName]; !ok { filename := object.NewAttribute() filename.SetKey(object.AttributeFileName) filename.SetValue(file.FileName()) - attributes = append(attributes, filename) } - // sets Timestamp attribute if it wasn't set from header and enabled by settings if _, ok := filtered[object.AttributeTimestamp]; !ok && a.enableDefaultTimestamp { timestamp := object.NewAttribute() timestamp.SetKey(object.AttributeTimestamp) timestamp.SetValue(strconv.FormatInt(time.Now().Unix(), 10)) - attributes = append(attributes, timestamp) } - + oid, bt := a.fetchOwnerAndBearerToken(c) // prepares new object and fill it raw := object.NewRaw() raw.SetContainerID(cid) - raw.SetOwnerID(a.fetchOwner(c)) + raw.SetOwnerID(oid) raw.SetAttributes(attributes...) - // tries to put file into NeoFS or throw error - if addr, err = a.cli.Object().Put(c, raw.Object(), sdk.WithPutReader(file)); err != nil { - log.Error("could not store file in NeoFS", zap.Error(err)) - c.Error("could not store file in NeoFS", fasthttp.StatusBadRequest) - + // if addr, err = a.plant.Object().Put(c, raw.Object(), sdk.WithPutReader(file)); err != nil { + // TODO: Take this from a sync pool. + putOpts := new(neofs.PutOptions) + putOpts.Client, putOpts.SessionToken, err = a.plant.GetReusableArtifacts(c) + if err != nil { + log.Error("failed to get neofs client's reusable artifacts", zap.Error(err)) + c.Error("failed to get neofs client's reusable artifacts", fasthttp.StatusInternalServerError) + return + } + putOpts.BearerToken = bt + putOpts.ContainerID = cid + putOpts.OwnerID = oid + putOpts.PrepareObjectOnsite = false + putOpts.Reader = file + if addr, err = a.plant.Object().Put(c, putOpts); err != nil { + log.Error("could not store file in NeoFS", zap.Error(err)) + c.Error("could not store file in NeoFS", fasthttp.StatusBadRequest) return } - // tries to return response, otherwise, if something went wrong throw error - if err = newPutResponse(addr).Encode(c); err != nil { + if err = newPutResponse(addr).encode(c); err != nil { log.Error("could not prepare response", zap.Error(err)) c.Error("could not prepare response", fasthttp.StatusBadRequest) return } - // reports status code and content type c.Response.SetStatusCode(fasthttp.StatusOK) c.Response.Header.SetContentType(jsonHeader)