github.com/kubevela/workflow@v0.6.0/pkg/generator/generator.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 generator
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"errors"
    23  
    24  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    25  	"k8s.io/utils/pointer"
    26  	"sigs.k8s.io/controller-runtime/pkg/client"
    27  
    28  	monitorContext "github.com/kubevela/pkg/monitor/context"
    29  	"github.com/kubevela/pkg/util/rand"
    30  
    31  	"github.com/oam-dev/kubevela/pkg/config/provider"
    32  
    33  	"github.com/kubevela/workflow/api/v1alpha1"
    34  	"github.com/kubevela/workflow/pkg/cue/process"
    35  	"github.com/kubevela/workflow/pkg/executor"
    36  	"github.com/kubevela/workflow/pkg/monitor/metrics"
    37  	"github.com/kubevela/workflow/pkg/providers"
    38  	"github.com/kubevela/workflow/pkg/providers/email"
    39  	"github.com/kubevela/workflow/pkg/providers/http"
    40  	"github.com/kubevela/workflow/pkg/providers/kube"
    41  	metrics2 "github.com/kubevela/workflow/pkg/providers/metrics"
    42  	"github.com/kubevela/workflow/pkg/providers/util"
    43  	"github.com/kubevela/workflow/pkg/providers/workspace"
    44  	"github.com/kubevela/workflow/pkg/tasks"
    45  	"github.com/kubevela/workflow/pkg/tasks/template"
    46  	"github.com/kubevela/workflow/pkg/types"
    47  )
    48  
    49  // GenerateRunners generates task runners
    50  func GenerateRunners(ctx monitorContext.Context, instance *types.WorkflowInstance, options types.StepGeneratorOptions) ([]types.TaskRunner, error) {
    51  	ctx.V(options.LogLevel)
    52  	subCtx := ctx.Fork("generate-task-runners", monitorContext.DurationMetric(func(v float64) {
    53  		metrics.GenerateTaskRunnersDurationHistogram.WithLabelValues("workflowrun").Observe(v)
    54  	}))
    55  	defer subCtx.Commit("finish generate task runners")
    56  	options = initStepGeneratorOptions(ctx, instance, options)
    57  	taskDiscover := tasks.NewTaskDiscover(ctx, options)
    58  	var tasks []types.TaskRunner
    59  	for _, step := range instance.Steps {
    60  		opt := &types.TaskGeneratorOptions{
    61  			ID:              generateStepID(instance.Status, step.Name),
    62  			PackageDiscover: options.PackageDiscover,
    63  			ProcessContext:  options.ProcessCtx,
    64  		}
    65  		for typ, convertor := range options.StepConvertor {
    66  			if step.Type == typ {
    67  				opt.StepConvertor = convertor
    68  			}
    69  		}
    70  		task, err := generateTaskRunner(ctx, instance, step, taskDiscover, opt, options)
    71  		if err != nil {
    72  			return nil, err
    73  		}
    74  		tasks = append(tasks, task)
    75  	}
    76  	return tasks, nil
    77  }
    78  
    79  // GenerateWorkflowInstance generates a workflow instance
    80  func GenerateWorkflowInstance(ctx context.Context, cli client.Client, run *v1alpha1.WorkflowRun) (*types.WorkflowInstance, error) {
    81  	var steps []v1alpha1.WorkflowStep
    82  	mode := run.Spec.Mode
    83  	switch {
    84  	case run.Spec.WorkflowSpec != nil:
    85  		steps = run.Spec.WorkflowSpec.Steps
    86  	case run.Spec.WorkflowRef != "":
    87  		template := new(v1alpha1.Workflow)
    88  		if err := cli.Get(ctx, client.ObjectKey{
    89  			Name:      run.Spec.WorkflowRef,
    90  			Namespace: run.Namespace,
    91  		}, template); err != nil {
    92  			return nil, err
    93  		}
    94  		steps = template.WorkflowSpec.Steps
    95  		if template.Mode != nil && mode == nil {
    96  			mode = template.Mode
    97  		}
    98  	default:
    99  		return nil, errors.New("failed to generate workflow instance")
   100  	}
   101  
   102  	debug := false
   103  	if run.Annotations != nil && run.Annotations[types.AnnotationWorkflowRunDebug] == "true" {
   104  		debug = true
   105  	}
   106  
   107  	contextData := make(map[string]interface{})
   108  	if run.Spec.Context != nil {
   109  		contextByte, err := run.Spec.Context.MarshalJSON()
   110  		if err != nil {
   111  			return nil, err
   112  		}
   113  		if err := json.Unmarshal(contextByte, &contextData); err != nil {
   114  			return nil, err
   115  		}
   116  	}
   117  	instance := &types.WorkflowInstance{
   118  		WorkflowMeta: types.WorkflowMeta{
   119  			Name:        run.Name,
   120  			Namespace:   run.Namespace,
   121  			Annotations: run.Annotations,
   122  			Labels:      run.Labels,
   123  			UID:         run.UID,
   124  			ChildOwnerReferences: []metav1.OwnerReference{
   125  				{
   126  					APIVersion: v1alpha1.SchemeGroupVersion.String(),
   127  					Kind:       v1alpha1.WorkflowRunKind,
   128  					Name:       run.Name,
   129  					UID:        run.UID,
   130  					Controller: pointer.Bool(true),
   131  				},
   132  			},
   133  		},
   134  		Context: contextData,
   135  		Debug:   debug,
   136  		Mode:    mode,
   137  		Steps:   steps,
   138  		Status:  run.Status,
   139  	}
   140  	executor.InitializeWorkflowInstance(instance)
   141  	return instance, nil
   142  }
   143  
   144  func initStepGeneratorOptions(ctx monitorContext.Context, instance *types.WorkflowInstance, options types.StepGeneratorOptions) types.StepGeneratorOptions {
   145  	if options.Providers == nil {
   146  		options.Providers = providers.NewProviders()
   147  	}
   148  	if options.ProcessCtx == nil {
   149  		options.ProcessCtx = process.NewContext(generateContextDataFromWorkflowRun(instance))
   150  	}
   151  	installBuiltinProviders(instance, options.Client, options.Providers, options.ProcessCtx)
   152  	if options.TemplateLoader == nil {
   153  		options.TemplateLoader = template.NewWorkflowStepTemplateLoader(options.Client)
   154  	}
   155  	return options
   156  }
   157  
   158  func installBuiltinProviders(instance *types.WorkflowInstance, client client.Client, providerHandlers types.Providers, pCtx process.Context) {
   159  	workspace.Install(providerHandlers, pCtx)
   160  	email.Install(providerHandlers)
   161  	util.Install(providerHandlers, pCtx)
   162  	http.Install(providerHandlers, client, instance.Namespace)
   163  	provider.Install(providerHandlers, client, nil)
   164  	metrics2.Install(providerHandlers)
   165  	kube.Install(providerHandlers, client, map[string]string{
   166  		types.LabelWorkflowRunName:      instance.Name,
   167  		types.LabelWorkflowRunNamespace: instance.Namespace,
   168  	}, nil)
   169  }
   170  
   171  func generateTaskRunner(ctx context.Context,
   172  	instance *types.WorkflowInstance,
   173  	step v1alpha1.WorkflowStep,
   174  	taskDiscover types.TaskDiscover,
   175  	options *types.TaskGeneratorOptions,
   176  	stepOptions types.StepGeneratorOptions) (types.TaskRunner, error) {
   177  	if step.Type == types.WorkflowStepTypeStepGroup {
   178  		var subTaskRunners []types.TaskRunner
   179  		for _, subStep := range step.SubSteps {
   180  			workflowStep := v1alpha1.WorkflowStep{
   181  				WorkflowStepBase: subStep,
   182  			}
   183  			o := &types.TaskGeneratorOptions{
   184  				ID:              generateSubStepID(instance.Status, subStep.Name, step.Name),
   185  				PackageDiscover: options.PackageDiscover,
   186  				ProcessContext:  options.ProcessContext,
   187  			}
   188  			for typ, convertor := range stepOptions.StepConvertor {
   189  				if subStep.Type == typ {
   190  					o.StepConvertor = convertor
   191  				}
   192  			}
   193  			subTask, err := generateTaskRunner(ctx, instance, workflowStep, taskDiscover, o, stepOptions)
   194  			if err != nil {
   195  				return nil, err
   196  			}
   197  			subTaskRunners = append(subTaskRunners, subTask)
   198  		}
   199  		options.SubTaskRunners = subTaskRunners
   200  		options.SubStepExecuteMode = v1alpha1.WorkflowModeDAG
   201  		if instance.Mode != nil {
   202  			options.SubStepExecuteMode = instance.Mode.SubSteps
   203  		}
   204  		if step.Mode != "" {
   205  			options.SubStepExecuteMode = step.Mode
   206  		}
   207  	}
   208  
   209  	genTask, err := taskDiscover.GetTaskGenerator(ctx, step.Type)
   210  	if err != nil {
   211  		return nil, err
   212  	}
   213  
   214  	task, err := genTask(step, options)
   215  	if err != nil {
   216  		return nil, err
   217  	}
   218  	return task, nil
   219  }
   220  
   221  func generateStepID(status v1alpha1.WorkflowRunStatus, name string) string {
   222  	for _, ss := range status.Steps {
   223  		if ss.Name == name {
   224  			return ss.ID
   225  		}
   226  	}
   227  
   228  	return rand.RandomString(10)
   229  }
   230  
   231  func generateSubStepID(status v1alpha1.WorkflowRunStatus, name, parentStepName string) string {
   232  	for _, ss := range status.Steps {
   233  		if ss.Name == parentStepName {
   234  			for _, sub := range ss.SubStepsStatus {
   235  				if sub.Name == name {
   236  					return sub.ID
   237  				}
   238  			}
   239  		}
   240  	}
   241  
   242  	return rand.RandomString(10)
   243  }
   244  
   245  func generateContextDataFromWorkflowRun(instance *types.WorkflowInstance) process.ContextData {
   246  	data := process.ContextData{
   247  		Name:       instance.Name,
   248  		Namespace:  instance.Namespace,
   249  		CustomData: instance.Context,
   250  	}
   251  	return data
   252  }