func CopyEmbedFSToDir(embedFS embed.FS, source, dest string, permissions map[string]int) (err error) {
	f, err := embedFS.Open(GetUnixPath(source))
	if err != nil {
		logrus.Errorf("Error while reading embedded file : %s", err)
		return err
	}
	finfo, err := f.Stat()
	if err != nil {
		logrus.Errorf("Error while reading stat of embedded file : %s", err)
		return err
	}
	if finfo != nil && !finfo.Mode().IsDir() {
		permission, ok := permissions[GetUnixPath(source)]
		if !ok {
			logrus.Errorf("Permission missing for file %s. Do `make generate` to update permissions file.", dest)
		}
		df, err := os.OpenFile(dest, os.O_RDWR|os.O_CREATE|os.O_TRUNC, os.FileMode(permission))
		if err != nil {
			logrus.Errorf("Error while opening temp dest assets file : %s", err)
			return err
		}
		defer df.Close()
		size, err := io.Copy(df, f)
		if err != nil {
			logrus.Errorf("Error while copying embedded file : %s", err)
			return err
		}
		if size != finfo.Size() {
			return fmt.Errorf("size mismatch: Wrote %d, Expected %d", size, finfo.Size())
		}
		return nil
	}
	if err := os.MkdirAll(dest, DefaultDirectoryPermission); err != nil {
		return err
	}
	dirEntries, err := embedFS.ReadDir(GetUnixPath(source))
	if err != nil {
		logrus.Errorf("Error while trying to read directory : %s", err)
		return err
	}
	for _, de := range dirEntries {
		CopyEmbedFSToDir(embedFS, filepath.Join(source, de.Name()), filepath.Join(dest, removeDollarPrefixFromHiddenDir(de.Name())), permissions)
	}
	return nil
}
CopyEmbedFSToDir converts a string into a directory
package common
import (
	"bytes"
	"crypto/sha256"
	"embed"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"hash/crc64"
	"io"
	"io/ioutil"
	"math"
	"net/url"
	"os"
	"path/filepath"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"text/template"
	"github.com/go-git/go-git/v5"
	"github.com/konveyor/move2kube/filesystem"
	"github.com/konveyor/move2kube/types"
	"github.com/mitchellh/mapstructure"
	"github.com/sirupsen/logrus"
	"github.com/xrash/smetrics"
	"gopkg.in/yaml.v3"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
)
func GetFilesByExt(inputPath string, exts []string) ([]string, error) {
	var files []string
	if info, err := os.Stat(inputPath); os.IsNotExist(err) {
		logrus.Warnf("Error in walking through files due to : %q", err)
		return nil, err
	} else if !info.IsDir() {
		logrus.Warnf("The path %q is not a directory.", inputPath)
	}
	err := filepath.Walk(inputPath, func(path string, info os.FileInfo, err error) error {
		if err != nil && path == inputPath {  
			return err
		}
		if err != nil {
			logrus.Warnf("Skipping path %q due to error: %q", path, err)
			return nil
		}
		if info.IsDir() {
			for _, dirRegExp := range DefaultIgnoreDirRegexps {
				if dirRegExp.Match([]byte(filepath.Base(path))) {
					return filepath.SkipDir
				}
			}
			return nil
		}
		fext := filepath.Ext(path)
		for _, ext := range exts {
			if fext == ext {
				files = append(files, path)
			}
		}
		return nil
	})
	if err != nil {
		logrus.Warnf("Error in walking through files due to : %q", err)
		return files, err
	}
	logrus.Debugf("No of files with %s ext identified : %d", exts, len(files))
	return files, nil
}
func GetFilesByName(inputPath string, names []string, nameRegexes []string) ([]string, error) {
	var files []string
	if info, err := os.Stat(inputPath); os.IsNotExist(err) {
		logrus.Warnf("Error in walking through files due to : %q", err)
		return files, err
	} else if !info.IsDir() {
		logrus.Warnf("The path %q is not a directory.", inputPath)
	}
	compiledNameRegexes := []*regexp.Regexp{}
	for _, nameRegex := range nameRegexes {
		compiledNameRegex, err := regexp.Compile(nameRegex)
		if err != nil {
			logrus.Errorf("Could not compile regular expression (%s): %s. Ignoring regex during search", err, nameRegex)
			continue
		}
		compiledNameRegexes = append(compiledNameRegexes, compiledNameRegex)
	}
	err := filepath.Walk(inputPath, func(path string, info os.FileInfo, err error) error {
		if err != nil && path == inputPath {  
			return err
		}
		if err != nil {
			logrus.Warnf("Skipping path %q due to error: %q", path, err)
			return nil
		}
		if info.IsDir() {
			for _, dirRegExp := range DefaultIgnoreDirRegexps {
				if dirRegExp.Match([]byte(filepath.Base(path))) {
					return filepath.SkipDir
				}
			}
			return nil
		}
		fname := filepath.Base(path)
		for _, name := range names {
			if name == fname {
				files = append(files, path)
				return nil
			}
		}
		for _, compiledNameRegex := range compiledNameRegexes {
			if compiledNameRegex.MatchString(fname) {
				files = append(files, path)
				return nil
			}
		}
		return nil
	})
	if err != nil {
		logrus.Warnf("Error in walking through files due to : %s", err)
		return files, err
	}
	logrus.Debugf("No of files with %s names identified : %d", names, len(files))
	return files, nil
}
func GetFilesInCurrentDirectory(path string, fileNames, fileNameRegexes []string) (files []string, err error) {
	files = []string{}
	dirhandle, err := os.Open(path)
	if err != nil {
		logrus.Errorf("Unable to open directory: %s", err)
		return nil, err
	}
	defer dirhandle.Close()
	compiledNameRegexes := []*regexp.Regexp{}
	for _, nameRegex := range fileNameRegexes {
		compiledNameRegex, err := regexp.Compile(nameRegex)
		if err != nil {
			logrus.Errorf("Could not compile regular expression (%s): %s. Ignoring regex during search", err, nameRegex)
			continue
		}
		compiledNameRegexes = append(compiledNameRegexes, compiledNameRegex)
	}
	currFileNames, err := dirhandle.Readdirnames(0)  
	if err != nil {
		logrus.Errorf("Unable to read dir names in %s : %s", path, err)
		return nil, err
	}
	for _, fname := range currFileNames {
		for _, name := range fileNames {
			if name == fname {
				files = append(files, filepath.Join(path, fname))
			}
		}
		for _, compiledNameRegex := range compiledNameRegexes {
			if compiledNameRegex.MatchString(fname) {
				files = append(files, filepath.Join(path, fname))
			}
		}
	}
	return files, nil
}
func YamlAttrPresent(path string, attr string) (bool, interface{}) {
	yamlFile, err := ioutil.ReadFile(path)
	if err != nil {
		logrus.Warnf("Error in reading yaml file %s: %s. Skipping", path, err)
		return false, nil
	}
	var fileContents map[string]interface{}
	err = yaml.Unmarshal(yamlFile, &fileContents)
	if err != nil {
		logrus.Warnf("Error in unmarshalling yaml file %s: %s. Skipping", path, err)
		return false, nil
	}
	if value, ok := fileContents[attr]; ok {
		logrus.Debugf("%s file has %s attribute", path, attr)
		return true, value
	}
	return false, nil
}
func GetImageNameAndTag(image string) (string, string) {
	parts := strings.Split(image, "/")
	imageAndTag := strings.Split(parts[len(parts)-1], ":")
	imageName := imageAndTag[0]
	var tag string
	if len(imageAndTag) == 1 {
		tag = "latest"
	} else {
		tag = imageAndTag[1]
	}
	return imageName, tag
}
func ObjectToYamlBytes(data interface{}) ([]byte, error) {
	var b bytes.Buffer
	encoder := yaml.NewEncoder(&b)
	encoder.SetIndent(2)
	if err := encoder.Encode(data); err != nil {
		logrus.Errorf("Failed to encode the object to yaml. Error: %q", err)
		return nil, err
	}
	if err := encoder.Close(); err != nil {
		logrus.Errorf("Failed to close the yaml encoder. Error: %q", err)
		return nil, err
	}
	return b.Bytes(), nil
}
func WriteYaml(outputPath string, data interface{}) error {
	yamlBytes, err := ObjectToYamlBytes(data)
	if err != nil {
		logrus.Errorf("Failed to encode the object as a yaml string. Error: %q", err)
		return err
	}
	return ioutil.WriteFile(outputPath, yamlBytes, DefaultFilePermission)
}
func ReadYaml(file string, data interface{}) error {
	yamlFile, err := ioutil.ReadFile(file)
	if err != nil {
		logrus.Debugf("Error in reading yaml file %s: %s.", file, err)
		return err
	}
	err = yaml.Unmarshal(yamlFile, data)
	if err != nil {
		logrus.Debugf("Error in unmarshalling yaml file %s: %s.", file, err)
		return err
	}
	rv := reflect.ValueOf(data)
	if rv.Kind() == reflect.Ptr && rv.Elem().Kind() == reflect.Struct {
		rv = rv.Elem()
		fv := rv.FieldByName("APIVersion")
		if fv.IsValid() {
			if fv.Kind() == reflect.String {
				val := strings.TrimSpace(fv.String())
				if strings.HasPrefix(val, types.SchemeGroupVersion.Group) && !strings.HasSuffix(val, types.SchemeGroupVersion.Version) {
					logrus.Warnf("The application file (%s) was generated using a different version than (%s)", val, types.SchemeGroupVersion.String())
				}
			}
		}
	}
	return nil
}
func ReadMove2KubeYaml(path string, out interface{}) error {
	yamlData, err := ioutil.ReadFile(path)
	if err != nil {
		logrus.Errorf("Failed to read the yaml file at path %s Error: %q", path, err)
		return err
	}
	yamlMap := map[string]interface{}{}
	if err := yaml.Unmarshal([]byte(yamlData), yamlMap); err != nil {
		logrus.Debugf("Error occurred while unmarshalling yaml file at path %s Error: %q", path, err)
		return err
	}
	groupVersionI, ok := yamlMap["apiVersion"]
	if !ok {
		err := fmt.Errorf("did not find apiVersion in the yaml file at path %s", path)
		logrus.Debug(err)
		return err
	}
	groupVersionStr, ok := groupVersionI.(string)
	if !ok {
		err := fmt.Errorf("the apiVersion is not a string in the yaml file at path %s", path)
		logrus.Debug(err)
		return err
	}
	groupVersion, err := schema.ParseGroupVersion(groupVersionStr)
	if err != nil {
		logrus.Debugf("Failed to parse the apiVersion %s Error: %q", groupVersionStr, err)
		return err
	}
	if groupVersion.Group != types.SchemeGroupVersion.Group {
		err := fmt.Errorf("the file at path %s doesn't have the correct group. Expected group %s Actual group %s", path, types.SchemeGroupVersion.Group, groupVersion.Group)
		logrus.Debug(err)
		return err
	}
	if groupVersion.Version != types.SchemeGroupVersion.Version {
		logrus.Warnf("The file at path %s was generated using a different version. File version is %s and move2kube version is %s", path, groupVersion.Version, types.SchemeGroupVersion.Version)
	}
	if err := yaml.Unmarshal(yamlData, out); err != nil {
		logrus.Debugf("Error occurred while unmarshalling yaml file at path %s Error: %q", path, err)
		return err
	}
	return nil
}
func ReadMove2KubeYamlStrict(path string, out interface{}, kind string) error {
	yamlData, err := ioutil.ReadFile(path)
	if err != nil {
		logrus.Debugf("Failed to read the yaml file at path %s Error: %q", path, err)
		return err
	}
	yamlMap := map[string]interface{}{}
	if err := yaml.Unmarshal([]byte(yamlData), yamlMap); err != nil {
		logrus.Debugf("Error occurred while unmarshalling yaml file at path %s Error: %q", path, err)
		return err
	}
	groupVersionI, ok := yamlMap["apiVersion"]
	if !ok {
		err := fmt.Errorf("did not find apiVersion in the yaml file at path %s", path)
		logrus.Debug(err)
		return err
	}
	groupVersionStr, ok := groupVersionI.(string)
	if !ok {
		err := fmt.Errorf("the apiVersion is not a string in the yaml file at path %s", path)
		logrus.Debug(err)
		return err
	}
	groupVersion, err := schema.ParseGroupVersion(groupVersionStr)
	if err != nil {
		logrus.Debugf("Failed to parse the apiVersion %s Error: %q", groupVersionStr, err)
		return err
	}
	if groupVersion.Group != types.SchemeGroupVersion.Group {
		err := fmt.Errorf("the file at path %s doesn't have the correct group. Expected group %s Actual group %s", path, types.SchemeGroupVersion.Group, groupVersion.Group)
		logrus.Debug(err)
		return err
	}
	if groupVersion.Version != types.SchemeGroupVersion.Version {
		logrus.Warnf("The file at path %s was generated using a different version. File version is %s and move2kube version is %s", path, groupVersion.Version, types.SchemeGroupVersion.Version)
	}
	actualKindI, ok := yamlMap["kind"]
	if !ok {
		err := fmt.Errorf("the file at path %s does not have a kind specified", path)
		logrus.Debug(err)
		return err
	}
	actualKind, ok := actualKindI.(string)
	if !ok {
		err := fmt.Errorf("the kind is not a string in the yaml file at path %s", path)
		logrus.Debug(err)
		return err
	}
	if kind != "" && actualKind != kind {
		err := fmt.Errorf("the file at path %s does not have the expected kind. Expected: %s Actual: %s", path, kind, actualKind)
		logrus.Debug(err)
		return err
	}
	jsonBytes, err := json.Marshal(yamlMap)
	if err != nil {
		return err
	}
	dec := json.NewDecoder(bytes.NewReader(jsonBytes))
	dec.DisallowUnknownFields()
	if err := dec.Decode(out); err != nil {
		logrus.Debugf("Error occurred while unmarshalling yaml file at path %s Error: %q", path, err)
		return err
	}
	return nil
}
func WriteJSON(outputPath string, data interface{}) error {
	var b bytes.Buffer
	encoder := json.NewEncoder(&b)
	if err := encoder.Encode(data); err != nil {
		logrus.Error("Error while Encoding object")
		return err
	}
	err := ioutil.WriteFile(outputPath, b.Bytes(), DefaultFilePermission)
	if err != nil {
		logrus.Errorf("Error writing json to file: %s", err)
		return err
	}
	return nil
}
func ReadJSON(file string, data interface{}) error {
	jsonFile, err := ioutil.ReadFile(file)
	if err != nil {
		logrus.Debugf("Error in reading json file %s: %s.", file, err)
		return err
	}
	jsonFile = bytes.TrimPrefix(jsonFile, []byte("\xef\xbb\xbf"))
	err = json.Unmarshal(jsonFile, &data)
	if err != nil {
		logrus.Debugf("Error in unmarshalling json file %s: %s.", file, err)
		return err
	}
	return nil
}
func ReadXML(file string, data interface{}) error {
	xmlFile, err := ioutil.ReadFile(file)
	if err != nil {
		logrus.Debugf("Error in reading xml file %s: %s.", file, err)
		return err
	}
	err = xml.Unmarshal(xmlFile, &data)
	if err != nil {
		logrus.Debugf("Error in unmarshalling xml file %s: %s.", file, err)
		return err
	}
	return nil
}
func NormalizeForFilename(name string) string {
	processedString := MakeFileNameCompliant(name)
	const maxPrefixLength = 15
	if len(processedString) > maxPrefixLength {
		processedString = processedString[0:maxPrefixLength]
	}
	crc64Table := crc64.MakeTable(0xC96C5795D7870F42)
	crc64Int := crc64.Checksum([]byte(name), crc64Table)
	return processedString + "-" + strconv.FormatUint(crc64Int, 16)
}
func NormalizeForServiceName(svcName string) string {
	newName := disallowedDNSCharactersRegex.ReplaceAllLiteralString(strings.ToLower(svcName), "-")
	if newName != svcName {
		logrus.Infof("Changing service name to %s from %s", svcName, newName)
	}
	return newName
}
func IsStringPresent(list []string, value string) bool {
	for _, val := range list {
		if strings.EqualFold(val, value) {
			return true
		}
	}
	return false
}
func IsIntPresent(list []int, value int) bool {
	for _, val := range list {
		if val == value {
			return true
		}
	}
	return false
}
func MergeStringSlices(slice1 []string, slice2 ...string) []string {
	for _, item := range slice2 {
		if !IsStringPresent(slice1, item) {
			slice1 = append(slice1, item)
		}
	}
	return slice1
}
func MergeIntSlices(slice1 []int, slice2 []int) []int {
	for _, item := range slice2 {
		if !IsIntPresent(slice1, item) {
			slice1 = append(slice1, item)
		}
	}
	return slice1
}
func MergeInt32Slices(slice1 []int32, slice2 []int32) []int32 {
	for _, item := range slice2 {
		if !IsInt32Present(slice1, item) {
			slice1 = append(slice1, item)
		}
	}
	return slice1
}
func IsInt32Present(list []int32, value int32) bool {
	for _, val := range list {
		if val == value {
			return true
		}
	}
	return false
}
func GetStringFromTemplate(tpl string, config interface{}) (string, error) {
	var tplbuffer bytes.Buffer
	var packageTemplate = template.Must(template.New("").Parse(tpl))
	err := packageTemplate.Execute(&tplbuffer, config)
	if err != nil {
		logrus.Warnf("Unable to transform template %q to string using the data %v", tpl, config)
		return "", err
	}
	return tplbuffer.String(), nil
}
func GetClosestMatchingString(options []string, searchstring string) string {
	reg := regexp.MustCompile("[^a-zA-Z0-9]+")
	searchstring = reg.ReplaceAllLiteralString(searchstring, "")
	searchstring = strings.ToLower(searchstring)
	leastDistance := math.MaxInt32
	matchString := ""
	for _, option := range options {
		tokenizedOption := reg.ReplaceAllLiteralString(option, "")
		tokenizedOption = strings.ToLower(tokenizedOption)
		currDistance := smetrics.WagnerFischer(tokenizedOption, searchstring, 1, 1, 2)
		if currDistance < leastDistance {
			matchString = option
			leastDistance = currDistance
		}
	}
	return matchString
}
func MergeStringMaps(map1 map[string]string, map2 map[string]string) map[string]string {
	if map1 == nil {
		return map2
	}
	if map2 == nil {
		return map1
	}
	for k, v := range map2 {
		map1[k] = v
	}
	return map1
}
func MergeStringSliceMaps(map1 map[string][]string, map2 map[string][]string) map[string][]string {
	if map1 == nil {
		return map2
	}
	if map2 == nil {
		return map1
	}
	for k, v := range map2 {
		map1[k] = MergeStringSlices(map1[k], v...)
	}
	return map1
}
func MakeFileNameCompliant(name string) string {
	if len(name) == 0 {
		logrus.Error("The input name is empty.")
		return ""
	}
	baseName := filepath.Base(name)
	invalidChars := regexp.MustCompile("[^a-zA-Z0-9-.]+")
	processedName := invalidChars.ReplaceAllLiteralString(baseName, "-")
	if len(processedName) > 63 {
		logrus.Debugf("Warning: The processed name %q is longer than 63 characters long.", processedName)
	}
	first := processedName[0]
	last := processedName[len(processedName)-1]
	if first == '-' || first == '.' || last == '-' || last == '.' {
		logrus.Debugf("Warning: The first and/or last characters of the name %q are not alphanumeric.", processedName)
	}
	return processedName
}
func GetSHA256Hash(s string) string {
	return fmt.Sprintf("%x", sha256.Sum256([]byte(s)))
}
func MakeStringDNSNameCompliant(s string) string {
	name := strings.ToLower(s)
	name = regexp.MustCompile(`[^a-z0-9-.]`).ReplaceAllLiteralString(name, "-")
	start, end := name[0], name[len(name)-1]
	if start == '-' || start == '.' || end == '-' || end == '.' {
		logrus.Debugf("The first and/or last characters of the string %q are not alphanumeric.", s)
	}
	return name
}
func MakeStringContainerImageNameCompliant(s string) string {
	name := strings.ToLower(s)
	name = regexp.MustCompile(`[^a-z0-9-.:]`).ReplaceAllLiteralString(name, "-")
	start, end := name[0], name[len(name)-1]
	if start == '-' || start == '.' || end == '-' || end == '.' {
		logrus.Debugf("The first and/or last characters of the string %q are not alphanumeric.", s)
	}
	return name
}
func MakeStringDNSSubdomainNameCompliant(s string) string {
	name := s
	if len(name) > 253 {
		hash := GetSHA256Hash(name)
		name = name[:253-65]  
		name = name + "-" + hash
	}
	return MakeStringDNSNameCompliant(name)
}
func MakeStringDNSLabelNameCompliant(s string) string {
	name := s
	if len(name) > 63 {
		hash := GetSHA256Hash(name)
		hash = hash[:32]
		name = name[:63-33]  
		name = name + "-" + hash
	}
	return MakeStringDNSNameCompliant(name)
}
func MakeStringEnvNameCompliant(s string) string {
	name := strings.ToUpper(s)
	name = regexp.MustCompile(`[^a-z0-9_]`).ReplaceAllLiteralString(name, "_")
	if regexp.MustCompile(`^[0-9]`).Match([]byte(name)) {
		logrus.Debugf("The first characters of the string %q must not be a digit.", s)
	}
	return name
}
func CleanAndFindCommonDirectory(paths []string) string {
	cleanedpaths := make([]string, len(paths))
	for i, path := range paths {
		cleanedpaths[i] = filepath.Clean(path)
	}
	return FindCommonDirectory(cleanedpaths)
}
func FindCommonDirectory(paths []string) string {
	if len(paths) == 0 {
		return ""
	}
	slash := string(filepath.Separator)
	commonDir := paths[0]
	for commonDir != slash {
		found := true
		for _, path := range paths {
			if !strings.HasPrefix(path+slash, commonDir+slash) {
				found = false
				break
			}
		}
		if found {
			break
		}
		commonDir = filepath.Dir(commonDir)
	}
	return commonDir
}
func CreateAssetsData(assetsFS embed.FS, permissions map[string]int) (assetsPath string, tempPath string, err error) {
	tempPath, err = filepath.Abs(TempPath)
	if err != nil {
		logrus.Errorf("Unable to make the temporary directory path %q absolute. Error: %q", tempPath, err)
		return "", "", err
	}
	assetsPath, err = filepath.Abs(AssetsPath)
	if err != nil {
		logrus.Errorf("Unable to make the assets path %q absolute. Error: %q", assetsPath, err)
		return "", "", err
	}
	if newTempPath, err := ioutil.TempDir("", types.AppName+"*"); err != nil {
		logrus.Errorf("Unable to create temp dir. Defaulting to local path.")
	} else {
		tempPath = newTempPath
		assetsPath = filepath.Join(newTempPath, AssetsDir)
	}
	if err := os.MkdirAll(assetsPath, DefaultDirectoryPermission); err != nil {
		logrus.Errorf("Unable to create the assets directory at path %q Error: %q", assetsPath, err)
		return "", "", err
	}
	if err := CopyEmbedFSToDir(assetsFS, ".", assetsPath, permissions); err != nil {
		logrus.Errorf("Unable to untar the assets into the assets directory at path %q Error: %q", assetsPath, err)
		return "", "", err
	}
	return assetsPath, tempPath, nil
}
func CopyCustomizationsAssetsData(customizationsPath string) (err error) {
	if customizationsPath == "" {
		return nil
	}
	customizationsPath, err = filepath.Abs(customizationsPath)
	if err != nil {
		logrus.Errorf("Unable to make the customizations directory path %q absolute. Error: %q", customizationsPath, err)
		return err
	}
	assetsPath, err := filepath.Abs(AssetsPath)
	if err != nil {
		logrus.Errorf("Unable to make the assets path %q absolute. Error: %q", assetsPath, err)
		return err
	}
	customizationsAssetsPath := filepath.Join(assetsPath, "custom")
	if err = os.MkdirAll(customizationsAssetsPath, DefaultDirectoryPermission); err != nil {
		logrus.Errorf("Unable to create the custom assets directory at path %q Error: %q", customizationsAssetsPath, err)
		return err
	}
	if err = filesystem.Replicate(customizationsPath, customizationsAssetsPath); err != nil {
		logrus.Errorf("Failed to copy the customizations %s over to the directory at path %s Error: %q", customizationsPath, customizationsAssetsPath, err)
		return err
	}
	return nil
}