github.com/smartcontractkit/chainlink-testing-framework/libs@v0.0.0-20240227141906-ec710b4eb1a3/utils/osutil/osutil.go (about)

     1  package osutil
     2  
     3  import (
     4  	"bufio"
     5  	"context"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"io/fs"
    10  	"os"
    11  	"os/exec"
    12  	"path/filepath"
    13  	"strings"
    14  
    15  	"github.com/rs/zerolog"
    16  
    17  	"github.com/smartcontractkit/chainlink-testing-framework/libs/k8s/config"
    18  )
    19  
    20  // GetEnv returns the value of the environment variable named by the key
    21  // and sets the environment variable up to be used in the remote runner
    22  func GetEnv(key string) (string, error) {
    23  	val := os.Getenv(key)
    24  	if val != "" {
    25  		prefixedKey := fmt.Sprintf("%s%s", config.EnvVarPrefix, key)
    26  		if os.Getenv(prefixedKey) != "" && os.Getenv(prefixedKey) != val {
    27  			return val, fmt.Errorf("environment variable collision with prefixed key, Original: %s=%s, Prefixed: %s=%s", key, val, prefixedKey, os.Getenv(prefixedKey))
    28  		}
    29  		err := os.Setenv(prefixedKey, val)
    30  		if err != nil {
    31  			return val, fmt.Errorf("failed to set environment variable %s err: %w", prefixedKey, err)
    32  		}
    33  	}
    34  	return val, nil
    35  }
    36  
    37  // SetupEnvVarsForRemoteRunner sets up the environment variables in the list to propagate to the remote runner
    38  func SetupEnvVarsForRemoteRunner(envVars []string) error {
    39  	for _, envVar := range envVars {
    40  		_, err := GetEnv(envVar)
    41  		if err != nil {
    42  			return err
    43  		}
    44  	}
    45  	return nil
    46  }
    47  
    48  // ExecCmd executes a command and logs the output
    49  func ExecCmd(l zerolog.Logger, command string) error {
    50  	return ExecCmdWithContext(context.Background(), l, command)
    51  }
    52  
    53  // ExecCmdWithContext executes a command with ctx and logs the output
    54  func ExecCmdWithContext(ctx context.Context, l zerolog.Logger, command string) error {
    55  	return ExecCmdWithOptions(ctx, l, command, func(m string) {
    56  		l.Debug().Str("Text", m).Msg("Std Pipe")
    57  	})
    58  }
    59  
    60  // readStdPipe continuously read a pipe from the command
    61  func readStdPipe(pipe io.ReadCloser, outputFunction func(string)) {
    62  	scanner := bufio.NewScanner(pipe)
    63  	scanner.Split(bufio.ScanLines)
    64  	for scanner.Scan() {
    65  		m := scanner.Text()
    66  		if outputFunction != nil {
    67  			outputFunction(m)
    68  		}
    69  	}
    70  }
    71  
    72  // ExecCmdWithOptions executes a command with ctx and logs the output with a custom logging func
    73  func ExecCmdWithOptions(ctx context.Context, l zerolog.Logger, command string, outputFunction func(string)) error {
    74  	c := strings.Split(command, " ")
    75  	l.Info().Interface("Command", c).Msg("Executing command")
    76  	cmd := exec.CommandContext(ctx, c[0], c[1:]...) // #nosec: G204
    77  	stderr, err := cmd.StderrPipe()
    78  	if err != nil {
    79  		return err
    80  	}
    81  	stdout, err := cmd.StdoutPipe()
    82  	if err != nil {
    83  		return err
    84  	}
    85  	if err := cmd.Start(); err != nil {
    86  		return err
    87  	}
    88  	go readStdPipe(stderr, outputFunction)
    89  	go readStdPipe(stdout, outputFunction)
    90  	return cmd.Wait()
    91  }
    92  
    93  func GetAbsoluteFolderPath(folder string) (string, error) {
    94  	wd, err := os.Getwd()
    95  	if err != nil {
    96  		return "", err
    97  	}
    98  
    99  	return filepath.Join(wd, folder), nil
   100  }
   101  
   102  const (
   103  	DEFAULT_STOP_FILE_NAME         = ".root_dir"
   104  	ErrStopFileNotFoundWithinLimit = "stop file not found in any parent directory within search limit"
   105  )
   106  
   107  // FindFile looks for given file in the current directory and its parent directories first by locating
   108  // top level parent folder where the search should begin (defined by stopFile, which cannot be located
   109  // further "up" than limit parent folders) and then by searching from there for the file all subdirectories
   110  func FindFile(filename, stopFile string, limit int) (string, error) {
   111  	workingDir, err := os.Getwd()
   112  	if err != nil {
   113  		return "", err
   114  	}
   115  
   116  	currentFilePath := filepath.Join(workingDir, filename)
   117  	if _, err := os.Stat(currentFilePath); err == nil {
   118  		return currentFilePath, nil
   119  	}
   120  
   121  	rootDirPath, err := findTopParentFolderWithLimit(workingDir, stopFile, limit)
   122  	if err != nil {
   123  		return "", err
   124  	}
   125  
   126  	configFilePath, err := findFileInSubfolders(rootDirPath, filename)
   127  	if err != nil {
   128  		return "", err
   129  	}
   130  	return configFilePath, nil
   131  }
   132  
   133  func findTopParentFolderWithLimit(startDir, stopFileName string, limit int) (string, error) {
   134  	currentDir := startDir
   135  
   136  	for level := 0; level < limit; level++ {
   137  		stopFilePath := filepath.Join(currentDir, stopFileName)
   138  		if _, err := os.Stat(stopFilePath); err == nil {
   139  			return currentDir, nil
   140  		}
   141  
   142  		parentDir := filepath.Dir(currentDir)
   143  		if parentDir == currentDir {
   144  			break
   145  		}
   146  		currentDir = parentDir
   147  	}
   148  
   149  	return "", fmt.Errorf("%s: %s", ErrStopFileNotFoundWithinLimit, stopFileName)
   150  }
   151  
   152  func findFileInSubfolders(startDir, targetFileName string) (string, error) {
   153  	var filePath string
   154  	ErrFileFound := "file found"
   155  
   156  	err := filepath.WalkDir(startDir, func(path string, info fs.DirEntry, err error) error {
   157  		if err != nil {
   158  			return err
   159  		}
   160  
   161  		if !info.IsDir() && info.Name() == targetFileName {
   162  			filePath = path
   163  			return errors.New(ErrFileFound)
   164  		}
   165  
   166  		return nil
   167  	})
   168  
   169  	if err != nil && err.Error() != ErrFileFound {
   170  		return "", err
   171  	}
   172  
   173  	if filePath == "" {
   174  		return "", os.ErrNotExist
   175  	}
   176  
   177  	return filePath, nil
   178  }