github.com/swiftstack/proxyfs@v0.0.0-20201223034610-5434d919416e/swiftclient/container.go (about) 1 // Swift Container-specific API access implementation 2 3 package swiftclient 4 5 import ( 6 "fmt" 7 "net/url" 8 9 "github.com/swiftstack/ProxyFS/blunder" 10 "github.com/swiftstack/ProxyFS/evtlog" 11 "github.com/swiftstack/ProxyFS/logger" 12 "github.com/swiftstack/ProxyFS/stats" 13 ) 14 15 func containerDeleteWithRetry(accountName string, containerName string) (err error) { 16 // request is a function that, through the miracle of closure, calls 17 // containerDelete() with the paramaters passed to this function, stashes 18 // the relevant return values into the local variables of this function, 19 // and then returns err and whether it is retriable to RequestWithRetry() 20 request := func() (bool, error) { 21 var err error 22 err = containerDelete(accountName, containerName) 23 return true, err 24 } 25 26 var ( 27 retryObj *RetryCtrl = NewRetryCtrl(globals.retryLimit, globals.retryDelay, globals.retryExpBackoff) 28 opname string = fmt.Sprintf("swiftclient.containerDelete(\"%v/%v\")", accountName, containerName) 29 30 statnm requestStatistics = requestStatistics{ 31 retryCnt: &stats.SwiftContainerDeleteRetryOps, 32 retrySuccessCnt: &stats.SwiftContainerDeleteRetrySuccessOps, 33 clientRequestTime: &globals.ContainerDeleteUsec, 34 clientFailureCnt: &globals.ContainerDeleteFailure, 35 swiftRequestTime: &globals.SwiftContainerDeleteUsec, 36 swiftRetryOps: &globals.SwiftContainerDeleteRetryOps, 37 } 38 ) 39 err = retryObj.RequestWithRetry(request, &opname, &statnm) 40 return err 41 } 42 43 func containerDelete(accountName string, containerName string) (err error) { 44 var ( 45 connection *connectionStruct 46 fsErr blunder.FsError 47 headers map[string][]string 48 httpPayload string 49 httpStatus int 50 isError bool 51 ) 52 53 connection, err = acquireNonChunkedConnection() 54 if err != nil { 55 // acquireNonChunkedConnection()/openConnection() logged a warning 56 return 57 } 58 59 err = writeHTTPRequestLineAndHeaders(connection.tcpConn, "DELETE", "/"+swiftVersion+"/"+pathEscape(accountName, containerName), nil) 60 if nil != err { 61 releaseNonChunkedConnection(connection, false) 62 err = blunder.AddError(err, blunder.BadHTTPDeleteError) 63 logger.WarnfWithError(err, "swiftclient.containerDelete(\"%v/%v\") got writeHTTPRequestLineAndHeaders() error", accountName, containerName) 64 return 65 } 66 67 httpStatus, headers, err = readHTTPStatusAndHeaders(connection.tcpConn) 68 if nil != err { 69 releaseNonChunkedConnection(connection, false) 70 err = blunder.AddError(err, blunder.BadHTTPDeleteError) 71 logger.WarnfWithError(err, "swiftclient.containerDelete(\"%v/%v\") got readHTTPStatusAndHeaders() error", accountName, containerName) 72 return 73 } 74 evtlog.Record(evtlog.FormatContainerDelete, accountName, containerName, uint32(httpStatus)) 75 isError, fsErr = httpStatusIsError(httpStatus) 76 if isError { 77 httpPayload, _ = readHTTPPayloadAsString(connection.tcpConn, headers) 78 releaseNonChunkedConnection(connection, false) 79 err = blunder.NewError(fsErr, "DELETE %s/%s returned HTTP StatusCode %d Payload %s", accountName, containerName, httpStatus, httpPayload) 80 err = blunder.AddHTTPCode(err, httpStatus) 81 logger.WarnfWithError(err, "swiftclient.containerDelete(\"%v/%v\") got readHTTPStatusAndHeaders() bad status", accountName, containerName) 82 return 83 } 84 85 releaseNonChunkedConnection(connection, parseConnection(headers)) 86 87 stats.IncrementOperations(&stats.SwiftContainerDeleteOps) 88 89 return 90 } 91 92 func containerGetWithRetry(accountName string, containerName string) (headers map[string][]string, objectList []string, err error) { 93 // request is a function that, through the miracle of closure, calls 94 // containerGet() with the paramaters passed to this function, stashes 95 // the relevant return values into the local variables of this function, 96 // and then returns err and whether it is retriable to RequestWithRetry() 97 var ( 98 connection *connectionStruct 99 marker string 100 opname string 101 retryObj *RetryCtrl 102 statnm requestStatistics 103 toAddHeaders map[string][]string 104 toAddObjectList []string 105 ) 106 107 retryObj = NewRetryCtrl(globals.retryLimit, globals.retryDelay, globals.retryExpBackoff) 108 statnm = requestStatistics{ 109 retryCnt: &stats.SwiftContainerGetRetryOps, 110 retrySuccessCnt: &stats.SwiftContainerGetRetrySuccessOps, 111 clientRequestTime: &globals.ContainerGetUsec, 112 clientFailureCnt: &globals.ContainerGetFailure, 113 swiftRequestTime: &globals.SwiftContainerGetUsec, 114 swiftRetryOps: &globals.SwiftContainerGetRetryOps, 115 } 116 117 request := func() (bool, error) { 118 var err error 119 toAddHeaders, toAddObjectList, err = containerGet(connection, accountName, containerName, marker) 120 return true, err 121 } 122 123 headers = make(map[string][]string) 124 objectList = make([]string, 0) 125 126 connection, err = acquireNonChunkedConnection() 127 if err != nil { 128 // acquireNonChunkedConnection()/openConnection() logged a warning 129 return 130 } 131 132 marker = "" 133 134 for { 135 opname = fmt.Sprintf("swiftclient.containerGet(,\"%v\",\"%v\",\"%v\")", accountName, containerName, marker) 136 137 err = retryObj.RequestWithRetry(request, &opname, &statnm) 138 139 if nil == err { 140 mergeHeadersAndList(headers, &objectList, toAddHeaders, &toAddObjectList) 141 142 if 0 == len(toAddObjectList) { 143 releaseNonChunkedConnection(connection, parseConnection(headers)) 144 145 break 146 } else { 147 marker = toAddObjectList[len(toAddObjectList)-1] 148 } 149 } else { 150 releaseNonChunkedConnection(connection, false) 151 152 break 153 } 154 } 155 156 stats.IncrementOperations(&stats.SwiftContainerGetOps) 157 158 return 159 } 160 161 func containerGet(connection *connectionStruct, accountName string, containerName string, marker string) (headers map[string][]string, objectList []string, err error) { 162 var ( 163 fsErr blunder.FsError 164 httpPayload string 165 httpStatus int 166 isError bool 167 ) 168 169 err = writeHTTPRequestLineAndHeaders(connection.tcpConn, "GET", "/"+swiftVersion+"/"+pathEscape(accountName, containerName)+"?marker="+url.QueryEscape(marker), nil) 170 if nil != err { 171 err = blunder.AddError(err, blunder.BadHTTPGetError) 172 logger.WarnfWithError(err, "swiftclient.containerGet(,\"%v\",\"%v\",\"%v\") got writeHTTPRequestLineAndHeaders() error", accountName, containerName, marker) 173 return 174 } 175 176 httpStatus, headers, err = readHTTPStatusAndHeaders(connection.tcpConn) 177 if nil != err { 178 err = blunder.AddError(err, blunder.BadHTTPGetError) 179 logger.WarnfWithError(err, "swiftclient.containerGet(,\"%v\",\"%v\",\"%v\") got readHTTPStatusAndHeaders() error", accountName, containerName, marker) 180 return 181 } 182 evtlog.Record(evtlog.FormatContainerGet, accountName, containerName, uint32(httpStatus)) 183 isError, fsErr = httpStatusIsError(httpStatus) 184 if isError { 185 httpPayload, _ = readHTTPPayloadAsString(connection.tcpConn, headers) 186 err = blunder.NewError(fsErr, "GET %s/%s returned HTTP StatusCode %d Payload %s", accountName, containerName, httpStatus, httpPayload) 187 err = blunder.AddHTTPCode(err, httpStatus) 188 logger.WarnfWithError(err, "swiftclient.containerGet(,\"%v\",\"%v\",\"%v\") got readHTTPStatusAndHeaders() bad status", accountName, containerName, marker) 189 return 190 } 191 192 objectList, err = readHTTPPayloadLines(connection.tcpConn, headers) 193 if nil != err { 194 err = blunder.AddError(err, blunder.BadHTTPGetError) 195 logger.WarnfWithError(err, "swiftclient.containerGet(,\"%v\",\"%v\",\"%v\") got readHTTPPayloadLines() error", accountName, containerName, marker) 196 return 197 } 198 199 return 200 } 201 202 func containerHeadWithRetry(accountName string, containerName string) (map[string][]string, error) { 203 // request is a function that, through the miracle of closure, calls 204 // containerHead() with the paramaters passed to this function, stashes 205 // the relevant return values into the local variables of this function, 206 // and then returns err and whether it is retriable to RequestWithRetry() 207 var ( 208 headers map[string][]string 209 err error 210 ) 211 request := func() (bool, error) { 212 var err error 213 headers, err = containerHead(accountName, containerName) 214 return true, err 215 } 216 217 var ( 218 retryObj *RetryCtrl = NewRetryCtrl(globals.retryLimit, globals.retryDelay, globals.retryExpBackoff) 219 opname string = fmt.Sprintf("swiftclient.containerHead(\"%v/%v\")", accountName, containerName) 220 221 statnm requestStatistics = requestStatistics{ 222 retryCnt: &stats.SwiftContainerHeadRetryOps, 223 retrySuccessCnt: &stats.SwiftContainerHeadRetrySuccessOps, 224 clientRequestTime: &globals.ContainerHeadUsec, 225 clientFailureCnt: &globals.ContainerHeadFailure, 226 swiftRequestTime: &globals.SwiftContainerHeadUsec, 227 swiftRetryOps: &globals.SwiftContainerHeadRetryOps, 228 } 229 ) 230 err = retryObj.RequestWithRetry(request, &opname, &statnm) 231 return headers, err 232 } 233 234 func containerHead(accountName string, containerName string) (headers map[string][]string, err error) { 235 var ( 236 connection *connectionStruct 237 fsErr blunder.FsError 238 httpPayload string 239 httpStatus int 240 isError bool 241 ) 242 243 connection, err = acquireNonChunkedConnection() 244 if err != nil { 245 // acquireNonChunkedConnection()/openConnection() logged a warning 246 return 247 } 248 249 err = writeHTTPRequestLineAndHeaders(connection.tcpConn, "HEAD", "/"+swiftVersion+"/"+pathEscape(accountName, containerName), nil) 250 if nil != err { 251 releaseNonChunkedConnection(connection, false) 252 err = blunder.AddError(err, blunder.BadHTTPHeadError) 253 logger.WarnfWithError(err, "swiftclient.containerHead(\"%v/%v\") got writeHTTPRequestLineAndHeaders() error", accountName, containerName) 254 return 255 } 256 257 httpStatus, headers, err = readHTTPStatusAndHeaders(connection.tcpConn) 258 if nil != err { 259 releaseNonChunkedConnection(connection, false) 260 err = blunder.AddError(err, blunder.BadHTTPHeadError) 261 logger.WarnfWithError(err, "swiftclient.containerHead(\"%v/%v\") got readHTTPStatusAndHeaders() error", accountName, containerName) 262 return 263 } 264 evtlog.Record(evtlog.FormatContainerHead, accountName, containerName, uint32(httpStatus)) 265 isError, fsErr = httpStatusIsError(httpStatus) 266 if isError { 267 httpPayload, _ = readHTTPPayloadAsString(connection.tcpConn, headers) 268 releaseNonChunkedConnection(connection, false) 269 err = blunder.NewError(fsErr, "HEAD %s/%s returned HTTP StatusCode %d Payload %s", accountName, containerName, httpStatus, httpPayload) 270 err = blunder.AddHTTPCode(err, httpStatus) 271 logger.WarnfWithError(err, "swiftclient.containerHead(\"%v/%v\") got readHTTPStatusAndHeaders() bad status", accountName, containerName) 272 return 273 } 274 275 releaseNonChunkedConnection(connection, parseConnection(headers)) 276 277 stats.IncrementOperations(&stats.SwiftContainerHeadOps) 278 279 return 280 } 281 282 func containerPostWithRetry(accountName string, containerName string, requestHeaders map[string][]string) (err error) { 283 // request is a function that, through the miracle of closure, calls 284 // containerPost() with the paramaters passed to this function, stashes 285 // the relevant return values into the local variables of this function, 286 // and then returns err and whether it is retriable to RequestWithRetry() 287 request := func() (bool, error) { 288 var err error 289 err = containerPost(accountName, containerName, requestHeaders) 290 return true, err 291 } 292 293 var ( 294 retryObj *RetryCtrl = NewRetryCtrl(globals.retryLimit, globals.retryDelay, globals.retryExpBackoff) 295 opname string = fmt.Sprintf("swiftclient.containerPost(\"%v/%v\")", accountName, containerName) 296 297 statnm requestStatistics = requestStatistics{ 298 retryCnt: &stats.SwiftContainerPostRetryOps, 299 retrySuccessCnt: &stats.SwiftContainerPostRetrySuccessOps, 300 clientRequestTime: &globals.ContainerPostUsec, 301 clientFailureCnt: &globals.ContainerPostFailure, 302 swiftRequestTime: &globals.SwiftContainerPostUsec, 303 swiftRetryOps: &globals.SwiftContainerPostRetryOps, 304 } 305 ) 306 err = retryObj.RequestWithRetry(request, &opname, &statnm) 307 return err 308 } 309 310 func containerPost(accountName string, containerName string, requestHeaders map[string][]string) (err error) { 311 var ( 312 connection *connectionStruct 313 contentLength int 314 fsErr blunder.FsError 315 httpPayload string 316 httpStatus int 317 isError bool 318 responseHeaders map[string][]string 319 ) 320 321 connection, err = acquireNonChunkedConnection() 322 if err != nil { 323 // acquireNonChunkedConnection()/openConnection() logged a warning 324 return 325 } 326 327 requestHeaders["Content-Length"] = []string{"0"} 328 329 err = writeHTTPRequestLineAndHeaders(connection.tcpConn, "POST", "/"+swiftVersion+"/"+pathEscape(accountName, containerName), requestHeaders) 330 if nil != err { 331 releaseNonChunkedConnection(connection, false) 332 err = blunder.AddError(err, blunder.BadHTTPPutError) 333 logger.WarnfWithError(err, "swiftclient.containerPost(\"%v/%v\") got writeHTTPRequestLineAndHeaders() error", accountName, containerName) 334 return 335 } 336 337 httpStatus, responseHeaders, err = readHTTPStatusAndHeaders(connection.tcpConn) 338 if nil != err { 339 releaseNonChunkedConnection(connection, false) 340 err = blunder.AddError(err, blunder.BadHTTPPutError) 341 logger.WarnfWithError(err, "swiftclient.containerPost(\"%v/%v\") got readHTTPStatusAndHeaders() error", accountName, containerName) 342 return 343 } 344 evtlog.Record(evtlog.FormatContainerPost, accountName, containerName, uint32(httpStatus)) 345 isError, fsErr = httpStatusIsError(httpStatus) 346 if isError { 347 httpPayload, _ = readHTTPPayloadAsString(connection.tcpConn, responseHeaders) 348 releaseNonChunkedConnection(connection, false) 349 err = blunder.NewError(fsErr, "POST %s/%s returned HTTP StatusCode %d Payload %s", accountName, containerName, httpStatus, httpPayload) 350 err = blunder.AddHTTPCode(err, httpStatus) 351 logger.WarnfWithError(err, "swiftclient.containerPost(\"%v/%v\") got readHTTPStatusAndHeaders() bad status", accountName, containerName) 352 return 353 } 354 contentLength, err = parseContentLength(responseHeaders) 355 if nil != err { 356 releaseNonChunkedConnection(connection, false) 357 err = blunder.AddError(err, blunder.BadHTTPPutError) 358 logger.WarnfWithError(err, "swiftclient.containerPost(\"%v/%v\") got parseContentLength() error", accountName, containerName) 359 return 360 } 361 if 0 < contentLength { 362 _, err = readBytesFromTCPConn(connection.tcpConn, contentLength) 363 if nil != err { 364 releaseNonChunkedConnection(connection, false) 365 err = blunder.AddError(err, blunder.BadHTTPPutError) 366 logger.WarnfWithError(err, "swiftclient.containerPost(\"%v/%v\") got readBytesFromTCPConn() error", accountName, containerName) 367 return 368 } 369 } 370 371 releaseNonChunkedConnection(connection, parseConnection(responseHeaders)) 372 373 stats.IncrementOperations(&stats.SwiftContainerPostOps) 374 375 return 376 } 377 378 func containerPutWithRetry(accountName string, containerName string, requestHeaders map[string][]string) (err error) { 379 // request is a function that, through the miracle of closure, calls 380 // containerPut() with the paramaters passed to this function, stashes 381 // the relevant return values into the local variables of this function, 382 // and then returns err and whether it is retriable to RequestWithRetry() 383 request := func() (bool, error) { 384 var err error 385 err = containerPut(accountName, containerName, requestHeaders) 386 return true, err 387 } 388 389 var ( 390 retryObj *RetryCtrl = NewRetryCtrl(globals.retryLimit, globals.retryDelay, globals.retryExpBackoff) 391 opname string = fmt.Sprintf("swiftclient.containerPut(\"%v/%v\")", accountName, containerName) 392 393 statnm requestStatistics = requestStatistics{ 394 retryCnt: &stats.SwiftContainerPutRetryOps, 395 retrySuccessCnt: &stats.SwiftContainerPutRetrySuccessOps, 396 clientRequestTime: &globals.ContainerPutUsec, 397 clientFailureCnt: &globals.ContainerPutFailure, 398 swiftRequestTime: &globals.SwiftContainerPutUsec, 399 swiftRetryOps: &globals.SwiftContainerPutRetryOps, 400 } 401 ) 402 err = retryObj.RequestWithRetry(request, &opname, &statnm) 403 return err 404 } 405 406 func containerPut(accountName string, containerName string, requestHeaders map[string][]string) (err error) { 407 var ( 408 connection *connectionStruct 409 contentLength int 410 fsErr blunder.FsError 411 httpPayload string 412 httpStatus int 413 isError bool 414 responseHeaders map[string][]string 415 ) 416 417 connection, err = acquireNonChunkedConnection() 418 if err != nil { 419 // acquireNonChunkedConnection()/openConnection() logged a warning 420 return 421 } 422 423 requestHeaders["Content-Length"] = []string{"0"} 424 425 err = writeHTTPRequestLineAndHeaders(connection.tcpConn, "PUT", "/"+swiftVersion+"/"+pathEscape(accountName, containerName), requestHeaders) 426 if nil != err { 427 releaseNonChunkedConnection(connection, false) 428 err = blunder.AddError(err, blunder.BadHTTPPutError) 429 logger.WarnfWithError(err, "swiftclient.containerPut(\"%v/%v\") got writeHTTPRequestLineAndHeaders() error", accountName, containerName) 430 return 431 } 432 433 httpStatus, responseHeaders, err = readHTTPStatusAndHeaders(connection.tcpConn) 434 if nil != err { 435 releaseNonChunkedConnection(connection, false) 436 err = blunder.AddError(err, blunder.BadHTTPPutError) 437 logger.WarnfWithError(err, "swiftclient.containerPut(\"%v/%v\") got readHTTPStatusAndHeaders() error", accountName, containerName) 438 return 439 } 440 evtlog.Record(evtlog.FormatContainerPut, accountName, containerName, uint32(httpStatus)) 441 isError, fsErr = httpStatusIsError(httpStatus) 442 if isError { 443 httpPayload, _ = readHTTPPayloadAsString(connection.tcpConn, responseHeaders) 444 releaseNonChunkedConnection(connection, false) 445 err = blunder.NewError(fsErr, "PUT %s/%s returned HTTP StatusCode %d Payload %s", accountName, containerName, httpStatus, httpPayload) 446 err = blunder.AddHTTPCode(err, httpStatus) 447 logger.WarnfWithError(err, "swiftclient.containerPut(\"%v/%v\") got readHTTPStatusAndHeaders() bad status", accountName, containerName) 448 return 449 } 450 contentLength, err = parseContentLength(responseHeaders) 451 if nil != err { 452 releaseNonChunkedConnection(connection, false) 453 err = blunder.AddError(err, blunder.BadHTTPPutError) 454 logger.WarnfWithError(err, "swiftclient.containerPut(\"%v/%v\") got parseContentLength() error", accountName, containerName) 455 return 456 } 457 if 0 < contentLength { 458 _, err = readBytesFromTCPConn(connection.tcpConn, contentLength) 459 if nil != err { 460 releaseNonChunkedConnection(connection, false) 461 err = blunder.AddError(err, blunder.BadHTTPPutError) 462 logger.WarnfWithError(err, "swiftclient.containerPut(\"%v/%v\") got readBytesFromTCPConn() error", accountName, containerName) 463 return 464 } 465 } 466 467 releaseNonChunkedConnection(connection, parseConnection(responseHeaders)) 468 469 stats.IncrementOperations(&stats.SwiftContainerPutOps) 470 471 return 472 }