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 }