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, &params); 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  }