git.frostfs.info/TrueCloudLab/frostfs-sdk-go@v0.0.0-20241022124111-5361f0ecebd3/client/container_put.go (about) 1 package client 2 3 import ( 4 "context" 5 "fmt" 6 7 v2container "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container" 8 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" 9 rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc" 10 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client" 11 v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session" 12 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature" 13 apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status" 14 "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container" 15 cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" 16 frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto" 17 "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session" 18 ) 19 20 // PrmContainerPut groups parameters of ContainerPut operation. 21 type PrmContainerPut struct { 22 // FrostFS request X-Headers 23 XHeaders []string 24 25 Container *container.Container 26 27 Session *session.Container 28 } 29 30 // SetContainer sets structured information about new FrostFS container. 31 // Required parameter. 32 // 33 // Deprecated: Use PrmContainerPut.Container instead. 34 func (x *PrmContainerPut) SetContainer(cnr container.Container) { 35 x.Container = &cnr 36 } 37 38 // WithinSession specifies session within which container should be saved. 39 // 40 // Creator of the session acquires the authorship of the request. This affects 41 // the execution of an operation (e.g. access control). 42 // 43 // Session is optional, if set the following requirements apply: 44 // - session operation MUST be session.VerbContainerPut (ForVerb) 45 // - token MUST be signed using private key of the owner of the container to be saved 46 // 47 // Deprecated: Use PrmContainerPut.Session instead. 48 func (x *PrmContainerPut) WithinSession(s session.Container) { 49 x.Session = &s 50 } 51 52 func (x *PrmContainerPut) buildRequest(c *Client) (*v2container.PutRequest, error) { 53 if x.Container == nil { 54 return nil, errorMissingContainer 55 } 56 57 if len(x.XHeaders)%2 != 0 { 58 return nil, errorInvalidXHeaders 59 } 60 61 // TODO: check private key is set before forming the request 62 var cnr v2container.Container 63 x.Container.WriteToV2(&cnr) 64 65 var sig frostfscrypto.Signature 66 67 err := container.CalculateSignature(&sig, *x.Container, c.prm.Key) 68 if err != nil { 69 return nil, fmt.Errorf("calculate container signature: %w", err) 70 } 71 72 var sigv2 refs.Signature 73 sig.WriteToV2(&sigv2) 74 75 reqBody := new(v2container.PutRequestBody) 76 reqBody.SetContainer(&cnr) 77 reqBody.SetSignature(&sigv2) 78 79 var meta v2session.RequestMetaHeader 80 writeXHeadersToMeta(x.XHeaders, &meta) 81 82 if x.Session != nil { 83 var tokv2 v2session.Token 84 x.Session.WriteToV2(&tokv2) 85 86 meta.SetSessionToken(&tokv2) 87 } 88 89 var req v2container.PutRequest 90 req.SetBody(reqBody) 91 c.prepareRequest(&req, &meta) 92 return &req, nil 93 } 94 95 // ResContainerPut groups resulting values of ContainerPut operation. 96 type ResContainerPut struct { 97 statusRes 98 99 id cid.ID 100 } 101 102 // ID returns identifier of the container declared to be stored in the system. 103 // Used as a link to information about the container (in particular, you can 104 // asynchronously check if the save was successful). 105 func (x ResContainerPut) ID() cid.ID { 106 return x.id 107 } 108 109 // ContainerPut sends request to save container in FrostFS. 110 // 111 // Exactly one return value is non-nil. By default, server status is returned in res structure. 112 // Any client's internal or transport errors are returned as `error`. 113 // If PrmInit.DisableFrostFSFailuresResolution has been called, unsuccessful 114 // FrostFS status codes are included in the returned result structure, 115 // otherwise, are also returned as `error`. 116 // 117 // Operation is asynchronous and no guaranteed even in the absence of errors. 118 // The required time is also not predictable. 119 // 120 // Success can be verified by reading by identifier (see ResContainerPut.ID). 121 // 122 // Returns an error if parameters are set incorrectly (see PrmContainerPut docs). 123 // Context is required and must not be nil. It is used for network communication. 124 // 125 // Return statuses: 126 // - global (see Client docs). 127 // 128 // nolint: funlen 129 func (c *Client) ContainerPut(ctx context.Context, prm PrmContainerPut) (*ResContainerPut, error) { 130 req, err := prm.buildRequest(c) 131 if err != nil { 132 return nil, err 133 } 134 135 if err := signature.SignServiceMessage(&c.prm.Key, req); err != nil { 136 return nil, fmt.Errorf("sign request: %w", err) 137 } 138 139 resp, err := rpcapi.PutContainer(&c.c, req, client.WithContext(ctx)) 140 if err != nil { 141 return nil, err 142 } 143 144 var res ResContainerPut 145 res.st, err = c.processResponse(resp) 146 if err != nil || !apistatus.IsSuccessful(res.st) { 147 return &res, err 148 } 149 150 const fieldCnrID = "container ID" 151 152 cidV2 := resp.GetBody().GetContainerID() 153 if cidV2 == nil { 154 return &res, newErrMissingResponseField(fieldCnrID) 155 } 156 if err := res.id.ReadFromV2(*cidV2); err != nil { 157 return &res, newErrInvalidResponseField(fieldCnrID, err) 158 } 159 return &res, nil 160 }