github.com/upcmd/up@v0.8.1-0.20230108151705-ad8b797bf04f/biz/impl/template.go (about) 1 // Ultimate Provisioner: UP cmd 2 // Copyright (c) 2019 Stephen Cheng and contributors 3 4 /* This Source Code Form is subject to the terms of the Mozilla Public 5 * License, v. 2.0. If a copy of the MPL was not distributed with this 6 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 7 8 package impl 9 10 import ( 11 "bytes" 12 "github.com/Masterminds/sprig/v3" 13 "github.com/upcmd/up/model/core" 14 u "github.com/upcmd/up/utils" 15 "io/ioutil" 16 "os" 17 "path/filepath" 18 "runtime" 19 "strings" 20 "time" 21 22 "github.com/leekchan/gtf" 23 "text/template" 24 ) 25 26 var ( 27 templateFuncs template.FuncMap 28 taskFuncs template.FuncMap 29 ) 30 31 func safeReg(varname string, object interface{}) string { 32 //if this is in dvar processing: 33 //need to one way sync the var to the returning var 34 TaskRuntime().ExecbaseVars.Put(varname, object) 35 36 //remove this as it will cause dirty data due to dvar processing 37 //StepRuntime().ContextVars.Put(varname, object) 38 //instead we do a callback to save it to dvar processing scope 39 if !StepRuntime().DataSyncInDvarExpand(varname, object) { 40 StepRuntime().ContextVars.Put(varname, object) 41 } 42 43 return core.ObjToYaml(object) 44 } 45 46 func FuncMapInit() { 47 taskFuncs = template.FuncMap{ 48 "OS": func() string { return runtime.GOOS }, 49 "ARCH": func() string { return runtime.GOARCH }, 50 "catLines": func(s string) string { 51 s = strings.Replace(s, "\r\n", " ", -1) 52 return strings.Replace(s, "\n", " ", -1) 53 }, 54 "splitLines": func(s string) []string { 55 s = strings.Replace(s, "\r\n", "\n", -1) 56 return strings.Split(s, "\n") 57 }, 58 "fromSlash": func(path string) string { 59 return filepath.FromSlash(path) 60 }, 61 "toSlash": func(path string) string { 62 return filepath.ToSlash(path) 63 }, 64 //-------------------------------------------------------- 65 "now": func() string { 66 t := time.Now() 67 return t.Format("2006-01-02T15:04:05+11:00") 68 }, 69 "printObj": func(obj interface{}) string { 70 u.Ppmsg(obj) 71 return u.Sppmsg(obj) 72 }, 73 "objToYml": func(obj interface{}) string { 74 yml := core.ObjToYaml(obj) 75 u.PpmsgvvvvvHigh("objToYml", yml) 76 return yml 77 }, 78 "ymlToObj": func(yml string) interface{} { 79 obj := core.YamlToObj(yml) 80 u.PpmsgvvvvvHigh("ymlToObj", obj) 81 return obj 82 }, 83 //use the encryption key stored in vault instead of plain value in general cache 84 "encrypteAesWithVault": func(enckeyName string, plain string) string { 85 var encryptionkey string 86 if enckeyName != "" { 87 //use vault as first priority 88 opt := GetVault().Get(enckeyName) 89 if opt == nil { 90 opt = (StepRuntime().ContextVars).Get(enckeyName) 91 } 92 encryptionkey = opt.(string) 93 } 94 95 encrypted := Render(u.Spf(`{{encryptAES "%s" "%s"}}`, encryptionkey, plain), "") 96 97 return encrypted 98 }, 99 //retrieve value from vault 100 "fromVault": func(varname string) string { 101 var val string = "NotExistInVault" 102 opt := GetVault().Get(varname) 103 if opt != nil { 104 val = opt.(string) 105 } 106 107 return val 108 }, 109 //regObj will keep the golang object intact and register it to cache 110 //same effect of: '{{ func_return_a_obj arg | objToYml|ymlToObj|reg "instances" }}' 111 "regObj": func(varname string, obj interface{}) interface{} { 112 yml := core.ObjToYaml(obj) 113 regObj := core.YamlToObj(yml) 114 safeReg(varname, regObj) 115 u.PpmsgvvvvvHigh("ymlToObj", regObj) 116 return obj 117 }, 118 "loopRange": func(start, end int64, regname string) string { 119 var looplist []int64 = []int64{} 120 for i := start; i <= end; i++ { 121 looplist = append(looplist, i) 122 } 123 safeReg(regname, looplist) 124 return regname 125 }, 126 //reg do not return any value, so do not expect the dvar value will be something other than empty 127 "reg": safeReg, 128 "deReg": func(varname string) string { 129 TaskRuntime().ExecbaseVars.Delete(varname) 130 return "" 131 }, 132 //keep envExport only in template func as to be carried to any type of func implementation 133 "envExport": func(expType, fileToSave string) string { 134 var expStr string 135 switch expType { 136 case "exec_base_env_vars_configured": 137 expStr = TaskerRuntime().Tasker.reportContextualEnvVars(TaskRuntime().ExecbaseVars) 138 case "exec_env_vars_configured": 139 expStr = TaskerRuntime().Tasker.reportContextualEnvVars(StepRuntime().ContextVars) 140 } 141 142 if fileToSave != "" { 143 ioutil.WriteFile(fileToSave, []byte(expStr), 0644) 144 } 145 146 return expStr 147 }, 148 "pathExisted": func(path string) bool { 149 pathtstr := u.Spf("{{.%s}}", path) 150 return ElementValid(pathtstr, StepRuntime().ContextVars) 151 }, 152 "fileContent": func(filepath string) string { 153 if _, err := os.Stat(filepath); os.IsNotExist(err) { 154 u.LogWarn("fileContent readFile", u.Spf("please fix file path: %s", filepath)) 155 return "" 156 } else { 157 content, err := ioutil.ReadFile(filepath) 158 if err != nil { 159 u.LogWarn("fileContent readFile", u.Spf("please fix file read error, path: %s", filepath)) 160 } 161 return string(content) 162 } 163 }, 164 "validateMandatoryFailIfNone": func(varname, varvalue string) string { 165 if varvalue == "" { 166 u.InvalidAndPanic("validateMandatoryFailIfNone", u.Spf("Required var:(%s) must not be empty, please fix it", varname)) 167 } 168 return varvalue 169 }, 170 } 171 172 templateFuncs = sprig.TxtFuncMap() 173 174 for k, v := range gtf.GtfTextFuncMap { 175 if _, ok := templateFuncs[k]; !ok { 176 //does not exit, add it to funcmap now 177 templateFuncs[k] = v 178 } 179 } 180 181 for k, v := range taskFuncs { 182 templateFuncs[k] = v 183 } 184 } 185 186 func ToJson(str string) string { 187 return Render("{{toJson .}}", str) 188 } 189 190 func Render(tstr string, obj interface{}) string { 191 tname := "." 192 t, err := template.New(tname).Funcs(templateFuncs).Parse(tstr) 193 194 u.LogErrorAndPanic("template creating", err, u.ContentWithLineNumber(tstr)) 195 196 var result bytes.Buffer 197 err = t.Execute(&result, obj) 198 u.LogErrorAndContinue("template rendering", err, u.ContentWithLineNumber(tstr)) 199 200 val := result.String() 201 if "<no value>" == val { 202 val = NONE_VALUE 203 } 204 205 return val 206 } 207 208 func ElementValid(path string, obj interface{}) bool { 209 210 t, err := template.New("validator").Funcs(templateFuncs).Parse(path) 211 212 u.LogErrorAndContinue("template element validating", err, "Please fix the template issue and try again") 213 214 var result bytes.Buffer 215 err = t.Execute(&result, obj) 216 u.LogErrorAndContinue("element validating problem", err, u.ContentWithLineNumber(path)) 217 218 if err != nil { 219 return false 220 } else if result.String() == "<no value>" { 221 return false 222 } else { 223 return true 224 } 225 } 226 227 func ListAllFuncs() { 228 229 var builtins = map[string]string{ 230 "and": "and", 231 "call": "call", 232 "html": "HTMLEscaper", 233 "index": "index", 234 "js": "JSEscaper", 235 "len": "length", 236 "not": "not", 237 "or": "or", 238 "print": "fmt.Sprint", 239 "printf": "fmt.Sprintf", 240 "println": "fmt.Sprintln", 241 "urlquery": "URLQueryEscaper", 242 243 // Comparisons 244 "eq": "eq", // == 245 "ge": "ge", // >= 246 "gt": "gt", // > 247 "le": "le", // <= 248 "lt": "lt", // < 249 "ne": "ne", // != 250 } 251 252 for k, v := range builtins { 253 u.Pf("%30s : %#v\n", k, v) 254 } 255 256 for k, v := range templateFuncs { 257 u.Pf("%30s : %#v\n", k, v) 258 } 259 260 } 261 func ListUpcmdFuncs() { 262 for k, v := range taskFuncs { 263 u.Pf("%30s : %#v\n", k, v) 264 } 265 266 }