storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/gateway-common.go (about)

     1  /*
     2   * MinIO Cloud Storage, (C) 2017-2019 MinIO, Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package cmd
    18  
    19  import (
    20  	"context"
    21  	"net"
    22  	"net/http"
    23  	"strings"
    24  	"time"
    25  
    26  	"storj.io/minio/cmd/config"
    27  	xhttp "storj.io/minio/cmd/http"
    28  	"storj.io/minio/cmd/logger"
    29  	"storj.io/minio/pkg/env"
    30  	"storj.io/minio/pkg/hash"
    31  	xnet "storj.io/minio/pkg/net"
    32  
    33  	minio "github.com/minio/minio-go/v7"
    34  )
    35  
    36  var (
    37  	// CanonicalizeETag provides canonicalizeETag function alias.
    38  	CanonicalizeETag = canonicalizeETag
    39  
    40  	// MustGetUUID function alias.
    41  	MustGetUUID = mustGetUUID
    42  
    43  	// CleanMetadataKeys provides cleanMetadataKeys function alias.
    44  	CleanMetadataKeys = cleanMetadataKeys
    45  
    46  	// PathJoin function alias.
    47  	PathJoin = pathJoin
    48  
    49  	// ListObjects function alias.
    50  	ListObjects = listObjects
    51  
    52  	// FilterListEntries function alias.
    53  	FilterListEntries = filterListEntries
    54  
    55  	// IsStringEqual is string equal.
    56  	IsStringEqual = isStringEqual
    57  )
    58  
    59  // FromMinioClientMetadata converts minio metadata to map[string]string
    60  func FromMinioClientMetadata(metadata map[string][]string) map[string]string {
    61  	mm := make(map[string]string, len(metadata))
    62  	for k, v := range metadata {
    63  		mm[http.CanonicalHeaderKey(k)] = v[0]
    64  	}
    65  	return mm
    66  }
    67  
    68  // FromMinioClientObjectPart converts minio ObjectPart to PartInfo
    69  func FromMinioClientObjectPart(op minio.ObjectPart) PartInfo {
    70  	return PartInfo{
    71  		Size:         op.Size,
    72  		ETag:         canonicalizeETag(op.ETag),
    73  		LastModified: op.LastModified,
    74  		PartNumber:   op.PartNumber,
    75  	}
    76  }
    77  
    78  // FromMinioClientListPartsInfo converts minio ListObjectPartsResult to ListPartsInfo
    79  func FromMinioClientListPartsInfo(lopr minio.ListObjectPartsResult) ListPartsInfo {
    80  	// Convert minio ObjectPart to PartInfo
    81  	fromMinioClientObjectParts := func(parts []minio.ObjectPart) []PartInfo {
    82  		toParts := make([]PartInfo, len(parts))
    83  		for i, part := range parts {
    84  			toParts[i] = FromMinioClientObjectPart(part)
    85  		}
    86  		return toParts
    87  	}
    88  
    89  	return ListPartsInfo{
    90  		UploadID:             lopr.UploadID,
    91  		Bucket:               lopr.Bucket,
    92  		Object:               lopr.Key,
    93  		StorageClass:         "",
    94  		PartNumberMarker:     lopr.PartNumberMarker,
    95  		NextPartNumberMarker: lopr.NextPartNumberMarker,
    96  		MaxParts:             lopr.MaxParts,
    97  		IsTruncated:          lopr.IsTruncated,
    98  		Parts:                fromMinioClientObjectParts(lopr.ObjectParts),
    99  	}
   100  }
   101  
   102  // FromMinioClientListMultipartsInfo converts minio ListMultipartUploadsResult to ListMultipartsInfo
   103  func FromMinioClientListMultipartsInfo(lmur minio.ListMultipartUploadsResult) ListMultipartsInfo {
   104  	uploads := make([]MultipartInfo, len(lmur.Uploads))
   105  
   106  	for i, um := range lmur.Uploads {
   107  		uploads[i] = MultipartInfo{
   108  			Object:    um.Key,
   109  			UploadID:  um.UploadID,
   110  			Initiated: um.Initiated,
   111  		}
   112  	}
   113  
   114  	commonPrefixes := make([]string, len(lmur.CommonPrefixes))
   115  	for i, cp := range lmur.CommonPrefixes {
   116  		commonPrefixes[i] = cp.Prefix
   117  	}
   118  
   119  	return ListMultipartsInfo{
   120  		KeyMarker:          lmur.KeyMarker,
   121  		UploadIDMarker:     lmur.UploadIDMarker,
   122  		NextKeyMarker:      lmur.NextKeyMarker,
   123  		NextUploadIDMarker: lmur.NextUploadIDMarker,
   124  		MaxUploads:         int(lmur.MaxUploads),
   125  		IsTruncated:        lmur.IsTruncated,
   126  		Uploads:            uploads,
   127  		Prefix:             lmur.Prefix,
   128  		Delimiter:          lmur.Delimiter,
   129  		CommonPrefixes:     commonPrefixes,
   130  		EncodingType:       lmur.EncodingType,
   131  	}
   132  
   133  }
   134  
   135  // FromMinioClientObjectInfo converts minio ObjectInfo to gateway ObjectInfo
   136  func FromMinioClientObjectInfo(bucket string, oi minio.ObjectInfo) ObjectInfo {
   137  	userDefined := FromMinioClientMetadata(oi.Metadata)
   138  	userDefined[xhttp.ContentType] = oi.ContentType
   139  
   140  	return ObjectInfo{
   141  		Bucket:          bucket,
   142  		Name:            oi.Key,
   143  		ModTime:         oi.LastModified,
   144  		Size:            oi.Size,
   145  		ETag:            canonicalizeETag(oi.ETag),
   146  		UserDefined:     userDefined,
   147  		ContentType:     oi.ContentType,
   148  		ContentEncoding: oi.Metadata.Get(xhttp.ContentEncoding),
   149  		StorageClass:    oi.StorageClass,
   150  		Expires:         oi.Expires,
   151  	}
   152  }
   153  
   154  // FromMinioClientListBucketV2Result converts minio ListBucketResult to ListObjectsInfo
   155  func FromMinioClientListBucketV2Result(bucket string, result minio.ListBucketV2Result) ListObjectsV2Info {
   156  	objects := make([]ObjectInfo, len(result.Contents))
   157  
   158  	for i, oi := range result.Contents {
   159  		objects[i] = FromMinioClientObjectInfo(bucket, oi)
   160  	}
   161  
   162  	prefixes := make([]string, len(result.CommonPrefixes))
   163  	for i, p := range result.CommonPrefixes {
   164  		prefixes[i] = p.Prefix
   165  	}
   166  
   167  	return ListObjectsV2Info{
   168  		IsTruncated: result.IsTruncated,
   169  		Prefixes:    prefixes,
   170  		Objects:     objects,
   171  
   172  		ContinuationToken:     result.ContinuationToken,
   173  		NextContinuationToken: result.NextContinuationToken,
   174  	}
   175  }
   176  
   177  // FromMinioClientListBucketResult converts minio ListBucketResult to ListObjectsInfo
   178  func FromMinioClientListBucketResult(bucket string, result minio.ListBucketResult) ListObjectsInfo {
   179  	objects := make([]ObjectInfo, len(result.Contents))
   180  
   181  	for i, oi := range result.Contents {
   182  		objects[i] = FromMinioClientObjectInfo(bucket, oi)
   183  	}
   184  
   185  	prefixes := make([]string, len(result.CommonPrefixes))
   186  	for i, p := range result.CommonPrefixes {
   187  		prefixes[i] = p.Prefix
   188  	}
   189  
   190  	return ListObjectsInfo{
   191  		IsTruncated: result.IsTruncated,
   192  		NextMarker:  result.NextMarker,
   193  		Prefixes:    prefixes,
   194  		Objects:     objects,
   195  	}
   196  }
   197  
   198  // FromMinioClientListBucketResultToV2Info converts minio ListBucketResult to ListObjectsV2Info
   199  func FromMinioClientListBucketResultToV2Info(bucket string, result minio.ListBucketResult) ListObjectsV2Info {
   200  	objects := make([]ObjectInfo, len(result.Contents))
   201  
   202  	for i, oi := range result.Contents {
   203  		objects[i] = FromMinioClientObjectInfo(bucket, oi)
   204  	}
   205  
   206  	prefixes := make([]string, len(result.CommonPrefixes))
   207  	for i, p := range result.CommonPrefixes {
   208  		prefixes[i] = p.Prefix
   209  	}
   210  
   211  	return ListObjectsV2Info{
   212  		IsTruncated:           result.IsTruncated,
   213  		Prefixes:              prefixes,
   214  		Objects:               objects,
   215  		ContinuationToken:     result.Marker,
   216  		NextContinuationToken: result.NextMarker,
   217  	}
   218  }
   219  
   220  // ToMinioClientObjectInfoMetadata convertes metadata to map[string][]string
   221  func ToMinioClientObjectInfoMetadata(metadata map[string]string) map[string][]string {
   222  	mm := make(map[string][]string, len(metadata))
   223  	for k, v := range metadata {
   224  		mm[http.CanonicalHeaderKey(k)] = []string{v}
   225  	}
   226  	return mm
   227  }
   228  
   229  // ToMinioClientMetadata converts metadata to map[string]string
   230  func ToMinioClientMetadata(metadata map[string]string) map[string]string {
   231  	mm := make(map[string]string, len(metadata))
   232  	for k, v := range metadata {
   233  		mm[http.CanonicalHeaderKey(k)] = v
   234  	}
   235  	return mm
   236  }
   237  
   238  // ToMinioClientCompletePart converts CompletePart to minio CompletePart
   239  func ToMinioClientCompletePart(part CompletePart) minio.CompletePart {
   240  	return minio.CompletePart{
   241  		ETag:       part.ETag,
   242  		PartNumber: part.PartNumber,
   243  	}
   244  }
   245  
   246  // ToMinioClientCompleteParts converts []CompletePart to minio []CompletePart
   247  func ToMinioClientCompleteParts(parts []CompletePart) []minio.CompletePart {
   248  	mparts := make([]minio.CompletePart, len(parts))
   249  	for i, part := range parts {
   250  		mparts[i] = ToMinioClientCompletePart(part)
   251  	}
   252  	return mparts
   253  }
   254  
   255  // IsBackendOnline - verifies if the backend is reachable
   256  // by performing a GET request on the URL. returns 'true'
   257  // if backend is reachable.
   258  func IsBackendOnline(ctx context.Context, host string) bool {
   259  	var d net.Dialer
   260  
   261  	ctx, cancel := context.WithTimeout(ctx, 1*time.Second)
   262  	defer cancel()
   263  
   264  	conn, err := d.DialContext(ctx, "tcp", host)
   265  	if err != nil {
   266  		return false
   267  	}
   268  
   269  	conn.Close()
   270  	return true
   271  }
   272  
   273  // ErrorRespToObjectError converts MinIO errors to minio object layer errors.
   274  func ErrorRespToObjectError(err error, params ...string) error {
   275  	if err == nil {
   276  		return nil
   277  	}
   278  
   279  	bucket := ""
   280  	object := ""
   281  	if len(params) >= 1 {
   282  		bucket = params[0]
   283  	}
   284  	if len(params) == 2 {
   285  		object = params[1]
   286  	}
   287  
   288  	if xnet.IsNetworkOrHostDown(err, false) {
   289  		return BackendDown{}
   290  	}
   291  
   292  	minioErr, ok := err.(minio.ErrorResponse)
   293  	if !ok {
   294  		// We don't interpret non MinIO errors. As minio errors will
   295  		// have StatusCode to help to convert to object errors.
   296  		return err
   297  	}
   298  
   299  	switch minioErr.Code {
   300  	case "BucketAlreadyOwnedByYou":
   301  		err = BucketAlreadyOwnedByYou{}
   302  	case "BucketNotEmpty":
   303  		err = BucketNotEmpty{}
   304  	case "NoSuchBucketPolicy":
   305  		err = BucketPolicyNotFound{}
   306  	case "NoSuchLifecycleConfiguration":
   307  		err = BucketLifecycleNotFound{}
   308  	case "InvalidBucketName":
   309  		err = BucketNameInvalid{Bucket: bucket}
   310  	case "InvalidPart":
   311  		err = InvalidPart{}
   312  	case "NoSuchBucket":
   313  		err = BucketNotFound{Bucket: bucket}
   314  	case "NoSuchKey":
   315  		if object != "" {
   316  			err = ObjectNotFound{Bucket: bucket, Object: object}
   317  		} else {
   318  			err = BucketNotFound{Bucket: bucket}
   319  		}
   320  	case "XMinioInvalidObjectName":
   321  		err = ObjectNameInvalid{}
   322  	case "AccessDenied":
   323  		err = PrefixAccessDenied{
   324  			Bucket: bucket,
   325  			Object: object,
   326  		}
   327  	case "XAmzContentSHA256Mismatch":
   328  		err = hash.SHA256Mismatch{}
   329  	case "NoSuchUpload":
   330  		err = InvalidUploadID{}
   331  	case "EntityTooSmall":
   332  		err = PartTooSmall{}
   333  	}
   334  
   335  	return err
   336  }
   337  
   338  // ComputeCompleteMultipartMD5 calculates MD5 ETag for complete multipart responses
   339  func ComputeCompleteMultipartMD5(parts []CompletePart) string {
   340  	return getCompleteMultipartMD5(parts)
   341  }
   342  
   343  // parse gateway sse env variable
   344  func parseGatewaySSE(s string) (gatewaySSE, error) {
   345  	l := strings.Split(s, ";")
   346  	var gwSlice gatewaySSE
   347  	for _, val := range l {
   348  		v := strings.ToUpper(val)
   349  		switch v {
   350  		case "":
   351  			continue
   352  		case gatewaySSES3:
   353  			fallthrough
   354  		case gatewaySSEC:
   355  			gwSlice = append(gwSlice, v)
   356  			continue
   357  		default:
   358  			return nil, config.ErrInvalidGWSSEValue(nil).Msg("gateway SSE cannot be (%s) ", v)
   359  		}
   360  	}
   361  	return gwSlice, nil
   362  }
   363  
   364  // handle gateway env vars
   365  func gatewayHandleEnvVars() {
   366  	// Handle common env vars.
   367  	HandleCommonEnvVars()
   368  
   369  	if !globalActiveCred.IsValid() {
   370  		logger.Fatal(config.ErrInvalidCredentials(nil),
   371  			"Unable to validate credentials inherited from the shell environment")
   372  	}
   373  
   374  	gwsseVal := env.Get("MINIO_GATEWAY_SSE", "")
   375  	if gwsseVal != "" {
   376  		var err error
   377  		GlobalGatewaySSE, err = parseGatewaySSE(gwsseVal)
   378  		if err != nil {
   379  			logger.Fatal(err, "Unable to parse MINIO_GATEWAY_SSE value (`%s`)", gwsseVal)
   380  		}
   381  	}
   382  }
   383  
   384  // shouldMeterRequest checks whether incoming request should be added to prometheus gateway metrics
   385  func shouldMeterRequest(req *http.Request) bool {
   386  	return !(guessIsBrowserReq(req) || guessIsHealthCheckReq(req) || guessIsMetricsReq(req))
   387  }
   388  
   389  // MetricsTransport is a custom wrapper around Transport to track metrics
   390  type MetricsTransport struct {
   391  	Transport *http.Transport
   392  	Metrics   *BackendMetrics
   393  }
   394  
   395  // RoundTrip implements the RoundTrip method for MetricsTransport
   396  func (m MetricsTransport) RoundTrip(r *http.Request) (*http.Response, error) {
   397  	metered := shouldMeterRequest(r)
   398  	if metered && (r.Method == http.MethodPost || r.Method == http.MethodPut) {
   399  		m.Metrics.IncRequests(r.Method)
   400  		if r.ContentLength > 0 {
   401  			m.Metrics.IncBytesSent(uint64(r.ContentLength))
   402  		}
   403  	}
   404  	// Make the request to the server.
   405  	resp, err := m.Transport.RoundTrip(r)
   406  	if err != nil {
   407  		return nil, err
   408  	}
   409  	if metered && (r.Method == http.MethodGet || r.Method == http.MethodHead) {
   410  		m.Metrics.IncRequests(r.Method)
   411  		if resp.ContentLength > 0 {
   412  			m.Metrics.IncBytesReceived(uint64(resp.ContentLength))
   413  		}
   414  	}
   415  	return resp, nil
   416  }