github.com/oam-dev/kubevela@v1.9.11/pkg/controller/core.oam.dev/v1beta1/application/generator.go (about)

     1  /*Copyright 2021 The KubeVela Authors.
     2  
     3  Licensed under the Apache License, Version 2.0 (the "License");
     4  you may not use this file except in compliance with the License.
     5  You may obtain a copy of the License at
     6  
     7      http://www.apache.org/licenses/LICENSE-2.0
     8  
     9  Unless required by applicable law or agreed to in writing, software
    10  distributed under the License is distributed on an "AS IS" BASIS,
    11  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  See the License for the specific language governing permissions and
    13  limitations under the License.
    14  */
    15  
    16  package application
    17  
    18  import (
    19  	"context"
    20  	"encoding/json"
    21  	"os"
    22  	"strings"
    23  	"time"
    24  
    25  	"github.com/pkg/errors"
    26  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    28  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    29  	"k8s.io/klog/v2"
    30  	"k8s.io/utils/pointer"
    31  	"sigs.k8s.io/controller-runtime/pkg/client"
    32  
    33  	monitorContext "github.com/kubevela/pkg/monitor/context"
    34  	pkgmulticluster "github.com/kubevela/pkg/multicluster"
    35  	"github.com/kubevela/pkg/util/slices"
    36  	workflowv1alpha1 "github.com/kubevela/workflow/api/v1alpha1"
    37  	"github.com/kubevela/workflow/pkg/cue/model/value"
    38  	"github.com/kubevela/workflow/pkg/executor"
    39  	"github.com/kubevela/workflow/pkg/generator"
    40  	"github.com/kubevela/workflow/pkg/providers"
    41  	"github.com/kubevela/workflow/pkg/providers/kube"
    42  	wfTypes "github.com/kubevela/workflow/pkg/types"
    43  
    44  	"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
    45  	"github.com/oam-dev/kubevela/apis/core.oam.dev/condition"
    46  	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
    47  	"github.com/oam-dev/kubevela/apis/types"
    48  	"github.com/oam-dev/kubevela/pkg/appfile"
    49  	"github.com/oam-dev/kubevela/pkg/auth"
    50  	configprovider "github.com/oam-dev/kubevela/pkg/config/provider"
    51  	"github.com/oam-dev/kubevela/pkg/controller/core.oam.dev/v1beta1/application/assemble"
    52  	ctrlutil "github.com/oam-dev/kubevela/pkg/controller/utils"
    53  	velaprocess "github.com/oam-dev/kubevela/pkg/cue/process"
    54  	"github.com/oam-dev/kubevela/pkg/features"
    55  	"github.com/oam-dev/kubevela/pkg/monitor/metrics"
    56  	"github.com/oam-dev/kubevela/pkg/multicluster"
    57  	"github.com/oam-dev/kubevela/pkg/oam"
    58  	"github.com/oam-dev/kubevela/pkg/oam/util"
    59  	"github.com/oam-dev/kubevela/pkg/stdlib"
    60  	"github.com/oam-dev/kubevela/pkg/utils/apply"
    61  	"github.com/oam-dev/kubevela/pkg/velaql/providers/query"
    62  	multiclusterProvider "github.com/oam-dev/kubevela/pkg/workflow/providers/multicluster"
    63  	oamProvider "github.com/oam-dev/kubevela/pkg/workflow/providers/oam"
    64  	terraformProvider "github.com/oam-dev/kubevela/pkg/workflow/providers/terraform"
    65  	"github.com/oam-dev/kubevela/pkg/workflow/template"
    66  )
    67  
    68  func init() {
    69  	if err := stdlib.SetupBuiltinImports(); err != nil {
    70  		klog.ErrorS(err, "Unable to set up builtin imports on package initialization")
    71  		os.Exit(1)
    72  	}
    73  }
    74  
    75  var (
    76  	// DisableResourceApplyDoubleCheck optimize applyComponentFunc by disable post resource existing check after dispatch
    77  	DisableResourceApplyDoubleCheck = false
    78  )
    79  
    80  // GenerateApplicationSteps generate application steps.
    81  // nolint:gocyclo
    82  func (h *AppHandler) GenerateApplicationSteps(ctx monitorContext.Context,
    83  	app *v1beta1.Application,
    84  	appParser *appfile.Parser,
    85  	af *appfile.Appfile) (*wfTypes.WorkflowInstance, []wfTypes.TaskRunner, error) {
    86  
    87  	appRev := h.currentAppRev
    88  	t := time.Now()
    89  	defer func() {
    90  		metrics.AppReconcileStageDurationHistogram.WithLabelValues("generate-app-steps").Observe(time.Since(t).Seconds())
    91  	}()
    92  
    93  	appLabels := map[string]string{
    94  		oam.LabelAppName:      app.Name,
    95  		oam.LabelAppNamespace: app.Namespace,
    96  	}
    97  	handlerProviders := providers.NewProviders()
    98  	kube.Install(handlerProviders, h.Client, appLabels, &kube.Handlers{
    99  		Apply:  h.Dispatch,
   100  		Delete: h.Delete,
   101  	})
   102  	configprovider.Install(handlerProviders, h.Client, func(ctx context.Context, resources []*unstructured.Unstructured, applyOptions []apply.ApplyOption) error {
   103  		for _, res := range resources {
   104  			res.SetLabels(util.MergeMapOverrideWithDst(res.GetLabels(), appLabels))
   105  		}
   106  		return h.resourceKeeper.Dispatch(ctx, resources, applyOptions)
   107  	})
   108  	oamProvider.Install(handlerProviders, app, af, h.Client,
   109  		h.applyComponentFunc(appParser, appRev, af),
   110  		h.renderComponentFunc(appParser, appRev, af),
   111  	)
   112  	pCtx := velaprocess.NewContext(generateContextDataFromApp(app, appRev.Name))
   113  	renderer := func(ctx context.Context, comp common.ApplicationComponent) (*appfile.Component, error) {
   114  		return appParser.ParseComponentFromRevisionAndClient(ctx, comp, appRev)
   115  	}
   116  	multiclusterProvider.Install(handlerProviders, h.Client, app, af,
   117  		h.applyComponentFunc(appParser, appRev, af),
   118  		h.checkComponentHealth(appParser, appRev, af),
   119  		renderer)
   120  	terraformProvider.Install(handlerProviders, app, renderer)
   121  	query.Install(handlerProviders, h.Client, nil)
   122  
   123  	instance := generateWorkflowInstance(af, app)
   124  	executor.InitializeWorkflowInstance(instance)
   125  	runners, err := generator.GenerateRunners(ctx, instance, wfTypes.StepGeneratorOptions{
   126  		Providers:       handlerProviders,
   127  		PackageDiscover: h.pd,
   128  		ProcessCtx:      pCtx,
   129  		TemplateLoader:  template.NewWorkflowStepTemplateRevisionLoader(appRev, h.Client.RESTMapper()),
   130  		Client:          h.Client,
   131  		StepConvertor: map[string]func(step workflowv1alpha1.WorkflowStep) (workflowv1alpha1.WorkflowStep, error){
   132  			wfTypes.WorkflowStepTypeApplyComponent: func(lstep workflowv1alpha1.WorkflowStep) (workflowv1alpha1.WorkflowStep, error) {
   133  				copierStep := lstep.DeepCopy()
   134  				if err := convertStepProperties(copierStep, app); err != nil {
   135  					return lstep, errors.WithMessage(err, "convert [apply-component]")
   136  				}
   137  				copierStep.Type = wfTypes.WorkflowStepTypeBuiltinApplyComponent
   138  				return *copierStep, nil
   139  			},
   140  		},
   141  	})
   142  	if err != nil {
   143  		return nil, nil, err
   144  	}
   145  	return instance, runners, nil
   146  }
   147  
   148  // CheckWorkflowRestart check if application workflow need restart and return the desired
   149  // rev to be set in status
   150  // 1. If workflow status is empty, it means no previous running record, the
   151  // workflow will restart (cold start)
   152  // 2. If workflow status is not empty, and publishVersion is set, the desired
   153  // rev will be the publishVersion
   154  // 3. If workflow status is not empty, the desired rev will be the
   155  // ApplicationRevision name. For backward compatibility, the legacy style
   156  // <rev>:<hash> will be recognized and reduced into <rev>
   157  func (h *AppHandler) CheckWorkflowRestart(ctx monitorContext.Context, app *v1beta1.Application) {
   158  	desiredRev, currentRev := h.currentAppRev.Name, ""
   159  	if app.Status.Workflow != nil {
   160  		currentRev = app.Status.Workflow.AppRevision
   161  	}
   162  	if metav1.HasAnnotation(app.ObjectMeta, oam.AnnotationPublishVersion) {
   163  		desiredRev = app.GetAnnotations()[oam.AnnotationPublishVersion]
   164  	} else { // nolint
   165  		// backward compatibility
   166  		// legacy versions use <rev>:<hash> as currentRev, extract <rev>
   167  		if idx := strings.LastIndexAny(currentRev, ":"); idx >= 0 {
   168  			currentRev = currentRev[:idx]
   169  		}
   170  	}
   171  	if currentRev != "" && desiredRev == currentRev {
   172  		return
   173  	}
   174  	// record in revision
   175  	if h.latestAppRev != nil && h.latestAppRev.Status.Workflow == nil && app.Status.Workflow != nil {
   176  		app.Status.Workflow.Terminated = true
   177  		app.Status.Workflow.Finished = true
   178  		if app.Status.Workflow.EndTime.IsZero() {
   179  			app.Status.Workflow.EndTime = metav1.Now()
   180  		}
   181  		h.UpdateApplicationRevisionStatus(ctx, h.latestAppRev, app.Status.Workflow)
   182  	}
   183  
   184  	// clean recorded resources info.
   185  	app.Status.Services = nil
   186  	app.Status.AppliedResources = nil
   187  
   188  	// clean conditions after render
   189  	var reservedConditions []condition.Condition
   190  	for i, cond := range app.Status.Conditions {
   191  		condTpy, err := common.ParseApplicationConditionType(string(cond.Type))
   192  		if err == nil {
   193  			if condTpy <= common.RenderCondition {
   194  				reservedConditions = append(reservedConditions, app.Status.Conditions[i])
   195  			}
   196  		}
   197  	}
   198  	app.Status.Conditions = reservedConditions
   199  	app.Status.Workflow = &common.WorkflowStatus{
   200  		AppRevision: desiredRev,
   201  	}
   202  }
   203  
   204  func generateWorkflowInstance(af *appfile.Appfile, app *v1beta1.Application) *wfTypes.WorkflowInstance {
   205  	instance := &wfTypes.WorkflowInstance{
   206  		WorkflowMeta: wfTypes.WorkflowMeta{
   207  			Name:        af.Name,
   208  			Namespace:   af.Namespace,
   209  			Annotations: app.Annotations,
   210  			Labels:      app.Labels,
   211  			UID:         app.UID,
   212  			ChildOwnerReferences: []metav1.OwnerReference{
   213  				{
   214  					APIVersion: v1beta1.SchemeGroupVersion.String(),
   215  					Kind:       v1beta1.ApplicationKind,
   216  					Name:       app.Name,
   217  					UID:        app.GetUID(),
   218  					Controller: pointer.Bool(true),
   219  				},
   220  			},
   221  		},
   222  		Debug: af.Debug,
   223  		Steps: af.WorkflowSteps,
   224  		Mode:  af.WorkflowMode,
   225  	}
   226  	status := app.Status.Workflow
   227  	instance.Status = workflowv1alpha1.WorkflowRunStatus{
   228  		Mode:           *af.WorkflowMode,
   229  		Phase:          status.Phase,
   230  		Message:        status.Message,
   231  		Suspend:        status.Suspend,
   232  		SuspendState:   status.SuspendState,
   233  		Terminated:     status.Terminated,
   234  		Finished:       status.Finished,
   235  		ContextBackend: status.ContextBackend,
   236  		Steps:          status.Steps,
   237  		StartTime:      status.StartTime,
   238  		EndTime:        status.EndTime,
   239  	}
   240  	switch app.Status.Phase {
   241  	case common.ApplicationRunning:
   242  		instance.Status.Phase = workflowv1alpha1.WorkflowStateSucceeded
   243  	case common.ApplicationWorkflowSuspending:
   244  		instance.Status.Phase = workflowv1alpha1.WorkflowStateSuspending
   245  	case common.ApplicationWorkflowTerminated:
   246  		instance.Status.Phase = workflowv1alpha1.WorkflowStateTerminated
   247  	default:
   248  		instance.Status.Phase = workflowv1alpha1.WorkflowStateExecuting
   249  	}
   250  	return instance
   251  }
   252  
   253  func convertStepProperties(step *workflowv1alpha1.WorkflowStep, app *v1beta1.Application) error {
   254  	o := struct {
   255  		Component string `json:"component"`
   256  		Cluster   string `json:"cluster"`
   257  		Namespace string `json:"namespace"`
   258  	}{}
   259  	js, err := common.RawExtensionPointer{RawExtension: step.Properties}.MarshalJSON()
   260  	if err != nil {
   261  		return err
   262  	}
   263  	if err := json.Unmarshal(js, &o); err != nil {
   264  		return err
   265  	}
   266  
   267  	var componentNames []string
   268  	for _, c := range app.Spec.Components {
   269  		componentNames = append(componentNames, c.Name)
   270  	}
   271  
   272  	for _, c := range app.Spec.Components {
   273  		if c.Name == o.Component {
   274  			if dcName, ok := checkDependsOnValidComponent(c.DependsOn, componentNames); !ok {
   275  				return errors.Errorf("component %s not found, which is depended by %s", dcName, c.Name)
   276  			}
   277  			step.Inputs = append(step.Inputs, c.Inputs...)
   278  			for index := range step.Inputs {
   279  				parameterKey := strings.TrimSpace(step.Inputs[index].ParameterKey)
   280  				if parameterKey != "" && !strings.HasPrefix(parameterKey, "properties") && !strings.HasPrefix(parameterKey, "traits[") {
   281  					parameterKey = "properties." + parameterKey
   282  				}
   283  				if parameterKey != "" {
   284  					parameterKey = "value." + parameterKey
   285  				}
   286  				step.Inputs[index].ParameterKey = parameterKey
   287  			}
   288  			step.Outputs = append(step.Outputs, c.Outputs...)
   289  			step.DependsOn = append(step.DependsOn, c.DependsOn...)
   290  			c.Inputs = nil
   291  			c.Outputs = nil
   292  			c.DependsOn = nil
   293  			stepProperties := map[string]interface{}{
   294  				"value":   c,
   295  				"cluster": o.Cluster,
   296  			}
   297  			if o.Namespace != "" {
   298  				stepProperties["namespace"] = o.Namespace
   299  			}
   300  			step.Properties = util.Object2RawExtension(stepProperties)
   301  			return nil
   302  		}
   303  	}
   304  	return errors.Errorf("component %s not found", o.Component)
   305  }
   306  
   307  func checkDependsOnValidComponent(dependsOnComponentNames, allComponentNames []string) (string, bool) {
   308  	// does not depend on other components
   309  	if dependsOnComponentNames == nil {
   310  		return "", true
   311  	}
   312  	for _, dc := range dependsOnComponentNames {
   313  		if !slices.Contains(allComponentNames, dc) {
   314  			return dc, false
   315  		}
   316  	}
   317  	return "", true
   318  }
   319  
   320  func (h *AppHandler) renderComponentFunc(appParser *appfile.Parser, appRev *v1beta1.ApplicationRevision, af *appfile.Appfile) oamProvider.ComponentRender {
   321  	return func(baseCtx context.Context, comp common.ApplicationComponent, patcher *value.Value, clusterName string, overrideNamespace string) (*unstructured.Unstructured, []*unstructured.Unstructured, error) {
   322  		ctx := multicluster.ContextWithClusterName(baseCtx, clusterName)
   323  
   324  		_, manifest, err := h.prepareWorkloadAndManifests(ctx, appParser, comp, appRev, patcher, af)
   325  		if err != nil {
   326  			return nil, nil, err
   327  		}
   328  		return renderComponentsAndTraits(manifest, appRev, clusterName, overrideNamespace)
   329  	}
   330  }
   331  
   332  func (h *AppHandler) checkComponentHealth(appParser *appfile.Parser, appRev *v1beta1.ApplicationRevision, af *appfile.Appfile) oamProvider.ComponentHealthCheck {
   333  	return func(baseCtx context.Context, comp common.ApplicationComponent, patcher *value.Value, clusterName string, overrideNamespace string) (bool, *unstructured.Unstructured, []*unstructured.Unstructured, error) {
   334  		ctx := multicluster.ContextWithClusterName(baseCtx, clusterName)
   335  		ctx = contextWithComponentNamespace(ctx, overrideNamespace)
   336  		ctx = contextWithReplicaKey(ctx, comp.ReplicaKey)
   337  
   338  		wl, manifest, err := h.prepareWorkloadAndManifests(ctx, appParser, comp, appRev, patcher, af)
   339  		if err != nil {
   340  			return false, nil, nil, err
   341  		}
   342  		wl.Ctx.SetCtx(auth.ContextWithUserInfo(ctx, h.app))
   343  
   344  		readyWorkload, readyTraits, err := renderComponentsAndTraits(manifest, appRev, clusterName, overrideNamespace)
   345  		if err != nil {
   346  			return false, nil, nil, err
   347  		}
   348  		checkSkipApplyWorkload(wl)
   349  
   350  		dispatchResources := readyTraits
   351  		if !wl.SkipApplyWorkload {
   352  			dispatchResources = append([]*unstructured.Unstructured{readyWorkload}, readyTraits...)
   353  		}
   354  		if !h.resourceKeeper.ContainsResources(dispatchResources) {
   355  			return false, nil, nil, err
   356  		}
   357  
   358  		_, output, outputs, isHealth, err := h.collectHealthStatus(auth.ContextWithUserInfo(ctx, h.app), wl, appRev, overrideNamespace, false)
   359  		if err != nil {
   360  			return false, nil, nil, err
   361  		}
   362  
   363  		return isHealth, output, outputs, err
   364  	}
   365  }
   366  
   367  func (h *AppHandler) applyComponentFunc(appParser *appfile.Parser, appRev *v1beta1.ApplicationRevision, af *appfile.Appfile) oamProvider.ComponentApply {
   368  	return func(baseCtx context.Context, comp common.ApplicationComponent, patcher *value.Value, clusterName string, overrideNamespace string) (*unstructured.Unstructured, []*unstructured.Unstructured, bool, error) {
   369  		t := time.Now()
   370  		defer func() { metrics.ApplyComponentTimeHistogram.WithLabelValues("-").Observe(time.Since(t).Seconds()) }()
   371  
   372  		ctx := multicluster.ContextWithClusterName(baseCtx, clusterName)
   373  		ctx = contextWithComponentNamespace(ctx, overrideNamespace)
   374  		ctx = contextWithReplicaKey(ctx, comp.ReplicaKey)
   375  
   376  		wl, manifest, err := h.prepareWorkloadAndManifests(ctx, appParser, comp, appRev, patcher, af)
   377  		if err != nil {
   378  			return nil, nil, false, err
   379  		}
   380  		wl.Ctx.SetCtx(auth.ContextWithUserInfo(ctx, h.app))
   381  
   382  		readyWorkload, readyTraits, err := renderComponentsAndTraits(manifest, appRev, clusterName, overrideNamespace)
   383  		if err != nil {
   384  			return nil, nil, false, err
   385  		}
   386  		checkSkipApplyWorkload(wl)
   387  
   388  		isHealth := true
   389  		if utilfeature.DefaultMutableFeatureGate.Enabled(features.MultiStageComponentApply) {
   390  			manifestDispatchers, err := h.generateDispatcher(appRev, readyWorkload, readyTraits, overrideNamespace)
   391  			if err != nil {
   392  				return nil, nil, false, errors.WithMessage(err, "generateDispatcher")
   393  			}
   394  
   395  			for _, dispatcher := range manifestDispatchers {
   396  				if isHealth, err := dispatcher.run(ctx, wl, appRev, clusterName); !isHealth || err != nil {
   397  					return nil, nil, false, err
   398  				}
   399  			}
   400  		} else {
   401  			dispatchResources := readyTraits
   402  			if !wl.SkipApplyWorkload {
   403  				dispatchResources = append([]*unstructured.Unstructured{readyWorkload}, readyTraits...)
   404  			}
   405  
   406  			if err := h.Dispatch(ctx, clusterName, common.WorkflowResourceCreator, dispatchResources...); err != nil {
   407  				return nil, nil, false, errors.WithMessage(err, "Dispatch")
   408  			}
   409  			_, _, _, isHealth, err = h.collectHealthStatus(ctx, wl, appRev, overrideNamespace, false)
   410  			if err != nil {
   411  				return nil, nil, false, errors.WithMessage(err, "CollectHealthStatus")
   412  			}
   413  		}
   414  
   415  		if DisableResourceApplyDoubleCheck {
   416  			return readyWorkload, readyTraits, isHealth, nil
   417  		}
   418  		workload, traits, err := getComponentResources(auth.ContextWithUserInfo(ctx, h.app), manifest, wl.SkipApplyWorkload, h.Client)
   419  		return workload, traits, isHealth, err
   420  	}
   421  }
   422  
   423  // redirectTraitToLocalIfNeed will override cluster field to be local for traits which are control plane only
   424  func redirectTraitToLocalIfNeed(appRev *v1beta1.ApplicationRevision, readyTraits []*unstructured.Unstructured) []*unstructured.Unstructured {
   425  	traits := readyTraits
   426  	for index, readyTrait := range readyTraits {
   427  		for _, trait := range appRev.Spec.TraitDefinitions {
   428  			if trait.Spec.ControlPlaneOnly && trait.Name == readyTrait.GetLabels()[oam.TraitTypeLabel] {
   429  				oam.SetCluster(traits[index], multicluster.ClusterLocalName)
   430  				traits[index].SetNamespace(appRev.GetNamespace())
   431  				break
   432  			}
   433  		}
   434  	}
   435  	return traits
   436  }
   437  
   438  func (h *AppHandler) prepareWorkloadAndManifests(ctx context.Context,
   439  	appParser *appfile.Parser,
   440  	comp common.ApplicationComponent,
   441  	appRev *v1beta1.ApplicationRevision,
   442  	patcher *value.Value,
   443  	af *appfile.Appfile) (*appfile.Component, *types.ComponentManifest, error) {
   444  	wl, err := appParser.ParseComponentFromRevisionAndClient(ctx, comp, appRev)
   445  	if err != nil {
   446  		return nil, nil, errors.WithMessage(err, "ParseWorkload")
   447  	}
   448  	wl.Patch = patcher
   449  	manifest, err := af.GenerateComponentManifest(wl, func(ctxData *velaprocess.ContextData) {
   450  		if ns := componentNamespaceFromContext(ctx); ns != "" {
   451  			ctxData.Namespace = ns
   452  		}
   453  		if rk := replicaKeyFromContext(ctx); rk != "" {
   454  			ctxData.ReplicaKey = rk
   455  		}
   456  		ctxData.Cluster = pkgmulticluster.Local
   457  		if cluster, ok := pkgmulticluster.ClusterFrom(ctx); ok && cluster != "" {
   458  			ctxData.Cluster = cluster
   459  		}
   460  		// cluster info are secrets stored in the control plane cluster
   461  		ctxData.ClusterVersion = multicluster.GetVersionInfoFromObject(pkgmulticluster.WithCluster(ctx, types.ClusterLocalName), h.Client, ctxData.Cluster)
   462  		ctxData.CompRevision, _ = ctrlutil.ComputeSpecHash(comp)
   463  	})
   464  	if err != nil {
   465  		return nil, nil, errors.WithMessage(err, "GenerateComponentManifest")
   466  	}
   467  	if err := af.SetOAMContract(manifest); err != nil {
   468  		return nil, nil, errors.WithMessage(err, "SetOAMContract")
   469  	}
   470  	return wl, manifest, nil
   471  }
   472  
   473  func renderComponentsAndTraits(manifest *types.ComponentManifest, appRev *v1beta1.ApplicationRevision, clusterName string, overrideNamespace string) (*unstructured.Unstructured, []*unstructured.Unstructured, error) {
   474  	readyWorkload, readyTraits, err := assemble.PrepareBeforeApply(manifest, appRev)
   475  	if err != nil {
   476  		return nil, nil, errors.WithMessage(err, "assemble resources before apply fail")
   477  	}
   478  	if clusterName != "" {
   479  		oam.SetClusterIfEmpty(readyWorkload, clusterName)
   480  		for _, readyTrait := range readyTraits {
   481  			oam.SetClusterIfEmpty(readyTrait, clusterName)
   482  		}
   483  	}
   484  	if overrideNamespace != "" {
   485  		readyWorkload.SetNamespace(overrideNamespace)
   486  		for _, readyTrait := range readyTraits {
   487  			readyTrait.SetNamespace(overrideNamespace)
   488  		}
   489  	}
   490  	readyTraits = redirectTraitToLocalIfNeed(appRev, readyTraits)
   491  	return readyWorkload, readyTraits, nil
   492  }
   493  
   494  func checkSkipApplyWorkload(comp *appfile.Component) {
   495  	for _, trait := range comp.Traits {
   496  		if trait.FullTemplate.TraitDefinition.Spec.ManageWorkload {
   497  			comp.SkipApplyWorkload = true
   498  			break
   499  		}
   500  	}
   501  }
   502  
   503  func getComponentResources(ctx context.Context, manifest *types.ComponentManifest, skipStandardWorkload bool, cli client.Client) (*unstructured.Unstructured, []*unstructured.Unstructured, error) {
   504  	var (
   505  		workload *unstructured.Unstructured
   506  		traits   []*unstructured.Unstructured
   507  	)
   508  	if !skipStandardWorkload {
   509  		v := manifest.ComponentOutput.DeepCopy()
   510  		if err := cli.Get(ctx, client.ObjectKeyFromObject(manifest.ComponentOutput), v); err != nil {
   511  			return nil, nil, err
   512  		}
   513  		workload = v
   514  	}
   515  
   516  	for _, trait := range manifest.ComponentOutputsAndTraits {
   517  		v := trait.DeepCopy()
   518  		remoteCtx := multicluster.ContextWithClusterName(ctx, oam.GetCluster(v))
   519  		if err := cli.Get(remoteCtx, client.ObjectKeyFromObject(trait), v); err != nil {
   520  			return workload, nil, err
   521  		}
   522  		traits = append(traits, v)
   523  	}
   524  	return workload, traits, nil
   525  }
   526  
   527  func generateContextDataFromApp(app *v1beta1.Application, appRev string) velaprocess.ContextData {
   528  	data := velaprocess.ContextData{
   529  		Namespace:       app.Namespace,
   530  		AppName:         app.Name,
   531  		CompName:        app.Name,
   532  		AppRevisionName: appRev,
   533  	}
   534  	if app.Annotations != nil {
   535  		data.WorkflowName = app.Annotations[oam.AnnotationWorkflowName]
   536  		data.PublishVersion = app.Annotations[oam.AnnotationPublishVersion]
   537  	}
   538  	return data
   539  }