package client import ( "context" "crypto/ecdsa" "fmt" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/acl" v2object "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/object" rpcapi "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/rpc" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/rpc/client" v2session "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/session" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/signature" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session" ) // PrmObjectPutSingle groups parameters of PutSingle operation. type PrmObjectPutSingle struct { XHeaders []string BearerToken *bearer.Token Session *session.Object Local bool CopiesNumber []uint32 Object *object.Object Key *ecdsa.PrivateKey } // SetCopiesNumber sets ordered list of minimal required object copies numbers // per placement vector. List's length MUST equal container's placement vector number, // otherwise request will fail. // // Deprecated: Use PrmObjectPutSingle.CopiesNumber instead. func (prm *PrmObjectPutSingle) SetCopiesNumber(v []uint32) { prm.CopiesNumber = v } // UseKey specifies private key to sign the requests. // If key is not provided, then Client default key is used. // // Deprecated: Use PrmObjectPutSingle.Key instead. func (prm *PrmObjectPutSingle) UseKey(key *ecdsa.PrivateKey) { prm.Key = key } // WithBearerToken attaches bearer token to be used for the operation. // Should be called once before any writing steps. // // Deprecated: Use PrmObjectPutSingle.BearerToken instead. func (prm *PrmObjectPutSingle) WithBearerToken(t bearer.Token) { prm.BearerToken = &t } // WithinSession specifies session within which object should be stored. // Should be called once before any writing steps. // // Deprecated: Use PrmObjectPutSingle.Session instead. func (prm *PrmObjectPutSingle) WithinSession(t session.Object) { prm.Session = &t } // ExecuteLocal tells the server to execute the operation locally. // // Deprecated: Use PrmObjectPutSingle.Local instead. func (prm *PrmObjectPutSingle) ExecuteLocal() { prm.Local = true } // WithXHeaders specifies list of extended headers (string key-value pairs) // to be attached to the request. Must have an even length. // // Slice must not be mutated until the operation completes. // // Deprecated: Use PrmObjectPutSingle.XHeaders instead. func (prm *PrmObjectPutSingle) WithXHeaders(hs ...string) { prm.XHeaders = hs } // SetObject specifies prepared object to put. // // Deprecated: Use PrmObjectPutSingle.Object instead. func (prm *PrmObjectPutSingle) SetObject(o *v2object.Object) { prm.Object = object.NewFromV2(o) } // ResObjectPutSingle groups resulting values of PutSingle operation. type ResObjectPutSingle struct { statusRes epoch uint64 } // Epoch returns creation epoch of the saved object. func (r *ResObjectPutSingle) Epoch() uint64 { return r.epoch } func (prm *PrmObjectPutSingle) buildRequest(c *Client) (*v2object.PutSingleRequest, error) { if len(prm.XHeaders)%2 != 0 { return nil, errorInvalidXHeaders } body := new(v2object.PutSingleRequestBody) body.SetCopiesNumber(prm.CopiesNumber) body.SetObject(prm.Object.ToV2()) meta := new(v2session.RequestMetaHeader) writeXHeadersToMeta(prm.XHeaders, meta) if prm.BearerToken != nil { v2BearerToken := new(acl.BearerToken) prm.BearerToken.WriteToV2(v2BearerToken) meta.SetBearerToken(v2BearerToken) } if prm.Session != nil { v2SessionToken := new(v2session.Token) prm.Session.WriteToV2(v2SessionToken) meta.SetSessionToken(v2SessionToken) } if prm.Local { meta.SetTTL(1) } req := &v2object.PutSingleRequest{} req.SetBody(body) c.prepareRequest(req, meta) return req, nil } // ObjectPutSingle writes prepared object to FrostFS. // Object must have payload, also containerID, objectID, ownerID, payload hash, payload length of an object must be set. // Exactly one return value is non-nil. By default, server status is returned in res structure. // Any client's internal or transport errors are returned as Go built-in error. // If Client is tuned to resolve FrostFS API statuses, then FrostFS failures // codes are returned as error. func (c *Client) ObjectPutSingle(ctx context.Context, prm PrmObjectPutSingle) (*ResObjectPutSingle, error) { req, err := prm.buildRequest(c) if err != nil { return nil, err } key := &c.prm.Key if prm.Key != nil { key = prm.Key } err = signature.SignServiceMessage(key, req) if err != nil { return nil, fmt.Errorf("sign request: %w", err) } resp, err := rpcapi.PutSingleObject(&c.c, req, client.WithContext(ctx)) if err != nil { return nil, err } var res ResObjectPutSingle res.st, err = c.processResponse(resp) if err != nil { return &res, err } res.epoch = resp.GetMetaHeader().GetEpoch() return &res, nil }