[#232] Use contract to get container info
Some checks failed
/ DCO (pull_request) Successful in 32s
/ Vulncheck (pull_request) Successful in 49s
/ Builds (pull_request) Successful in 1m12s
/ OCI image (pull_request) Successful in 1m33s
/ Lint (pull_request) Successful in 2m14s
/ Tests (pull_request) Successful in 51s
/ Integration tests (pull_request) Failing after 5m50s
Some checks failed
/ DCO (pull_request) Successful in 32s
/ Vulncheck (pull_request) Successful in 49s
/ Builds (pull_request) Successful in 1m12s
/ OCI image (pull_request) Successful in 1m33s
/ Lint (pull_request) Successful in 2m14s
/ Tests (pull_request) Successful in 51s
/ Integration tests (pull_request) Failing after 5m50s
Signed-off-by: Nikita Zinkevich <n.zinkevich@yadro.com>
This commit is contained in:
parent
ee628617a3
commit
c84b7aa27c
13 changed files with 306 additions and 89 deletions
|
@ -23,6 +23,7 @@ import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
|
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
|
||||||
internalnet "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/net"
|
internalnet "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/net"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/service/frostfs"
|
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/service/frostfs"
|
||||||
|
containerClient "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/service/frostfs/container"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/templates"
|
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/templates"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/metrics"
|
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/metrics"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/resolver"
|
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/resolver"
|
||||||
|
@ -39,6 +40,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/cli/flags"
|
"github.com/nspcc-dev/neo-go/cli/flags"
|
||||||
"github.com/nspcc-dev/neo-go/cli/input"
|
"github.com/nspcc-dev/neo-go/cli/input"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
"github.com/panjf2000/ants/v2"
|
"github.com/panjf2000/ants/v2"
|
||||||
|
@ -275,6 +277,14 @@ func (a *app) initContainers(ctx context.Context) {
|
||||||
a.corsCnrID = *corsCnrID
|
a.corsCnrID = *corsCnrID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *app) initRPCClient(ctx context.Context) *rpcclient.Client {
|
||||||
|
rpcCli, err := rpcclient.New(ctx, a.config().GetString(cfgRPCEndpoint), rpcclient.Options{})
|
||||||
|
if err != nil {
|
||||||
|
a.log.Fatal(logs.InitRPCClientFailed, zap.Error(err), logs.TagField(logs.TagApp))
|
||||||
|
}
|
||||||
|
return rpcCli
|
||||||
|
}
|
||||||
|
|
||||||
func (a *app) initAppSettings(lc *logLevelConfig) {
|
func (a *app) initAppSettings(lc *logLevelConfig) {
|
||||||
a.settings = &appSettings{
|
a.settings = &appSettings{
|
||||||
reconnectInterval: fetchReconnectInterval(a.config()),
|
reconnectInterval: fetchReconnectInterval(a.config()),
|
||||||
|
@ -741,7 +751,17 @@ func (a *app) stopServices() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *app) configureRouter(workerPool *ants.Pool) {
|
func (a *app) configureRouter(workerPool *ants.Pool) {
|
||||||
a.handle = handler.New(a.AppParams(), a.settings, tree.NewTree(frostfs.NewPoolWrapper(a.treePool), a.log), workerPool)
|
rpcCli := a.initRPCClient(a.ctx)
|
||||||
|
cnrClient, err := containerClient.New(containerClient.Config{
|
||||||
|
RPCAddress: a.config().GetString(cfgRPCEndpoint),
|
||||||
|
Contract: a.config().GetString(cfgContractsContainerName),
|
||||||
|
Key: a.key,
|
||||||
|
RPCClient: rpcCli,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
a.log.Fatal(logs.InitContainerContractFailed, zap.Error(err), logs.TagField(logs.TagApp))
|
||||||
|
}
|
||||||
|
a.handle = handler.New(a.AppParams(), a.settings, tree.NewTree(frostfs.NewPoolWrapper(a.treePool), a.log), cnrClient, workerPool)
|
||||||
|
|
||||||
r := router.New()
|
r := router.New()
|
||||||
r.RedirectTrailingSlash = true
|
r.RedirectTrailingSlash = true
|
||||||
|
|
|
@ -62,6 +62,8 @@ const (
|
||||||
|
|
||||||
defaultMultinetFallbackDelay = 300 * time.Millisecond
|
defaultMultinetFallbackDelay = 300 * time.Millisecond
|
||||||
|
|
||||||
|
defaultContainerContractName = "container.frostfs"
|
||||||
|
|
||||||
cfgServer = "server"
|
cfgServer = "server"
|
||||||
cfgTLSEnabled = "tls.enabled"
|
cfgTLSEnabled = "tls.enabled"
|
||||||
cfgTLSCertFile = "tls.cert_file"
|
cfgTLSCertFile = "tls.cert_file"
|
||||||
|
@ -196,6 +198,9 @@ const (
|
||||||
cmdConfig = "config"
|
cmdConfig = "config"
|
||||||
cmdConfigDir = "config-dir"
|
cmdConfigDir = "config-dir"
|
||||||
cmdListenAddress = "listen_address"
|
cmdListenAddress = "listen_address"
|
||||||
|
|
||||||
|
// Contracts.
|
||||||
|
cfgContractsContainerName = "contracts.container.name"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ignore = map[string]struct{}{
|
var ignore = map[string]struct{}{
|
||||||
|
@ -400,6 +405,9 @@ func setDefaults(v *viper.Viper, flags *pflag.FlagSet) {
|
||||||
// multinet
|
// multinet
|
||||||
v.SetDefault(cfgMultinetFallbackDelay, defaultMultinetFallbackDelay)
|
v.SetDefault(cfgMultinetFallbackDelay, defaultMultinetFallbackDelay)
|
||||||
|
|
||||||
|
// contracts
|
||||||
|
v.SetDefault(cfgContractsContainerName, defaultContainerContractName)
|
||||||
|
|
||||||
if resolveMethods, err := flags.GetStringSlice(cfgResolveOrder); err == nil {
|
if resolveMethods, err := flags.GetStringSlice(cfgResolveOrder); err == nil {
|
||||||
v.SetDefault(cfgResolveOrder, resolveMethods)
|
v.SetDefault(cfgResolveOrder, resolveMethods)
|
||||||
}
|
}
|
||||||
|
|
|
@ -179,3 +179,6 @@ HTTP_GW_FEATURES_TREE_POOL_NETMAP_SUPPORT=true
|
||||||
|
|
||||||
# Containers properties
|
# Containers properties
|
||||||
HTTP_GW_CONTAINERS_CORS=AZjLTXfK4vs4ovxMic2xEJKSymMNLqdwq9JT64ASFCRj
|
HTTP_GW_CONTAINERS_CORS=AZjLTXfK4vs4ovxMic2xEJKSymMNLqdwq9JT64ASFCRj
|
||||||
|
|
||||||
|
# Container contract hash (LE) or name in NNS.
|
||||||
|
HTTP_GW_CONTRACTS_CONTAINER_NAME=container.frostfs
|
||||||
|
|
|
@ -197,3 +197,8 @@ features:
|
||||||
|
|
||||||
containers:
|
containers:
|
||||||
cors: AZjLTXfK4vs4ovxMic2xEJKSymMNLqdwq9JT64ASFCRj
|
cors: AZjLTXfK4vs4ovxMic2xEJKSymMNLqdwq9JT64ASFCRj
|
||||||
|
|
||||||
|
contracts:
|
||||||
|
container:
|
||||||
|
# Container contract hash (LE) or name in NNS.
|
||||||
|
name: container.frostfs
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -3,6 +3,7 @@ module git.frostfs.info/TrueCloudLab/frostfs-http-gw
|
||||||
go 1.23
|
go 1.23
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240621131249-49e5270f673e
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20241125133852-37bd75821121
|
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20241125133852-37bd75821121
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-qos v0.0.0-20250128150313-cfbca7fa1dfe
|
git.frostfs.info/TrueCloudLab/frostfs-qos v0.0.0-20250128150313-cfbca7fa1dfe
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20250317082814-87bb55f992dc
|
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20250317082814-87bb55f992dc
|
||||||
|
@ -33,7 +34,6 @@ require (
|
||||||
|
|
||||||
require (
|
require (
|
||||||
dario.cat/mergo v1.0.0 // indirect
|
dario.cat/mergo v1.0.0 // indirect
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240621131249-49e5270f673e // indirect
|
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0 // indirect
|
git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0 // indirect
|
||||||
git.frostfs.info/TrueCloudLab/hrw v1.2.1 // indirect
|
git.frostfs.info/TrueCloudLab/hrw v1.2.1 // indirect
|
||||||
git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 // indirect
|
git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 // indirect
|
||||||
|
|
|
@ -1,14 +1,21 @@
|
||||||
package data
|
package data
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
||||||
)
|
)
|
||||||
|
|
||||||
type BucketInfo struct {
|
type BucketInfo struct {
|
||||||
Name string // container name from system attribute
|
Name string // container name from system attribute
|
||||||
Zone string // container zone from system attribute
|
Zone string // container zone from system attribute
|
||||||
CID cid.ID
|
CID cid.ID
|
||||||
|
Owner user.ID
|
||||||
|
Created time.Time
|
||||||
|
LocationConstraint string
|
||||||
|
ObjectLockEnabled bool
|
||||||
HomomorphicHashDisabled bool
|
HomomorphicHashDisabled bool
|
||||||
PlacementPolicy netmap.PlacementPolicy
|
PlacementPolicy netmap.PlacementPolicy
|
||||||
}
|
}
|
||||||
|
|
84
internal/handler/container.go
Normal file
84
internal/handler/container.go
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
containerContract "git.frostfs.info/TrueCloudLab/frostfs-contract/container"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/data"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/handler/middleware"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
||||||
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
attributeLocationConstraint = ".s3-location-constraint"
|
||||||
|
AttributeLockEnabled = "LockEnabled"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (h *Handler) containerInfo(ctx context.Context, cnrID cid.ID) (*data.BucketInfo, error) {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
res *container.Container
|
||||||
|
log = h.reqLogger(ctx).With(zap.Stringer("cid", cnrID))
|
||||||
|
|
||||||
|
info = &data.BucketInfo{
|
||||||
|
CID: cnrID,
|
||||||
|
Name: cnrID.EncodeToString(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
res, err = h.cnrContract.GetContainerByID(cnrID)
|
||||||
|
if err != nil {
|
||||||
|
if strings.Contains(err.Error(), containerContract.NotFoundError) {
|
||||||
|
return nil, fmt.Errorf("get container: %s", err.Error())
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("get frostfs container: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cnr := *res
|
||||||
|
|
||||||
|
info.Owner = cnr.Owner()
|
||||||
|
if domain := container.ReadDomain(cnr); domain.Name() != "" {
|
||||||
|
info.Name = domain.Name()
|
||||||
|
info.Zone = domain.Zone()
|
||||||
|
}
|
||||||
|
info.Created = container.CreatedAt(cnr)
|
||||||
|
info.LocationConstraint = cnr.Attribute(attributeLocationConstraint)
|
||||||
|
info.HomomorphicHashDisabled = container.IsHomomorphicHashingDisabled(cnr)
|
||||||
|
info.PlacementPolicy = cnr.PlacementPolicy()
|
||||||
|
|
||||||
|
attrLockEnabled := cnr.Attribute(AttributeLockEnabled)
|
||||||
|
if len(attrLockEnabled) > 0 {
|
||||||
|
info.ObjectLockEnabled, err = strconv.ParseBool(attrLockEnabled)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(logs.CouldNotParseContainerObjectLockEnabledAttribute,
|
||||||
|
zap.String("lock_enabled", attrLockEnabled),
|
||||||
|
zap.Error(err),
|
||||||
|
logs.TagField(logs.TagDatapath),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ns, err := middleware.GetNamespace(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("get namespace: %w", err)
|
||||||
|
}
|
||||||
|
zone := h.config.FormContainerZone(ns)
|
||||||
|
if zone != info.Zone {
|
||||||
|
return nil, fmt.Errorf("ns '%s' and zone '%s' are mismatched for container '%s'", zone, info.Zone, cnrID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = h.cache.Put(info); err != nil {
|
||||||
|
h.reqLogger(ctx).Warn(logs.CouldntPutBucketIntoCache,
|
||||||
|
zap.String("bucket name", info.Name),
|
||||||
|
zap.Stringer("bucket cid", info.CID),
|
||||||
|
zap.Error(err),
|
||||||
|
logs.TagField(logs.TagDatapath))
|
||||||
|
}
|
||||||
|
|
||||||
|
return info, nil
|
||||||
|
}
|
|
@ -233,6 +233,16 @@ func (t *TestFrostFS) SearchObjects(_ context.Context, prm PrmObjectSearch) (Res
|
||||||
return &resObjectSearchMock{res: res}, nil
|
return &resObjectSearchMock{res: res}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *TestFrostFS) GetContainerByID(cid cid.ID) (*container.Container, error) {
|
||||||
|
for k, v := range t.containers {
|
||||||
|
if k == cid.EncodeToString() {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("container does not exist %s", cid)
|
||||||
|
}
|
||||||
|
|
||||||
func (t *TestFrostFS) InitMultiObjectReader(context.Context, PrmInitMultiObjectReader) (io.Reader, error) {
|
func (t *TestFrostFS) InitMultiObjectReader(context.Context, PrmInitMultiObjectReader) (io.Reader, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -166,12 +166,18 @@ type ContainerResolver interface {
|
||||||
Resolve(ctx context.Context, zone, name string) (*cid.ID, error)
|
Resolve(ctx context.Context, zone, name string) (*cid.ID, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ContainerContract interface {
|
||||||
|
// GetContainerByID reads a container from contract by ID.
|
||||||
|
GetContainerByID(cid.ID) (*container.Container, error)
|
||||||
|
}
|
||||||
|
|
||||||
type Handler struct {
|
type Handler struct {
|
||||||
log *zap.Logger
|
log *zap.Logger
|
||||||
frostfs FrostFS
|
frostfs FrostFS
|
||||||
ownerID *user.ID
|
ownerID *user.ID
|
||||||
config Config
|
config Config
|
||||||
containerResolver ContainerResolver
|
containerResolver ContainerResolver
|
||||||
|
cnrContract ContainerContract
|
||||||
tree layer.TreeService
|
tree layer.TreeService
|
||||||
cache *cache.BucketCache
|
cache *cache.BucketCache
|
||||||
workerPool *ants.Pool
|
workerPool *ants.Pool
|
||||||
|
@ -189,7 +195,7 @@ type AppParams struct {
|
||||||
CORSCache *cache.CORSCache
|
CORSCache *cache.CORSCache
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(params *AppParams, config Config, tree layer.TreeService, workerPool *ants.Pool) *Handler {
|
func New(params *AppParams, config Config, tree layer.TreeService, rpcCli ContainerContract, workerPool *ants.Pool) *Handler {
|
||||||
return &Handler{
|
return &Handler{
|
||||||
log: params.Logger,
|
log: params.Logger,
|
||||||
frostfs: params.FrostFS,
|
frostfs: params.FrostFS,
|
||||||
|
@ -201,6 +207,7 @@ func New(params *AppParams, config Config, tree layer.TreeService, workerPool *a
|
||||||
workerPool: workerPool,
|
workerPool: workerPool,
|
||||||
corsCnrID: params.CORSCnrID,
|
corsCnrID: params.CORSCnrID,
|
||||||
corsCache: params.CORSCache,
|
corsCache: params.CORSCache,
|
||||||
|
cnrContract: rpcCli,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -379,43 +386,7 @@ func (h *Handler) getBucketInfo(ctx context.Context, containerName string) (*dat
|
||||||
return nil, fmt.Errorf("resolve container: %w", err)
|
return nil, fmt.Errorf("resolve container: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
bktInfo, err := h.readContainer(ctx, *cnrID)
|
return h.containerInfo(ctx, *cnrID)
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("read container: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = h.cache.Put(bktInfo); err != nil {
|
|
||||||
h.reqLogger(ctx).Warn(logs.CouldntPutBucketIntoCache,
|
|
||||||
zap.String("bucket name", bktInfo.Name),
|
|
||||||
zap.Stringer("bucket cid", bktInfo.CID),
|
|
||||||
zap.Error(err),
|
|
||||||
logs.TagField(logs.TagDatapath))
|
|
||||||
}
|
|
||||||
|
|
||||||
return bktInfo, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Handler) readContainer(ctx context.Context, cnrID cid.ID) (*data.BucketInfo, error) {
|
|
||||||
prm := PrmContainer{ContainerID: cnrID}
|
|
||||||
res, err := h.frostfs.Container(ctx, prm)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("get frostfs container '%s': %w", cnrID.String(), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
bktInfo := &data.BucketInfo{
|
|
||||||
CID: cnrID,
|
|
||||||
Name: cnrID.EncodeToString(),
|
|
||||||
}
|
|
||||||
|
|
||||||
if domain := container.ReadDomain(*res); domain.Name() != "" {
|
|
||||||
bktInfo.Name = domain.Name()
|
|
||||||
bktInfo.Zone = domain.Zone()
|
|
||||||
}
|
|
||||||
|
|
||||||
bktInfo.HomomorphicHashDisabled = container.IsHomomorphicHashingDisabled(*res)
|
|
||||||
bktInfo.PlacementPolicy = res.PlacementPolicy()
|
|
||||||
|
|
||||||
return bktInfo, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) browseIndex(ctx context.Context, req *fasthttp.RequestCtx, cidParam, oidParam string, isNativeList bool) {
|
func (h *Handler) browseIndex(ctx context.Context, req *fasthttp.RequestCtx, cidParam, oidParam string, isNativeList bool) {
|
||||||
|
|
|
@ -174,7 +174,7 @@ func prepareHandlerContextBase(logger *zap.Logger) (*handlerContext, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
handler := New(params, cfgMock, treeMock, workerPool)
|
handler := New(params, cfgMock, treeMock, testFrostFS, workerPool)
|
||||||
|
|
||||||
return &handlerContext{
|
return &handlerContext{
|
||||||
key: key,
|
key: key,
|
||||||
|
|
|
@ -73,58 +73,61 @@ const (
|
||||||
FailedToReadIndexPageTemplate = "failed to read index page template"
|
FailedToReadIndexPageTemplate = "failed to read index page template"
|
||||||
SetCustomIndexPageTemplate = "set custom index page template"
|
SetCustomIndexPageTemplate = "set custom index page template"
|
||||||
CouldNotFetchCORSContainerInfo = "couldn't fetch CORS container info"
|
CouldNotFetchCORSContainerInfo = "couldn't fetch CORS container info"
|
||||||
|
InitRPCClientFailed = "init rpc client faileds"
|
||||||
|
InitContainerContractFailed = "init container contract failed"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Log messages with the "datapath" tag.
|
// Log messages with the "datapath" tag.
|
||||||
const (
|
const (
|
||||||
CouldntParseCreationDate = "couldn't parse creation date"
|
CouldntParseCreationDate = "couldn't parse creation date"
|
||||||
FailedToDetectContentTypeFromPayload = "failed to detect Content-Type from payload"
|
FailedToDetectContentTypeFromPayload = "failed to detect Content-Type from payload"
|
||||||
FailedToAddObjectToArchive = "failed to add object to archive"
|
FailedToAddObjectToArchive = "failed to add object to archive"
|
||||||
CloseZipWriter = "close zip writer"
|
CloseZipWriter = "close zip writer"
|
||||||
IgnorePartEmptyFormName = "ignore part, empty form name"
|
IgnorePartEmptyFormName = "ignore part, empty form name"
|
||||||
IgnorePartEmptyFilename = "ignore part, empty filename"
|
IgnorePartEmptyFilename = "ignore part, empty filename"
|
||||||
CouldNotParseClientTime = "could not parse client time"
|
CouldNotParseClientTime = "could not parse client time"
|
||||||
CouldNotPrepareExpirationHeader = "could not prepare expiration header"
|
CouldNotPrepareExpirationHeader = "could not prepare expiration header"
|
||||||
CouldNotEncodeResponse = "could not encode response"
|
CouldNotEncodeResponse = "could not encode response"
|
||||||
AddAttributeToResultObject = "add attribute to result object"
|
AddAttributeToResultObject = "add attribute to result object"
|
||||||
Request = "request"
|
Request = "request"
|
||||||
CouldNotFetchAndStoreBearerToken = "could not fetch and store bearer token"
|
CouldNotFetchAndStoreBearerToken = "could not fetch and store bearer token"
|
||||||
CouldntPutBucketIntoCache = "couldn't put bucket info into cache"
|
CouldntPutBucketIntoCache = "couldn't put bucket info into cache"
|
||||||
FailedToIterateOverResponse = "failed to iterate over search response"
|
FailedToIterateOverResponse = "failed to iterate over search response"
|
||||||
InvalidCacheEntryType = "invalid cache entry type"
|
InvalidCacheEntryType = "invalid cache entry type"
|
||||||
FailedToUnescapeQuery = "failed to unescape query"
|
FailedToUnescapeQuery = "failed to unescape query"
|
||||||
CouldntCacheNetmap = "couldn't cache netmap"
|
CouldntCacheNetmap = "couldn't cache netmap"
|
||||||
FailedToCloseReader = "failed to close reader"
|
FailedToCloseReader = "failed to close reader"
|
||||||
FailedToFilterHeaders = "failed to filter headers"
|
FailedToFilterHeaders = "failed to filter headers"
|
||||||
FailedToReadFileFromTar = "failed to read file from tar"
|
FailedToReadFileFromTar = "failed to read file from tar"
|
||||||
FailedToGetAttributes = "failed to get attributes"
|
FailedToGetAttributes = "failed to get attributes"
|
||||||
CloseGzipWriter = "close gzip writer"
|
CloseGzipWriter = "close gzip writer"
|
||||||
CloseTarWriter = "close tar writer"
|
CloseTarWriter = "close tar writer"
|
||||||
FailedToCreateGzipReader = "failed to create gzip reader"
|
FailedToCreateGzipReader = "failed to create gzip reader"
|
||||||
GzipReaderSelected = "gzip reader selected"
|
GzipReaderSelected = "gzip reader selected"
|
||||||
CouldNotReceiveMultipartForm = "could not receive multipart/form"
|
CouldNotReceiveMultipartForm = "could not receive multipart/form"
|
||||||
ObjectsNotFound = "objects not found"
|
ObjectsNotFound = "objects not found"
|
||||||
IteratingOverSelectedObjectsFailed = "iterating over selected objects failed"
|
IteratingOverSelectedObjectsFailed = "iterating over selected objects failed"
|
||||||
FailedToGetBucketInfo = "could not get bucket info"
|
FailedToGetBucketInfo = "could not get bucket info"
|
||||||
FailedToSubmitTaskToPool = "failed to submit task to pool"
|
FailedToSubmitTaskToPool = "failed to submit task to pool"
|
||||||
ObjectWasDeleted = "object was deleted"
|
ObjectWasDeleted = "object was deleted"
|
||||||
FailedToGetLatestVersionOfObject = "failed to get latest version of object"
|
FailedToGetLatestVersionOfObject = "failed to get latest version of object"
|
||||||
FailedToCheckIfSettingsNodeExist = "failed to check if settings node exists"
|
FailedToCheckIfSettingsNodeExist = "failed to check if settings node exists"
|
||||||
FailedToListObjects = "failed to list objects"
|
FailedToListObjects = "failed to list objects"
|
||||||
FailedToParseTemplate = "failed to parse template"
|
FailedToParseTemplate = "failed to parse template"
|
||||||
FailedToExecuteTemplate = "failed to execute template"
|
FailedToExecuteTemplate = "failed to execute template"
|
||||||
FailedToUploadObject = "failed to upload object"
|
FailedToUploadObject = "failed to upload object"
|
||||||
FailedToHeadObject = "failed to head object"
|
FailedToHeadObject = "failed to head object"
|
||||||
FailedToGetObject = "failed to get object"
|
FailedToGetObject = "failed to get object"
|
||||||
FailedToGetObjectPayload = "failed to get object payload"
|
FailedToGetObjectPayload = "failed to get object payload"
|
||||||
FailedToFindObjectByAttribute = "failed to get find object by attribute"
|
FailedToFindObjectByAttribute = "failed to get find object by attribute"
|
||||||
FailedToUnescapeOIDParam = "failed to unescape oid param"
|
FailedToUnescapeOIDParam = "failed to unescape oid param"
|
||||||
InvalidOIDParam = "invalid oid param"
|
InvalidOIDParam = "invalid oid param"
|
||||||
CouldNotGetCORSConfiguration = "could not get cors configuration"
|
CouldNotGetCORSConfiguration = "could not get cors configuration"
|
||||||
EmptyOriginRequestHeader = "empty Origin request header"
|
EmptyOriginRequestHeader = "empty Origin request header"
|
||||||
EmptyAccessControlRequestMethodHeader = "empty Access-Control-Request-Method request header"
|
EmptyAccessControlRequestMethodHeader = "empty Access-Control-Request-Method request header"
|
||||||
CORSRuleWasNotMatched = "cors rule was not matched"
|
CORSRuleWasNotMatched = "cors rule was not matched"
|
||||||
CouldntCacheCors = "couldn't cache cors"
|
CouldntCacheCors = "couldn't cache cors"
|
||||||
|
CouldNotParseContainerObjectLockEnabledAttribute = "could not parse container object lock enabled attribute"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Log messages with the "external_storage" tag.
|
// Log messages with the "external_storage" tag.
|
||||||
|
|
72
internal/service/frostfs/container/client.go
Normal file
72
internal/service/frostfs/container/client.go
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
package container
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
containerContract "git.frostfs.info/TrueCloudLab/frostfs-contract/rpcclient/container"
|
||||||
|
frostfsutil "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/service/frostfs/util"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
||||||
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
contract *containerContract.Contract
|
||||||
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
RPCAddress string
|
||||||
|
Contract string
|
||||||
|
Key *keys.PrivateKey
|
||||||
|
RPCClient *rpcclient.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(cfg Config) (*Client, error) {
|
||||||
|
contractHash, err := frostfsutil.ResolveContractHash(cfg.Contract, cfg.RPCAddress)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("resolve frostfs contract hash: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
key := cfg.Key
|
||||||
|
if key == nil {
|
||||||
|
if key, err = keys.NewPrivateKey(); err != nil {
|
||||||
|
return nil, fmt.Errorf("generate anon private key for container contract: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
acc := wallet.NewAccountFromPrivateKey(key)
|
||||||
|
|
||||||
|
act, err := actor.NewSimple(cfg.RPCClient, acc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("create new actor: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Client{
|
||||||
|
contract: containerContract.New(act, contractHash),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) GetContainerByID(cnrID cid.ID) (*container.Container, error) {
|
||||||
|
items, err := c.contract.Get(cnrID[:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("get container by cid: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(items) != 4 {
|
||||||
|
return nil, fmt.Errorf("unexpected container stack item count: %d", len(items))
|
||||||
|
}
|
||||||
|
|
||||||
|
cnrBytes, err := items[0].TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not get byte array of container: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var cnr container.Container
|
||||||
|
if err = cnr.Unmarshal(cnrBytes); err != nil {
|
||||||
|
return nil, fmt.Errorf("can't unmarshal container: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &cnr, nil
|
||||||
|
}
|
34
internal/service/frostfs/util/util.go
Normal file
34
internal/service/frostfs/util/util.go
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/ns"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ResolveContractHash determine contract hash by resolving NNS name.
|
||||||
|
func ResolveContractHash(contractHash, rpcAddress string) (util.Uint160, error) {
|
||||||
|
if hash, err := util.Uint160DecodeStringLE(contractHash); err == nil {
|
||||||
|
return hash, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
splitName := strings.Split(contractHash, ".")
|
||||||
|
if len(splitName) != 2 {
|
||||||
|
return util.Uint160{}, fmt.Errorf("invalid contract name: '%s'", contractHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
var domain container.Domain
|
||||||
|
domain.SetName(splitName[0])
|
||||||
|
domain.SetZone(splitName[1])
|
||||||
|
|
||||||
|
var nns ns.NNS
|
||||||
|
if err := nns.Dial(rpcAddress); err != nil {
|
||||||
|
return util.Uint160{}, fmt.Errorf("dial nns %s: %w", rpcAddress, err)
|
||||||
|
}
|
||||||
|
defer nns.Close()
|
||||||
|
|
||||||
|
return nns.ResolveContractHash(domain)
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue