From 69527b257cbe490a25958d293ea95b1d88e26264 Mon Sep 17 00:00:00 2001 From: Somefive Date: Mon, 17 Jan 2022 19:29:28 +0800 Subject: [PATCH] Feat: support external revision in patch component (#3106) Signed-off-by: Somefive --- .../core.oam.dev/v1alpha1/envbinding_types.go | 9 +- pkg/policy/envbinding/patch.go | 5 + pkg/policy/envbinding/patch_test.go | 392 ++++++++++-------- pkg/stdlib/pkgs/multicluster.cue | 1 + 4 files changed, 230 insertions(+), 177 deletions(-) diff --git a/apis/core.oam.dev/v1alpha1/envbinding_types.go b/apis/core.oam.dev/v1alpha1/envbinding_types.go index 25a414c42..d2b694e2b 100644 --- a/apis/core.oam.dev/v1alpha1/envbinding_types.go +++ b/apis/core.oam.dev/v1alpha1/envbinding_types.go @@ -45,10 +45,11 @@ func (in *EnvTraitPatch) ToApplicationTrait() *common.ApplicationTrait { // EnvComponentPatch is the patch to component type EnvComponentPatch struct { - Name string `json:"name"` - Type string `json:"type"` - Properties *runtime.RawExtension `json:"properties,omitempty"` - Traits []EnvTraitPatch `json:"traits,omitempty"` + Name string `json:"name"` + Type string `json:"type"` + Properties *runtime.RawExtension `json:"properties,omitempty"` + Traits []EnvTraitPatch `json:"traits,omitempty"` + ExternalRevision string `json:"externalRevision,omitempty"` } // ToApplicationComponent convert EnvComponentPatch into ApplicationComponent diff --git a/pkg/policy/envbinding/patch.go b/pkg/policy/envbinding/patch.go index 2c276f911..5c0fa2321 100644 --- a/pkg/policy/envbinding/patch.go +++ b/pkg/policy/envbinding/patch.go @@ -66,6 +66,11 @@ func MergeComponent(base *common.ApplicationComponent, patch *v1alpha1.EnvCompon return nil, errors.Wrapf(err, "failed to merge component properties") } + // merge component external revision + if patch.ExternalRevision != "" { + newComponent.ExternalRevision = patch.ExternalRevision + } + // prepare traits traitMaps := map[string]*common.ApplicationTrait{} var traitOrders []string diff --git a/pkg/policy/envbinding/patch_test.go b/pkg/policy/envbinding/patch_test.go index a34979d27..e90e75485 100644 --- a/pkg/policy/envbinding/patch_test.go +++ b/pkg/policy/envbinding/patch_test.go @@ -19,7 +19,7 @@ package envbinding import ( "testing" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/oam-dev/kubevela/apis/core.oam.dev/common" @@ -29,111 +29,68 @@ import ( ) func Test_EnvBindApp_GenerateConfiguredApplication(t *testing.T) { - testcases := []struct { + testCases := map[string]struct { baseApp *v1beta1.Application envName string envPatch v1alpha1.EnvPatch expectedApp *v1beta1.Application selector *v1alpha1.EnvSelector - }{{ - baseApp: baseApp, - envName: "prod", - envPatch: v1alpha1.EnvPatch{ - Components: []v1alpha1.EnvComponentPatch{{ - Name: "express-server", - Type: "webservice", - Properties: util.Object2RawExtension(map[string]interface{}{ - "image": "busybox", - }), - Traits: []v1alpha1.EnvTraitPatch{{ - Type: "ingress-1-20", - Properties: util.Object2RawExtension(map[string]interface{}{ - "domain": "newTestsvc.example.com", - }), - }}, - }}, - }, - expectedApp: &v1beta1.Application{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "v1beta1", - Kind: "Application", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - }, - Spec: v1beta1.ApplicationSpec{ - Components: []common.ApplicationComponent{{ + }{ + "normal-test": { + baseApp: baseApp, + envName: "prod", + envPatch: v1alpha1.EnvPatch{ + Components: []v1alpha1.EnvComponentPatch{{ Name: "express-server", Type: "webservice", Properties: util.Object2RawExtension(map[string]interface{}{ "image": "busybox", - "port": 8000, }), - Traits: []common.ApplicationTrait{{ + Traits: []v1alpha1.EnvTraitPatch{{ Type: "ingress-1-20", Properties: util.Object2RawExtension(map[string]interface{}{ "domain": "newTestsvc.example.com", - "http": map[string]interface{}{ - "/": 8000, - }, }), }}, }}, }, - }, - }, { - baseApp: baseApp, - envName: "prod", - envPatch: v1alpha1.EnvPatch{ - Components: []v1alpha1.EnvComponentPatch{{ - Name: "express-server", - Type: "webservice", - Traits: []v1alpha1.EnvTraitPatch{{ - Type: "labels", - Properties: util.Object2RawExtension(map[string]interface{}{ - "test": "label", - }), - }}, - }, { - Name: "new-server", - Type: "worker", - Properties: util.Object2RawExtension(map[string]interface{}{ - "image": "busybox", - "cmd": []string{"sleep", "1000"}, - }), - Traits: []v1alpha1.EnvTraitPatch{{ - Type: "labels", - Properties: util.Object2RawExtension(map[string]interface{}{ - "test": "label", - }), - }}, - }}, - }, - expectedApp: &v1beta1.Application{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "v1beta1", - Kind: "Application", + expectedApp: &v1beta1.Application{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1beta1", + Kind: "Application", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + Spec: v1beta1.ApplicationSpec{ + Components: []common.ApplicationComponent{{ + Name: "express-server", + Type: "webservice", + Properties: util.Object2RawExtension(map[string]interface{}{ + "image": "busybox", + "port": 8000, + }), + Traits: []common.ApplicationTrait{{ + Type: "ingress-1-20", + Properties: util.Object2RawExtension(map[string]interface{}{ + "domain": "newTestsvc.example.com", + "http": map[string]interface{}{ + "/": 8000, + }, + }), + }}, + }}, + }, }, - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - }, - Spec: v1beta1.ApplicationSpec{ - Components: []common.ApplicationComponent{{ + }, + "add-component": { + baseApp: baseApp, + envName: "prod", + envPatch: v1alpha1.EnvPatch{ + Components: []v1alpha1.EnvComponentPatch{{ Name: "express-server", Type: "webservice", - Properties: util.Object2RawExtension(map[string]interface{}{ - "image": "crccheck/hello-world", - "port": 8000, - }), - Traits: []common.ApplicationTrait{{ - Type: "ingress-1-20", - Properties: util.Object2RawExtension(map[string]interface{}{ - "domain": "testsvc.example.com", - "http": map[string]interface{}{ - "/": 8000, - }, - }), - }, { + Traits: []v1alpha1.EnvTraitPatch{{ Type: "labels", Properties: util.Object2RawExtension(map[string]interface{}{ "test": "label", @@ -146,7 +103,7 @@ func Test_EnvBindApp_GenerateConfiguredApplication(t *testing.T) { "image": "busybox", "cmd": []string{"sleep", "1000"}, }), - Traits: []common.ApplicationTrait{{ + Traits: []v1alpha1.EnvTraitPatch{{ Type: "labels", Properties: util.Object2RawExtension(map[string]interface{}{ "test": "label", @@ -154,67 +111,92 @@ func Test_EnvBindApp_GenerateConfiguredApplication(t *testing.T) { }}, }}, }, - }, - }, { - // Test Disable Trait - baseApp: baseApp, - envName: "prod", - envPatch: v1alpha1.EnvPatch{ - Components: []v1alpha1.EnvComponentPatch{{ - Name: "express-server", - Type: "webservice", - Traits: []v1alpha1.EnvTraitPatch{{ - Type: "ingress-1-20", - Disable: true, - }}, - }}, - }, - expectedApp: &v1beta1.Application{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "v1beta1", - Kind: "Application", + expectedApp: &v1beta1.Application{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1beta1", + Kind: "Application", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + Spec: v1beta1.ApplicationSpec{ + Components: []common.ApplicationComponent{{ + Name: "express-server", + Type: "webservice", + Properties: util.Object2RawExtension(map[string]interface{}{ + "image": "crccheck/hello-world", + "port": 8000, + }), + Traits: []common.ApplicationTrait{{ + Type: "ingress-1-20", + Properties: util.Object2RawExtension(map[string]interface{}{ + "domain": "testsvc.example.com", + "http": map[string]interface{}{ + "/": 8000, + }, + }), + }, { + Type: "labels", + Properties: util.Object2RawExtension(map[string]interface{}{ + "test": "label", + }), + }}, + }, { + Name: "new-server", + Type: "worker", + Properties: util.Object2RawExtension(map[string]interface{}{ + "image": "busybox", + "cmd": []string{"sleep", "1000"}, + }), + Traits: []common.ApplicationTrait{{ + Type: "labels", + Properties: util.Object2RawExtension(map[string]interface{}{ + "test": "label", + }), + }}, + }}, + }, }, - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - }, - Spec: v1beta1.ApplicationSpec{ - Components: []common.ApplicationComponent{{ + }, + "disable-trait": { + baseApp: baseApp, + envName: "prod", + envPatch: v1alpha1.EnvPatch{ + Components: []v1alpha1.EnvComponentPatch{{ Name: "express-server", Type: "webservice", - Properties: util.Object2RawExtension(map[string]interface{}{ - "image": "crccheck/hello-world", - "port": 8000, - }), - Traits: []common.ApplicationTrait{}, + Traits: []v1alpha1.EnvTraitPatch{{ + Type: "ingress-1-20", + Disable: true, + }}, }}, }, - }, - }, { - // Test component selector - baseApp: baseApp, - envName: "prod", - envPatch: v1alpha1.EnvPatch{ - Components: []v1alpha1.EnvComponentPatch{{ - Name: "new-server", - Type: "worker", - Properties: util.Object2RawExtension(map[string]interface{}{ - "image": "busybox", - }), - }}, - }, - selector: &v1alpha1.EnvSelector{ - Components: []string{"new-server"}, - }, - expectedApp: &v1beta1.Application{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "v1beta1", - Kind: "Application", + expectedApp: &v1beta1.Application{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1beta1", + Kind: "Application", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + Spec: v1beta1.ApplicationSpec{ + Components: []common.ApplicationComponent{{ + Name: "express-server", + Type: "webservice", + Properties: util.Object2RawExtension(map[string]interface{}{ + "image": "crccheck/hello-world", + "port": 8000, + }), + Traits: []common.ApplicationTrait{}, + }}, + }, }, - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - }, - Spec: v1beta1.ApplicationSpec{ - Components: []common.ApplicationComponent{{ + }, + "component-selector": { + baseApp: baseApp, + envName: "prod", + envPatch: v1alpha1.EnvPatch{ + Components: []v1alpha1.EnvComponentPatch{{ Name: "new-server", Type: "worker", Properties: util.Object2RawExtension(map[string]interface{}{ @@ -222,41 +204,105 @@ func Test_EnvBindApp_GenerateConfiguredApplication(t *testing.T) { }), }}, }, - }, - }, { - // Test empty component selector - baseApp: baseApp, - envName: "prod", - envPatch: v1alpha1.EnvPatch{ - Components: []v1alpha1.EnvComponentPatch{{ - Name: "new-server", - Type: "worker", - Properties: util.Object2RawExtension(map[string]interface{}{ - "image": "busybox", - }), - }}, - }, - selector: &v1alpha1.EnvSelector{ - Components: []string{}, - }, - expectedApp: &v1beta1.Application{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "v1beta1", - Kind: "Application", + selector: &v1alpha1.EnvSelector{ + Components: []string{"new-server"}, }, - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - }, - Spec: v1beta1.ApplicationSpec{ - Components: []common.ApplicationComponent{}, + expectedApp: &v1beta1.Application{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1beta1", + Kind: "Application", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + Spec: v1beta1.ApplicationSpec{ + Components: []common.ApplicationComponent{{ + Name: "new-server", + Type: "worker", + Properties: util.Object2RawExtension(map[string]interface{}{ + "image": "busybox", + }), + }}, + }, }, }, - }} + "empty-component-selector": { + baseApp: baseApp, + envName: "prod", + envPatch: v1alpha1.EnvPatch{ + Components: []v1alpha1.EnvComponentPatch{{ + Name: "new-server", + Type: "worker", + Properties: util.Object2RawExtension(map[string]interface{}{ + "image": "busybox", + }), + }}, + }, + selector: &v1alpha1.EnvSelector{ + Components: []string{}, + }, + expectedApp: &v1beta1.Application{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1beta1", + Kind: "Application", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + Spec: v1beta1.ApplicationSpec{ + Components: []common.ApplicationComponent{}, + }, + }, + }, + "patch-external-revision": { + baseApp: baseApp, + envName: "prod", + envPatch: v1alpha1.EnvPatch{ + Components: []v1alpha1.EnvComponentPatch{{ + Name: "express-server", + Type: "webservice", + ExternalRevision: "external-rev", + }}, + }, + expectedApp: &v1beta1.Application{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1beta1", + Kind: "Application", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + Spec: v1beta1.ApplicationSpec{ + Components: []common.ApplicationComponent{{ + Name: "express-server", + Type: "webservice", + Properties: util.Object2RawExtension(map[string]interface{}{ + "image": "crccheck/hello-world", + "port": 8000, + }), + ExternalRevision: "external-rev", + Traits: []common.ApplicationTrait{{ + Type: "ingress-1-20", + Properties: util.Object2RawExtension(map[string]interface{}{ + "domain": "testsvc.example.com", + "http": map[string]interface{}{ + "/": 8000, + }, + }), + }}, + }}, + }, + }, + }, + } - for _, testcase := range testcases { - app, err := PatchApplication(testcase.baseApp, &testcase.envPatch, testcase.selector) - assert.NoError(t, err) - assert.Equal(t, testcase.expectedApp, app) + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + app, err := PatchApplication(tc.baseApp, &tc.envPatch, tc.selector) + r := require.New(t) + r.NoError(err) + r.Equal(tc.expectedApp, app) + }) } } diff --git a/pkg/stdlib/pkgs/multicluster.cue b/pkg/stdlib/pkgs/multicluster.cue index 431e3738d..751f107df 100644 --- a/pkg/stdlib/pkgs/multicluster.cue +++ b/pkg/stdlib/pkgs/multicluster.cue @@ -23,6 +23,7 @@ disable?: bool properties: {...} }] + externalRevision?: string } #ReadPlacementDecisions: {