github.com/swaros/contxt/module/taskrun@v0.0.0-20240305083542-3dbd4436ac40/varimport.go (about)

     1  // Copyright (c) 2020 Thomas Ziegler <thomas.zglr@googlemail.com>. All rights reserved.
     2  //
     3  // # Licensed under the MIT License
     4  //
     5  // Permission is hereby granted, free of charge, to any person obtaining a copy
     6  // of this software and associated documentation files (the "Software"), to deal
     7  // in the Software without restriction, including without limitation the rights
     8  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     9  // copies of the Software, and to permit persons to whom the Software is
    10  // furnished to do so, subject to the following conditions:
    11  //
    12  // The above copyright notice and this permission notice shall be included in all
    13  // copies or substantial portions of the Software.
    14  //
    15  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    16  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    17  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    18  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    19  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    20  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    21  // SOFTWARE.
    22  package taskrun
    23  
    24  import (
    25  	"bytes"
    26  	"encoding/json"
    27  	"errors"
    28  	"fmt"
    29  
    30  	"os"
    31  	"path/filepath"
    32  	"reflect"
    33  	"regexp"
    34  	"strconv"
    35  	"strings"
    36  	"text/template"
    37  
    38  	"github.com/imdario/mergo"
    39  	"github.com/tidwall/gjson"
    40  
    41  	"github.com/swaros/contxt/module/configure"
    42  	"github.com/swaros/contxt/module/systools"
    43  	"github.com/swaros/manout"
    44  
    45  	"github.com/sirupsen/logrus"
    46  
    47  	"github.com/Masterminds/sprig/v3"
    48  	"github.com/ghodss/yaml"
    49  )
    50  
    51  const (
    52  	inlineCmdSep    = " "
    53  	startMark       = "#@"
    54  	inlineMark      = "#@-"
    55  	iterateMark     = "#@foreach"
    56  	endMark         = "#@end"
    57  	fromJSONMark    = "#@import-json"
    58  	fromJSONCmdMark = "#@import-json-exec"
    59  	parseVarsMark   = "#@var"
    60  	setvarMark      = "#@set"
    61  	setvarInMap     = "#@set-in-map"
    62  	exportToYaml    = "#@export-to-yaml"
    63  	exportToJson    = "#@export-to-json"
    64  	addvarMark      = "#@add"
    65  	equalsMark      = "#@if-equals"
    66  	notEqualsMark   = "#@if-not-equals"
    67  	osCheck         = "#@if-os"
    68  	codeLinePH      = "__LINE__"
    69  	codeKeyPH       = "__KEY__"
    70  	writeVarToFile  = "#@var-to-file"
    71  )
    72  
    73  // TryParse to parse a line and set a value depending on the line command
    74  func TryParse(script []string, regularScript func(string) (bool, int)) (bool, int, []string) {
    75  	inIteration := false
    76  	inIfState := false
    77  	ifState := true
    78  	var iterationLines []string
    79  	var parsedScript []string
    80  	var iterationCollect gjson.Result
    81  	for _, line := range script {
    82  		line = HandlePlaceHolder(line)
    83  		if len(line) > len(startMark) && line[0:len(startMark)] == startMark {
    84  			//parts := strings.Split(line, inlineCmdSep)
    85  			parts := SplitQuoted(line, inlineCmdSep)
    86  
    87  			GetLogger().WithField("keywords", parts).Debug("try to parse parts")
    88  			if len(parts) < 1 {
    89  				continue
    90  			}
    91  			switch parts[0] {
    92  
    93  			case osCheck:
    94  				if !inIfState {
    95  					if len(parts) == 2 {
    96  						leftEq := parts[1]
    97  						rightEq := configure.GetOs()
    98  						inIfState = true
    99  						ifState = leftEq == rightEq
   100  					} else {
   101  						manout.Error("invalid usage ", equalsMark, " need: str1 str2 ")
   102  					}
   103  				} else {
   104  					manout.Error("invalid usage ", equalsMark, " can not be used in another if")
   105  				}
   106  
   107  			case equalsMark:
   108  				if !inIfState {
   109  					if len(parts) == 3 {
   110  						leftEq := parts[1]
   111  						rightEq := parts[2]
   112  						inIfState = true
   113  						ifState = leftEq == rightEq
   114  						GetLogger().WithFields(logrus.Fields{"condition": ifState, "left": leftEq, "right": rightEq}).Debug(equalsMark)
   115  					} else {
   116  						manout.Error("invalid usage ", equalsMark, " need: str1 str2 (got:", len(parts), ")")
   117  					}
   118  				} else {
   119  					manout.Error("invalid usage ", equalsMark, " can not be used in another if")
   120  				}
   121  
   122  			case notEqualsMark:
   123  				if !inIfState {
   124  					if len(parts) == 3 {
   125  						leftEq := parts[1]
   126  						rightEq := parts[2]
   127  						inIfState = true
   128  						ifState = leftEq != rightEq
   129  						GetLogger().WithFields(logrus.Fields{"condition": ifState, "left": leftEq, "right": rightEq}).Debug(notEqualsMark)
   130  					} else {
   131  						manout.Error("invalid usage ", notEqualsMark, " need: str1 str2 (got:", len(parts), ")")
   132  					}
   133  				} else {
   134  					manout.Error("invalid usage ", equalsMark, " can not be used in another if")
   135  				}
   136  
   137  			case inlineMark:
   138  				if inIteration {
   139  					iterationLines = append(iterationLines, strings.Replace(line, inlineMark+" ", "", 4))
   140  					GetLogger().WithField("code", iterationLines).Debug("append to subscript")
   141  				} else {
   142  					manout.Error("invalid usage", inlineMark, " only valid while in iteration")
   143  				}
   144  
   145  			case fromJSONMark:
   146  				if len(parts) == 3 {
   147  					err := AddJSON(parts[1], parts[2])
   148  					if err != nil {
   149  						manout.Error("import from json string failed", parts[2], err)
   150  					}
   151  				} else {
   152  					manout.Error("invalid usage", fromJSONMark, " needs 2 arguments. <keyname> <json-source-string>")
   153  				}
   154  
   155  			case fromJSONCmdMark:
   156  				if len(parts) >= 3 {
   157  					returnValue := ""
   158  					restSlice := parts[2:]
   159  					keyname := parts[1]
   160  					cmd := strings.Join(restSlice, " ")
   161  					GetLogger().WithFields(logrus.Fields{"key": keyname, "cmd": restSlice}).Info("execute for import-json-exec")
   162  					//GetLogger().WithField("slice", restSlice).Info("execute for import-json-exec")
   163  					exec, args := GetExecDefaults()
   164  					execCode, realExitCode, execErr := ExecuteScriptLine(exec, args, cmd, func(output string, e error) bool {
   165  						returnValue = returnValue + output
   166  						GetLogger().WithField("cmd-output", output).Info("result of command")
   167  						return true
   168  					}, func(proc *os.Process) {
   169  						GetLogger().WithField("import-json-proc", proc).Trace("import-json-process")
   170  					})
   171  
   172  					if execErr != nil {
   173  						GetLogger().WithFields(logrus.Fields{
   174  							"intern":       execCode,
   175  							"process-exit": realExitCode,
   176  							"key":          keyname,
   177  							"cmd":          restSlice}).Error("execute for import-json-exec failed")
   178  					} else {
   179  
   180  						err := AddJSON(keyname, returnValue)
   181  						if err != nil {
   182  							GetLogger().WithField("error-on-parsing-string", returnValue).Debug("result of command")
   183  							manout.Error("import from json string failed", err, ' ', systools.StringSubLeft(returnValue, 75))
   184  						}
   185  					}
   186  				} else {
   187  					manout.Error("invalid usage", fromJSONCmdMark, " needs 2 arguments at least. <keyname> <bash-command>")
   188  				}
   189  			case addvarMark:
   190  				if len(parts) >= 2 {
   191  					setKeyname := parts[1]
   192  					setValue := strings.Join(parts[2:], " ")
   193  					if ok := AppendToPH(setKeyname, HandlePlaceHolder(setValue)); !ok {
   194  						manout.Error("variable must exists for add ", addvarMark, " ", setKeyname)
   195  					}
   196  				} else {
   197  					manout.Error("invalid usage", setvarMark, " needs 2 arguments at least. <keyname> <value>")
   198  				}
   199  			case setvarMark:
   200  				if len(parts) >= 2 {
   201  					setKeyname := parts[1]
   202  					setValue := strings.Join(parts[2:], " ")
   203  					SetPH(setKeyname, HandlePlaceHolder(setValue))
   204  				} else {
   205  					manout.Error("invalid usage", setvarMark, " needs 2 arguments at least. <keyname> <value>")
   206  				}
   207  			case setvarInMap:
   208  				if len(parts) >= 3 {
   209  					mapName := parts[1]
   210  					path := parts[2]
   211  					setValue := strings.Join(parts[3:], " ")
   212  					if err := SetJSONValueByPath(mapName, path, setValue); err != nil {
   213  						manout.Error(err.Error())
   214  					}
   215  				} else {
   216  					manout.Error("invalid usage", setvarInMap, " needs 3 arguments at least. <mapName> <json.path> <value>")
   217  				}
   218  			case writeVarToFile:
   219  				if len(parts) == 3 {
   220  					varName := parts[1]
   221  					fileName := parts[2]
   222  					ExportVarToFile(varName, fileName)
   223  				} else {
   224  					manout.Error("invalid usage", writeVarToFile, " needs 2 arguments at least. <variable> <filename>")
   225  				}
   226  			case exportToJson:
   227  				if len(parts) == 3 {
   228  					mapKey := parts[1]
   229  					varName := parts[2]
   230  					if exists, newStr := GetDataAsJson(mapKey); exists {
   231  						SetPH(varName, HandlePlaceHolder(newStr))
   232  					} else {
   233  						manout.Error("map with key ", mapKey, " not exists")
   234  					}
   235  				} else {
   236  					manout.Error("invalid usage", exportToJson, " needs 2 arguments at least. <map-key> <variable>")
   237  				}
   238  			case exportToYaml:
   239  				if len(parts) == 3 {
   240  					mapKey := parts[1]
   241  					varName := parts[2]
   242  					if exists, newStr := GetDataAsYaml(mapKey); exists {
   243  						SetPH(varName, HandlePlaceHolder(newStr))
   244  					} else {
   245  						manout.Error("map with key ", mapKey, " not exists")
   246  					}
   247  				} else {
   248  					manout.Error("invalid usage", exportToYaml, " needs 2 arguments at least. <map-key> <variable>")
   249  				}
   250  			case parseVarsMark:
   251  				if len(parts) >= 2 {
   252  					var returnValues []string
   253  					restSlice := parts[2:]
   254  					cmd := strings.Join(restSlice, " ")
   255  					exec, args := GetExecDefaults()
   256  					internalCode, cmdCode, errorFromCm := ExecuteScriptLine(exec, args, cmd, func(output string, e error) bool {
   257  						if e == nil {
   258  							returnValues = append(returnValues, output)
   259  						}
   260  						return true
   261  
   262  					}, func(proc *os.Process) {
   263  						GetLogger().WithField(parseVarsMark, proc).Trace("sub process")
   264  					})
   265  
   266  					if internalCode == systools.ExitOk && errorFromCm == nil && cmdCode == 0 {
   267  						GetLogger().WithField("values", returnValues).Trace("got values")
   268  						SetPH(parts[1], HandlePlaceHolder(strings.Join(returnValues, "\n")))
   269  					} else {
   270  						GetLogger().WithFields(logrus.Fields{
   271  							"returnCode": cmdCode,
   272  							"error":      errorFromCm.Error,
   273  						}).Error("subcommand failed.")
   274  						manout.Error("Subcommand failed", cmd, " ... was used to get json context. ", errorFromCm.Error())
   275  						manout.Error("cmd:", exec, "  ", args, " ", cmd)
   276  					}
   277  
   278  				} else {
   279  					manout.Error("invalid usage", parseVarsMark, " needs 2 arguments at least. <varibale-name> <bash-command>")
   280  				}
   281  
   282  			case endMark:
   283  
   284  				if inIfState {
   285  					GetLogger().Debug("IF: DONE")
   286  					inIfState = false
   287  					ifState = true
   288  				}
   289  				if inIteration {
   290  					GetLogger().Debug("ITERATION: DONE")
   291  					inIteration = false
   292  					abortFound := false
   293  					returnCode := systools.ExitOk
   294  
   295  					iterationCollect.ForEach(func(key gjson.Result, value gjson.Result) bool {
   296  						var parsedExecLines []string
   297  						for _, iLine := range iterationLines {
   298  							iLine = strings.Replace(iLine, codeLinePH, value.String(), 1)
   299  							iLine = strings.Replace(iLine, codeKeyPH, key.String(), 1)
   300  							parsedExecLines = append(parsedExecLines, iLine)
   301  						}
   302  						GetLogger().WithFields(logrus.Fields{
   303  							"key":       key,
   304  							"value":     value,
   305  							"subscript": parsedExecLines,
   306  						}).Debug("... delegate script")
   307  						abort, rCode, subs := TryParse(parsedExecLines, regularScript)
   308  						returnCode = rCode
   309  						parsedScript = append(parsedScript, subs...)
   310  
   311  						if abort {
   312  							abortFound = true
   313  							return false
   314  						}
   315  						return true
   316  					})
   317  
   318  					if abortFound {
   319  						return true, returnCode, parsedScript
   320  					}
   321  				}
   322  
   323  			case iterateMark:
   324  				if len(parts) == 3 {
   325  					impMap, found := GetJSONPathResult(parts[1], parts[2])
   326  					if !found {
   327  						manout.Error("undefined data from path", parts[1], parts[2])
   328  					} else {
   329  						inIteration = true
   330  						iterationCollect = impMap
   331  						GetLogger().WithField("data", impMap).Debug("ITERATION: START")
   332  					}
   333  				} else {
   334  					manout.Error("invalid arguments", "#@iterate needs <name-of-import> <path-to-data>")
   335  				}
   336  			default:
   337  				GetLogger().WithField("unknown", parts[0]).Error("there is no command exists")
   338  				manout.Error("ERROR depending inline macros annotated with "+startMark, " there is no macro defined named ", parts[0])
   339  			}
   340  		} else {
   341  			parsedScript = append(parsedScript, line)
   342  			// execute the *real* script lines
   343  			if ifState {
   344  				abort, returnCode := regularScript(line)
   345  				if abort {
   346  					return true, returnCode, parsedScript
   347  				}
   348  			} else {
   349  				GetLogger().WithField("code", line).Debug("ignored because of if state")
   350  			}
   351  		}
   352  	}
   353  	GetLogger().WithFields(logrus.Fields{
   354  		"parsed": parsedScript,
   355  	}).Debug("... parsed result")
   356  	return false, systools.ExitOk, parsedScript
   357  }
   358  
   359  func GetArgQuotedEntries(oristr string) ([]string, bool) {
   360  	var result []string
   361  	found := false
   362  	re := regexp.MustCompile(`'[^']+'`)
   363  	newStrs := re.FindAllString(oristr, -1)
   364  	for _, s := range newStrs {
   365  		found = true
   366  		result = append(result, s)
   367  
   368  	}
   369  	return result, found
   370  }
   371  
   372  func SplitQuoted(oristr string, sep string) []string {
   373  	var result []string
   374  	var placeHolder map[string]string = make(map[string]string)
   375  
   376  	found := false
   377  	re := regexp.MustCompile(`'[^']+'`)
   378  	newStrs := re.FindAllString(oristr, -1)
   379  	i := 0
   380  	for _, s := range newStrs {
   381  		pl := "[$" + strconv.Itoa(i) + "]"
   382  		placeHolder[pl] = strings.ReplaceAll(s, `'`, "")
   383  		oristr = strings.Replace(oristr, s, pl, 1)
   384  		found = true
   385  		i++
   386  	}
   387  	result = strings.Split(oristr, sep)
   388  	if !found {
   389  		return result
   390  	}
   391  
   392  	for index, val := range result {
   393  		if orgStr, fnd := placeHolder[val]; fnd {
   394  			result[index] = orgStr
   395  		}
   396  	}
   397  
   398  	return result
   399  }
   400  
   401  // YAMLToMap Convert yaml source string into map
   402  func YAMLToMap(source string) (map[string]interface{}, error) {
   403  	jsond, jerr := yaml.YAMLToJSON([]byte(source))
   404  	if jerr != nil {
   405  		return nil, jerr
   406  	}
   407  	m := make(map[string]interface{})
   408  	if err := json.Unmarshal([]byte(jsond), &m); err != nil {
   409  		return nil, err
   410  	}
   411  	return m, nil
   412  }
   413  
   414  func ExportVarToFile(variable string, filename string) error {
   415  	strData := GetPH(variable)
   416  	if strData == "" {
   417  		return errors.New("variable " + variable + " can not be used for export to file. not exists or empty")
   418  	}
   419  	f, err := os.Create(filename)
   420  	if err != nil {
   421  		return err
   422  	}
   423  	defer f.Close()
   424  	var scopeVars map[string]string // empty but required
   425  	if _, err2 := f.WriteString(handlePlaceHolder(strData, scopeVars)); err != nil {
   426  		return err2
   427  	}
   428  
   429  	return nil
   430  }
   431  
   432  // ImportYAMLFile imports a yaml file as used for json map
   433  func ImportYAMLFile(filename string) (map[string]interface{}, error) {
   434  	if data, err := parseFileAsTemplateToByte(filename); err == nil {
   435  		jsond, jerr := yaml.YAMLToJSON(data)
   436  		if jerr != nil {
   437  			return nil, jerr
   438  		}
   439  		m := make(map[string]interface{})
   440  		if err := json.Unmarshal([]byte(jsond), &m); err != nil {
   441  			return nil, err
   442  		}
   443  		if GetLogger().IsLevelEnabled(logrus.TraceLevel) {
   444  			traceMap(m, filename)
   445  		}
   446  		return m, nil
   447  	} else {
   448  		return nil, err
   449  	}
   450  }
   451  
   452  // ImportJSONFile imports a json file for reading
   453  func ImportJSONFile(fileName string) (map[string]interface{}, error) {
   454  
   455  	if data, err := parseFileAsTemplateToByte(fileName); err == nil {
   456  		m := make(map[string]interface{})
   457  		err = json.Unmarshal([]byte(data), &m)
   458  		if err != nil {
   459  			return testAndConvertJsonType(data)
   460  		}
   461  		if GetLogger().IsLevelEnabled(logrus.TraceLevel) {
   462  			traceMap(m, fileName)
   463  		}
   464  		return m, nil
   465  	} else {
   466  		return nil, err
   467  	}
   468  
   469  }
   470  
   471  // testAndConvertJsonType try to read a json string that might be an []interface{}
   472  // if this succeeds then we convert it to an map[string]interface{}
   473  // or return the UNmarschal error if this is failing too
   474  func testAndConvertJsonType(data []byte) (map[string]interface{}, error) {
   475  	var m []interface{}
   476  	convert := make(map[string]interface{})
   477  	if err := json.Unmarshal([]byte(data), &m); err == nil {
   478  		for key, val := range m {
   479  			keyStr := fmt.Sprintf("%d", key)
   480  			switch val.(type) {
   481  			case string, interface{}:
   482  				convert[keyStr] = val
   483  			default:
   484  				return nil, errors.New("unsupported json structure")
   485  
   486  			}
   487  
   488  		}
   489  		return convert, err
   490  	} else {
   491  		return convert, err
   492  	}
   493  }
   494  
   495  func traceMap(mapShow map[string]interface{}, add string) {
   496  	for k, v := range mapShow {
   497  		//mapShow[k] = v
   498  		//GetLogger().WithField("VAR", v).Trace("imported placeholder from " + add + " " + k)
   499  		GetLogger().WithFields(logrus.Fields{
   500  			"source":  add,
   501  			"key":     k,
   502  			"content": v,
   503  		}).Trace("imported content")
   504  	}
   505  }
   506  
   507  // MergeVariableMap merges two maps
   508  func MergeVariableMap(mapin map[string]interface{}, maporigin map[string]interface{}) map[string]interface{} {
   509  	if err := mergo.Merge(&maporigin, mapin, mergo.WithOverride); err != nil {
   510  		manout.Error("FATAL", "error while trying merge map")
   511  		systools.Exit(systools.ErrorTemplate)
   512  	}
   513  	return maporigin
   514  }
   515  
   516  // ImportFolders import a list of folders recusiv
   517  func ImportFolders(templatePath string, paths ...string) (string, error) {
   518  	mapOrigin := GetOriginMap()
   519  
   520  	template, terr := ImportFileContent(templatePath)
   521  	if terr != nil {
   522  		return "", terr
   523  	}
   524  
   525  	for _, path := range paths {
   526  		path = HandlePlaceHolder(path)
   527  		GetLogger().WithField("folder", path).Debug("process path")
   528  		pathMap, parseErr := ImportFolder(path, templatePath)
   529  		if parseErr != nil {
   530  			return "", parseErr
   531  		}
   532  		mapOrigin = MergeVariableMap(pathMap, mapOrigin)
   533  		UpdateOriginMap(mapOrigin)
   534  	}
   535  	result, herr := HandleJSONMap(template, mapOrigin)
   536  	if herr != nil {
   537  		return "", herr
   538  	}
   539  	template = result
   540  
   541  	return template, nil
   542  }
   543  
   544  func parseFileAsTemplateToByte(path string) ([]byte, error) {
   545  	var data []byte
   546  	if parsedCnt, err := ParseFileAsTemplate(path); err != nil {
   547  		return nil, err
   548  	} else {
   549  		data = []byte(parsedCnt)
   550  		return data, nil
   551  	}
   552  
   553  }
   554  
   555  func ParseFileAsTemplate(path string) (string, error) {
   556  	path = HandlePlaceHolder(path)               // take care about placeholders
   557  	mapOrigin := GetOriginMap()                  // load the current maps
   558  	fileContent, terr := ImportFileContent(path) // load file content as string
   559  	if terr != nil {
   560  		return "", terr
   561  	}
   562  
   563  	// parsing as template
   564  	if result, herr := HandleJSONMap(fileContent, mapOrigin); herr != nil {
   565  		return "", herr
   566  	} else {
   567  		return result, nil
   568  	}
   569  }
   570  
   571  func GetOriginMap() map[string]interface{} {
   572  	exists, storedData := GetData("CTX_VAR_MAP")
   573  	if exists {
   574  		GetLogger().WithField("DATA", storedData).Trace("returning existing Variables map")
   575  		return storedData
   576  	}
   577  	mapOrigin := make(map[string]interface{})
   578  	GetLogger().Trace("returning NEW Variables map")
   579  	return mapOrigin
   580  }
   581  
   582  // UpdateOriginMap updates the main templating map with an new one
   583  func UpdateOriginMap(mapData map[string]interface{}) {
   584  	GetLogger().WithField("DATA", mapData).Trace("update variables map")
   585  	AddData("CTX_VAR_MAP", mapData)
   586  }
   587  
   588  // copies the placeholder to the origin map
   589  // so there can be used in templates to
   590  // this should be done after initilize the application.
   591  // but not while runtime
   592  func CopyPlaceHolder2Origin() {
   593  	origin := GetOriginMap()
   594  	GetPlaceHoldersFnc(func(phKey, phValue string) {
   595  		origin[phKey] = phValue
   596  	})
   597  	UpdateOriginMap(origin)
   598  }
   599  
   600  // ImportFolder reads folder recursiv and reads all .json, .yml and .yaml files
   601  func ImportFolder(path string, _ string) (map[string]interface{}, error) {
   602  
   603  	//var mapOrigin map[string]interface{}
   604  	//mapOrigin = make(map[string]interface{})
   605  	mapOrigin := GetOriginMap()
   606  
   607  	err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
   608  		if err != nil {
   609  			return err
   610  		}
   611  		var jsonMap map[string]interface{}
   612  		var loaderr error
   613  		hit := false
   614  		if !info.IsDir() {
   615  			var extension = filepath.Ext(path)
   616  			var basename = filepath.Base(path)
   617  			if basename == ".contxt.yml" {
   618  				return nil
   619  			}
   620  			switch extension {
   621  			case ".json":
   622  				GetLogger().WithField("file", path).Debug("parsing included file (JSON)")
   623  				jsonMap, loaderr = ImportJSONFile(path)
   624  				hit = true
   625  			case ".yaml", ".yml":
   626  				GetLogger().WithField("file", path).Debug("parsing included file (YAML)")
   627  				jsonMap, loaderr = ImportYAMLFile(path)
   628  				hit = true
   629  			}
   630  			if loaderr != nil {
   631  				return loaderr
   632  			}
   633  			if hit {
   634  				GetLogger().WithFields(logrus.Fields{
   635  					"origin":   mapOrigin,
   636  					"imported": jsonMap,
   637  				}).Trace("merged Variable map")
   638  				mapOrigin = MergeVariableMap(jsonMap, mapOrigin)
   639  				GetLogger().WithField("result", mapOrigin).Trace("result of merge")
   640  			}
   641  		}
   642  
   643  		return nil
   644  	})
   645  	return mapOrigin, err
   646  }
   647  
   648  // ImportFileContent imports a file and returns content as string
   649  func ImportFileContent(filename string) (string, error) {
   650  	GetLogger().WithField("file", filename).Debug("import file content")
   651  	data, err := os.ReadFile(filename)
   652  	if err != nil {
   653  		fmt.Println("File reading error", err)
   654  		return "", err
   655  	}
   656  	return string(data), nil
   657  }
   658  
   659  // HandleJSONMap parsing json content for text/template
   660  func HandleJSONMap(tmpl string, m map[string]interface{}) (string, error) {
   661  	tf := template.FuncMap{
   662  		"isInt": func(i interface{}) bool {
   663  			v := reflect.ValueOf(i)
   664  			switch v.Kind() {
   665  			case reflect.Int, reflect.Int8, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64:
   666  				return true
   667  			default:
   668  				return false
   669  			}
   670  		},
   671  		"isString": func(i interface{}) bool {
   672  			v := reflect.ValueOf(i)
   673  			switch v.Kind() {
   674  			case reflect.String:
   675  				return true
   676  			default:
   677  				return false
   678  			}
   679  		},
   680  		"isSlice": func(i interface{}) bool {
   681  			v := reflect.ValueOf(i)
   682  			switch v.Kind() {
   683  			case reflect.Slice:
   684  				return true
   685  			default:
   686  				return false
   687  			}
   688  		},
   689  		"isArray": func(i interface{}) bool {
   690  			v := reflect.ValueOf(i)
   691  			switch v.Kind() {
   692  			case reflect.Array:
   693  				return true
   694  			default:
   695  				return false
   696  			}
   697  		},
   698  		"isMap": func(i interface{}) bool {
   699  			v := reflect.ValueOf(i)
   700  			switch v.Kind() {
   701  			case reflect.Map:
   702  				return true
   703  			default:
   704  				return false
   705  			}
   706  		},
   707  	}
   708  	funcMap := MergeVariableMap(tf, sprig.FuncMap())
   709  	tpl := template.New("contxt-map-string-func").Funcs(funcMap)
   710  	tt, err := tpl.Parse(tmpl)
   711  	if err != nil {
   712  		return "", err
   713  	}
   714  	out := new(bytes.Buffer)
   715  	tt.Execute(out, &m)
   716  	if err != nil {
   717  		return "", err
   718  	}
   719  	return out.String(), nil
   720  
   721  }
   722  
   723  /*
   724  func IsList(i interface{}) bool {
   725  	v := reflect.ValueOf(i).Kind()
   726  	return v == reflect.Array || v == reflect.Slice
   727  }
   728  
   729  func IsNumber(i interface{}) bool {
   730  	v := reflect.ValueOf(i).Kind()
   731  	switch v {
   732  	case reflect.Int, reflect.Int8, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64:
   733  		return true
   734  	default:
   735  		return false
   736  	}
   737  }
   738  
   739  func IsInt(i interface{}) bool {
   740  	v := reflect.ValueOf(i).Kind()
   741  	switch v {
   742  	case reflect.Int, reflect.Int8, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint32, reflect.Uint64:
   743  		return true
   744  	default:
   745  		return false
   746  	}
   747  }
   748  
   749  func IsFloat(i interface{}) bool {
   750  	v := reflect.ValueOf(i).Kind()
   751  	return v == reflect.Float32 || v == reflect.Float64
   752  }
   753  */