github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/warm-backend.go (about)

     1  // Copyright (c) 2015-2021 MinIO, Inc.
     2  //
     3  // This file is part of MinIO Object Storage stack
     4  //
     5  // This program is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Affero General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // This program is distributed in the hope that it will be useful
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU Affero General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Affero General Public License
    16  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package cmd
    19  
    20  import (
    21  	"bytes"
    22  	"context"
    23  	"errors"
    24  	"fmt"
    25  	"io"
    26  
    27  	"github.com/minio/madmin-go/v3"
    28  	xhttp "github.com/minio/minio/internal/http"
    29  )
    30  
    31  // WarmBackendGetOpts is used to express byte ranges within an object. The zero
    32  // value represents the entire byte range of an object.
    33  type WarmBackendGetOpts struct {
    34  	startOffset int64
    35  	length      int64
    36  }
    37  
    38  // WarmBackend provides interface to be implemented by remote tier backends
    39  type WarmBackend interface {
    40  	Put(ctx context.Context, object string, r io.Reader, length int64) (remoteVersionID, error)
    41  	Get(ctx context.Context, object string, rv remoteVersionID, opts WarmBackendGetOpts) (io.ReadCloser, error)
    42  	Remove(ctx context.Context, object string, rv remoteVersionID) error
    43  	InUse(ctx context.Context) (bool, error)
    44  }
    45  
    46  const probeObject = "probeobject"
    47  
    48  // checkWarmBackend checks if tier config credentials have sufficient privileges
    49  // to perform all operations defined in the WarmBackend interface.
    50  func checkWarmBackend(ctx context.Context, w WarmBackend) error {
    51  	var empty bytes.Reader
    52  	remoteVersionID, err := w.Put(ctx, probeObject, &empty, 0)
    53  	if err != nil {
    54  		if _, ok := err.(BackendDown); ok {
    55  			return err
    56  		}
    57  		return tierPermErr{
    58  			Op:  tierPut,
    59  			Err: err,
    60  		}
    61  	}
    62  
    63  	r, err := w.Get(ctx, probeObject, "", WarmBackendGetOpts{})
    64  	xhttp.DrainBody(r)
    65  	if err != nil {
    66  		if _, ok := err.(BackendDown); ok {
    67  			return err
    68  		}
    69  		switch {
    70  		case isErrBucketNotFound(err):
    71  			return errTierBucketNotFound
    72  		case isErrSignatureDoesNotMatch(err):
    73  			return errTierInvalidCredentials
    74  		default:
    75  			return tierPermErr{
    76  				Op:  tierGet,
    77  				Err: err,
    78  			}
    79  		}
    80  	}
    81  	if err = w.Remove(ctx, probeObject, remoteVersionID); err != nil {
    82  		if _, ok := err.(BackendDown); ok {
    83  			return err
    84  		}
    85  		return tierPermErr{
    86  			Op:  tierDelete,
    87  			Err: err,
    88  		}
    89  	}
    90  	return err
    91  }
    92  
    93  type tierOp uint8
    94  
    95  const (
    96  	_ tierOp = iota
    97  	tierGet
    98  	tierPut
    99  	tierDelete
   100  )
   101  
   102  func (op tierOp) String() string {
   103  	switch op {
   104  	case tierGet:
   105  		return "GET"
   106  	case tierPut:
   107  		return "PUT"
   108  	case tierDelete:
   109  		return "DELETE"
   110  	}
   111  	return "UNKNOWN"
   112  }
   113  
   114  type tierPermErr struct {
   115  	Op  tierOp
   116  	Err error
   117  }
   118  
   119  func (te tierPermErr) Error() string {
   120  	return fmt.Sprintf("failed to perform %s: %v", te.Op, te.Err)
   121  }
   122  
   123  func errIsTierPermError(err error) bool {
   124  	var tpErr tierPermErr
   125  	return errors.As(err, &tpErr)
   126  }
   127  
   128  // remoteVersionID represents the version id of an object in the remote tier.
   129  // Its usage is remote tier cloud implementation specific.
   130  type remoteVersionID string
   131  
   132  // newWarmBackend instantiates the tier type specific WarmBackend, runs
   133  // checkWarmBackend on it.
   134  func newWarmBackend(ctx context.Context, tier madmin.TierConfig) (d WarmBackend, err error) {
   135  	switch tier.Type {
   136  	case madmin.S3:
   137  		d, err = newWarmBackendS3(*tier.S3, tier.Name)
   138  	case madmin.Azure:
   139  		d, err = newWarmBackendAzure(*tier.Azure, tier.Name)
   140  	case madmin.GCS:
   141  		d, err = newWarmBackendGCS(*tier.GCS, tier.Name)
   142  	case madmin.MinIO:
   143  		d, err = newWarmBackendMinIO(*tier.MinIO, tier.Name)
   144  	default:
   145  		return nil, errTierTypeUnsupported
   146  	}
   147  	if err != nil {
   148  		return nil, errTierTypeUnsupported
   149  	}
   150  
   151  	err = checkWarmBackend(ctx, d)
   152  	if err != nil {
   153  		return nil, err
   154  	}
   155  	return d, nil
   156  }