mirror of
https://github.com/kubevela/kubevela.git
synced 2026-08-18 03:56:36 +00:00
Feat: modify apply component cue action to support skipWorkload trait (#2167)
fix lint fix comments
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: rollout-multi-comp
|
||||
namespace: default
|
||||
spec:
|
||||
components:
|
||||
- name: back-end
|
||||
externalRevision: backend-v1
|
||||
type: webservice
|
||||
properties:
|
||||
image: nginx:1.21
|
||||
port: 80
|
||||
traits:
|
||||
- type: rollout
|
||||
properties:
|
||||
targetSize: 2
|
||||
# This means to rollout two more replicas in two batches.
|
||||
rolloutBatches:
|
||||
- replicas: 1
|
||||
- replicas: 1
|
||||
|
||||
- name: front-end
|
||||
externalRevision: front-end-v1
|
||||
type: webservice
|
||||
properties:
|
||||
image: node:16.7-alpine3.12
|
||||
port: 80
|
||||
traits:
|
||||
- type: rollout
|
||||
properties:
|
||||
targetSize: 4
|
||||
# This means to rollout two more replicas in two batches.
|
||||
rolloutBatches:
|
||||
- replicas: 2
|
||||
- replicas: 2
|
||||
|
||||
workflow:
|
||||
steps:
|
||||
# rollout back-end component first, workflow will block until rollout succeed
|
||||
- name: rollout-back-end
|
||||
type: rollout-wait-succeed
|
||||
properties:
|
||||
appName: rollout-multi-comp
|
||||
component: back-end
|
||||
|
||||
# rollout front-end component
|
||||
- name: rollout-front-end
|
||||
type: rollout-wait-succeed
|
||||
properties:
|
||||
appName: rollout-multi-comp
|
||||
component: front-end
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
# Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file.
|
||||
# Definition source cue file: vela-templates/definitions/internal/apply-component.cue
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Apply components and traits for your workflow steps
|
||||
name: rollout-wait-succeed
|
||||
namespace: vela-system
|
||||
spec:
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
import (
|
||||
"vela/op"
|
||||
)
|
||||
|
||||
// apply components and traits
|
||||
apply: op.#ApplyComponent & {
|
||||
component: parameter.component
|
||||
}
|
||||
|
||||
parameter: {
|
||||
// +usage=Declare the name of the application
|
||||
appName: string
|
||||
// +usage=Declare the name of the component
|
||||
component: string
|
||||
}
|
||||
|
||||
wait: op.#ConditionalWait & {
|
||||
continue: apply.traits["rollout"].value.status.rollingState=="rolloutSucceed"
|
||||
}
|
||||
|
||||
@@ -138,6 +138,9 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
||||
r.Recorder.Event(app, event.Warning(velatypes.ReasonFailedRender, err))
|
||||
return r.endWithNegativeCondition(ctx, app, condition.ErrorCondition("Render", err))
|
||||
}
|
||||
|
||||
handler.handleCheckManageWorkloadTrait(handler.currentAppRev.Spec.TraitDefinitions, comps)
|
||||
|
||||
if err := handler.HandleComponentsRevision(ctx, comps); err != nil {
|
||||
klog.ErrorS(err, "Failed to handle compoents revision", "application", klog.KObj(app))
|
||||
r.Recorder.Event(app, event.Warning(velatypes.ReasonFailedRevision, err))
|
||||
|
||||
@@ -1839,7 +1839,10 @@ spec:
|
||||
}
|
||||
Expect(k8sClient.Create(ctx, app)).Should(BeNil())
|
||||
appKey := types.NamespacedName{Namespace: ns.Name, Name: app.Name}
|
||||
testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: appKey})
|
||||
// first reconcile handle finalizer
|
||||
testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey})
|
||||
// second reconcile apply all resources
|
||||
testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey})
|
||||
checkApp := &v1beta1.Application{}
|
||||
Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil())
|
||||
Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationRunning))
|
||||
@@ -1848,6 +1851,81 @@ spec:
|
||||
By("verify targetRevision will be filled with real compRev by context.Revision")
|
||||
Expect(checkRollout.Spec.TargetRevisionName).Should(BeEquivalentTo(externalRevision))
|
||||
})
|
||||
|
||||
It("Test rollout trait in workflow", func() {
|
||||
rolloutTdDef, err := yaml.YAMLToJSON([]byte(rolloutTraitDefinition))
|
||||
Expect(err).Should(BeNil())
|
||||
rolloutTrait := &v1beta1.TraitDefinition{}
|
||||
Expect(json.Unmarshal([]byte(rolloutTdDef), rolloutTrait)).Should(BeNil())
|
||||
|
||||
wfStepDef, err := yaml.YAMLToJSON([]byte(applyCompWfStepDefinition))
|
||||
Expect(err).Should(BeNil())
|
||||
wfStep := &v1beta1.WorkflowStepDefinition{}
|
||||
Expect(json.Unmarshal([]byte(wfStepDef), wfStep)).Should(BeNil())
|
||||
|
||||
ns := corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app-with-rollout-workflow",
|
||||
},
|
||||
}
|
||||
rolloutTrait.SetNamespace(ns.Name)
|
||||
wfStep.SetNamespace(ns.Name)
|
||||
Expect(k8sClient.Create(ctx, &ns)).Should(BeNil())
|
||||
Expect(k8sClient.Create(ctx, rolloutTrait)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
Expect(k8sClient.Create(ctx, wfStep)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
app := &v1beta1.Application{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Application",
|
||||
APIVersion: "core.oam.dev/v1beta1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app-with-rollout-workflow",
|
||||
Namespace: ns.Name,
|
||||
},
|
||||
Spec: v1beta1.ApplicationSpec{
|
||||
Components: []common.ApplicationComponent{
|
||||
{
|
||||
Name: "myweb1",
|
||||
Type: "worker",
|
||||
Properties: runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)},
|
||||
Traits: []common.ApplicationTrait{
|
||||
{
|
||||
Type: "rollout",
|
||||
Properties: runtime.RawExtension{Raw: []byte(`{}`)},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Workflow: &v1beta1.Workflow{
|
||||
Steps: []v1beta1.WorkflowStep{
|
||||
{
|
||||
Name: "apply",
|
||||
Type: "apply-component",
|
||||
Properties: runtime.RawExtension{Raw: []byte(`{"component" : "myweb1"}`)},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Expect(k8sClient.Create(ctx, app)).Should(BeNil())
|
||||
appKey := types.NamespacedName{Namespace: ns.Name, Name: app.Name}
|
||||
testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: appKey})
|
||||
checkApp := &v1beta1.Application{}
|
||||
Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil())
|
||||
Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationRunning))
|
||||
|
||||
By("verify workflow apply component had apply rollout")
|
||||
checkRollout := &stdv1alpha1.Rollout{}
|
||||
Expect(k8sClient.Get(ctx, types.NamespacedName{Name: "myweb1", Namespace: ns.Name}, checkRollout)).Should(BeNil())
|
||||
By("verify targetRevision will be filled with real compRev by context.ComponentRevName")
|
||||
Expect(checkRollout.Spec.TargetRevisionName).Should(BeEquivalentTo("myweb1-v1"))
|
||||
|
||||
By("verify workflow apply component didn't apply workload")
|
||||
deploy := &v1.Deployment{}
|
||||
Expect(k8sClient.Get(ctx, types.NamespacedName{Name: "myweb1", Namespace: ns.Name}, deploy)).Should(util.NotFoundMatcher{})
|
||||
})
|
||||
})
|
||||
|
||||
const (
|
||||
@@ -2732,6 +2810,31 @@ spec:
|
||||
parameter: {
|
||||
targetRevision: *context.revision|string
|
||||
}
|
||||
`
|
||||
applyCompWfStepDefinition = `
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Apply components and traits for your workflow steps
|
||||
name: apply-component
|
||||
namespace: vela-system
|
||||
spec:
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
import (
|
||||
"vela/op"
|
||||
)
|
||||
|
||||
// apply components and traits
|
||||
apply: op.#ApplyComponent & {
|
||||
component: parameter.component
|
||||
}
|
||||
parameter: {
|
||||
// +usage=Declare the name of the component
|
||||
component: string
|
||||
}
|
||||
`
|
||||
)
|
||||
|
||||
|
||||
@@ -235,6 +235,26 @@ func (h *AppHandler) aggregateHealthStatus(appFile *appfile.Appfile) ([]common.A
|
||||
return appStatus, healthy, nil
|
||||
}
|
||||
|
||||
func (h *AppHandler) handleCheckManageWorkloadTrait(traitDefs map[string]v1beta1.TraitDefinition, comps []*types.ComponentManifest) {
|
||||
manageWorkloadTrait := map[string]bool{}
|
||||
for traitName, definition := range traitDefs {
|
||||
if definition.Spec.ManageWorkload {
|
||||
manageWorkloadTrait[traitName] = true
|
||||
}
|
||||
}
|
||||
if len(manageWorkloadTrait) == 0 {
|
||||
return
|
||||
}
|
||||
for _, comp := range comps {
|
||||
for _, trait := range comp.Traits {
|
||||
traitType := trait.GetLabels()[oam.TraitTypeLabel]
|
||||
if manageWorkloadTrait[traitType] {
|
||||
trait.SetLabels(oamutil.MergeMapOverrideWithDst(trait.GetLabels(), map[string]string{oam.LabelManageWorkloadTrait: "true"}))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func generateScopeReference(scopes []appfile.Scope) []corev1.ObjectReference {
|
||||
var references []corev1.ObjectReference
|
||||
for _, scope := range scopes {
|
||||
|
||||
@@ -30,6 +30,7 @@ import (
|
||||
. "github.com/onsi/gomega"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
@@ -40,6 +41,7 @@ import (
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
velatypes "github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/appfile"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
)
|
||||
|
||||
const workloadDefinition = `
|
||||
@@ -215,3 +217,37 @@ var _ = Describe("Test statusAggregate", func() {
|
||||
Expect(err).Should(BeNil())
|
||||
})
|
||||
})
|
||||
|
||||
var _ = Describe("Test handleCheckManageWorkloadTrait func", func() {
|
||||
It("Test every situation", func() {
|
||||
traitDefs := map[string]v1beta1.TraitDefinition{
|
||||
"rollout": v1beta1.TraitDefinition{
|
||||
Spec: v1beta1.TraitDefinitionSpec{
|
||||
ManageWorkload: true,
|
||||
},
|
||||
},
|
||||
"normal": v1beta1.TraitDefinition{
|
||||
Spec: v1beta1.TraitDefinitionSpec{},
|
||||
},
|
||||
}
|
||||
rolloutTrait := &unstructured.Unstructured{}
|
||||
rolloutTrait.SetLabels(map[string]string{oam.TraitTypeLabel: "rollout"})
|
||||
|
||||
normalTrait := &unstructured.Unstructured{}
|
||||
normalTrait.SetLabels(map[string]string{oam.TraitTypeLabel: "normal"})
|
||||
comps := []*velatypes.ComponentManifest{
|
||||
{
|
||||
Traits: []*unstructured.Unstructured{
|
||||
rolloutTrait,
|
||||
normalTrait,
|
||||
},
|
||||
},
|
||||
}
|
||||
h := AppHandler{}
|
||||
h.handleCheckManageWorkloadTrait(traitDefs, comps)
|
||||
Expect(len(rolloutTrait.GetLabels())).Should(BeEquivalentTo(2))
|
||||
Expect(rolloutTrait.GetLabels()[oam.LabelManageWorkloadTrait]).Should(BeEquivalentTo("true"))
|
||||
Expect(len(normalTrait.GetLabels())).Should(BeEquivalentTo(1))
|
||||
Expect(normalTrait.GetLabels()[oam.LabelManageWorkloadTrait]).Should(BeEquivalentTo(""))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -108,19 +108,22 @@ func (am *AppManifests) AssembledManifests() ([]*unstructured.Unstructured, erro
|
||||
if am.err != nil {
|
||||
return nil, am.err
|
||||
}
|
||||
am.CheckSkipApplyWorkloadComp()
|
||||
r := make([]*unstructured.Unstructured, 0)
|
||||
for compName, wl := range am.assembledWorkloads {
|
||||
if !am.skipWorkloadApplyComp[compName] {
|
||||
skipApplyWorkload := false
|
||||
ts := am.assembledTraits[compName]
|
||||
for _, t := range ts {
|
||||
r = append(r, t.DeepCopy())
|
||||
if v := t.GetLabels()[oam.LabelManageWorkloadTrait]; v == "true" {
|
||||
skipApplyWorkload = true
|
||||
}
|
||||
}
|
||||
if !skipApplyWorkload {
|
||||
r = append(r, wl.DeepCopy())
|
||||
} else {
|
||||
klog.InfoS("assemble meet a managedByTrait workload, so skip apply it",
|
||||
"namespace", am.AppRevision.Namespace, "appRev", am.AppRevision.Name)
|
||||
}
|
||||
ts := am.assembledTraits[compName]
|
||||
for _, t := range ts {
|
||||
r = append(r, t.DeepCopy())
|
||||
}
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
@@ -414,25 +417,3 @@ func (am *AppManifests) setWorkloadRefToTrait(wlRef corev1.ObjectReference, trai
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckSkipApplyWorkloadComp check component's workload is manage by trait, if yes skip apply it
|
||||
func (am *AppManifests) CheckSkipApplyWorkloadComp() {
|
||||
app := am.AppRevision.Spec.Application
|
||||
traitDefs := am.AppRevision.Spec.TraitDefinitions
|
||||
manageWorkloadTrait := map[string]bool{}
|
||||
for traitName, definition := range traitDefs {
|
||||
if definition.Spec.ManageWorkload {
|
||||
manageWorkloadTrait[traitName] = true
|
||||
}
|
||||
}
|
||||
if len(manageWorkloadTrait) == 0 {
|
||||
return
|
||||
}
|
||||
for _, component := range app.Spec.Components {
|
||||
for _, trait := range component.Traits {
|
||||
if manageWorkloadTrait[trait.Type] {
|
||||
am.skipWorkloadApplyComp[component.Name] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ import (
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
)
|
||||
@@ -207,58 +206,3 @@ var _ = Describe("Test Assemble Options", func() {
|
||||
Expect(annotationKeys).Should(ContainElements("canPassAnno"))
|
||||
})
|
||||
})
|
||||
|
||||
var _ = Describe("Test CheckSkipApplyWorkloadComp func", func() {
|
||||
It("Test every situation", func() {
|
||||
a := AppManifests{
|
||||
AppRevision: &v1beta1.ApplicationRevision{
|
||||
Spec: v1beta1.ApplicationRevisionSpec{
|
||||
Application: v1beta1.Application{
|
||||
Spec: v1beta1.ApplicationSpec{
|
||||
Components: []common.ApplicationComponent{
|
||||
{
|
||||
Name: "comp1",
|
||||
Traits: []common.ApplicationTrait{
|
||||
{
|
||||
Type: "rollout",
|
||||
},
|
||||
{
|
||||
Type: "normal",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "comp2",
|
||||
Traits: []common.ApplicationTrait{
|
||||
{
|
||||
Type: "normal",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "comp3",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
TraitDefinitions: map[string]v1beta1.TraitDefinition{
|
||||
"rollout": v1beta1.TraitDefinition{
|
||||
Spec: v1beta1.TraitDefinitionSpec{
|
||||
ManageWorkload: true,
|
||||
},
|
||||
},
|
||||
"normal": v1beta1.TraitDefinition{
|
||||
Spec: v1beta1.TraitDefinitionSpec{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
skipWorkloadApplyComp: make(map[string]bool),
|
||||
}
|
||||
a.CheckSkipApplyWorkloadComp()
|
||||
Expect(len(a.skipWorkloadApplyComp)).Should(BeEquivalentTo(1))
|
||||
Expect(a.skipWorkloadApplyComp["comp1"]).Should(BeTrue())
|
||||
Expect(a.skipWorkloadApplyComp["comp2"]).Should(BeFalse())
|
||||
Expect(a.skipWorkloadApplyComp["comp3"]).Should(BeFalse())
|
||||
})
|
||||
})
|
||||
|
||||
@@ -395,6 +395,14 @@ func (h *AppHandler) handleComponentRevisionNameSpecified(ctx context.Context, c
|
||||
if err := h.createControllerRevision(ctx, comp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// when controllerRevision not exist handle replace context.RevisionName
|
||||
for _, trait := range comp.Traits {
|
||||
if err := replaceComponentRevisionContext(trait, comp.RevisionName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,8 @@ const (
|
||||
LabelComponentDefinitionName = "componentdefinition.oam.dev/name"
|
||||
// LabelTraitDefinitionName records the name of TraitDefinition
|
||||
LabelTraitDefinitionName = "trait.oam.dev/name"
|
||||
// LabelManageWorkloadTrait indicates if the trait will manage the lifecycle of the workload
|
||||
LabelManageWorkloadTrait = "trait.oam.dev/manage-workload"
|
||||
// LabelPolicyDefinitionName records the name of PolicyDefinition
|
||||
LabelPolicyDefinitionName = "policydefinition.oam.dev/name"
|
||||
// LabelWorkflowStepDefinitionName records the name of WorkflowStepDefinition
|
||||
|
||||
+28
-18
@@ -16,26 +16,36 @@ import (
|
||||
#Apply: kube.#Apply
|
||||
|
||||
#ApplyComponent: #Steps & {
|
||||
component: string
|
||||
_componentName: component
|
||||
load: ws.#Load & {
|
||||
component: _componentName
|
||||
} @step(1)
|
||||
|
||||
workload: workload__.value
|
||||
workload__: kube.#Apply & {
|
||||
value: load.value.workload
|
||||
...
|
||||
} @step(2)
|
||||
component: string
|
||||
_componentName: component
|
||||
load: ws.#Load & {
|
||||
component: _componentName
|
||||
} @step(1)
|
||||
|
||||
traits: #Steps & {
|
||||
_key: "trait.oam.dev/resource"
|
||||
_manWlKey: "trait.oam.dev/manage-workload"
|
||||
skipApplyWorkload: *false | bool
|
||||
if load.value.auxiliaries != _|_ {
|
||||
for o in load.value.auxiliaries {
|
||||
"\(o.metadata.labels[_key])": kube.#Apply & {value: o}
|
||||
if o.metadata.labels[_manWlKey] != _|_ {
|
||||
skipApplyWorkload: true
|
||||
}
|
||||
}
|
||||
}
|
||||
} @step(2)
|
||||
|
||||
workload__: {
|
||||
if !traits.skipApplyWorkload {
|
||||
kube.#Apply & {
|
||||
value: load.value.workload
|
||||
...
|
||||
}
|
||||
}
|
||||
} @step(3)
|
||||
|
||||
traits: #Steps & {
|
||||
_key: "trait.oam.dev/resource"
|
||||
if load.value.auxiliaries != _|_ {
|
||||
for o in load.value.auxiliaries {
|
||||
"\(o.metadata.labels[_key])": kube.#Apply & {value: o}
|
||||
}
|
||||
}
|
||||
} @step(3)
|
||||
}
|
||||
|
||||
#ApplyRemaining: #Steps & {
|
||||
|
||||
Reference in New Issue
Block a user