github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/tools/common.go (about)

     1  // Package tools provides common tools and utilities for all unit and integration tests
     2  /*
     3   * Copyright (c) 2018-2024, NVIDIA CORPORATION. All rights reserved.
     4   */
     5  package tools
     6  
     7  import (
     8  	"errors"
     9  	"fmt"
    10  	"net/http"
    11  	"os"
    12  	"path/filepath"
    13  	"strconv"
    14  	"strings"
    15  	"testing"
    16  
    17  	"github.com/NVIDIA/aistore/api"
    18  	"github.com/NVIDIA/aistore/api/apc"
    19  	"github.com/NVIDIA/aistore/cmn"
    20  	"github.com/NVIDIA/aistore/cmn/cos"
    21  	"github.com/NVIDIA/aistore/cmn/fname"
    22  	"github.com/NVIDIA/aistore/core/meta"
    23  	"github.com/NVIDIA/aistore/tools/readers"
    24  	"github.com/NVIDIA/aistore/tools/tassert"
    25  	"github.com/NVIDIA/aistore/tools/trand"
    26  )
    27  
    28  // Generates an object name that hashes to a different target than `baseName`.
    29  func GenerateNotConflictingObjectName(baseName, newNamePrefix string, bck cmn.Bck, smap *meta.Smap) string {
    30  	// Init digests - HrwTarget() requires it
    31  	smap.InitDigests()
    32  
    33  	newName := newNamePrefix
    34  
    35  	cbck := meta.CloneBck(&bck)
    36  	baseNameHrw, e1 := smap.HrwName2T(cbck.MakeUname(baseName))
    37  	newNameHrw, e2 := smap.HrwName2T(cbck.MakeUname(newName))
    38  	cos.Assert(e1 == nil && e2 == nil)
    39  
    40  	for i := 0; baseNameHrw == newNameHrw; i++ {
    41  		newName = newNamePrefix + strconv.Itoa(i)
    42  		newNameHrw, e1 = smap.HrwName2T(cbck.MakeUname(newName))
    43  		cos.AssertNoErr(e1)
    44  	}
    45  	return newName
    46  }
    47  
    48  func GenerateNonexistentBucketName(prefix string, bp api.BaseParams) (string, error) {
    49  	for range 100 {
    50  		bck := cmn.Bck{
    51  			Name:     prefix + trand.String(8),
    52  			Provider: apc.AIS,
    53  		}
    54  		_, err := api.HeadBucket(bp, bck, true /* don't add to cluster MD */)
    55  		if err == nil {
    56  			continue
    57  		}
    58  		errHTTP, ok := err.(*cmn.ErrHTTP)
    59  		if !ok {
    60  			return "",
    61  				fmt.Errorf("error generating bucket name: expected error of type *cmn.ErrHTTP, but got: %T", err)
    62  		}
    63  		if errHTTP.Status == http.StatusNotFound {
    64  			return bck.Name, nil
    65  		}
    66  
    67  		return "", fmt.Errorf("error generating bucket name: unexpected HEAD request error: %v", err)
    68  	}
    69  
    70  	return "", errors.New("error generating bucket name: too many tries gave no result")
    71  }
    72  
    73  func BucketsContain(bcks cmn.Bcks, qbck cmn.QueryBcks) bool {
    74  	for i := range bcks {
    75  		bck := bcks[i]
    76  		if qbck.Equal(&bck) || qbck.Contains(&bck) {
    77  			return true
    78  		}
    79  	}
    80  	return false
    81  }
    82  
    83  func BucketExists(tb testing.TB, proxyURL string, bck cmn.Bck) (bool, error) {
    84  	if bck.IsQuery() {
    85  		if tb == nil {
    86  			return false, fmt.Errorf("expecting a named bucket, got %q", bck)
    87  		}
    88  		tassert.CheckFatal(tb, fmt.Errorf("expecting a named bucket, got %q", bck))
    89  	}
    90  	bp := api.BaseParams{Client: gctx.Client, URL: proxyURL, Token: LoggedUserToken}
    91  	_, err := api.HeadBucket(bp, bck, true /*dontAddRemote*/)
    92  	if err == nil {
    93  		return true, nil
    94  	}
    95  	errHTTP, ok := err.(*cmn.ErrHTTP)
    96  	if !ok {
    97  		err = fmt.Errorf("expected an error of the type *cmn.ErrHTTP, got %v(%T)", err, err)
    98  		if tb == nil {
    99  			return false, err
   100  		}
   101  		tassert.CheckFatal(tb, err)
   102  	}
   103  	if errHTTP.Status != http.StatusNotFound {
   104  		err = fmt.Errorf("tools.BucketExists: expected status 404 (NotFound), got [%s]", errHTTP.Message)
   105  		if tb != nil {
   106  			tassert.CheckFatal(tb, err)
   107  		} else {
   108  			cos.Exitf("%v", err)
   109  		}
   110  	}
   111  	return false, err
   112  }
   113  
   114  func isRemoteBucket(tb testing.TB, proxyURL string, bck cmn.Bck) bool {
   115  	if !bck.IsRemote() {
   116  		return false
   117  	}
   118  	exists, err := BucketExists(tb, proxyURL, bck)
   119  	tassert.CheckFatal(tb, err)
   120  	return exists
   121  }
   122  
   123  func isCloudBucket(tb testing.TB, proxyURL string, bck cmn.Bck) bool {
   124  	if !bck.IsCloud() {
   125  		return false
   126  	}
   127  	exists, err := BucketExists(tb, proxyURL, bck)
   128  	tassert.CheckFatal(tb, err)
   129  	return exists
   130  }
   131  
   132  func PutObjRR(bp api.BaseParams, bck cmn.Bck, objName string, objSize int64, cksumType string) error {
   133  	reader, err := readers.NewRand(objSize, cksumType)
   134  	if err != nil {
   135  		return err
   136  	}
   137  	putArgs := api.PutArgs{
   138  		BaseParams: bp,
   139  		Bck:        bck,
   140  		ObjName:    objName,
   141  		Cksum:      reader.Cksum(),
   142  		Reader:     reader,
   143  	}
   144  	_, err = api.PutObject(&putArgs)
   145  	return err
   146  }
   147  
   148  func PutRR(tb testing.TB, bp api.BaseParams, objSize int64, cksumType string,
   149  	bck cmn.Bck, dir string, objCount int) []string {
   150  	objNames := make([]string, objCount)
   151  	for i := range objCount {
   152  		fname := trand.String(20)
   153  		objName := filepath.Join(dir, fname)
   154  		objNames[i] = objName
   155  		// FIXME: Separate RandReader per object created inside PutObjRR to workaround
   156  		// https://github.com/golang/go/issues/30597
   157  		err := PutObjRR(bp, bck, objName, objSize, cksumType)
   158  		tassert.CheckFatal(tb, err)
   159  	}
   160  
   161  	return objNames
   162  }
   163  
   164  func isClusterK8s() (isK8s bool, err error) {
   165  	// NOTE: The test suite doesn't have to be deployed on K8s, the cluster has to be.
   166  	_, err = api.ETLList(BaseAPIParams(GetPrimaryURL()))
   167  	isK8s = err == nil
   168  	// HACK: Check based on error message. Unfortunately, there is no relevant HTTP code.
   169  	if err != nil && strings.Contains(err.Error(), "requires Kubernetes") {
   170  		err = nil
   171  	}
   172  	return
   173  }
   174  
   175  func isClusterLocal() (isLocal bool, err error) {
   176  	var (
   177  		primaryURL = GetPrimaryURL()
   178  		smap       *meta.Smap
   179  		bp         = BaseAPIParams(primaryURL)
   180  		config     *cmn.Config
   181  		fileData   []byte
   182  	)
   183  	if smap, err = api.GetClusterMap(bp); err != nil {
   184  		return
   185  	}
   186  	if config, err = api.GetDaemonConfig(bp, smap.Primary); err != nil {
   187  		return
   188  	}
   189  	fileData, err = os.ReadFile(filepath.Join(config.ConfigDir, fname.ProxyID))
   190  	if err != nil {
   191  		if os.IsNotExist(err) {
   192  			err = nil
   193  		}
   194  		return
   195  	}
   196  
   197  	isLocal = strings.TrimSpace(string(fileData)) == smap.Primary.ID()
   198  	return
   199  }