github.com/upcmd/up@v0.8.1-0.20230108151705-ad8b797bf04f/biz/impl/cmdfunc.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  	"bufio"
    12  	"bytes"
    13  	"encoding/base64"
    14  	"fmt"
    15  	"github.com/fatih/color"
    16  	ms "github.com/mitchellh/mapstructure"
    17  	"github.com/upcmd/up/model"
    18  	"github.com/upcmd/up/model/core"
    19  	u "github.com/upcmd/up/utils"
    20  	yq "github.com/upcmd/yq/v3/cmd"
    21  	"gopkg.in/yaml.v2"
    22  	"io/ioutil"
    23  	"os"
    24  	"os/exec"
    25  	"path"
    26  	"strconv"
    27  	"strings"
    28  )
    29  
    30  type CmdFuncAction struct {
    31  	Do   interface{}
    32  	Vars *core.Cache
    33  	Cmds *CmdCmds
    34  }
    35  
    36  type CmdCmd struct {
    37  	Name  string
    38  	Desc  string
    39  	Cmd   interface{}
    40  	Cmdx  interface{}
    41  	Flags []string
    42  }
    43  
    44  type GeneralCmd struct {
    45  	Name  string
    46  	Value string
    47  }
    48  
    49  type CmdCmds []CmdCmd
    50  
    51  func (f *CmdFuncAction) Adapt() {
    52  	var cmds CmdCmds
    53  
    54  	switch f.Do.(type) {
    55  	case []interface{}:
    56  		err := ms.Decode(f.Do, &cmds)
    57  		u.LogErrorAndPanic("Cmd adapter", err, "please fix cmd command configuration")
    58  
    59  	default:
    60  		u.LogWarn("cmd", "Not implemented or void for no action!")
    61  	}
    62  	f.Cmds = &cmds
    63  }
    64  
    65  func (cmdCmd *CmdCmd) runCmd(whichtype string, f func()) {
    66  	invalidTypeHint := func(typeGot string) {
    67  		u.LogWarn("type mismatch", u.Spf("cmd name: %s -> type wanted: %s, got :%s", cmdCmd.Name, whichtype, typeGot))
    68  	}
    69  	switch t := cmdCmd.Cmd.(type) {
    70  	case string:
    71  		if whichtype == "string" {
    72  			f()
    73  		} else {
    74  			invalidTypeHint("string")
    75  		}
    76  
    77  	case int:
    78  		if whichtype == "int" {
    79  			f()
    80  		} else {
    81  			invalidTypeHint("int")
    82  		}
    83  
    84  	case map[interface{}]interface{}:
    85  		if whichtype == "map" {
    86  			f()
    87  		} else {
    88  			invalidTypeHint("map")
    89  		}
    90  
    91  	case []interface{}:
    92  		if whichtype == "array" {
    93  			f()
    94  		} else {
    95  			invalidTypeHint("array")
    96  		}
    97  
    98  	case nil:
    99  		if cmdCmd.Cmdx != nil {
   100  			u.LogWarn("cmd", "temporarily deactivated")
   101  		} else {
   102  			u.LogWarn("cmd", "lacking detailed implementation yet")
   103  		}
   104  
   105  	default:
   106  		u.LogWarn("cmd", u.Spf("Not implemented type(%T) or void for no action!", t))
   107  	}
   108  
   109  }
   110  
   111  func decodeABinaryFile(srcpath, destpath string) {
   112  	if _, err := os.Stat(srcpath); !os.IsNotExist(err) {
   113  		f, _ := os.Open(srcpath)
   114  		defer f.Close()
   115  		reader := bufio.NewReader(f)
   116  		encoded, _ := ioutil.ReadAll(reader)
   117  
   118  		decoded, err := base64.StdEncoding.DecodeString(string(encoded))
   119  		if err != nil {
   120  			u.LogWarn("base64 decode", "base64 decoding failed")
   121  		}
   122  
   123  		e := ioutil.WriteFile(destpath, decoded, 0644)
   124  
   125  		if e != nil {
   126  			u.LogWarn("base64 decode create file", "create file failed")
   127  			return
   128  		}
   129  
   130  	} else {
   131  		u.LogWarn("base64 decode binary file", "file does not exist")
   132  	}
   133  
   134  }
   135  
   136  func encodeABinaryFile(filepath string) string {
   137  	if _, err := os.Stat(filepath); !os.IsNotExist(err) {
   138  		f, _ := os.Open(filepath)
   139  		defer f.Close()
   140  		reader := bufio.NewReader(f)
   141  		content, _ := ioutil.ReadAll(reader)
   142  		encoded := base64.StdEncoding.EncodeToString(content)
   143  		return encoded
   144  	} else {
   145  		u.LogWarn("base64 encode binary file", "file does not exist")
   146  		return ""
   147  	}
   148  }
   149  
   150  func (f *CmdFuncAction) Exec() {
   151  
   152  	for idx, cmdItem := range *f.Cmds {
   153  		if cmdItem.Cmd != nil {
   154  			u.Pfvvvvv("%s\n", color.MagentaString("%s", cmdItem.Cmd))
   155  		}
   156  
   157  		taskLayerCnt := TaskerRuntime().Tasker.TaskStack.GetLen()
   158  		desc := Render(cmdItem.Desc, f.Vars)
   159  		u.LogDesc("substep", idx+1, taskLayerCnt, cmdItem.Name, desc)
   160  
   161  		doFlag := func(flag string, doFlagFunc func()) {
   162  			if cmdItem.Flags != nil && u.Contains(cmdItem.Flags, flag) {
   163  				doFlagFunc()
   164  			}
   165  		}
   166  
   167  		switch cmdItem.Name {
   168  		case "print":
   169  			cmdItem.runCmd("string", func() {
   170  				cmdRendered := Render(cmdItem.Cmd.(string), f.Vars)
   171  				u.Pfv("%s\n", color.HiGreenString("%s", cmdRendered))
   172  			})
   173  
   174  		case "tmpFile":
   175  			cmdItem.runCmd("map", func() {
   176  				cmd := cmdItem.Cmd.(map[interface{}]interface{})
   177  
   178  				var raw, reg, content string
   179  
   180  				for k, v := range cmd {
   181  					switch k.(string) {
   182  					case "reg":
   183  						raw = v.(string)
   184  						reg = Render(raw, f.Vars)
   185  					case "content":
   186  						raw = v.(string)
   187  						content = Render(raw, f.Vars)
   188  					}
   189  				}
   190  
   191  				u.Pfv("tmp file handler: %s\n", color.HiGreenString("%s", reg))
   192  				tmpfile, err := ioutil.TempFile("", u.RandString(24))
   193  				if err != nil {
   194  					u.LogErrorAndExit("tmpfile creation", err, "can not create tmp file")
   195  				}
   196  
   197  				contentBuf := bytes.NewBufferString(content)
   198  				if _, err := tmpfile.Write(contentBuf.Bytes()); err != nil {
   199  					u.LogErrorAndExit("upVenv source write", err, "can not write to upVenv file")
   200  				}
   201  				if err := tmpfile.Close(); err != nil {
   202  					u.LogErrorAndExit("upVenv source close", err, "can not close upVenv file")
   203  				}
   204  
   205  				filename := tmpfile.Name()
   206  				f.Vars.Put(reg, filename)
   207  				TaskRuntime().ExecbaseVars.Put(reg, filename)
   208  			})
   209  
   210  		case "base64EncodeFile":
   211  			cmdItem.runCmd("map", func() {
   212  				cmd := cmdItem.Cmd.(map[interface{}]interface{})
   213  
   214  				var raw, dest, src string
   215  
   216  				for k, v := range cmd {
   217  					switch k.(string) {
   218  					case "dest":
   219  						raw = v.(string)
   220  						dest = Render(raw, f.Vars)
   221  					case "src":
   222  						raw = v.(string)
   223  						src = Render(raw, f.Vars)
   224  					}
   225  				}
   226  				if src == "" || dest == "" {
   227  					u.LogWarn("param validate", "src and dest can not be empty")
   228  				}
   229  
   230  				b64Str := encodeABinaryFile(src)
   231  				ioutil.WriteFile(dest, []byte(b64Str), 0644)
   232  			})
   233  
   234  		case "base64DecodeFile":
   235  			cmdItem.runCmd("map", func() {
   236  				cmd := cmdItem.Cmd.(map[interface{}]interface{})
   237  
   238  				var raw, dest, src string
   239  
   240  				for k, v := range cmd {
   241  					switch k.(string) {
   242  					case "dest":
   243  						raw = v.(string)
   244  						dest = Render(raw, f.Vars)
   245  					case "src":
   246  						raw = v.(string)
   247  						src = Render(raw, f.Vars)
   248  					}
   249  				}
   250  				if src == "" || dest == "" {
   251  					u.LogWarn("param validate", "src and dest can not be empty")
   252  				}
   253  				decodeABinaryFile(src, dest)
   254  			})
   255  
   256  		case "colorPrint":
   257  
   258  			cmdItem.runCmd("map", func() {
   259  				cmd := cmdItem.Cmd.(map[interface{}]interface{})
   260  				var raw, msg, fg, bg, object string
   261  
   262  				for k, v := range cmd {
   263  					switch k.(string) {
   264  					case "msg":
   265  						raw = v.(string)
   266  						msg = Render(raw, f.Vars)
   267  					case "fg":
   268  						raw = v.(string)
   269  						fg = Render(raw, f.Vars)
   270  					case "bg":
   271  						raw = v.(string)
   272  						bg = Render(raw, f.Vars)
   273  					case "object":
   274  						raw = v.(string)
   275  						object = Render(raw, f.Vars)
   276  					}
   277  				}
   278  
   279  				var fgcolor, bgcolor color.Attribute
   280  				if fg != "" {
   281  					if c, ok := u.FgColorMap[fg]; ok {
   282  						fgcolor = c
   283  					} else {
   284  						fgcolor = color.FgWhite
   285  					}
   286  				} else {
   287  					fgcolor = color.FgWhite
   288  				}
   289  				if bg != "" {
   290  					if c, ok := u.BgColorMap[bg]; ok {
   291  						bgcolor = c
   292  					} else {
   293  						bgcolor = color.BgBlack
   294  					}
   295  				} else {
   296  					bgcolor = color.BgBlack
   297  				}
   298  
   299  				c := color.New(bgcolor, fgcolor)
   300  				u.Pln(color.FgWhite, color.BgBlue)
   301  
   302  				if msg != "" && object != "" {
   303  					u.LogWarn("colorPrint", "msg and object can not coexist")
   304  				} else {
   305  					if msg != "" {
   306  						c.Printf("%s\n", msg)
   307  					}
   308  
   309  					if object != "" {
   310  						obj := f.Vars.Get(object)
   311  						c.Printf("object %s:\n %s", object, u.Sppmsg(obj))
   312  					}
   313  				}
   314  			})
   315  
   316  		case "trace":
   317  			cmdItem.runCmd("string", func() {
   318  				cmdRendered := Render(cmdItem.Cmd.(string), f.Vars)
   319  				u.Ptrace("Trace:", cmdRendered)
   320  			})
   321  
   322  		case "panic":
   323  			s := "manual trigger a panic cmd"
   324  			u.LogWarn("manual panic", s)
   325  			panic(s)
   326  
   327  		case "printObj":
   328  			u.Dvvvv(cmdItem.Cmd)
   329  			cmdItem.runCmd("string", func() {
   330  				objname := Render(cmdItem.Cmd.(string), f.Vars)
   331  				obj := f.Vars.Get(objname)
   332  				u.Ppfmsg(u.Spf("object:\n %s", objname), obj)
   333  			})
   334  
   335  		case "deReg":
   336  			cmdItem.runCmd("string", func() {
   337  				varname := Render(cmdItem.Cmd.(string), f.Vars)
   338  				u.Pfv("deRegister var: %s\n", color.HiGreenString("%s", varname))
   339  				TaskRuntime().ExecbaseVars.Delete(varname)
   340  				f.Vars.Delete(varname)
   341  			})
   342  			u.Ppmsgvvvvvhint("after reg the var - contextual global:", TaskRuntime().ExecbaseVars)
   343  			u.Ppmsgvvvvvhint("after reg the var - local:", f.Vars)
   344  
   345  		case "sleep":
   346  			cmdItem.runCmd("int", func() {
   347  				mscnt := cmdItem.Cmd.(int)
   348  				u.Sleep(mscnt)
   349  			})
   350  
   351  		case "pause":
   352  			pause(f.Vars)
   353  
   354  		case "exit":
   355  			u.GraceExit("exit", "client choose to exit")
   356  
   357  		case "fail":
   358  			u.Fail("fail", "fail and exit")
   359  
   360  		case "break":
   361  			TaskerRuntime().Tasker.TaskBreak = true
   362  
   363  		case "virtualEnv":
   364  			cmdItem.runCmd("map", func() {
   365  				cmd := cmdItem.Cmd.(map[interface{}]interface{})
   366  				var raw, name, source, srcfile, action string
   367  
   368  				for k, v := range cmd {
   369  					switch k.(string) {
   370  					case "name":
   371  						raw = v.(string)
   372  						name = Render(raw, f.Vars)
   373  					case "source":
   374  						raw = v.(string)
   375  						source = Render(raw, f.Vars)
   376  					case "action":
   377  						raw = v.(string)
   378  						action = Render(raw, f.Vars)
   379  					case "srcfile":
   380  						raw = v.(string)
   381  						srcfile = Render(raw, f.Vars)
   382  					}
   383  				}
   384  
   385  				if (name == "" && action == "") || (name != "" && action != "") {
   386  				} else if action == "pure" {
   387  					if source != "" || srcfile != "" || name != "" {
   388  						u.InvalidAndPanic("param validation", "no name, source and srcfile is required when clean")
   389  					}
   390  				} else {
   391  					u.InvalidAndPanic("param validation", "name and action are required or missed at the same time")
   392  				}
   393  
   394  				if action == "restore" {
   395  					if source != "" || srcfile != "" {
   396  						u.InvalidAndPanic("param validation", "no source or srcfile is required when restore")
   397  					}
   398  				}
   399  
   400  				defer func() {
   401  					if srcfile != "" && source != "" {
   402  						os.Remove(srcfile)
   403  					}
   404  				}()
   405  
   406  				if source != "" {
   407  					//save source content to a file
   408  					func() {
   409  						content := bytes.NewBufferString(source)
   410  
   411  						tmpfile, err := ioutil.TempFile("", "upVenv")
   412  						if err != nil {
   413  							u.LogErrorAndExit("upVenv source creation", err, "can not create upVenv file")
   414  						}
   415  
   416  						if _, err := tmpfile.Write(content.Bytes()); err != nil {
   417  							u.LogErrorAndExit("upVenv source write", err, "can not write to upVenv file")
   418  						}
   419  						if err := tmpfile.Close(); err != nil {
   420  							u.LogErrorAndExit("upVenv source close", err, "can not close upVenv file")
   421  						}
   422  
   423  						srcfile = tmpfile.Name()
   424  
   425  					}()
   426  
   427  				}
   428  
   429  				if source != "" || srcfile != "" {
   430  					if _, err := os.Stat(srcfile); os.IsNotExist(err) {
   431  						u.LogErrorAndExit("check upVenv source file existence", err, u.Spf("file %s does not exist", srcfile))
   432  					}
   433  				}
   434  
   435  				switch u.MainConfig.ShellType {
   436  				case "GOSH":
   437  					u.InvalidAndPanic("TODO", "to be implementated in future")
   438  
   439  				default:
   440  					var sourceContent string
   441  					if source == "" && srcfile == "" {
   442  						sourceContent = `
   443  set -e
   444  echo '<<<ENVIRONMENT>>>'
   445  env
   446  `
   447  					} else {
   448  						sourceContent = u.Spf(`
   449  set -e
   450  source %s
   451  echo '<<<ENVIRONMENT>>>'
   452  env
   453  `, srcfile)
   454  					}
   455  
   456  					cmd := exec.Command(u.MainConfig.ShellType, "-c", sourceContent)
   457  					bs, err := cmd.CombinedOutput()
   458  					if err != nil {
   459  						u.LogErrorAndPanic("source upVenv", err, srcfile)
   460  					}
   461  					venv := func() model.Venv {
   462  						s := bufio.NewScanner(bytes.NewReader(bs))
   463  						start := false
   464  						output := bytes.NewBufferString("")
   465  						venv := model.Venv{}
   466  						for s.Scan() {
   467  							if s.Text() == "<<<ENVIRONMENT>>>" {
   468  								start = true
   469  							} else if start {
   470  								kv := strings.SplitN(s.Text(), "=", 2)
   471  								if len(kv) == 2 {
   472  									k := kv[0]
   473  									v := kv[1]
   474  									os.Setenv(k, v)
   475  									venv = append(venv, model.Env{
   476  										Name:  k,
   477  										Value: v,
   478  									})
   479  								}
   480  							} else if !start {
   481  								output.WriteString(s.Text() + "\n")
   482  							}
   483  						}
   484  						u.PlnInfoHighlight("-sourcing execution result:")
   485  						u.PlnBlue(output.String())
   486  
   487  						if name != "" && action != "" {
   488  							if action == "snapshot" {
   489  								model.PutVenv(name, venv)
   490  							}
   491  						}
   492  						return venv
   493  					}()
   494  
   495  					if action == "restore" {
   496  						venvSaved := model.GetVenv(name)
   497  						if venvSaved == nil {
   498  							u.LogWarn(name, " does not exist")
   499  						} else {
   500  							for _, x := range venv {
   501  								os.Unsetenv(x.Name)
   502  							}
   503  							for _, x := range venvSaved {
   504  								os.Setenv(x.Name, x.Value)
   505  							}
   506  						}
   507  					}
   508  
   509  					if action == "pure" {
   510  						for _, x := range venv {
   511  							os.Unsetenv(x.Name)
   512  						}
   513  					}
   514  
   515  				}
   516  			})
   517  
   518  		case "assert":
   519  			cmdItem.runCmd("array", func() {
   520  				conditions := cmdItem.Cmd.([]interface{})
   521  				var condition string
   522  
   523  				var failed bool
   524  				for idx, v := range conditions {
   525  					raw := v.(string)
   526  					condition = Render(raw, f.Vars)
   527  					succeeded, err := strconv.ParseBool(condition)
   528  					if !succeeded {
   529  						color.Red("%2d ASSERT FAILED: [%s]", idx+1, raw)
   530  						failed = true
   531  						u.LogError("Reason:", err)
   532  					} else {
   533  						color.Green("%2d ASSERT OK:     [%s]", idx+1, raw)
   534  					}
   535  				}
   536  
   537  				if failed {
   538  					doFlag("failFast", func() {
   539  						u.InvalidAndPanic("Assert Failed", "failFast and STOPS here!!!")
   540  					})
   541  				}
   542  
   543  			})
   544  
   545  		case "inspect":
   546  			cmdItem.runCmd("array", func() {
   547  				whats := cmdItem.Cmd.([]interface{})
   548  
   549  				for idx, v := range whats {
   550  					what := v.(string)
   551  					u.Pf("%2d: inspect[%s]\n", idx+1, v)
   552  					switch what {
   553  					case "exec_base_vars":
   554  						u.Ppmsg(*TaskRuntime().ExecbaseVars)
   555  					case "exec_vars":
   556  						u.Ppmsg(f.Vars)
   557  					case "exec_base_env_vars_configured":
   558  						TaskerRuntime().Tasker.reportContextualEnvVars(TaskRuntime().ExecbaseVars)
   559  					case "exec_env_vars_configured":
   560  						TaskerRuntime().Tasker.reportContextualEnvVars(f.Vars)
   561  					case "debug_vars":
   562  						debugVars()
   563  					}
   564  
   565  				}
   566  			})
   567  
   568  		case "typeOf":
   569  			cmdItem.runCmd("array", func() {
   570  				whats := cmdItem.Cmd.([]interface{})
   571  				for idx, v := range whats {
   572  					raw := v.(string)
   573  					name := Render(raw, f.Vars)
   574  					value := f.Vars.Get(name)
   575  					u.Pf("%2d -  type of [%s] > [%s]\n", idx+1, name, fmt.Sprintf("%T", value))
   576  				}
   577  			})
   578  
   579  		case "readFile":
   580  			cmdItem.runCmd("map", func() {
   581  				cmd := cmdItem.Cmd.(map[interface{}]interface{})
   582  				var varname, filename, dir, filepath, raw string
   583  				var localOnly bool
   584  				for k, v := range cmd {
   585  					switch k.(string) {
   586  					case "reg":
   587  						raw = v.(string)
   588  						varname = Render(raw, f.Vars)
   589  					case "filename":
   590  						raw = v.(string)
   591  						filename = Render(raw, f.Vars)
   592  					case "dir":
   593  						raw = v.(string)
   594  						dir = Render(raw, f.Vars)
   595  					case "filepath":
   596  						raw = v.(string)
   597  						filepath = Render(raw, f.Vars)
   598  					}
   599  				}
   600  
   601  				doFlag("localOnly", func() {
   602  					localOnly = true
   603  				})
   604  
   605  				if filepath != "" && (filename != "" || dir != "") {
   606  					u.InvalidAndPanic("param validation", "filename and dir are not required when filepath is set")
   607  				}
   608  
   609  				if filepath == "" {
   610  					filepath = path.Join(dir, filename)
   611  				}
   612  
   613  				content, err := ioutil.ReadFile(filepath)
   614  				u.LogErrorAndPanic("cmd readFile", err, u.Spf("please fix filepath: %s", filepath))
   615  
   616  				if localOnly {
   617  					f.Vars.Put(varname, string(content))
   618  				} else {
   619  					TaskRuntime().ExecbaseVars.Put(varname, string(content))
   620  					f.Vars.Put(varname, string(content))
   621  				}
   622  
   623  			})
   624  
   625  			u.Ppmsgvvvvvhint("after reg the var - contextual global:", TaskRuntime().ExecbaseVars)
   626  			u.Ppmsgvvvvvhint("after reg the var - local:", f.Vars)
   627  
   628  		case "writeFile":
   629  			cmdItem.runCmd("map", func() {
   630  				cmd := cmdItem.Cmd.(map[interface{}]interface{})
   631  				var content, filename, dir, filepath, raw string
   632  				for k, v := range cmd {
   633  					switch k.(string) {
   634  					case "content":
   635  						contentRaw := v.(string)
   636  						content = Render(contentRaw, f.Vars)
   637  					case "filename":
   638  						raw = v.(string)
   639  						filename = Render(raw, f.Vars)
   640  					case "dir":
   641  						raw = v.(string)
   642  						dir = Render(raw, f.Vars)
   643  					case "filepath":
   644  						raw = v.(string)
   645  						filepath = Render(raw, f.Vars)
   646  					}
   647  				}
   648  
   649  				if filepath != "" && (filename != "" || dir != "") {
   650  					u.InvalidAndPanic("param validation", "filename and dir are not required when filepath is set")
   651  				}
   652  
   653  				if filepath == "" {
   654  					filepath = path.Join(dir, filename)
   655  				}
   656  
   657  				ioutil.WriteFile(filepath, []byte(content), 0644)
   658  			})
   659  
   660  		case "template":
   661  			cmdItem.runCmd("map", func() {
   662  				cmd := cmdItem.Cmd.(map[interface{}]interface{})
   663  				refdir := ConfigRuntime().RefDir
   664  				var src, dest, raw, datakey, datapath, datafile, rendered string
   665  				var data interface{}
   666  				dataCnt := 0
   667  				for k, v := range cmd {
   668  					switch k.(string) {
   669  					case "src":
   670  						raw = v.(string)
   671  						src = Render(raw, f.Vars)
   672  					case "refdir":
   673  						raw = v.(string)
   674  						refdir = Render(raw, f.Vars)
   675  					case "datafile":
   676  						raw = v.(string)
   677  						datafile = Render(raw, f.Vars)
   678  						dataCnt += 1
   679  					case "datakey":
   680  						raw = v.(string)
   681  						datakey = Render(raw, f.Vars)
   682  						data = f.Vars.Get(datakey)
   683  						dataCnt += 1
   684  					case "datapath":
   685  						raw = v.(string)
   686  						datapath = Render(raw, f.Vars)
   687  						data = core.GetSubObjectFromCache(f.Vars, datapath, false, ConfigRuntime().Verbose)
   688  						u.PpmsgvvvvvHigh("sub object:", data)
   689  						dataCnt += 1
   690  					case "dest":
   691  						raw = v.(string)
   692  						dest = Render(raw, f.Vars)
   693  					}
   694  				}
   695  
   696  				if dataCnt > 1 {
   697  					u.InvalidAndPanic("data validation", "only one data source is alllowed")
   698  				}
   699  
   700  				if datafile != "" {
   701  					data = core.LoadObjectFromFile(path.Join(refdir, datafile))
   702  				}
   703  
   704  				tbuf, err := ioutil.ReadFile(src)
   705  				if data == nil || data == "" {
   706  					rendered = Render(string(tbuf), f.Vars)
   707  				} else {
   708  					rendered = Render(string(tbuf), data)
   709  				}
   710  
   711  				u.LogErrorAndPanic("read template", err, "please fix file path and name issues")
   712  				err = ioutil.WriteFile(dest, []byte(rendered), 0644)
   713  				u.LogErrorAndPanic("write template", err, "please fix file path and name issues")
   714  
   715  			})
   716  
   717  		case "query":
   718  			cmdItem.runCmd("map", func() {
   719  				cmd := cmdItem.Cmd.(map[interface{}]interface{})
   720  				var raw, reg, ymlkey, ymlfile, yqpath string
   721  				var collect, localOnly, ymlOnly bool
   722  				refdir := ConfigRuntime().RefDir
   723  				var data interface{}
   724  				for k, v := range cmd {
   725  					switch k.(string) {
   726  					case "ymlkey":
   727  						raw = v.(string)
   728  						ymlkey = Render(raw, f.Vars)
   729  					case "ymlfile":
   730  						raw = v.(string)
   731  						ymlfile = Render(raw, f.Vars)
   732  					case "refdir":
   733  						raw = v.(string)
   734  						refdir = Render(raw, f.Vars)
   735  					case "reg":
   736  						raw = v.(string)
   737  						reg = Render(raw, f.Vars)
   738  					case "path":
   739  						//yqpath used as:
   740  						//1. a yqpath ref in yml content
   741  						//2. a yqpath ref in cached object
   742  						raw = v.(string)
   743  						yqpath = Render(raw, f.Vars)
   744  					}
   745  				}
   746  
   747  				doFlag("localOnly", func() {
   748  					localOnly = true
   749  				})
   750  				doFlag("ymlOnly", func() {
   751  					ymlOnly = true
   752  				})
   753  				doFlag("collect", func() {
   754  					collect = true
   755  				})
   756  
   757  				if yqpath == "" || reg == "" {
   758  					u.InvalidAndPanic("query cmd mandatory attribute validation", "path and reg are all mandatory and required")
   759  				}
   760  
   761  				if ymlkey != "" {
   762  					tmpymlstr := f.Vars.Get(ymlkey)
   763  					if tmpymlstr == nil {
   764  						u.InvalidAndPanic("data validation", "ymlkey does not exist, please fix it")
   765  					}
   766  					ymlstr := tmpymlstr.(string)
   767  					if ymlOnly {
   768  						data = core.GetSubYmlFromYml(ymlstr, yqpath, collect, ConfigRuntime().Verbose)
   769  					} else {
   770  						data = core.GetSubObjectFromYml(ymlstr, yqpath, collect, ConfigRuntime().Verbose)
   771  					}
   772  				} else if ymlfile != "" {
   773  					filepath := path.Join(refdir, ymlfile)
   774  					if ymlOnly {
   775  						data = core.GetSubYmlFromFile(filepath, yqpath, collect, ConfigRuntime().Verbose)
   776  					} else {
   777  						data = core.GetSubObjectFromFile(filepath, yqpath, collect, ConfigRuntime().Verbose)
   778  					}
   779  				} else if yqpath != "" {
   780  					//means to retrieve from cache
   781  					if ymlOnly {
   782  						data = core.GetSubYmlFromCache(f.Vars, yqpath, collect, ConfigRuntime().Verbose)
   783  					} else {
   784  						data = core.GetSubObjectFromCache(f.Vars, yqpath, collect, ConfigRuntime().Verbose)
   785  					}
   786  				}
   787  
   788  				if localOnly {
   789  					f.Vars.Put(reg, data)
   790  				} else {
   791  					TaskRuntime().ExecbaseVars.Put(reg, data)
   792  					f.Vars.Put(reg, data)
   793  				}
   794  
   795  			})
   796  
   797  		case "ymlDelete":
   798  			cmdItem.runCmd("map", func() {
   799  				cmd := cmdItem.Cmd.(map[interface{}]interface{})
   800  				var raw, ymlfile, yqpath, reg string
   801  
   802  				refdir := ConfigRuntime().RefDir
   803  				verbose := ConfigRuntime().Verbose
   804  				var inplace, localOnly bool
   805  				for k, v := range cmd {
   806  					switch k.(string) {
   807  					case "ymlfile":
   808  						raw = v.(string)
   809  						ymlfile = Render(raw, f.Vars)
   810  					case "refdir":
   811  						raw = v.(string)
   812  						refdir = Render(raw, f.Vars)
   813  					case "path":
   814  						raw = v.(string)
   815  						yqpath = Render(raw, f.Vars)
   816  					case "verbose":
   817  						verbose = v.(string)
   818  					case "reg":
   819  						raw = v.(string)
   820  						reg = Render(raw, f.Vars)
   821  					}
   822  				}
   823  
   824  				doFlag("localOnly", func() {
   825  					localOnly = true
   826  				})
   827  				doFlag("inplace", func() {
   828  					inplace = true
   829  				})
   830  
   831  				if yqpath == "" || ymlfile == "" {
   832  					u.InvalidAndPanic("mandatory attribute validation", "ymlfile and path are mandatory and required")
   833  				}
   834  
   835  				if inplace == true && reg != "" {
   836  					u.InvalidAndPanic("ymlDelete criteria validation", "inplace and reg are mutual exclusive")
   837  				}
   838  
   839  				modified, err := yq.UpDeletePathFromFile(path.Join(refdir, ymlfile), yqpath, inplace, verbose)
   840  				u.LogErrorAndContinue("delete sub element in yml", err, u.Spf("please ensure correct yml query path: %s", yqpath))
   841  				u.Ppmsgvvvvvhint("yml modified:", modified)
   842  
   843  				if inplace != true && reg != "" {
   844  					if localOnly {
   845  						f.Vars.Put(reg, modified)
   846  					} else {
   847  						TaskRuntime().ExecbaseVars.Put(reg, modified)
   848  						f.Vars.Put(reg, modified)
   849  					}
   850  				}
   851  			})
   852  
   853  		case "ymlWrite":
   854  			cmdItem.runCmd("map", func() {
   855  				cmd := cmdItem.Cmd.(map[interface{}]interface{})
   856  				var raw, yqpath, ymlstr, reg, value, nodevalue, modified string
   857  				var err error
   858  
   859  				verbose := ConfigRuntime().Verbose
   860  				var localOnly bool
   861  				for k, v := range cmd {
   862  					switch k.(string) {
   863  					case "ymlstr":
   864  						raw = v.(string)
   865  						ymlstr = Render(raw, f.Vars)
   866  					case "value":
   867  						raw = v.(string)
   868  						value = Render(raw, f.Vars)
   869  					case "nodevalue":
   870  						raw = v.(string)
   871  						nodevalue = Render(raw, f.Vars)
   872  					case "path":
   873  						raw = v.(string)
   874  						yqpath = Render(raw, f.Vars)
   875  					case "verbose":
   876  						verbose = v.(string)
   877  					case "reg":
   878  						raw = v.(string)
   879  						reg = Render(raw, f.Vars)
   880  					}
   881  				}
   882  				doFlag("localOnly", func() {
   883  					localOnly = true
   884  				})
   885  
   886  				if ymlstr == "" || yqpath == "" || reg == "" {
   887  					u.InvalidAndPanic("mandatory attribute validation", "ymlstr, path and reg are required")
   888  				}
   889  
   890  				if value != "" && nodevalue != "" {
   891  					u.InvalidAndPanic("value validation", "value and nodevalue are mutual exclusive")
   892  				}
   893  
   894  				if value != "" {
   895  					modified, err = yq.UpWriteNodeFromStrForSimpleValue(ymlstr, yqpath, value, verbose)
   896  				} else if nodevalue != "" {
   897  					modified, err = yq.UpWriteNodeFromStrForComplexValueFromYmlStr(ymlstr, yqpath, nodevalue, verbose)
   898  				}
   899  
   900  				u.LogErrorAndContinue("write node in yml", err, u.Spf("please ensure correct yml query path: %s\nand check yml content validity:\n%s\n", yqpath, u.ContentWithLineNumber(ymlstr)))
   901  
   902  				u.Ppmsgvvvvvhint("yml modified:", modified)
   903  
   904  				if localOnly {
   905  					f.Vars.Put(reg, modified)
   906  				} else {
   907  					TaskRuntime().ExecbaseVars.Put(reg, modified)
   908  					f.Vars.Put(reg, modified)
   909  				}
   910  
   911  			})
   912  
   913  		case "reg":
   914  			cmdItem.runCmd("map", func() {
   915  				regCmd := cmdItem.Cmd.(map[interface{}]interface{})
   916  				var varname, varvalue string
   917  				var localOnly bool
   918  				for k, v := range regCmd {
   919  					if k.(string) == "name" {
   920  						varname = v.(string)
   921  					}
   922  					if k.(string) == "value" {
   923  						varvalueRaw := v.(string)
   924  						varvalue = Render(varvalueRaw, f.Vars)
   925  					}
   926  				}
   927  
   928  				doFlag("localOnly", func() {
   929  					localOnly = true
   930  				})
   931  
   932  				if varname == "" {
   933  					u.InvalidAndPanic("validate varname", "the reg varname must not be empty")
   934  				}
   935  				if localOnly {
   936  					f.Vars.Put(varname, varvalue)
   937  				} else {
   938  					TaskRuntime().ExecbaseVars.Put(varname, varvalue)
   939  					f.Vars.Put(varname, varvalue)
   940  				}
   941  			})
   942  			u.Ppmsgvvvvvhint("after reg the var - contextual global:", TaskRuntime().ExecbaseVars)
   943  			u.Ppmsgvvvvvhint("after reg the var - local:", f.Vars)
   944  
   945  		case "pathExisted":
   946  			cmd := cmdItem.Cmd.(map[interface{}]interface{})
   947  			var raw, path, pathtstr, reg string
   948  			for k, v := range cmd {
   949  				switch k.(string) {
   950  				case "path":
   951  					raw = v.(string)
   952  					path = Render(raw, f.Vars)
   953  					pathtstr = u.Spf("{{.%s}}", path)
   954  				case "reg":
   955  					raw = v.(string)
   956  					reg = Render(raw, f.Vars)
   957  				}
   958  			}
   959  			result := ElementValid(pathtstr, f.Vars)
   960  			TaskRuntime().ExecbaseVars.Put(reg, result)
   961  			f.Vars.Put(reg, result)
   962  
   963  		case "return":
   964  			cmdItem.runCmd("array", func() {
   965  				retNames := cmdItem.Cmd.([]interface{})
   966  				var retName string
   967  
   968  				if TaskRuntime().ReturnVars == nil {
   969  					TaskRuntime().ReturnVars = core.NewCache()
   970  				}
   971  
   972  				for _, v := range retNames {
   973  					rawName := v.(string)
   974  					retName = Render(rawName, f.Vars)
   975  					ret := f.Vars.Get(retName)
   976  					if ret != nil {
   977  						TaskRuntime().ReturnVars.Put(retName, f.Vars.Get(retName))
   978  					} else {
   979  						u.LogWarn("return validation", u.Spf("The referencing var name: (%s) not exist", retName))
   980  					}
   981  				}
   982  
   983  			})
   984  			u.Ppmsgvvvvvhint("contextual return vars:", TaskRuntime().ReturnVars)
   985  
   986  		case "toObj":
   987  			//src: a var name to get the yml content from
   988  			//reg: a registered name to cache the variable
   989  			//localOnly: if set, then the variable will not be saved to global space
   990  			cmdItem.runCmd("map", func() {
   991  				cmd := cmdItem.Cmd.(map[interface{}]interface{})
   992  				var fromkey, src, reg string
   993  				var localOnly bool
   994  				for k, v := range cmd {
   995  					if k.(string) == "fromkey" {
   996  						keyRaw := v.(string)
   997  						fromkey = Render(keyRaw, f.Vars)
   998  					}
   999  					if k.(string) == "src" {
  1000  						srcRaw := v.(string)
  1001  						src = Render(srcRaw, f.Vars)
  1002  					}
  1003  					if k.(string) == "reg" {
  1004  						regRaw := v.(string)
  1005  						reg = Render(regRaw, f.Vars)
  1006  					}
  1007  				}
  1008  				doFlag("localOnly", func() {
  1009  					localOnly = true
  1010  				})
  1011  
  1012  				srcyml := func() string {
  1013  					if src != "" && fromkey != "" {
  1014  						u.InvalidAndPanic("locate yml string", "you can only use either key or src, but not both")
  1015  					}
  1016  					if src != "" {
  1017  						return src
  1018  					}
  1019  					if fromkey != "" {
  1020  						t := f.Vars.Get(fromkey)
  1021  						if t != nil {
  1022  							return t.(string)
  1023  						} else {
  1024  							u.InvalidAndPanic("locate yml string", "please use a valid addressable varkey to locate a yml document")
  1025  							return ""
  1026  						}
  1027  					}
  1028  					return ""
  1029  				}()
  1030  				obj := new(interface{})
  1031  				err := yaml.Unmarshal([]byte(srcyml), obj)
  1032  				u.LogErrorAndPanic("cmd toObj:", err, "please validate the ymal content")
  1033  
  1034  				if localOnly {
  1035  					(*f.Vars).Put(reg, *obj)
  1036  				} else {
  1037  					TaskRuntime().ExecbaseVars.Put(src, reg)
  1038  					(*f.Vars).Put(reg, *obj)
  1039  				}
  1040  
  1041  			})
  1042  			u.Ppmsgvvvvvhint("after reg the var - contextual global:", TaskRuntime().ExecbaseVars)
  1043  			u.Ppmsgvvvvvhint("after reg the var - local:", f.Vars)
  1044  
  1045  		case "":
  1046  			u.LogWarn("cmd", "temporarily deactivated")
  1047  
  1048  		default:
  1049  			u.Pferror("warrning: check cmd name:(%s),%s\n", cmdItem.Name, "cmd not implemented")
  1050  		}
  1051  
  1052  	}
  1053  }