github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/etcd.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  	"context"
    22  	"errors"
    23  	"fmt"
    24  
    25  	"github.com/minio/minio/internal/logger"
    26  	etcd "go.etcd.io/etcd/client/v3"
    27  )
    28  
    29  var errEtcdUnreachable = errors.New("etcd is unreachable, please check your endpoints")
    30  
    31  func etcdErrToErr(err error, etcdEndpoints []string) error {
    32  	if err == nil {
    33  		return nil
    34  	}
    35  	switch err {
    36  	case context.DeadlineExceeded:
    37  		return fmt.Errorf("%w %s", errEtcdUnreachable, etcdEndpoints)
    38  	default:
    39  		return fmt.Errorf("unexpected error %w from etcd, please check your endpoints %s", err, etcdEndpoints)
    40  	}
    41  }
    42  
    43  func saveKeyEtcdWithTTL(ctx context.Context, client *etcd.Client, key string, data []byte, ttl int64) error {
    44  	timeoutCtx, cancel := context.WithTimeout(ctx, defaultContextTimeout)
    45  	defer cancel()
    46  	lease, err := client.Grant(timeoutCtx, ttl)
    47  	if err != nil {
    48  		return etcdErrToErr(err, client.Endpoints())
    49  	}
    50  	_, err = client.Put(timeoutCtx, key, string(data), etcd.WithLease(lease.ID))
    51  	logger.LogIf(ctx, err)
    52  	return etcdErrToErr(err, client.Endpoints())
    53  }
    54  
    55  func saveKeyEtcd(ctx context.Context, client *etcd.Client, key string, data []byte, opts ...options) error {
    56  	timeoutCtx, cancel := context.WithTimeout(ctx, defaultContextTimeout)
    57  	defer cancel()
    58  	if len(opts) > 0 {
    59  		return saveKeyEtcdWithTTL(ctx, client, key, data, opts[0].ttl)
    60  	}
    61  	_, err := client.Put(timeoutCtx, key, string(data))
    62  	logger.LogIf(ctx, err)
    63  	return etcdErrToErr(err, client.Endpoints())
    64  }
    65  
    66  func deleteKeyEtcd(ctx context.Context, client *etcd.Client, key string) error {
    67  	timeoutCtx, cancel := context.WithTimeout(ctx, defaultContextTimeout)
    68  	defer cancel()
    69  
    70  	_, err := client.Delete(timeoutCtx, key)
    71  	logger.LogIf(ctx, err)
    72  	return etcdErrToErr(err, client.Endpoints())
    73  }
    74  
    75  func readKeyEtcd(ctx context.Context, client *etcd.Client, key string) ([]byte, error) {
    76  	timeoutCtx, cancel := context.WithTimeout(ctx, defaultContextTimeout)
    77  	defer cancel()
    78  	resp, err := client.Get(timeoutCtx, key)
    79  	if err != nil {
    80  		logger.LogOnceIf(ctx, err, "etcd-retrieve-keys")
    81  		return nil, etcdErrToErr(err, client.Endpoints())
    82  	}
    83  	if resp.Count == 0 {
    84  		return nil, errConfigNotFound
    85  	}
    86  	for _, ev := range resp.Kvs {
    87  		if string(ev.Key) == key {
    88  			return ev.Value, nil
    89  		}
    90  	}
    91  	return nil, errConfigNotFound
    92  }