github.com/minio/madmin-go/v3@v3.0.51/tier.go (about) 1 // 2 // Copyright (c) 2015-2022 MinIO, Inc. 3 // 4 // This file is part of MinIO Object Storage stack 5 // 6 // This program is free software: you can redistribute it and/or modify 7 // it under the terms of the GNU Affero General Public License as 8 // published by the Free Software Foundation, either version 3 of the 9 // License, or (at your option) any later version. 10 // 11 // This program is distributed in the hope that it will be useful, 12 // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 // GNU Affero General Public License for more details. 15 // 16 // You should have received a copy of the GNU Affero General Public License 17 // along with this program. If not, see <http://www.gnu.org/licenses/>. 18 // 19 20 package madmin 21 22 import ( 23 "context" 24 "encoding/json" 25 "io" 26 "net/http" 27 "net/url" 28 "path" 29 "strconv" 30 "time" 31 ) 32 33 // tierAPI is API path prefix for tier related admin APIs 34 const tierAPI = "tier" 35 36 // AddTierIgnoreInUse adds a new remote tier, ignoring if it's being used by another MinIO deployment. 37 func (adm *AdminClient) AddTierIgnoreInUse(ctx context.Context, cfg *TierConfig) error { 38 return adm.addTier(ctx, cfg, true) 39 } 40 41 // AddTier adds a new remote tier. 42 func (adm *AdminClient) addTier(ctx context.Context, cfg *TierConfig, ignoreInUse bool) error { 43 data, err := json.Marshal(cfg) 44 if err != nil { 45 return err 46 } 47 48 encData, err := EncryptData(adm.getSecretKey(), data) 49 if err != nil { 50 return err 51 } 52 53 queryVals := url.Values{} 54 queryVals.Set("force", strconv.FormatBool(ignoreInUse)) 55 reqData := requestData{ 56 relPath: path.Join(adminAPIPrefix, tierAPI), 57 content: encData, 58 queryValues: queryVals, 59 } 60 61 // Execute PUT on /minio/admin/v3/tier to add a remote tier 62 resp, err := adm.executeMethod(ctx, http.MethodPut, reqData) 63 defer closeResponse(resp) 64 if err != nil { 65 return err 66 } 67 68 if resp.StatusCode != http.StatusNoContent { 69 return httpRespToErrorResponse(resp) 70 } 71 return nil 72 } 73 74 // AddTier adds a new remote tier. 75 func (adm *AdminClient) AddTier(ctx context.Context, cfg *TierConfig) error { 76 return adm.addTier(ctx, cfg, false) 77 } 78 79 // ListTiers returns a list of remote tiers configured. 80 func (adm *AdminClient) ListTiers(ctx context.Context) ([]*TierConfig, error) { 81 reqData := requestData{ 82 relPath: path.Join(adminAPIPrefix, tierAPI), 83 } 84 85 // Execute GET on /minio/admin/v3/tier to list remote tiers configured. 86 resp, err := adm.executeMethod(ctx, http.MethodGet, reqData) 87 defer closeResponse(resp) 88 if err != nil { 89 return nil, err 90 } 91 92 if resp.StatusCode != http.StatusOK { 93 return nil, httpRespToErrorResponse(resp) 94 } 95 96 var tiers []*TierConfig 97 b, err := io.ReadAll(resp.Body) 98 if err != nil { 99 return tiers, err 100 } 101 102 err = json.Unmarshal(b, &tiers) 103 if err != nil { 104 return tiers, err 105 } 106 107 return tiers, nil 108 } 109 110 // TierCreds is used to pass remote tier credentials in a tier-edit operation. 111 type TierCreds struct { 112 AccessKey string `json:"access,omitempty"` 113 SecretKey string `json:"secret,omitempty"` 114 115 AWSRole bool `json:"awsrole"` 116 AWSRoleWebIdentityTokenFile string `json:"awsroleWebIdentity,omitempty"` 117 AWSRoleARN string `json:"awsroleARN,omitempty"` 118 119 AzSP ServicePrincipalAuth `json:"azSP,omitempty"` 120 121 CredsJSON []byte `json:"creds,omitempty"` 122 } 123 124 // EditTier supports updating credentials for the remote tier identified by tierName. 125 func (adm *AdminClient) EditTier(ctx context.Context, tierName string, creds TierCreds) error { 126 data, err := json.Marshal(creds) 127 if err != nil { 128 return err 129 } 130 131 var encData []byte 132 encData, err = EncryptData(adm.getSecretKey(), data) 133 if err != nil { 134 return err 135 } 136 137 reqData := requestData{ 138 relPath: path.Join(adminAPIPrefix, tierAPI, tierName), 139 content: encData, 140 } 141 142 // Execute POST on /minio/admin/v3/tier/tierName to edit a tier 143 // configured. 144 resp, err := adm.executeMethod(ctx, http.MethodPost, reqData) 145 defer closeResponse(resp) 146 if err != nil { 147 return err 148 } 149 150 if resp.StatusCode != http.StatusNoContent { 151 return httpRespToErrorResponse(resp) 152 } 153 154 return nil 155 } 156 157 // RemoveTier removes an empty tier identified by tierName 158 func (adm *AdminClient) RemoveTier(ctx context.Context, tierName string) error { 159 if tierName == "" { 160 return ErrTierNameEmpty 161 } 162 reqData := requestData{ 163 relPath: path.Join(adminAPIPrefix, tierAPI, tierName), 164 } 165 166 // Execute DELETE on /minio/admin/v3/tier/tierName to remove an empty tier. 167 resp, err := adm.executeMethod(ctx, http.MethodDelete, reqData) 168 defer closeResponse(resp) 169 if err != nil { 170 return err 171 } 172 173 if resp.StatusCode != http.StatusNoContent { 174 return httpRespToErrorResponse(resp) 175 } 176 177 return nil 178 } 179 180 // VerifyTier verifies tierName's remote tier config 181 func (adm *AdminClient) VerifyTier(ctx context.Context, tierName string) error { 182 if tierName == "" { 183 return ErrTierNameEmpty 184 } 185 reqData := requestData{ 186 relPath: path.Join(adminAPIPrefix, tierAPI, tierName), 187 } 188 189 // Execute GET on /minio/admin/v3/tier/tierName to verify tierName's config. 190 resp, err := adm.executeMethod(ctx, http.MethodGet, reqData) 191 defer closeResponse(resp) 192 if err != nil { 193 return err 194 } 195 196 if resp.StatusCode != http.StatusNoContent { 197 return httpRespToErrorResponse(resp) 198 } 199 200 return nil 201 } 202 203 // TierInfo contains tier name, type and statistics 204 type TierInfo struct { 205 Name string 206 Type string 207 Stats TierStats 208 DailyStats DailyTierStats 209 } 210 211 type DailyTierStats struct { 212 Bins [24]TierStats 213 UpdatedAt time.Time 214 } 215 216 // TierStats returns per-tier stats of all configured tiers (incl. internal 217 // hot-tier) 218 func (adm *AdminClient) TierStats(ctx context.Context) ([]TierInfo, error) { 219 reqData := requestData{ 220 relPath: path.Join(adminAPIPrefix, "tier-stats"), 221 } 222 223 // Execute GET on /minio/admin/v3/tier-stats to list tier-stats. 224 resp, err := adm.executeMethod(ctx, http.MethodGet, reqData) 225 defer closeResponse(resp) 226 if err != nil { 227 return nil, err 228 } 229 230 if resp.StatusCode != http.StatusOK { 231 return nil, httpRespToErrorResponse(resp) 232 } 233 234 var tierInfos []TierInfo 235 b, err := io.ReadAll(resp.Body) 236 if err != nil { 237 return tierInfos, err 238 } 239 240 err = json.Unmarshal(b, &tierInfos) 241 if err != nil { 242 return tierInfos, err 243 } 244 245 return tierInfos, nil 246 }