github.com/minio/madmin-go/v2@v2.2.1/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/ioutil"
    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 := ioutil.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  	CredsJSON []byte `json:"creds,omitempty"`
   115  	AWSRole   bool   `json:"awsrole"`
   116  }
   117  
   118  // EditTier supports updating credentials for the remote tier identified by tierName.
   119  func (adm *AdminClient) EditTier(ctx context.Context, tierName string, creds TierCreds) error {
   120  	data, err := json.Marshal(creds)
   121  	if err != nil {
   122  		return err
   123  	}
   124  
   125  	var encData []byte
   126  	encData, err = EncryptData(adm.getSecretKey(), data)
   127  	if err != nil {
   128  		return err
   129  	}
   130  
   131  	reqData := requestData{
   132  		relPath: path.Join(adminAPIPrefix, tierAPI, tierName),
   133  		content: encData,
   134  	}
   135  
   136  	// Execute POST on /minio/admin/v3/tier/tierName to edit a tier
   137  	// configured.
   138  	resp, err := adm.executeMethod(ctx, http.MethodPost, reqData)
   139  	defer closeResponse(resp)
   140  	if err != nil {
   141  		return err
   142  	}
   143  
   144  	if resp.StatusCode != http.StatusNoContent {
   145  		return httpRespToErrorResponse(resp)
   146  	}
   147  
   148  	return nil
   149  }
   150  
   151  // RemoveTier removes an empty tier identified by tierName
   152  func (adm *AdminClient) RemoveTier(ctx context.Context, tierName string) error {
   153  	if tierName == "" {
   154  		return ErrTierNameEmpty
   155  	}
   156  	reqData := requestData{
   157  		relPath: path.Join(adminAPIPrefix, tierAPI, tierName),
   158  	}
   159  
   160  	// Execute DELETE on /minio/admin/v3/tier/tierName to remove an empty tier.
   161  	resp, err := adm.executeMethod(ctx, http.MethodDelete, reqData)
   162  	defer closeResponse(resp)
   163  	if err != nil {
   164  		return err
   165  	}
   166  
   167  	if resp.StatusCode != http.StatusNoContent {
   168  		return httpRespToErrorResponse(resp)
   169  	}
   170  
   171  	return nil
   172  }
   173  
   174  // VerifyTier verifies tierName's remote tier config
   175  func (adm *AdminClient) VerifyTier(ctx context.Context, tierName string) error {
   176  	if tierName == "" {
   177  		return ErrTierNameEmpty
   178  	}
   179  	reqData := requestData{
   180  		relPath: path.Join(adminAPIPrefix, tierAPI, tierName),
   181  	}
   182  
   183  	// Execute GET on /minio/admin/v3/tier/tierName to verify tierName's config.
   184  	resp, err := adm.executeMethod(ctx, http.MethodGet, reqData)
   185  	defer closeResponse(resp)
   186  	if err != nil {
   187  		return err
   188  	}
   189  
   190  	if resp.StatusCode != http.StatusNoContent {
   191  		return httpRespToErrorResponse(resp)
   192  	}
   193  
   194  	return nil
   195  }
   196  
   197  // TierInfo contains tier name, type and statistics
   198  type TierInfo struct {
   199  	Name       string
   200  	Type       string
   201  	Stats      TierStats
   202  	DailyStats DailyTierStats
   203  }
   204  
   205  type DailyTierStats struct {
   206  	Bins      [24]TierStats
   207  	UpdatedAt time.Time
   208  }
   209  
   210  // TierStats returns per-tier stats of all configured tiers (incl. internal
   211  // hot-tier)
   212  func (adm *AdminClient) TierStats(ctx context.Context) ([]TierInfo, error) {
   213  	reqData := requestData{
   214  		relPath: path.Join(adminAPIPrefix, "tier-stats"),
   215  	}
   216  
   217  	// Execute GET on /minio/admin/v3/tier-stats to list tier-stats.
   218  	resp, err := adm.executeMethod(ctx, http.MethodGet, reqData)
   219  	defer closeResponse(resp)
   220  	if err != nil {
   221  		return nil, err
   222  	}
   223  
   224  	if resp.StatusCode != http.StatusOK {
   225  		return nil, httpRespToErrorResponse(resp)
   226  	}
   227  
   228  	var tierInfos []TierInfo
   229  	b, err := ioutil.ReadAll(resp.Body)
   230  	if err != nil {
   231  		return tierInfos, err
   232  	}
   233  
   234  	err = json.Unmarshal(b, &tierInfos)
   235  	if err != nil {
   236  		return tierInfos, err
   237  	}
   238  
   239  	return tierInfos, nil
   240  }