2020-09-21 14:31:31 +00:00
|
|
|
package putsvc
|
|
|
|
|
|
|
|
import (
|
2023-04-03 11:23:53 +00:00
|
|
|
"context"
|
2021-05-18 08:12:51 +00:00
|
|
|
"fmt"
|
|
|
|
|
2023-03-07 13:38:26 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/client"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/network"
|
2024-08-30 09:09:14 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object/common/target"
|
2023-03-07 13:38:26 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object/internal"
|
|
|
|
internalclient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object/internal/client"
|
|
|
|
putsvc "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object/put"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object/util"
|
2023-05-31 09:24:04 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-observability/tracing"
|
2024-11-07 14:32:10 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/object"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/rpc"
|
|
|
|
rawclient "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/rpc/client"
|
|
|
|
sessionV2 "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/session"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/signature"
|
2023-04-12 14:01:29 +00:00
|
|
|
"go.opentelemetry.io/otel/attribute"
|
|
|
|
"go.opentelemetry.io/otel/trace"
|
2020-09-21 14:31:31 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type streamer struct {
|
2021-05-27 14:25:29 +00:00
|
|
|
stream *putsvc.Streamer
|
2021-05-28 13:26:32 +00:00
|
|
|
keyStorage *util.KeyStorage
|
2021-05-27 14:25:29 +00:00
|
|
|
saveChunks bool
|
|
|
|
init *object.PutRequest
|
|
|
|
chunks []*object.PutRequest
|
2021-06-07 09:29:38 +00:00
|
|
|
|
|
|
|
*sizes // only for relay streams
|
2020-09-21 14:31:31 +00:00
|
|
|
}
|
|
|
|
|
2021-06-07 09:29:38 +00:00
|
|
|
type sizes struct {
|
|
|
|
payloadSz uint64 // value from the header
|
|
|
|
|
|
|
|
writtenPayload uint64 // sum size of already cached chunks
|
|
|
|
}
|
|
|
|
|
2023-04-03 11:23:53 +00:00
|
|
|
func (s *streamer) Send(ctx context.Context, req *object.PutRequest) (err error) {
|
2023-04-12 14:01:29 +00:00
|
|
|
ctx, span := tracing.StartSpanFromContext(ctx, "putv2.streamer.Send")
|
|
|
|
defer span.End()
|
|
|
|
|
2020-09-21 14:31:31 +00:00
|
|
|
switch v := req.GetBody().GetObjectPart().(type) {
|
|
|
|
case *object.PutObjectPartInit:
|
2021-01-14 07:51:05 +00:00
|
|
|
var initPrm *putsvc.PutInitPrm
|
|
|
|
|
2021-05-27 14:25:29 +00:00
|
|
|
initPrm, err = s.toInitPrm(v, req)
|
2021-01-12 14:55:02 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-04-12 14:01:29 +00:00
|
|
|
if err = s.stream.Init(ctx, initPrm); err != nil {
|
2021-05-18 08:12:51 +00:00
|
|
|
err = fmt.Errorf("(%T) could not init object put stream: %w", s, err)
|
2020-09-21 14:31:31 +00:00
|
|
|
}
|
2021-05-27 14:25:29 +00:00
|
|
|
|
|
|
|
s.saveChunks = v.GetSignature() != nil
|
|
|
|
if s.saveChunks {
|
2024-08-30 09:09:14 +00:00
|
|
|
maxSz := s.stream.MaxSizeSrc.MaxObjectSize()
|
2021-06-07 09:29:38 +00:00
|
|
|
|
|
|
|
s.sizes = &sizes{
|
|
|
|
payloadSz: uint64(v.GetHeader().GetPayloadLength()),
|
|
|
|
}
|
|
|
|
|
|
|
|
// check payload size limit overflow
|
|
|
|
if s.payloadSz > maxSz {
|
2024-08-30 09:09:14 +00:00
|
|
|
return target.ErrExceedingMaxSize
|
2021-06-07 09:29:38 +00:00
|
|
|
}
|
|
|
|
|
2021-05-27 14:25:29 +00:00
|
|
|
s.init = req
|
|
|
|
}
|
2020-09-21 14:31:31 +00:00
|
|
|
case *object.PutObjectPartChunk:
|
2021-06-07 09:29:38 +00:00
|
|
|
if s.saveChunks {
|
|
|
|
s.writtenPayload += uint64(len(v.GetChunk()))
|
|
|
|
|
|
|
|
// check payload size overflow
|
|
|
|
if s.writtenPayload > s.payloadSz {
|
2024-08-30 09:09:14 +00:00
|
|
|
return target.ErrWrongPayloadSize
|
2021-06-07 09:29:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-03 11:23:53 +00:00
|
|
|
if err = s.stream.SendChunk(ctx, toChunkPrm(v)); err != nil {
|
2021-05-18 08:12:51 +00:00
|
|
|
err = fmt.Errorf("(%T) could not send payload chunk: %w", s, err)
|
2020-09-21 14:31:31 +00:00
|
|
|
}
|
2021-05-27 14:25:29 +00:00
|
|
|
|
|
|
|
if s.saveChunks {
|
|
|
|
s.chunks = append(s.chunks, req)
|
|
|
|
}
|
2020-09-21 14:31:31 +00:00
|
|
|
default:
|
2021-05-18 08:12:51 +00:00
|
|
|
err = fmt.Errorf("(%T) invalid object put stream part type %T", s, v)
|
2020-09-21 14:31:31 +00:00
|
|
|
}
|
|
|
|
|
2021-05-28 13:26:32 +00:00
|
|
|
if err != nil || !s.saveChunks {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
metaHdr := new(sessionV2.RequestMetaHeader)
|
|
|
|
meta := req.GetMetaHeader()
|
|
|
|
|
|
|
|
metaHdr.SetTTL(meta.GetTTL() - 1)
|
|
|
|
metaHdr.SetOrigin(meta)
|
|
|
|
req.SetMetaHeader(metaHdr)
|
|
|
|
|
2021-10-25 12:13:36 +00:00
|
|
|
// session token should not be used there
|
|
|
|
// otherwise remote nodes won't be able to
|
|
|
|
// process received part of split object
|
|
|
|
key, err := s.keyStorage.GetKey(nil)
|
2021-05-28 13:26:32 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return signature.SignServiceMessage(key, req)
|
2020-09-21 14:31:31 +00:00
|
|
|
}
|
|
|
|
|
2023-04-03 11:23:53 +00:00
|
|
|
func (s *streamer) CloseAndRecv(ctx context.Context) (*object.PutResponse, error) {
|
2023-04-12 14:01:29 +00:00
|
|
|
ctx, span := tracing.StartSpanFromContext(ctx, "putv2.streamer.CloseAndRecv")
|
|
|
|
defer span.End()
|
|
|
|
|
2021-06-07 09:29:38 +00:00
|
|
|
if s.saveChunks {
|
|
|
|
// check payload size correctness
|
|
|
|
if s.writtenPayload != s.payloadSz {
|
2024-08-30 09:09:14 +00:00
|
|
|
return nil, target.ErrWrongPayloadSize
|
2021-06-07 09:29:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-03 11:23:53 +00:00
|
|
|
resp, err := s.stream.Close(ctx)
|
2020-09-21 14:31:31 +00:00
|
|
|
if err != nil {
|
2021-05-18 08:12:51 +00:00
|
|
|
return nil, fmt.Errorf("(%T) could not object put stream: %w", s, err)
|
2020-09-21 14:31:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return fromPutResponse(resp), nil
|
|
|
|
}
|
2021-05-27 14:25:29 +00:00
|
|
|
|
2023-04-12 08:02:25 +00:00
|
|
|
func (s *streamer) relayRequest(ctx context.Context, info client.NodeInfo, c client.MultiAddressClient) error {
|
2023-04-12 14:01:29 +00:00
|
|
|
ctx, span := tracing.StartSpanFromContext(ctx, "putv2.streamer.relayRequest")
|
|
|
|
defer span.End()
|
|
|
|
|
2021-05-27 14:25:29 +00:00
|
|
|
// open stream
|
|
|
|
resp := new(object.PutResponse)
|
|
|
|
|
2021-09-28 06:02:02 +00:00
|
|
|
key := info.PublicKey()
|
|
|
|
|
2021-06-22 11:12:57 +00:00
|
|
|
var firstErr error
|
2021-05-27 14:25:29 +00:00
|
|
|
|
2021-09-28 05:36:41 +00:00
|
|
|
info.AddressGroup().IterateAddresses(func(addr network.Address) (stop bool) {
|
2023-04-12 14:01:29 +00:00
|
|
|
ctx, span := tracing.StartSpanFromContext(ctx, "putv2.streamer.iterateAddress",
|
|
|
|
trace.WithAttributes(
|
|
|
|
attribute.String("address", addr.String()),
|
|
|
|
))
|
|
|
|
defer span.End()
|
|
|
|
|
2021-06-22 11:12:57 +00:00
|
|
|
var err error
|
|
|
|
|
|
|
|
defer func() {
|
|
|
|
stop = err == nil
|
|
|
|
|
|
|
|
if stop || firstErr == nil {
|
|
|
|
firstErr = err
|
|
|
|
}
|
2021-05-27 14:25:29 +00:00
|
|
|
|
2021-06-22 11:12:57 +00:00
|
|
|
// would be nice to log otherwise
|
|
|
|
}()
|
|
|
|
|
|
|
|
var stream *rpc.PutRequestWriter
|
|
|
|
|
2023-04-18 09:04:59 +00:00
|
|
|
err = c.RawForAddress(ctx, addr, func(cli *rawclient.Client) error {
|
2023-04-12 08:02:25 +00:00
|
|
|
stream, err = rpc.PutObject(cli, resp, rawclient.WithContext(ctx))
|
2022-03-11 15:24:11 +00:00
|
|
|
return err
|
|
|
|
})
|
2021-06-22 11:12:57 +00:00
|
|
|
if err != nil {
|
|
|
|
err = fmt.Errorf("stream opening failed: %w", err)
|
|
|
|
return
|
2021-05-27 14:25:29 +00:00
|
|
|
}
|
|
|
|
|
2021-06-22 11:12:57 +00:00
|
|
|
// send init part
|
|
|
|
err = stream.Write(s.init)
|
|
|
|
if err != nil {
|
2022-12-19 14:47:28 +00:00
|
|
|
internalclient.ReportError(c, err)
|
2021-06-22 11:12:57 +00:00
|
|
|
err = fmt.Errorf("sending the initial message to stream failed: %w", err)
|
|
|
|
return
|
|
|
|
}
|
2021-05-27 14:25:29 +00:00
|
|
|
|
2021-06-22 11:12:57 +00:00
|
|
|
for i := range s.chunks {
|
|
|
|
if err = stream.Write(s.chunks[i]); err != nil {
|
2022-12-19 14:47:28 +00:00
|
|
|
internalclient.ReportError(c, err)
|
2021-06-22 11:12:57 +00:00
|
|
|
err = fmt.Errorf("sending the chunk %d failed: %w", i, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// close object stream and receive response from remote node
|
|
|
|
err = stream.Close()
|
|
|
|
if err != nil {
|
|
|
|
err = fmt.Errorf("closing the stream failed: %w", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-09-28 06:02:02 +00:00
|
|
|
// verify response key
|
|
|
|
if err = internal.VerifyResponseKeyV2(key, resp); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-06-22 11:12:57 +00:00
|
|
|
// verify response structure
|
|
|
|
err = signature.VerifyServiceMessage(resp)
|
|
|
|
if err != nil {
|
|
|
|
err = fmt.Errorf("response verification failed: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
})
|
|
|
|
|
|
|
|
return firstErr
|
2021-05-27 14:25:29 +00:00
|
|
|
}
|