mirror of
https://github.com/kubevela/kubevela.git
synced 2026-08-27 16:17:34 +00:00
[Backport release-1.5] Feat: Check def binding to a component (#4539)
* finish logic Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com> (cherry picked from commitaae7af45cb) * finish tests Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com> small fix Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com> fix ci Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com> (cherry picked from commit0418ad5207) * fix comments Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com> fix ci Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com> (cherry picked from commit7469b0497e) * fix test Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com> (cherry picked from commit49749d7c17) * fix tests Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com> (cherry picked from commit5b029147c1) Co-authored-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
This commit is contained in:
co-authored by
楚岳
parent
992e636211
commit
62df98818e
@@ -0,0 +1,50 @@
|
||||
"kustomize-json-patch-mock-adddon": {
|
||||
attributes: {
|
||||
podDisruptive: false
|
||||
}
|
||||
description: "A list of JSON6902 patch to selected target"
|
||||
labels: {
|
||||
"ui-hidden": "true"
|
||||
}
|
||||
type: "trait"
|
||||
}
|
||||
|
||||
template: {
|
||||
patch: {
|
||||
spec: {
|
||||
patches: parameter.patchesJson
|
||||
}
|
||||
}
|
||||
|
||||
parameter: {
|
||||
// +usage=A list of JSON6902 patch.
|
||||
patchesJson: [...#jsonPatchItem]
|
||||
}
|
||||
|
||||
// +usage=Contains a JSON6902 patch
|
||||
#jsonPatchItem: {
|
||||
target: #selector
|
||||
patch: [...{
|
||||
// +usage=operation to perform
|
||||
op: string | "add" | "remove" | "replace" | "move" | "copy" | "test"
|
||||
// +usage=operate path e.g. /foo/bar
|
||||
path: string
|
||||
// +usage=specify source path when op is copy/move
|
||||
from?: string
|
||||
// +usage=specify opraation value when op is test/add/replace
|
||||
value?: string
|
||||
}]
|
||||
}
|
||||
|
||||
// +usage=Selector specifies a set of resources
|
||||
#selector: {
|
||||
group?: string
|
||||
version?: string
|
||||
kind?: string
|
||||
namespace?: string
|
||||
name?: string
|
||||
annotationSelector?: string
|
||||
labelSelector?: string
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
Test addon readme.md file
|
||||
+16
-1
@@ -79,6 +79,9 @@ const (
|
||||
// ReadmeFileName is the addon readme file name
|
||||
ReadmeFileName string = "README.md"
|
||||
|
||||
// LegacyReadmeFileName is the addon readme lower case file name
|
||||
LegacyReadmeFileName string = "readme.md"
|
||||
|
||||
// MetadataFileName is the addon meatadata.yaml file name
|
||||
MetadataFileName string = "metadata.yaml"
|
||||
|
||||
@@ -205,7 +208,7 @@ type Pattern struct {
|
||||
var Patterns = []Pattern{
|
||||
{Value: ReadmeFileName}, {Value: MetadataFileName}, {Value: TemplateFileName},
|
||||
{Value: ParameterFileName}, {IsDir: true, Value: ResourcesDirName}, {IsDir: true, Value: DefinitionsDirName},
|
||||
{IsDir: true, Value: DefSchemaName}, {IsDir: true, Value: ViewDirName}, {Value: AppTemplateCueFileName}, {Value: GlobalParameterFileName}}
|
||||
{IsDir: true, Value: DefSchemaName}, {IsDir: true, Value: ViewDirName}, {Value: AppTemplateCueFileName}, {Value: GlobalParameterFileName}, {Value: LegacyReadmeFileName}}
|
||||
|
||||
// GetPatternFromItem will check if the file path has a valid pattern, return empty string if it's invalid.
|
||||
// AsyncReader is needed to calculate relative path
|
||||
@@ -285,6 +288,7 @@ func GetUIDataFromReader(r AsyncReader, meta *SourceMeta, opt ListOptions) (*UID
|
||||
read func(a *UIData, reader AsyncReader, readPath string) error
|
||||
}{
|
||||
ReadmeFileName: {!opt.GetDetail, readReadme},
|
||||
LegacyReadmeFileName: {!opt.GetDetail, readReadme},
|
||||
MetadataFileName: {false, readMetadata},
|
||||
DefinitionsDirName: {!opt.GetDefinition, readDefFile},
|
||||
ParameterFileName: {!opt.GetParameter, readParamFile},
|
||||
@@ -481,6 +485,10 @@ func readMetadata(a *UIData, reader AsyncReader, readPath string) error {
|
||||
}
|
||||
|
||||
func readReadme(a *UIData, reader AsyncReader, readPath string) error {
|
||||
// the detail will contain readme.md or README.md, if the content already is filled, don't read another.
|
||||
if len(a.Detail) != 0 {
|
||||
return nil
|
||||
}
|
||||
content, err := reader.ReadFile(readPath)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -1099,6 +1107,10 @@ func (h *Installer) dispatchAddonResource(addon *InstallPackage) error {
|
||||
}
|
||||
|
||||
for _, def := range defs {
|
||||
if !checkBondComponentExist(*def, *app) {
|
||||
continue
|
||||
}
|
||||
// if binding component exist, apply the definition
|
||||
addOwner(def, app)
|
||||
err = h.apply.Apply(h.ctx, def, apply.DisableUpdateAnnotation())
|
||||
if err != nil {
|
||||
@@ -1123,6 +1135,9 @@ func (h *Installer) dispatchAddonResource(addon *InstallPackage) error {
|
||||
}
|
||||
|
||||
for _, o := range auxiliaryOutputs {
|
||||
if !checkBondComponentExist(*o, *app) {
|
||||
continue
|
||||
}
|
||||
addOwner(o, app)
|
||||
err = h.apply.Apply(h.ctx, o, apply.DisableUpdateAnnotation())
|
||||
if err != nil {
|
||||
|
||||
@@ -58,6 +58,10 @@ const (
|
||||
func passDefInAppAnnotation(defs []*unstructured.Unstructured, app *v1beta1.Application) error {
|
||||
var comps, traits, workflowSteps, policies []string
|
||||
for _, def := range defs {
|
||||
if !checkBondComponentExist(*def, *app) {
|
||||
// if the definition binding a component, and the component not exist, skip recording.
|
||||
continue
|
||||
}
|
||||
switch def.GetObjectKind().GroupVersionKind().Kind {
|
||||
case v1beta1.ComponentDefinitionKind:
|
||||
comps = append(comps, def.GetName())
|
||||
@@ -474,3 +478,20 @@ func produceDefConflictError(conflictDefs map[string]string) error {
|
||||
errorInfo += "if you want override them, please use argument '--override-definitions' to enable \n"
|
||||
return errors.New(errorInfo)
|
||||
}
|
||||
|
||||
// checkBondComponentExistt will check the ready-to-apply object(def or auxiliary outputs) whether bind to a component
|
||||
// if the target component not exist, return false.
|
||||
func checkBondComponentExist(u unstructured.Unstructured, app v1beta1.Application) bool {
|
||||
comp, existKey := u.GetAnnotations()[oam.AnnotationIgnoreWithoutCompKey]
|
||||
if !existKey {
|
||||
// if an object(def or auxiliary outputs ) binding no components return true
|
||||
return true
|
||||
}
|
||||
for _, component := range app.Spec.Components {
|
||||
if component.Name == comp {
|
||||
// the bond component exists, return ture
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ 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"
|
||||
velatypes "github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
@@ -287,6 +288,36 @@ func TestMakeChart(t *testing.T) {
|
||||
assert.Equal(t, isChartDir, true)
|
||||
}
|
||||
|
||||
func TestCheckObjectBindingComponent(t *testing.T) {
|
||||
existingBindingDef := unstructured.Unstructured{}
|
||||
existingBindingDef.SetAnnotations(map[string]string{oam.AnnotationIgnoreWithoutCompKey: "kustomize"})
|
||||
|
||||
emptyAnnoDef := unstructured.Unstructured{}
|
||||
emptyAnnoDef.SetAnnotations(map[string]string{"test": "onlyForTest"})
|
||||
testCases := map[string]struct {
|
||||
object unstructured.Unstructured
|
||||
app v1beta1.Application
|
||||
res bool
|
||||
}{
|
||||
"bindingExist": {object: existingBindingDef,
|
||||
app: v1beta1.Application{Spec: v1beta1.ApplicationSpec{Components: []common.ApplicationComponent{{Name: "kustomize"}}}},
|
||||
res: true},
|
||||
"NotExisting": {object: existingBindingDef,
|
||||
app: v1beta1.Application{Spec: v1beta1.ApplicationSpec{Components: []common.ApplicationComponent{{Name: "helm"}}}},
|
||||
res: false},
|
||||
"NoBidingAnnotation": {object: emptyAnnoDef,
|
||||
app: v1beta1.Application{Spec: v1beta1.ApplicationSpec{Components: []common.ApplicationComponent{{Name: "kustomize"}}}},
|
||||
res: true},
|
||||
"EmptyApp": {object: existingBindingDef,
|
||||
app: v1beta1.Application{Spec: v1beta1.ApplicationSpec{Components: []common.ApplicationComponent{}}},
|
||||
res: false},
|
||||
}
|
||||
for _, s := range testCases {
|
||||
result := checkBondComponentExist(s.object, s.app)
|
||||
assert.Equal(t, result, s.res)
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
compDefYaml = `
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
|
||||
@@ -86,6 +86,7 @@ func (i *versionedRegistry) GetAddonUIData(ctx context.Context, addonName, versi
|
||||
Detail: wholePackage.Detail,
|
||||
Definitions: wholePackage.Definitions,
|
||||
AvailableVersions: wholePackage.AvailableVersions,
|
||||
CUEDefinitions: wholePackage.CUEDefinitions,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
errors3 "github.com/pkg/errors"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
errors2 "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
@@ -44,6 +45,7 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/utils"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/utils/log"
|
||||
"github.com/oam-dev/kubevela/pkg/definition"
|
||||
"github.com/oam-dev/kubevela/pkg/multicluster"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
addonutil "github.com/oam-dev/kubevela/pkg/utils/addon"
|
||||
@@ -68,7 +70,7 @@ type AddonService interface {
|
||||
}
|
||||
|
||||
// AddonImpl2AddonRes convert pkgaddon.UIData to the type apiserver need
|
||||
func AddonImpl2AddonRes(impl *pkgaddon.UIData) (*apis.DetailAddonResponse, error) {
|
||||
func AddonImpl2AddonRes(impl *pkgaddon.UIData, config *rest.Config) (*apis.DetailAddonResponse, error) {
|
||||
var defs []*apis.AddonDefinition
|
||||
for _, def := range impl.Definitions {
|
||||
obj := &unstructured.Unstructured{}
|
||||
@@ -83,6 +85,20 @@ func AddonImpl2AddonRes(impl *pkgaddon.UIData) (*apis.DetailAddonResponse, error
|
||||
Description: obj.GetAnnotations()["definition.oam.dev/description"],
|
||||
})
|
||||
}
|
||||
|
||||
for _, cueDef := range impl.CUEDefinitions {
|
||||
def := definition.Definition{Unstructured: unstructured.Unstructured{}}
|
||||
err := def.FromCUEString(cueDef.Data, config)
|
||||
if err != nil {
|
||||
return nil, errors3.Wrapf(err, "fail to render definition: %s in cue's format", cueDef.Name)
|
||||
}
|
||||
defs = append(defs, &apis.AddonDefinition{
|
||||
Name: def.GetName(),
|
||||
DefType: def.GetKind(),
|
||||
Description: def.GetAnnotations()["definition.oam.dev/description"],
|
||||
})
|
||||
}
|
||||
|
||||
if impl.Meta.DeployTo != nil && impl.Meta.DeployTo.LegacyRuntimeCluster != impl.Meta.DeployTo.RuntimeCluster {
|
||||
impl.Meta.DeployTo.LegacyRuntimeCluster = impl.Meta.DeployTo.LegacyRuntimeCluster || impl.Meta.DeployTo.RuntimeCluster
|
||||
impl.Meta.DeployTo.RuntimeCluster = impl.Meta.DeployTo.LegacyRuntimeCluster || impl.Meta.DeployTo.RuntimeCluster
|
||||
@@ -175,7 +191,7 @@ func (u *addonServiceImpl) GetAddon(ctx context.Context, name string, registry s
|
||||
|
||||
addon.UISchema = renderAddonCustomUISchema(ctx, u.kubeClient, name, renderDefaultUISchema(addon.APISchema))
|
||||
|
||||
a, err := AddonImpl2AddonRes(addon)
|
||||
a, err := AddonImpl2AddonRes(addon, u.config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -284,7 +300,7 @@ func (u *addonServiceImpl) ListAddons(ctx context.Context, registry, query strin
|
||||
|
||||
var addonResources []*apis.DetailAddonResponse
|
||||
for _, a := range addons {
|
||||
addonRes, err := AddonImpl2AddonRes(a)
|
||||
addonRes, err := AddonImpl2AddonRes(a, u.config)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("err while converting AddonImpl to DetailAddonResponse: %v", err)
|
||||
continue
|
||||
|
||||
@@ -226,6 +226,9 @@ const (
|
||||
|
||||
// AnnotationResourceURL records the source url of the Kubernetes object
|
||||
AnnotationResourceURL = "app.oam.dev/resource-url"
|
||||
|
||||
// AnnotationIgnoreWithoutCompKey indicates the bond component
|
||||
AnnotationIgnoreWithoutCompKey = "addon.oam.dev/ignore-without-component"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -101,6 +101,9 @@ var _ = Describe("Test addon rest api", func() {
|
||||
var addon apisv1.DetailAddonResponse
|
||||
Expect(decodeResponseBody(res, &addon)).Should(Succeed())
|
||||
Expect(addon.Name).Should(BeEquivalentTo("mock-addon"))
|
||||
Expect(addon.Detail).Should(BeEquivalentTo("Test addon readme.md file"))
|
||||
Expect(len(addon.Definitions)).Should(BeEquivalentTo(1))
|
||||
Expect(addon.Definitions[0].Name).Should(BeEquivalentTo("kustomize-json-patch-mock-adddon"))
|
||||
})
|
||||
|
||||
It("enable addon ", func() {
|
||||
|
||||
Reference in New Issue
Block a user