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  }