github.com/kubevela/workflow@v0.6.0/pkg/tasks/custom/task.go (about) 1 /* 2 Copyright 2022 The KubeVela Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package custom 18 19 import ( 20 "context" 21 "encoding/json" 22 "fmt" 23 "strings" 24 25 "cuelang.org/go/cue" 26 monitorContext "github.com/kubevela/pkg/monitor/context" 27 "github.com/kubevela/pkg/util/slices" 28 "github.com/pkg/errors" 29 30 "github.com/kubevela/workflow/api/v1alpha1" 31 wfContext "github.com/kubevela/workflow/pkg/context" 32 "github.com/kubevela/workflow/pkg/cue/model" 33 "github.com/kubevela/workflow/pkg/cue/model/sets" 34 "github.com/kubevela/workflow/pkg/cue/model/value" 35 "github.com/kubevela/workflow/pkg/cue/packages" 36 "github.com/kubevela/workflow/pkg/cue/process" 37 "github.com/kubevela/workflow/pkg/hooks" 38 "github.com/kubevela/workflow/pkg/types" 39 ) 40 41 // LoadTaskTemplate gets the workflowStep definition from cluster and resolve it. 42 type LoadTaskTemplate func(ctx context.Context, name string) (string, error) 43 44 // TaskLoader is a client that get taskGenerator. 45 type TaskLoader struct { 46 loadTemplate func(ctx context.Context, name string) (string, error) 47 pd *packages.PackageDiscover 48 handlers types.Providers 49 runOptionsProcess func(*types.TaskRunOptions) 50 logLevel int 51 } 52 53 // GetTaskGenerator get TaskGenerator by name. 54 func (t *TaskLoader) GetTaskGenerator(ctx context.Context, name string) (types.TaskGenerator, error) { 55 templ, err := t.loadTemplate(ctx, name) 56 if err != nil { 57 return nil, err 58 } 59 return t.makeTaskGenerator(templ) 60 } 61 62 type taskRunner struct { 63 name string 64 run func(ctx wfContext.Context, options *types.TaskRunOptions) (v1alpha1.StepStatus, *types.Operation, error) 65 checkPending func(ctx monitorContext.Context, wfCtx wfContext.Context, stepStatus map[string]v1alpha1.StepStatus) (bool, v1alpha1.StepStatus) 66 fillContext func(ctx monitorContext.Context, processCtx process.Context) types.ContextDataResetter 67 } 68 69 // Name return step name. 70 func (tr *taskRunner) Name() string { 71 return tr.name 72 } 73 74 // Run execute task. 75 func (tr *taskRunner) Run(ctx wfContext.Context, options *types.TaskRunOptions) (v1alpha1.StepStatus, *types.Operation, error) { 76 return tr.run(ctx, options) 77 } 78 79 // Pending check task should be executed or not. 80 func (tr *taskRunner) Pending(ctx monitorContext.Context, wfCtx wfContext.Context, stepStatus map[string]v1alpha1.StepStatus) (bool, v1alpha1.StepStatus) { 81 return tr.checkPending(ctx, wfCtx, stepStatus) 82 } 83 84 func (tr *taskRunner) FillContextData(ctx monitorContext.Context, processCtx process.Context) types.ContextDataResetter { 85 return tr.fillContext(ctx, processCtx) 86 } 87 88 // nolint:gocyclo 89 func (t *TaskLoader) makeTaskGenerator(templ string) (types.TaskGenerator, error) { 90 return func(wfStep v1alpha1.WorkflowStep, genOpt *types.TaskGeneratorOptions) (types.TaskRunner, error) { 91 92 initialStatus := v1alpha1.StepStatus{ 93 Name: wfStep.Name, 94 Type: wfStep.Type, 95 Phase: v1alpha1.WorkflowStepPhaseSucceeded, 96 } 97 exec := &executor{ 98 handlers: t.handlers, 99 wfStatus: initialStatus, 100 stepStatus: initialStatus, 101 } 102 103 var err error 104 105 if genOpt != nil { 106 exec.wfStatus.ID = genOpt.ID 107 if genOpt.StepConvertor != nil { 108 wfStep, err = genOpt.StepConvertor(wfStep) 109 if err != nil { 110 return nil, errors.WithMessage(err, "convert step") 111 } 112 } 113 } 114 115 paramsStr, err := GetParameterTemplate(wfStep) 116 if err != nil { 117 return nil, err 118 } 119 120 tRunner := new(taskRunner) 121 tRunner.name = wfStep.Name 122 tRunner.checkPending = func(ctx monitorContext.Context, wfCtx wfContext.Context, stepStatus map[string]v1alpha1.StepStatus) (bool, v1alpha1.StepStatus) { 123 options := &types.TaskRunOptions{} 124 if t.runOptionsProcess != nil { 125 t.runOptionsProcess(options) 126 } 127 resetter := tRunner.fillContext(ctx, options.PCtx) 128 defer resetter(options.PCtx) 129 basicVal, _, _ := MakeBasicValue(wfCtx, paramsStr, options.PCtx) 130 return CheckPending(wfCtx, wfStep, exec.wfStatus.ID, stepStatus, basicVal) 131 } 132 tRunner.fillContext = func(ctx monitorContext.Context, processCtx process.Context) types.ContextDataResetter { 133 metas := []process.StepMetaKV{ 134 process.WithName(wfStep.Name), 135 process.WithSessionID(exec.wfStatus.ID), 136 process.WithSpanID(ctx.GetID()), 137 } 138 manager := process.NewStepRunTimeMeta() 139 manager.Fill(processCtx, metas) 140 return func(processCtx process.Context) { 141 manager.Remove(processCtx, slices.Map(metas, 142 func(t process.StepMetaKV) string { 143 return t.Key 144 }), 145 ) 146 } 147 } 148 tRunner.run = func(ctx wfContext.Context, options *types.TaskRunOptions) (stepStatus v1alpha1.StepStatus, operations *types.Operation, rErr error) { 149 if options.GetTracer == nil { 150 options.GetTracer = func(id string, step v1alpha1.WorkflowStep) monitorContext.Context { 151 return monitorContext.NewTraceContext(context.Background(), "") 152 } 153 } 154 tracer := options.GetTracer(exec.wfStatus.ID, wfStep).AddTag("step_name", wfStep.Name, "step_type", wfStep.Type) 155 tracer.V(t.logLevel) 156 defer func() { 157 tracer.Commit(string(exec.status().Phase)) 158 }() 159 160 if t.runOptionsProcess != nil { 161 t.runOptionsProcess(options) 162 } 163 164 resetter := tRunner.fillContext(tracer, options.PCtx) 165 defer resetter(options.PCtx) 166 basicVal, basicTemplate, err := MakeBasicValue(ctx, paramsStr, options.PCtx) 167 if err != nil { 168 tracer.Error(err, "make context parameter") 169 return v1alpha1.StepStatus{}, nil, errors.WithMessage(err, "make context parameter") 170 } 171 172 var taskv *value.Value 173 defer func() { 174 if r := recover(); r != nil { 175 exec.err(ctx, false, fmt.Errorf("invalid cue task for evaluation: %v", r), types.StatusReasonRendering) 176 stepStatus = exec.status() 177 operations = exec.operation() 178 return 179 } 180 if taskv == nil { 181 taskv, err = value.NewValue(strings.Join([]string{templ, basicTemplate}, "\n"), t.pd, "", value.ProcessScript, value.TagFieldOrder) 182 if err != nil { 183 return 184 } 185 } 186 if options.Debug != nil { 187 if err := options.Debug(exec.wfStatus.ID, taskv); err != nil { 188 tracer.Error(err, "failed to debug") 189 } 190 } 191 for _, hook := range options.PostStopHooks { 192 if err := hook(ctx, taskv, wfStep, exec.status(), options.StepStatus); err != nil { 193 exec.wfStatus.Message = err.Error() 194 stepStatus = exec.status() 195 operations = exec.operation() 196 return 197 } 198 } 199 }() 200 201 for _, hook := range options.PreCheckHooks { 202 result, err := hook(wfStep, &types.PreCheckOptions{ 203 PackageDiscover: t.pd, 204 BasicTemplate: basicTemplate, 205 BasicValue: basicVal, 206 }) 207 if err != nil { 208 tracer.Error(err, "do preCheckHook") 209 exec.Skip(fmt.Sprintf("pre check error: %s", err.Error())) 210 return exec.status(), exec.operation(), nil 211 } 212 if result.Skip { 213 exec.Skip("") 214 return exec.status(), exec.operation(), nil 215 } 216 if result.Timeout { 217 exec.timeout("") 218 } 219 } 220 221 for _, hook := range options.PreStartHooks { 222 if err := hook(ctx, basicVal, wfStep); err != nil { 223 tracer.Error(err, "do preStartHook") 224 exec.err(ctx, false, err, types.StatusReasonInput) 225 return exec.status(), exec.operation(), nil 226 } 227 } 228 229 // refresh the basic template to get inputs value involved 230 basicTemplate, err = basicVal.String() 231 if err != nil { 232 exec.err(ctx, false, err, types.StatusReasonParameter) 233 return exec.status(), exec.operation(), nil 234 } 235 236 if status, ok := options.StepStatus[wfStep.Name]; ok { 237 exec.stepStatus = status 238 } 239 taskv, err = value.NewValue(strings.Join([]string{templ, basicTemplate}, "\n"), t.pd, "", value.ProcessScript, value.TagFieldOrder) 240 if err != nil { 241 exec.err(ctx, false, err, types.StatusReasonRendering) 242 return exec.status(), exec.operation(), nil 243 } 244 245 exec.tracer = tracer 246 if debugLog(taskv) { 247 exec.printStep("workflowStepStart", "workflow", "", taskv) 248 defer exec.printStep("workflowStepEnd", "workflow", "", taskv) 249 } 250 251 if err := exec.doSteps(tracer, ctx, taskv); err != nil { 252 tracer.Error(err, "do steps") 253 exec.err(ctx, true, err, types.StatusReasonExecute) 254 return exec.status(), exec.operation(), nil 255 } 256 257 return exec.status(), exec.operation(), nil 258 } 259 return tRunner, nil 260 }, nil 261 } 262 263 // ValidateIfValue validates the if value 264 func ValidateIfValue(ctx wfContext.Context, step v1alpha1.WorkflowStep, stepStatus map[string]v1alpha1.StepStatus, options *types.PreCheckOptions) (bool, error) { 265 if options == nil { 266 options = &types.PreCheckOptions{} 267 } 268 template := fmt.Sprintf("if: %s", step.If) 269 value, err := buildValueForStatus(ctx, step, template, stepStatus, options) 270 if err != nil { 271 return false, errors.WithMessage(err, "invalid if value") 272 } 273 check, err := value.GetBool("if") 274 if err != nil { 275 return false, err 276 } 277 return check, nil 278 } 279 280 func buildValueForStatus(ctx wfContext.Context, step v1alpha1.WorkflowStep, template string, stepStatus map[string]v1alpha1.StepStatus, options *types.PreCheckOptions) (*value.Value, error) { 281 inputsTemplate := getInputsTemplate(ctx, step, options.BasicValue) 282 statusTemplate := "\n" 283 statusMap := make(map[string]interface{}) 284 for name, ss := range stepStatus { 285 abbrStatus := struct { 286 v1alpha1.StepStatus `json:",inline"` 287 Failed bool `json:"failed"` 288 Succeeded bool `json:"succeeded"` 289 Skipped bool `json:"skipped"` 290 Timeout bool `json:"timeout"` 291 FailedAfterRetries bool `json:"failedAfterRetries"` 292 Terminate bool `json:"terminate"` 293 }{ 294 StepStatus: ss, 295 Failed: ss.Phase == v1alpha1.WorkflowStepPhaseFailed, 296 Succeeded: ss.Phase == v1alpha1.WorkflowStepPhaseSucceeded, 297 Skipped: ss.Phase == v1alpha1.WorkflowStepPhaseSkipped, 298 Timeout: ss.Reason == types.StatusReasonTimeout, 299 FailedAfterRetries: ss.Reason == types.StatusReasonFailedAfterRetries, 300 Terminate: ss.Reason == types.StatusReasonTerminate, 301 } 302 statusMap[name] = abbrStatus 303 } 304 status, err := json.Marshal(statusMap) 305 if err != nil { 306 return nil, err 307 } 308 statusTemplate = strings.Join([]string{statusTemplate, fmt.Sprintf("status: %s\n", status), options.BasicTemplate, inputsTemplate}, "\n") 309 v, err := value.NewValue(template+"\n"+statusTemplate, options.PackageDiscover, "") 310 if err != nil { 311 return nil, err 312 } 313 if v.Error() != nil { 314 return nil, v.Error() 315 } 316 return v, nil 317 } 318 319 // MakeBasicValue makes basic value 320 func MakeBasicValue(wfCtx wfContext.Context, parameterTemplate string, pCtx process.Context) (*value.Value, string, error) { 321 paramStr := model.ParameterFieldName + ": {}\n" 322 if parameterTemplate != "" { 323 paramStr = fmt.Sprintf(model.ParameterFieldName+": {%s}\n", parameterTemplate) 324 } 325 template := strings.Join([]string{getContextTemplate(pCtx), paramStr}, "\n") 326 v, err := wfCtx.MakeParameter(template) 327 if err != nil { 328 return nil, "", err 329 } 330 if v.Error() != nil { 331 return nil, "", v.Error() 332 } 333 return v, template, nil 334 } 335 336 func getContextTemplate(pCtx process.Context) string { 337 var contextTempl string 338 if pCtx == nil { 339 return contextTempl 340 } 341 c, err := pCtx.BaseContextFile() 342 if err != nil { 343 return "" 344 } 345 contextTempl += "\n" + c 346 return contextTempl 347 } 348 349 // GetParameterTemplate gets parameter template 350 func GetParameterTemplate(step v1alpha1.WorkflowStep) (string, error) { 351 if step.Properties != nil && len(step.Properties.Raw) > 0 { 352 params := map[string]interface{}{} 353 bt, err := step.Properties.MarshalJSON() 354 if err != nil { 355 return "", err 356 } 357 if err := json.Unmarshal(bt, ¶ms); err != nil { 358 return "", err 359 } 360 b, err := json.Marshal(params) 361 if err != nil { 362 return "", err 363 } 364 return string(b), nil 365 } 366 return "", nil 367 } 368 369 func getInputsTemplate(ctx wfContext.Context, step v1alpha1.WorkflowStep, basicVal *value.Value) string { 370 var inputsTempl string 371 for _, input := range step.Inputs { 372 inputValue, err := ctx.GetVar(strings.Split(input.From, ".")...) 373 if err != nil { 374 if basicVal == nil { 375 continue 376 } 377 inputValue, err = basicVal.LookupValue(input.From) 378 if err != nil { 379 continue 380 } 381 } 382 s, err := inputValue.String() 383 if err != nil { 384 continue 385 } 386 inputsTempl += fmt.Sprintf("\ninputs: \"%s\": {\n%s\n}", input.From, s) 387 } 388 return inputsTempl 389 } 390 391 type executor struct { 392 handlers types.Providers 393 394 wfStatus v1alpha1.StepStatus 395 stepStatus v1alpha1.StepStatus 396 suspend bool 397 terminated bool 398 failedAfterRetries bool 399 wait bool 400 skip bool 401 402 tracer monitorContext.Context 403 } 404 405 // Suspend let workflow pause. 406 func (exec *executor) Suspend(message string) { 407 if exec.wfStatus.Phase == v1alpha1.WorkflowStepPhaseFailed { 408 return 409 } 410 exec.suspend = true 411 exec.wfStatus.Phase = v1alpha1.WorkflowStepPhaseSuspending 412 if message != "" { 413 exec.wfStatus.Message = message 414 } 415 exec.wfStatus.Reason = types.StatusReasonSuspend 416 } 417 418 // Resume let workflow resume. 419 func (exec *executor) Resume(message string) { 420 exec.suspend = false 421 exec.wfStatus.Phase = v1alpha1.WorkflowStepPhaseSucceeded 422 if message != "" { 423 exec.wfStatus.Message = message 424 } 425 } 426 427 // Terminate let workflow terminate. 428 func (exec *executor) Terminate(message string) { 429 exec.terminated = true 430 exec.wfStatus.Phase = v1alpha1.WorkflowStepPhaseSucceeded 431 if message != "" { 432 exec.wfStatus.Message = message 433 } 434 exec.wfStatus.Reason = types.StatusReasonTerminate 435 } 436 437 // Wait let workflow wait. 438 func (exec *executor) Wait(message string) { 439 exec.wait = true 440 if exec.wfStatus.Phase != v1alpha1.WorkflowStepPhaseFailed { 441 exec.wfStatus.Phase = v1alpha1.WorkflowStepPhaseRunning 442 exec.wfStatus.Reason = types.StatusReasonWait 443 if message != "" { 444 exec.wfStatus.Message = message 445 } 446 } 447 } 448 449 // Fail let the step fail, its status is failed and reason is Action 450 func (exec *executor) Fail(message string) { 451 exec.terminated = true 452 exec.wfStatus.Phase = v1alpha1.WorkflowStepPhaseFailed 453 exec.wfStatus.Reason = types.StatusReasonAction 454 if message != "" { 455 exec.wfStatus.Message = message 456 } 457 } 458 459 // Message writes message to step status, note that the message will be overwritten by the next message. 460 func (exec *executor) Message(message string) { 461 if message != "" { 462 exec.wfStatus.Message = message 463 } 464 } 465 466 func (exec *executor) Skip(message string) { 467 exec.skip = true 468 exec.wfStatus.Phase = v1alpha1.WorkflowStepPhaseSkipped 469 exec.wfStatus.Reason = types.StatusReasonSkip 470 exec.wfStatus.Message = message 471 } 472 473 func (exec *executor) GetStatus() v1alpha1.StepStatus { 474 return exec.stepStatus 475 } 476 477 func (exec *executor) timeout(message string) { 478 exec.terminated = true 479 exec.wfStatus.Phase = v1alpha1.WorkflowStepPhaseFailed 480 exec.wfStatus.Reason = types.StatusReasonTimeout 481 exec.wfStatus.Message = message 482 } 483 484 func (exec *executor) err(ctx wfContext.Context, wait bool, err error, reason string) { 485 exec.wait = wait 486 exec.wfStatus.Phase = v1alpha1.WorkflowStepPhaseFailed 487 exec.wfStatus.Message = err.Error() 488 if exec.wfStatus.Reason == "" { 489 exec.wfStatus.Reason = reason 490 if reason != types.StatusReasonExecute { 491 exec.terminated = true 492 } 493 } 494 exec.checkErrorTimes(ctx) 495 } 496 497 func (exec *executor) checkErrorTimes(ctx wfContext.Context) { 498 times := ctx.IncreaseCountValueInMemory(types.ContextPrefixFailedTimes, exec.wfStatus.ID) 499 if times >= types.MaxWorkflowStepErrorRetryTimes { 500 exec.wait = false 501 exec.failedAfterRetries = true 502 exec.wfStatus.Reason = types.StatusReasonFailedAfterRetries 503 } 504 } 505 506 func (exec *executor) operation() *types.Operation { 507 return &types.Operation{ 508 Suspend: exec.suspend, 509 Terminated: exec.terminated, 510 Waiting: exec.wait, 511 Skip: exec.skip, 512 FailedAfterRetries: exec.failedAfterRetries, 513 } 514 } 515 516 func (exec *executor) status() v1alpha1.StepStatus { 517 return exec.wfStatus 518 } 519 520 func (exec *executor) printStep(phase string, provider string, do string, v *value.Value) { 521 msg, _ := v.String() 522 exec.tracer.Info("cue eval: "+msg, "phase", phase, "provider", provider, "do", do) 523 } 524 525 // Handle process task-step value by provider and do. 526 func (exec *executor) Handle(ctx monitorContext.Context, wfCtx wfContext.Context, provider string, do string, v *value.Value) error { 527 if debugLog(v) { 528 exec.printStep("stepStart", provider, do, v) 529 defer exec.printStep("stepEnd", provider, do, v) 530 } 531 h, exist := exec.handlers.GetHandler(provider, do) 532 if !exist { 533 return errors.Errorf("handler not found") 534 } 535 return h(ctx, wfCtx, v, exec) 536 } 537 538 func (exec *executor) doSteps(ctx monitorContext.Context, wfCtx wfContext.Context, v *value.Value) error { 539 do := OpTpy(v) 540 if do != "" && do != "steps" { 541 provider := opProvider(v) 542 if err := exec.Handle(ctx, wfCtx, provider, do, v); err != nil { 543 return errors.WithMessagef(err, "run step(provider=%s,do=%s)", provider, do) 544 } 545 return nil 546 } 547 return v.StepByFields(func(fieldName string, in *value.Value) (bool, error) { 548 if in.CueValue().IncompleteKind() == cue.BottomKind { 549 errInfo, err := sets.ToString(in.CueValue()) 550 if err != nil { 551 errInfo = "value is _|_" 552 } 553 return true, errors.New(errInfo + "(bottom kind)") 554 } 555 if retErr := in.CueValue().Err(); retErr != nil { 556 errInfo, err := sets.ToString(in.CueValue()) 557 if err == nil { 558 retErr = errors.WithMessage(retErr, errInfo) 559 } 560 return false, retErr 561 } 562 563 if isStepList(fieldName) { 564 return false, in.StepByList(func(name string, item *value.Value) (bool, error) { 565 do := OpTpy(item) 566 if do == "" { 567 return false, nil 568 } 569 return false, exec.doSteps(ctx, wfCtx, item) 570 }) 571 } 572 do := OpTpy(in) 573 if do == "" { 574 return false, nil 575 } 576 if do == "steps" { 577 if err := exec.doSteps(ctx, wfCtx, in); err != nil { 578 return false, err 579 } 580 } else { 581 provider := opProvider(in) 582 if err := exec.Handle(ctx, wfCtx, provider, do, in); err != nil { 583 return false, errors.WithMessagef(err, "run step(provider=%s,do=%s)", provider, do) 584 } 585 } 586 587 if exec.suspend || exec.terminated || exec.wait { 588 return true, nil 589 } 590 return false, nil 591 }) 592 } 593 594 func isStepList(fieldName string) bool { 595 if fieldName == "#up" { 596 return true 597 } 598 return strings.HasPrefix(fieldName, "#up_") 599 } 600 601 func debugLog(v *value.Value) bool { 602 debug, _ := v.CueValue().LookupPath(value.FieldPath("#debug")).Bool() 603 return debug 604 } 605 606 // OpTpy get label do 607 func OpTpy(v *value.Value) string { 608 return getLabel(v, "#do") 609 } 610 611 func opProvider(v *value.Value) string { 612 provider := getLabel(v, "#provider") 613 if provider == "" { 614 provider = "builtin" 615 } 616 return provider 617 } 618 619 func getLabel(v *value.Value, label string) string { 620 do, err := v.Field(label) 621 if err == nil && do.Exists() { 622 if str, err := do.String(); err == nil { 623 return str 624 } 625 } 626 return "" 627 } 628 629 // NewTaskLoader create a tasks loader. 630 func NewTaskLoader(lt LoadTaskTemplate, pkgDiscover *packages.PackageDiscover, handlers types.Providers, logLevel int, pCtx process.Context) *TaskLoader { 631 return &TaskLoader{ 632 loadTemplate: lt, 633 pd: pkgDiscover, 634 handlers: handlers, 635 runOptionsProcess: func(options *types.TaskRunOptions) { 636 if len(options.PreStartHooks) == 0 { 637 options.PreStartHooks = append(options.PreStartHooks, hooks.Input) 638 } 639 if len(options.PostStopHooks) == 0 { 640 options.PostStopHooks = append(options.PostStopHooks, hooks.Output) 641 } 642 options.PCtx = pCtx 643 }, 644 logLevel: logLevel, 645 } 646 } 647 648 // CheckPending checks whether to pending task run 649 func CheckPending(ctx wfContext.Context, step v1alpha1.WorkflowStep, id string, stepStatus map[string]v1alpha1.StepStatus, basicValue *value.Value) (bool, v1alpha1.StepStatus) { 650 pStatus := v1alpha1.StepStatus{ 651 Phase: v1alpha1.WorkflowStepPhasePending, 652 Type: step.Type, 653 ID: id, 654 Name: step.Name, 655 } 656 for _, depend := range step.DependsOn { 657 pStatus.Message = fmt.Sprintf("Pending on DependsOn: %s", depend) 658 if status, ok := stepStatus[depend]; ok { 659 if !types.IsStepFinish(status.Phase, status.Reason) { 660 return true, pStatus 661 } 662 } else { 663 return true, pStatus 664 } 665 } 666 for _, input := range step.Inputs { 667 pStatus.Message = fmt.Sprintf("Pending on Input: %s", input.From) 668 if _, err := ctx.GetVar(strings.Split(input.From, ".")...); err != nil { 669 if basicValue == nil { 670 return true, pStatus 671 } 672 _, err = basicValue.LookupValue(input.From) 673 if err != nil { 674 return true, pStatus 675 } 676 } 677 } 678 return false, v1alpha1.StepStatus{} 679 }