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 }