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 }