Files
odo/tests/helper/helper_filesystem.go
Armel Soro 7b9f214299 Bump Go to 1.19 (#6586)
* Set Go version in go.mod

go mod edit -go=1.19

* Fix formatting issues reported by gofmt

* Fix SA1019 check (usage of deprecated "io/ioutil"), reported by golangci-lint

SA1019: "io/ioutil" has been deprecated since Go 1.16:
As of Go 1.16, the same functionality is now provided by package io or package os,
and those implementations should be preferred in new code.
See the specific function documentation for details. (staticcheck)

* Use Go 1.19 in our Dockerfiles

* Use Go 1.19 in the rpm-prepare.sh script

* Update the tag for the IBM Cloud CI image
2023-02-16 09:03:48 -05:00

351 lines
10 KiB
Go

package helper
import (
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
"time"
"github.com/devfile/library/v2/pkg/devfile/parser"
dfutil "github.com/devfile/library/v2/pkg/util"
"k8s.io/utils/pointer"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
// CreateNewContext create new empty temporary directory
func CreateNewContext() string {
directory, err := os.MkdirTemp("", "")
Expect(err).NotTo(HaveOccurred())
fmt.Fprintf(GinkgoWriter, "Created dir: %s\n", directory)
return directory
}
// DeleteDir deletes the specified path; due to Windows behaviour (for example https://github.com/redhat-developer/odo/issues/3371)
// where Windows temporarily holds a lock on files and folders, we keep trying to delete until the operation passes (or it expires)
func DeleteDir(dir string) {
attempts := 0
errorReportedAtLeastOnce := false
err := RunWithExponentialBackoff(func() error {
attempts++
fmt.Fprintf(GinkgoWriter, "Deleting dir: %s\n", dir)
err := os.RemoveAll(dir)
if err == nil {
return nil
}
errorReportedAtLeastOnce = true
fmt.Fprintf(GinkgoWriter, "Unable to delete %s on attempt #%d, trying again...\n", dir, attempts)
return err
}, 16, 2*time.Minute)
Expect(err).NotTo(HaveOccurred())
if errorReportedAtLeastOnce {
fmt.Fprintf(GinkgoWriter, "Successfully deleted %s after #%d attempts\n", dir, attempts)
}
}
// RunWithExponentialBackoff keeps trying to run 'fxn' until it no longer returns an error; if the function never succeeded,
// then the most recent error is returned.
func RunWithExponentialBackoff(fxn func() error, maxDelayInSeconds int, expireDuration time.Duration) error {
expireTime := time.Now().Add(expireDuration)
delayInSeconds := 1
var err error
for {
err = fxn()
if err == nil || time.Now().After(expireTime) {
break
}
delayInSeconds *= 2 // exponential backoff
if delayInSeconds > maxDelayInSeconds {
delayInSeconds = maxDelayInSeconds
}
time.Sleep(time.Duration(delayInSeconds) * time.Second)
}
return err
}
// DeleteFile deletes file
func DeleteFile(filepath string) {
fmt.Fprintf(GinkgoWriter, "Deleting file: %s\n", filepath)
err := os.Remove(filepath)
Expect(err).NotTo(HaveOccurred())
}
// Chdir change current working dir
func Chdir(dir string) {
fmt.Fprintf(GinkgoWriter, "Setting current dir to: %s\n", dir)
err := os.Chdir(dir)
Expect(err).ShouldNot(HaveOccurred())
}
// MakeDir creates a new dir
func MakeDir(dir string) {
err := os.MkdirAll(dir, 0750)
Expect(err).ShouldNot(HaveOccurred())
}
// Getwd returns current working dir
func Getwd() string {
dir, err := os.Getwd()
Expect(err).NotTo(HaveOccurred())
fmt.Fprintf(GinkgoWriter, "Current working dir: %s\n", dir)
return dir
}
// CopyExample copies an example from tests/examples/<binaryOrSource>/<componentName>/<exampleName> into targetDir
func CopyExample(exampleName string, targetDir string) {
// filename of this file
_, filename, _, _ := runtime.Caller(0)
// path to the examples directory
examplesDir := filepath.Join(filepath.Dir(filename), "..", "examples")
src := filepath.Join(examplesDir, exampleName)
info, err := os.Stat(src)
Expect(err).NotTo(HaveOccurred())
err = copyDir(src, targetDir, info)
Expect(err).NotTo(HaveOccurred())
}
func CopyManifestFile(fileName, targetDst string) {
// filename of this file
_, filename, _, _ := runtime.Caller(0)
// path to the examples directory
manifestsDir := filepath.Join(filepath.Dir(filename), "..", "examples", "manifests")
src := filepath.Join(manifestsDir, fileName)
info, err := os.Stat(src)
Expect(err).NotTo(HaveOccurred())
err = dfutil.CopyFile(src, targetDst, info)
Expect(err).NotTo(HaveOccurred())
}
func GetExamplePath(args ...string) string {
_, filename, _, _ := runtime.Caller(0)
path := append([]string{filepath.Dir(filename), "..", "examples"}, args...)
return filepath.Join(path...)
}
// CopyExampleDevFile copies an example devfile from tests/examples/source/devfiles/<componentName>/devfile.yaml into targetDst.
// The Devfile updaters allow to perform operations against the target Devfile, like updating the component name (via DevfileMetadataNameSetter) or
// removing the component name (via DevfileMetadataNameRemover).
func CopyExampleDevFile(devfilePath, targetDst string, devfileUpdaters ...DevfileUpdater) {
// filename of this file
_, filename, _, _ := runtime.Caller(0)
// path to the examples directory
examplesDir := filepath.Join(filepath.Dir(filename), "..", "examples")
src := filepath.Join(examplesDir, devfilePath)
info, err := os.Stat(src)
Expect(err).NotTo(HaveOccurred())
err = dfutil.CopyFile(src, targetDst, info)
Expect(err).NotTo(HaveOccurred())
if len(devfileUpdaters) != 0 {
UpdateDevfileContent(targetDst, devfileUpdaters)
}
}
// FileShouldContainSubstring check if file contains subString
func FileShouldContainSubstring(file string, subString string) {
data, err := os.ReadFile(file)
Expect(err).NotTo(HaveOccurred())
Expect(string(data)).To(ContainSubstring(subString))
}
// FileShouldNotContainSubstring check if file does not contain subString
func FileShouldNotContainSubstring(file string, subString string) {
data, err := os.ReadFile(file)
Expect(err).NotTo(HaveOccurred())
Expect(string(data)).NotTo(ContainSubstring(subString))
}
// ReplaceString replaces oldString with newString in text file
func ReplaceString(filename string, oldString string, newString string) {
fmt.Fprintf(GinkgoWriter, "Replacing \"%s\" with \"%s\" in %s\n", oldString, newString, filename)
f, err := os.ReadFile(filename)
Expect(err).NotTo(HaveOccurred())
newContent := strings.ReplaceAll(string(f), oldString, newString)
err = os.WriteFile(filename, []byte(newContent), 0600)
Expect(err).NotTo(HaveOccurred())
}
// ReplaceStrings replaces oldStrings with newStrings in text file
// two arrays must be of same length, else will fail
func ReplaceStrings(filename string, oldStrings []string, newStrings []string) {
fmt.Fprintf(GinkgoWriter, "Replacing \"%v\" with \"%v\" in %s\n", oldStrings, newStrings, filename)
contentByte, err := os.ReadFile(filename)
Expect(err).NotTo(HaveOccurred())
newContent := string(contentByte)
for i := range oldStrings {
newContent = strings.ReplaceAll(newContent, oldStrings[i], newStrings[i])
}
err = os.WriteFile(filename, []byte(newContent), 0600)
Expect(err).NotTo(HaveOccurred())
}
// copyDir copy one directory to the other
// this function is called recursively info should start as os.Stat(src)
func copyDir(src string, dst string, info os.FileInfo) error {
if info.IsDir() {
entries, err := os.ReadDir(src)
if err != nil {
return err
}
for _, entry := range entries {
file, err := entry.Info()
if err != nil {
return err
}
dsrt := filepath.Join(src, file.Name())
ddst := filepath.Join(dst, file.Name())
if err := copyDir(dsrt, ddst, file); err != nil {
return err
}
}
return nil
}
if err := os.MkdirAll(filepath.Dir(dst), os.ModePerm); err != nil {
return err
}
return dfutil.CopyFile(src, dst, info)
}
// CreateFileWithContent creates a file at the given path and writes the given content
// path is the path to the required file
// fileContent is the content to be written to the given file
func CreateFileWithContent(path string, fileContent string) error {
// create and open file if not exists
var file, err = os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0600)
if err != nil {
return err
}
defer file.Close() // #nosec G307
// write to file
_, err = file.WriteString(fileContent)
if err != nil {
return err
}
return nil
}
// ListFilesInDir lists all the files in the directory
// directoryName is the name of the directory
func ListFilesInDir(directoryName string) []string {
var filesInDirectory []string
entries, err := os.ReadDir(directoryName)
Expect(err).ShouldNot(HaveOccurred())
for _, entry := range entries {
file, err := entry.Info()
Expect(err).ShouldNot(HaveOccurred())
filesInDirectory = append(filesInDirectory, file.Name())
}
return filesInDirectory
}
// VerifyFileExists receives a path to a file, and returns whether or not
// it points to an existing file
func VerifyFileExists(filename string) bool {
info, err := os.Stat(filename)
if os.IsNotExist(err) {
return false
}
return !info.IsDir()
}
// ReadFile reads the file from the filePath
func ReadFile(filePath string) (string, error) {
data, err := os.ReadFile(filePath)
if err != nil {
return "", err
}
return string(data), nil
}
// CreateSimpleFile creates a simple file
// return the file path with random string
func CreateSimpleFile(context, filePrefix, fileExtension string) (string, string) {
FilePath := filepath.Join(context, filePrefix+RandString(10)+fileExtension)
content := []byte(RandString(10))
err := os.WriteFile(FilePath, content, 0600)
Expect(err).NotTo(HaveOccurred())
return FilePath, string(content)
}
func AppendToFile(filepath string, s string) error {
f, err := os.OpenFile(filepath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600)
if err != nil {
return err
}
defer f.Close() // #nosec G307
if _, err := f.WriteString(s); err != nil {
return err
}
return nil
}
// DevfileUpdater is a helper type that can mutate a Devfile object.
// It is intended to be used in conjunction with the UpdateDevfileContent function.
type DevfileUpdater func(*parser.DevfileObj) error
// DevfileMetadataNameSetter sets the 'metadata.name' field into the given Devfile
var DevfileMetadataNameSetter = func(name string) DevfileUpdater {
return func(d *parser.DevfileObj) error {
return d.SetMetadataName(name)
}
}
// DevfileMetadataNameRemover removes the 'metadata.name' field from the given Devfile
var DevfileMetadataNameRemover = DevfileMetadataNameSetter("")
// UpdateDevfileContent parses the Devfile at the given path, then updates its content using the given handlers, and writes the updated Devfile to the given path.
//
// The handlers are invoked in the order they are provided.
//
// No operation is performed if no handler function is specified.
//
// See DevfileMetadataNameRemover for an example of handler function that can operate on the Devfile content.
func UpdateDevfileContent(path string, handlers []DevfileUpdater) {
if len(handlers) == 0 {
//Nothing to do => skip
return
}
d, err := parser.ParseDevfile(parser.ParserArgs{Path: path, FlattenedDevfile: pointer.BoolPtr(false)})
Expect(err).NotTo(HaveOccurred())
for _, h := range handlers {
err = h(&d)
Expect(err).NotTo(HaveOccurred())
}
err = d.WriteYamlDevfile()
Expect(err).NotTo(HaveOccurred())
}