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 }