github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/api/bucket.go (about) 1 // Package api provides native Go-based API/SDK over HTTP(S). 2 /* 3 * Copyright (c) 2018-2024, NVIDIA CORPORATION. All rights reserved. 4 */ 5 package api 6 7 import ( 8 "net/http" 9 "net/url" 10 "strconv" 11 12 "github.com/NVIDIA/aistore/api/apc" 13 "github.com/NVIDIA/aistore/cmn" 14 "github.com/NVIDIA/aistore/cmn/cos" 15 "github.com/NVIDIA/aistore/cmn/debug" 16 jsoniter "github.com/json-iterator/go" 17 ) 18 19 // SetBucketProps sets the properties of a bucket. 20 // Validation of the properties passed in is performed by AIStore Proxy. 21 func SetBucketProps(bp BaseParams, bck cmn.Bck, props *cmn.BpropsToSet) (string, error) { 22 b := cos.MustMarshal(apc.ActMsg{Action: apc.ActSetBprops, Value: props}) 23 return patchBprops(bp, bck, b) 24 } 25 26 // ResetBucketProps resets the properties of a bucket to the global configuration. 27 func ResetBucketProps(bp BaseParams, bck cmn.Bck) (string, error) { 28 b := cos.MustMarshal(apc.ActMsg{Action: apc.ActResetBprops}) 29 return patchBprops(bp, bck, b) 30 } 31 32 func patchBprops(bp BaseParams, bck cmn.Bck, body []byte) (xid string, err error) { 33 bp.Method = http.MethodPatch 34 path := apc.URLPathBuckets.Join(bck.Name) 35 reqParams := AllocRp() 36 { 37 reqParams.BaseParams = bp 38 reqParams.Path = path 39 reqParams.Body = body 40 reqParams.Header = http.Header{cos.HdrContentType: []string{cos.ContentJSON}} 41 reqParams.Query = bck.NewQuery() 42 } 43 _, err = reqParams.doReqStr(&xid) 44 FreeRp(reqParams) 45 return 46 } 47 48 // HEAD(bucket): apc.HdrBucketProps => cmn.Bprops{} and apc.HdrBucketInfo => BucketInfo{} 49 // 50 // Converts the string type fields returned from the HEAD request to their 51 // corresponding counterparts in the cmn.Bprops struct. 52 // 53 // By default, AIStore adds remote buckets to the cluster metadata on the fly. 54 // Remote bucket that was never accessed before just "shows up" when user performs 55 // HEAD, PUT, GET, SET-PROPS, and a variety of other operations. 56 // This is done only once (and after confirming the bucket's existence and accessibility) 57 // and doesn't require any action from the user. 58 // Use `dontAddRemote` to override the default behavior: as the name implies, setting 59 // `dontAddRemote = true` prevents AIS from adding remote bucket to the cluster's metadata. 60 func HeadBucket(bp BaseParams, bck cmn.Bck, dontAddRemote bool) (p *cmn.Bprops, err error) { 61 var ( 62 hdr http.Header 63 path = apc.URLPathBuckets.Join(bck.Name) 64 q = make(url.Values, 4) 65 status int 66 ) 67 if dontAddRemote { 68 q.Set(apc.QparamDontAddRemote, "true") 69 } 70 q = bck.AddToQuery(q) 71 72 bp.Method = http.MethodHead 73 reqParams := AllocRp() 74 { 75 reqParams.BaseParams = bp 76 reqParams.Path = path 77 reqParams.Query = q 78 } 79 if hdr, status, err = reqParams.doReqHdr(); err == nil { 80 p = &cmn.Bprops{} 81 err = jsoniter.Unmarshal([]byte(hdr.Get(apc.HdrBucketProps)), p) 82 } else { 83 err = hdr2msg(bck, status, err) 84 } 85 FreeRp(reqParams) 86 return 87 } 88 89 // fill-in herr message (HEAD response will never contain one) 90 func hdr2msg(bck cmn.Bck, status int, err error) error { 91 herr, ok := err.(*cmn.ErrHTTP) 92 if !ok { 93 return err 94 } 95 debug.Assert(herr.Status == status, herr.Status, " vs ", status) 96 97 quoted := "\"" + bck.Cname("") + "\"" 98 if !bck.IsQuery() && status == http.StatusNotFound { 99 herr.Message = "bucket " + quoted + " does not exist" 100 return herr 101 } 102 // common 103 herr.Message = "http error code '" + http.StatusText(status) + "'" 104 if status == http.StatusGone { 105 herr.Message += " (removed from the backend)" 106 } 107 herr.Message += ", bucket " 108 if bck.IsQuery() { 109 herr.Message += "query " 110 } 111 herr.Message += quoted 112 return herr 113 } 114 115 // CreateBucket sends request to create an AIS bucket with the given name and, 116 // optionally, specific non-default properties (via cmn.BpropsToSet). 117 // 118 // See also: 119 // - github.com/NVIDIA/aistore/blob/main/docs/bucket.md#default-bucket-properties 120 // - cmn.BpropsToSet (cmn/api.go) 121 // 122 // Bucket properties can be also changed at any time via SetBucketProps (above). 123 func CreateBucket(bp BaseParams, bck cmn.Bck, props *cmn.BpropsToSet, dontHeadRemote ...bool) error { 124 if err := bck.Validate(); err != nil { 125 return err 126 } 127 q := make(url.Values, 4) 128 if len(dontHeadRemote) > 0 && dontHeadRemote[0] { 129 q.Set(apc.QparamDontHeadRemote, "true") 130 } 131 bp.Method = http.MethodPost 132 reqParams := AllocRp() 133 { 134 reqParams.BaseParams = bp 135 reqParams.Path = apc.URLPathBuckets.Join(bck.Name) 136 reqParams.Body = cos.MustMarshal(apc.ActMsg{Action: apc.ActCreateBck, Value: props}) 137 reqParams.Header = http.Header{cos.HdrContentType: []string{cos.ContentJSON}} 138 reqParams.Query = bck.AddToQuery(q) 139 } 140 err := reqParams.DoRequest() 141 FreeRp(reqParams) 142 return err 143 } 144 145 // DestroyBucket sends request to remove an AIS bucket with the given name. 146 func DestroyBucket(bp BaseParams, bck cmn.Bck) error { 147 bp.Method = http.MethodDelete 148 reqParams := AllocRp() 149 { 150 reqParams.BaseParams = bp 151 reqParams.Path = apc.URLPathBuckets.Join(bck.Name) 152 reqParams.Body = cos.MustMarshal(apc.ActMsg{Action: apc.ActDestroyBck}) 153 reqParams.Header = http.Header{cos.HdrContentType: []string{cos.ContentJSON}} 154 reqParams.Query = bck.NewQuery() 155 } 156 err := reqParams.DoRequest() 157 FreeRp(reqParams) 158 return err 159 } 160 161 // CopyBucket copies existing `bckFrom` bucket to the destination `bckTo` thus, 162 // effectively, creating a copy of the `bckFrom`. 163 // - AIS will create `bckTo` on the fly but only if the destination bucket does not 164 // exist and _is_ provided by AIStore; 3rd party backend destination must exist - 165 // otherwise the copy operation won't be successful. 166 // - There are no limitations on copying buckets across Backend providers: 167 // you can copy AIS bucket to (or from) AWS bucket, and the latter to Google or Azure 168 // bucket, etc. 169 // - Copying multiple buckets to the same destination bucket is also permitted. 170 // 171 // `fltPresence` applies exclusively to remote `bckFrom` and is ignored if the source is ais:// 172 // The value is enum { apc.FltExists, apc.FltPresent, ... } - for complete enum, see api/apc/query.go 173 // Namely: 174 // * apc.FltExists - copy all objects, including those that are not (present) in AIS 175 // * apc.FltPresent - copy the current `bckFrom` content in the cluster (default) 176 // * apc.FltExistsOutside - copy only those remote objects that are not (present) in AIS 177 // 178 // msg.Prefix, if specified, applies always and regardless. 179 // 180 // Returns xaction ID if successful, an error otherwise. See also closely related api.ETLBucket 181 func CopyBucket(bp BaseParams, bckFrom, bckTo cmn.Bck, msg *apc.CopyBckMsg, fltPresence ...int) (xid string, err error) { 182 if err = bckTo.Validate(); err != nil { 183 return 184 } 185 q := bckFrom.NewQuery() 186 _ = bckTo.AddUnameToQuery(q, apc.QparamBckTo) 187 if len(fltPresence) > 0 { 188 q.Set(apc.QparamFltPresence, strconv.Itoa(fltPresence[0])) 189 } 190 bp.Method = http.MethodPost 191 reqParams := AllocRp() 192 { 193 reqParams.BaseParams = bp 194 reqParams.Path = apc.URLPathBuckets.Join(bckFrom.Name) 195 reqParams.Body = cos.MustMarshal(apc.ActMsg{Action: apc.ActCopyBck, Value: msg}) 196 reqParams.Header = http.Header{cos.HdrContentType: []string{cos.ContentJSON}} 197 reqParams.Query = q 198 } 199 _, err = reqParams.doReqStr(&xid) 200 FreeRp(reqParams) 201 return 202 } 203 204 // RenameBucket renames bckFrom as bckTo. 205 // Returns xaction ID if successful, an error otherwise. 206 func RenameBucket(bp BaseParams, bckFrom, bckTo cmn.Bck) (xid string, err error) { 207 if err = bckTo.Validate(); err != nil { 208 return 209 } 210 bp.Method = http.MethodPost 211 q := bckFrom.NewQuery() 212 _ = bckTo.AddUnameToQuery(q, apc.QparamBckTo) 213 reqParams := AllocRp() 214 { 215 reqParams.BaseParams = bp 216 reqParams.Path = apc.URLPathBuckets.Join(bckFrom.Name) 217 reqParams.Body = cos.MustMarshal(apc.ActMsg{Action: apc.ActMoveBck}) 218 reqParams.Header = http.Header{cos.HdrContentType: []string{cos.ContentJSON}} 219 reqParams.Query = q 220 } 221 _, err = reqParams.doReqStr(&xid) 222 FreeRp(reqParams) 223 return 224 } 225 226 // EvictRemoteBucket sends request to evict an entire remote bucket from the AIStore 227 // - keepMD: evict objects but keep bucket metadata 228 func EvictRemoteBucket(bp BaseParams, bck cmn.Bck, keepMD bool) error { 229 var q url.Values 230 if keepMD { 231 q = url.Values{apc.QparamKeepRemote: []string{"true"}} 232 } 233 234 bp.Method = http.MethodDelete 235 reqParams := AllocRp() 236 { 237 reqParams.BaseParams = bp 238 reqParams.Path = apc.URLPathBuckets.Join(bck.Name) 239 reqParams.Body = cos.MustMarshal(apc.ActMsg{Action: apc.ActEvictRemoteBck}) 240 reqParams.Header = http.Header{cos.HdrContentType: []string{cos.ContentJSON}} 241 reqParams.Query = bck.AddToQuery(q) 242 } 243 err := reqParams.DoRequest() 244 FreeRp(reqParams) 245 return err 246 } 247 248 // MakeNCopies starts an extended action (xaction) to bring a given bucket to a 249 // certain redundancy level (num copies). 250 // Returns xaction ID if successful, an error otherwise. 251 func MakeNCopies(bp BaseParams, bck cmn.Bck, copies int) (xid string, err error) { 252 bp.Method = http.MethodPost 253 reqParams := AllocRp() 254 { 255 reqParams.BaseParams = bp 256 reqParams.Path = apc.URLPathBuckets.Join(bck.Name) 257 reqParams.Body = cos.MustMarshal(apc.ActMsg{Action: apc.ActMakeNCopies, Value: copies}) 258 reqParams.Header = http.Header{cos.HdrContentType: []string{cos.ContentJSON}} 259 reqParams.Query = bck.NewQuery() 260 } 261 _, err = reqParams.doReqStr(&xid) 262 FreeRp(reqParams) 263 return 264 } 265 266 // Erasure-code entire `bck` bucket at a given `data`:`parity` redundancy. 267 // The operation requires at least (`data + `parity` + 1) storage targets in the cluster. 268 // Returns xaction ID if successful, an error otherwise. 269 func ECEncodeBucket(bp BaseParams, bck cmn.Bck, data, parity int) (xid string, err error) { 270 bp.Method = http.MethodPost 271 // Without `string` conversion it makes base64 from []byte in `Body`. 272 ecConf := string(cos.MustMarshal(&cmn.ECConfToSet{ 273 DataSlices: &data, 274 ParitySlices: &parity, 275 Enabled: apc.Ptr(true), 276 })) 277 reqParams := AllocRp() 278 { 279 reqParams.BaseParams = bp 280 reqParams.Path = apc.URLPathBuckets.Join(bck.Name) 281 reqParams.Body = cos.MustMarshal(apc.ActMsg{Action: apc.ActECEncode, Value: ecConf}) 282 reqParams.Header = http.Header{cos.HdrContentType: []string{cos.ContentJSON}} 283 reqParams.Query = bck.NewQuery() 284 } 285 _, err = reqParams.doReqStr(&xid) 286 FreeRp(reqParams) 287 return 288 }