mirror of
https://github.com/kubevela/kubevela.git
synced 2026-08-18 03:56:36 +00:00
Feat: filter definitions by which addon installed them (#4156)
* Feat: filter by source addon in `vela def list` Signed-off-by: Charlie Chiang <charlie_c_0129@outlook.com> * Style: change header year to 2022 Signed-off-by: Charlie Chiang <charlie_c_0129@outlook.com> * Refactor: use generic filters for extensibility Signed-off-by: Charlie Chiang <charlie_c_0129@outlook.com> * Refactor: change variable addonFilter to addonName Signed-off-by: Charlie Chiang <charlie_c_0129@outlook.com> * Test: update tests according to code changes Signed-off-by: Charlie Chiang <charlie_c_0129@outlook.com> * Refactor: unify SearchDefinition params using filters Signed-off-by: Charlie Chiang <charlie_c_0129@outlook.com> * Test: simplify tests Signed-off-by: Charlie Chiang <charlie_c_0129@outlook.com> * Style: remove redundant code Signed-off-by: Charlie Chiang <charlie_c_0129@outlook.com> * Test: add tests with multiple filters Signed-off-by: Charlie Chiang <charlie_c_0129@outlook.com> * Feat: show SOURCE-ADDON column in `def list`, if any Signed-off-by: Charlie Chiang <charlie_c_0129@outlook.com> * Feat: add addon filter to apiserver definition-lists Signed-off-by: Charlie Chiang <charlie_c_0129@outlook.com> * Style: fix lint issues Signed-off-by: Charlie Chiang <charlie_c_0129@outlook.com> * Chore: update swagger doc accordingly Signed-off-by: Charlie Chiang <charlie_c_0129@outlook.com> * Test: add tests for filter Applying Signed-off-by: Charlie Chiang <charlie_c_0129@outlook.com> * Feat: add a helper function to apply filters to lists Signed-off-by: Charlie Chiang <charlie_c_0129@outlook.com> * Style: format imports Signed-off-by: Charlie Chiang <charlie_c_0129@outlook.com> * Feat: add OwnerAddon to DefinitionBase Signed-off-by: Charlie Chiang <charlie_c_0129@outlook.com> * Test: add tests for OwnerAddon field Signed-off-by: Charlie Chiang <charlie_c_0129@outlook.com> * Test: add addon util tests Signed-off-by: Charlie Chiang <charlie_c_0129@outlook.com>
This commit is contained in:
+631
-102
File diff suppressed because it is too large
Load Diff
+8
-20
@@ -69,6 +69,7 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
"github.com/oam-dev/kubevela/pkg/utils"
|
||||
addonutil "github.com/oam-dev/kubevela/pkg/utils/addon"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/apply"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
version2 "github.com/oam-dev/kubevela/version"
|
||||
@@ -632,7 +633,7 @@ func formatAppFramework(addon *InstallPackage) *v1beta1.Application {
|
||||
if app.Spec.Components == nil {
|
||||
app.Spec.Components = []common2.ApplicationComponent{}
|
||||
}
|
||||
app.Name = Convert2AppName(addon.Name)
|
||||
app.Name = addonutil.Addon2AppName(addon.Name)
|
||||
// force override the namespace defined vela with DefaultVelaNS,this value can be modified by Env
|
||||
app.SetNamespace(types.DefaultKubeVelaNS)
|
||||
if app.Labels == nil {
|
||||
@@ -790,7 +791,7 @@ func RenderApp(ctx context.Context, addon *InstallPackage, k8sClient client.Clie
|
||||
func RenderDefinitions(addon *InstallPackage, config *rest.Config) ([]*unstructured.Unstructured, error) {
|
||||
defObjs := make([]*unstructured.Unstructured, 0)
|
||||
|
||||
// No matter runtime mode or control mode , definition only needs to control plane k8s.
|
||||
// No matter runtime mode or control mode, definition only needs to control plane k8s.
|
||||
for _, def := range addon.Definitions {
|
||||
obj, err := renderObject(def)
|
||||
if err != nil {
|
||||
@@ -1054,14 +1055,6 @@ func renderCUETemplate(elem ElementFile, parameters string, args map[string]inte
|
||||
return &comp, err
|
||||
}
|
||||
|
||||
const addonAppPrefix = "addon-"
|
||||
const addonSecPrefix = "addon-secret-"
|
||||
|
||||
// Convert2AppName -
|
||||
func Convert2AppName(name string) string {
|
||||
return addonAppPrefix + name
|
||||
}
|
||||
|
||||
// RenderArgsSecret render addon enable argument to secret
|
||||
func RenderArgsSecret(addon *InstallPackage, args map[string]interface{}) *unstructured.Unstructured {
|
||||
argsByte, err := json.Marshal(args)
|
||||
@@ -1071,7 +1064,7 @@ func RenderArgsSecret(addon *InstallPackage, args map[string]interface{}) *unstr
|
||||
sec := v1.Secret{
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: "v1", Kind: "Secret"},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: Convert2SecName(addon.Name),
|
||||
Name: addonutil.Addon2SecName(addon.Name),
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
@@ -1105,11 +1098,6 @@ func FetchArgsFromSecret(sec *v1.Secret) (map[string]interface{}, error) {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Convert2SecName generate addon argument secret name
|
||||
func Convert2SecName(name string) string {
|
||||
return addonSecPrefix + name
|
||||
}
|
||||
|
||||
// Installer helps addon enable, dependency-check, dispatch resources
|
||||
type Installer struct {
|
||||
ctx context.Context
|
||||
@@ -1221,7 +1209,7 @@ func (h *Installer) installDependency(addon *InstallPackage) error {
|
||||
for _, dep := range addon.Dependencies {
|
||||
err := h.cli.Get(h.ctx, client.ObjectKey{
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
Name: Convert2AppName(dep.Name),
|
||||
Name: addonutil.Addon2AppName(dep.Name),
|
||||
}, &app)
|
||||
if err == nil {
|
||||
continue
|
||||
@@ -1250,7 +1238,7 @@ func (h *Installer) checkDependency(addon *InstallPackage) ([]string, error) {
|
||||
for _, dep := range addon.Dependencies {
|
||||
err := h.cli.Get(h.ctx, client.ObjectKey{
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
Name: Convert2AppName(dep.Name),
|
||||
Name: addonutil.Addon2AppName(dep.Name),
|
||||
}, &app)
|
||||
if err == nil {
|
||||
continue
|
||||
@@ -1405,7 +1393,7 @@ func determineAddonAppName(ctx context.Context, cli client.Client, addonName str
|
||||
return "", err
|
||||
}
|
||||
// if the app still not exist, use addon-{addonName}
|
||||
return Convert2AppName(addonName), nil
|
||||
return addonutil.Addon2AppName(addonName), nil
|
||||
}
|
||||
return app.Name, nil
|
||||
}
|
||||
@@ -1414,7 +1402,7 @@ func determineAddonAppName(ctx context.Context, cli client.Client, addonName str
|
||||
// if not find will try to get 1.1 legacy addon related app by using NamespacedName(vela-system, `addonName`)
|
||||
func FetchAddonRelatedApp(ctx context.Context, cli client.Client, addonName string) (*v1beta1.Application, error) {
|
||||
app := &v1beta1.Application{}
|
||||
if err := cli.Get(ctx, types2.NamespacedName{Namespace: types.DefaultKubeVelaNS, Name: Convert2AppName(addonName)}, app); err != nil {
|
||||
if err := cli.Get(ctx, types2.NamespacedName{Namespace: types.DefaultKubeVelaNS, Name: addonutil.Addon2AppName(addonName)}, app); err != nil {
|
||||
if !apierrors.IsNotFound(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ import (
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
addonutil "github.com/oam-dev/kubevela/pkg/utils/addon"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/apply"
|
||||
)
|
||||
|
||||
@@ -252,7 +253,7 @@ var _ = Describe("Test addon util func", func() {
|
||||
secArgs := v1.Secret{
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: "v1", Kind: "Secret"},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: Convert2SecName("test-addon-old-args"),
|
||||
Name: addonutil.Addon2SecName("test-addon-old-args"),
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
},
|
||||
StringData: map[string]string{
|
||||
|
||||
@@ -49,6 +49,7 @@ import (
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
addonutil "github.com/oam-dev/kubevela/pkg/utils/addon"
|
||||
version2 "github.com/oam-dev/kubevela/version"
|
||||
)
|
||||
|
||||
@@ -417,7 +418,7 @@ func TestGetAddonStatus4Observability(t *testing.T) {
|
||||
|
||||
addonSecret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: Convert2SecName(ObservabilityAddon),
|
||||
Name: addonutil.Addon2SecName(ObservabilityAddon),
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
},
|
||||
Data: map[string][]byte{},
|
||||
|
||||
+3
-2
@@ -36,6 +36,7 @@ import (
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/multicluster"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
addonutil "github.com/oam-dev/kubevela/pkg/utils/addon"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/apply"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
)
|
||||
@@ -162,7 +163,7 @@ func GetAddonStatus(ctx context.Context, cli client.Client, name string) (Status
|
||||
|
||||
// Get addon parameters
|
||||
var sec v1.Secret
|
||||
err = cli.Get(ctx, client.ObjectKey{Namespace: types.DefaultKubeVelaNS, Name: Convert2SecName(name)}, &sec)
|
||||
err = cli.Get(ctx, client.ObjectKey{Namespace: types.DefaultKubeVelaNS, Name: addonutil.Addon2SecName(name)}, &sec)
|
||||
if err != nil {
|
||||
// Not found error can be ignored. Others can't.
|
||||
if !apierrors.IsNotFound(err) {
|
||||
@@ -186,7 +187,7 @@ func GetAddonStatus(ctx context.Context, cli client.Client, name string) (Status
|
||||
sec v1.Secret
|
||||
domain string
|
||||
)
|
||||
if err = cli.Get(ctx, client.ObjectKey{Namespace: types.DefaultKubeVelaNS, Name: Convert2SecName(name)}, &sec); err != nil {
|
||||
if err = cli.Get(ctx, client.ObjectKey{Namespace: types.DefaultKubeVelaNS, Name: addonutil.Addon2SecName(name)}, &sec); err != nil {
|
||||
klog.ErrorS(err, "failed to get observability secret")
|
||||
addonStatus.AddonPhase = enabling
|
||||
addonStatus.InstalledVersion = ""
|
||||
|
||||
@@ -46,6 +46,7 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/utils/log"
|
||||
"github.com/oam-dev/kubevela/pkg/multicluster"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
addonutil "github.com/oam-dev/kubevela/pkg/utils/addon"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/apply"
|
||||
velaerr "github.com/oam-dev/kubevela/pkg/utils/errors"
|
||||
)
|
||||
@@ -220,7 +221,7 @@ func (u *addonServiceImpl) StatusAddon(ctx context.Context, name string) (*apis.
|
||||
var sec v1.Secret
|
||||
err = u.kubeClient.Get(ctx, client.ObjectKey{
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
Name: pkgaddon.Convert2SecName(name),
|
||||
Name: addonutil.Addon2SecName(name),
|
||||
}, &sec)
|
||||
if err != nil && !errors2.IsNotFound(err) {
|
||||
return nil, bcode.ErrAddonSecretGet
|
||||
@@ -445,7 +446,7 @@ func (u *addonServiceImpl) UpdateAddon(ctx context.Context, name string, args ap
|
||||
// check addon application whether exist
|
||||
err := u.kubeClient.Get(ctx, client.ObjectKey{
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
Name: pkgaddon.Convert2AppName(name),
|
||||
Name: addonutil.Addon2AppName(name),
|
||||
}, &app)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -21,6 +21,10 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/utils/addon"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/filters"
|
||||
|
||||
"github.com/getkin/kin-openapi/openapi3"
|
||||
"github.com/pkg/errors"
|
||||
@@ -63,12 +67,13 @@ type definitionServiceImpl struct {
|
||||
type DefinitionQueryOption struct {
|
||||
Type string `json:"type"`
|
||||
AppliedWorkloads string `json:"appliedWorkloads"`
|
||||
OwnerAddon string `json:"sourceAddon"`
|
||||
QueryAll bool `json:"queryAll"`
|
||||
}
|
||||
|
||||
// String return cache key string
|
||||
func (d DefinitionQueryOption) String() string {
|
||||
return fmt.Sprintf("type:%s/appliedWorkloads:%s/queryAll:%v", d.Type, d.AppliedWorkloads, d.QueryAll)
|
||||
return fmt.Sprintf("type:%s/appliedWorkloads:%s/ownerAddon:%s/queryAll:%v", d.Type, d.AppliedWorkloads, d.OwnerAddon, d.QueryAll)
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -119,24 +124,17 @@ func (d *definitionServiceImpl) listDefinitions(ctx context.Context, list *unstr
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Apply filters to list
|
||||
filteredList := filters.ApplyToList(*list,
|
||||
// Filter by applied workload
|
||||
filters.ByAppliedWorkload(ops.AppliedWorkloads),
|
||||
// Filter by which addon installed this definition
|
||||
filters.ByOwnerAddon(ops.OwnerAddon),
|
||||
)
|
||||
|
||||
var defs []*apisv1.DefinitionBase
|
||||
for _, def := range list.Items {
|
||||
if ops.AppliedWorkloads != "" {
|
||||
traitDef := &v1beta1.TraitDefinition{}
|
||||
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(def.Object, traitDef); err != nil {
|
||||
return nil, errors.Wrap(err, "invalid trait definition")
|
||||
}
|
||||
filter := false
|
||||
for _, workload := range traitDef.Spec.AppliesToWorkloads {
|
||||
if workload == ops.AppliedWorkloads || workload == "*" {
|
||||
filter = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !filter {
|
||||
continue
|
||||
}
|
||||
}
|
||||
for _, def := range filteredList.Items {
|
||||
definition, err := convertDefinitionBase(def, kind)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("convert definition to base failure %s", err.Error())
|
||||
@@ -180,6 +178,14 @@ func convertDefinitionBase(def unstructured.Unstructured, kind string) (*apisv1.
|
||||
return "enable"
|
||||
}(),
|
||||
}
|
||||
// Set OwnerAddon field
|
||||
for _, ownerRef := range def.GetOwnerReferences() {
|
||||
if strings.HasPrefix(ownerRef.Name, addon.AddonAppPrefix) {
|
||||
definition.OwnerAddon = addon.AppName2Addon(ownerRef.Name)
|
||||
// We are only interested in one owner addon
|
||||
break
|
||||
}
|
||||
}
|
||||
if kind == kindComponentDefinition {
|
||||
compDef := &v1beta1.ComponentDefinition{}
|
||||
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(def.Object, compDef); err != nil {
|
||||
|
||||
@@ -88,6 +88,8 @@ var _ = Describe("Test namespace service functions", func() {
|
||||
// there is already a scaler trait definition in the test env
|
||||
Expect(cmp.Diff(len(traits), 2)).Should(BeEmpty())
|
||||
Expect(cmp.Diff(traits[0].Name, "myingress")).Should(BeEmpty())
|
||||
// The OwnAddon field of myingress should not be fluxcd
|
||||
Expect(traits[0].OwnerAddon).Should(Equal("fluxcd"))
|
||||
Expect(traits[0].Description).ShouldNot(BeEmpty())
|
||||
Expect(traits[0].Trait).ShouldNot(BeNil())
|
||||
Expect(traits[0].Alias).Should(Equal("test-alias"))
|
||||
@@ -145,6 +147,18 @@ var _ = Describe("Test namespace service functions", func() {
|
||||
Expect(policies[0].Description).ShouldNot(BeEmpty())
|
||||
Expect(policies[0].Policy.ManageHealthCheck).Should(BeTrue())
|
||||
Expect(policies[0].Alias).Should(Equal("test-alias"))
|
||||
|
||||
By("Filtering list by owner addon")
|
||||
list, err := definitionService.ListDefinitions(context.TODO(), DefinitionQueryOption{Type: "trait", OwnerAddon: "non-existent-addon"})
|
||||
Expect(err).Should(Succeed())
|
||||
// All results should be filtered out
|
||||
Expect(list).Should(HaveLen(0))
|
||||
|
||||
list, err = definitionService.ListDefinitions(context.TODO(), DefinitionQueryOption{Type: "trait", OwnerAddon: "fluxcd"})
|
||||
Expect(err).Should(Succeed())
|
||||
// We should see myingress being kept because fluxcd is its owner
|
||||
Expect(len(list) >= 1).Should(Equal(true))
|
||||
Expect(list[0].Name).Should(Equal("myingress"))
|
||||
})
|
||||
|
||||
It("Test DetailDefinition function", func() {
|
||||
|
||||
@@ -6,6 +6,13 @@ metadata:
|
||||
definition.oam.dev/alias: test-alias
|
||||
name: myingress
|
||||
namespace: vela-system
|
||||
ownerReferences:
|
||||
- apiVersion: core.oam.dev/v1beta1
|
||||
blockOwnerDeletion: true
|
||||
controller: true
|
||||
kind: Application
|
||||
name: addon-fluxcd
|
||||
uid: ed7ca39b-2420-44a1-b395-ee1820cc74c7
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- "*"
|
||||
|
||||
@@ -50,6 +50,7 @@ func (d *definitionAPIInterface) GetWebServiceRoute() *restful.WebService {
|
||||
Param(ws.QueryParameter("type", "query the definition type").DataType("string").Required(true).AllowableValues(map[string]string{"component": "", "trait": "", "workflowstep": ""})).
|
||||
Param(ws.QueryParameter("queryAll", "query all definitions include hidden in UI").DataType("boolean").DefaultValue("false")).
|
||||
Param(ws.QueryParameter("appliedWorkload", "if specified, query the trait definition applied to the workload").DataType("string")).
|
||||
Param(ws.QueryParameter("ownerAddon", "query by which addon created the definition").DataType("string")).
|
||||
Returns(200, "OK", apis.ListDefinitionResponse{}).
|
||||
Writes(apis.ListDefinitionResponse{}).Do(returns200, returns500))
|
||||
|
||||
@@ -95,6 +96,7 @@ func (d *definitionAPIInterface) listDefinitions(req *restful.Request, res *rest
|
||||
definitions, err := d.DefinitionService.ListDefinitions(req.Request.Context(), service.DefinitionQueryOption{
|
||||
Type: req.QueryParameter("type"),
|
||||
AppliedWorkloads: req.QueryParameter("appliedWorkload"),
|
||||
OwnerAddon: req.QueryParameter("ownerAddon"),
|
||||
QueryAll: queryAll,
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -838,7 +838,9 @@ type DefinitionBase struct {
|
||||
Labels map[string]string `json:"labels"`
|
||||
// WorkloadType the component workload type
|
||||
// Deprecated: it same as component.workload.type
|
||||
WorkloadType string `json:"workloadType,omitempty"`
|
||||
WorkloadType string `json:"workloadType,omitempty"`
|
||||
// OwnerAddon indicates which addon created this definition
|
||||
OwnerAddon string `json:"ownerAddon"`
|
||||
Trait *v1beta1.TraitDefinitionSpec `json:"trait,omitempty"`
|
||||
Component *v1beta1.ComponentDefinitionSpec `json:"component,omitempty"`
|
||||
Policy *v1beta1.PolicyDefinitionSpec `json:"policy,omitempty"`
|
||||
|
||||
@@ -23,6 +23,8 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/utils/filters"
|
||||
|
||||
"cuelang.org/go/cue"
|
||||
"cuelang.org/go/cue/ast"
|
||||
"cuelang.org/go/cue/format"
|
||||
@@ -385,7 +387,7 @@ func ValidDefinitionTypes() []string {
|
||||
}
|
||||
|
||||
// SearchDefinition search the Definition in k8s by traversing all possible results across types or namespaces
|
||||
func SearchDefinition(definitionName string, c client.Client, definitionType string, namespace string) ([]unstructured.Unstructured, error) {
|
||||
func SearchDefinition(c client.Client, definitionType, namespace string, additionalFilters ...filters.Filter) ([]unstructured.Unstructured, error) {
|
||||
ctx := context.Background()
|
||||
var kinds []string
|
||||
if definitionType != "" {
|
||||
@@ -414,11 +416,11 @@ func SearchDefinition(definitionName string, c client.Client, definitionType str
|
||||
if err := c.List(ctx, &objs, listOptions...); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get %s", kind)
|
||||
}
|
||||
for _, obj := range objs.Items {
|
||||
if definitionName == "*" || obj.GetName() == definitionName {
|
||||
definitions = append(definitions, obj)
|
||||
}
|
||||
}
|
||||
|
||||
// Apply filters to the object list
|
||||
filteredList := filters.ApplyToList(objs, additionalFilters...)
|
||||
|
||||
definitions = append(definitions, filteredList.Items...)
|
||||
}
|
||||
return definitions, nil
|
||||
}
|
||||
|
||||
@@ -22,10 +22,15 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/utils/filters"
|
||||
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
addonutils "github.com/oam-dev/kubevela/pkg/utils/addon"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
)
|
||||
|
||||
@@ -42,6 +47,9 @@ func TestDefinitionBasicFunctions(t *testing.T) {
|
||||
})
|
||||
def.SetName("test-trait")
|
||||
def.SetGVK("TraitDefinition")
|
||||
def.SetOwnerReferences([]v1.OwnerReference{{
|
||||
Name: addonutils.Addon2AppName("test-addon"),
|
||||
}})
|
||||
if _type := def.GetType(); _type != "trait" {
|
||||
t.Fatalf("set gvk invalid, expected trait got %s", _type)
|
||||
}
|
||||
@@ -107,10 +115,31 @@ func TestDefinitionBasicFunctions(t *testing.T) {
|
||||
_ = GetDefinitionDefaultSpec("WorkloadDefinition")
|
||||
_ = ValidDefinitionTypes()
|
||||
|
||||
if _, err = SearchDefinition("*", c, "", ""); err != nil {
|
||||
if _, err = SearchDefinition(c, "", ""); err != nil {
|
||||
t.Fatalf("failed to search definition: %v", err)
|
||||
}
|
||||
if _, err = SearchDefinition("*", c, "trait", "default"); err != nil {
|
||||
if _, err = SearchDefinition(c, "trait", "default"); err != nil {
|
||||
t.Fatalf("failed to search definition: %v", err)
|
||||
}
|
||||
res, err := SearchDefinition(c, "", "", filters.ByOwnerAddon("test-addon"))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to search definition: %v", err)
|
||||
}
|
||||
if len(res) < 1 {
|
||||
t.Fatalf("failed to search definition with addon filter applied: %s", "no result returned")
|
||||
}
|
||||
res, err = SearchDefinition(c, "", "", filters.ByName("test-trait"), filters.ByOwnerAddon("test-addon"))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to search definition: %v", err)
|
||||
}
|
||||
if len(res) < 1 {
|
||||
t.Fatalf("failed to search definition with addon filter applied: %s", "no result returned")
|
||||
}
|
||||
res, err = SearchDefinition(c, "", "", filters.ByOwnerAddon("this-is-a-non-existent-addon"))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to search definition: %v", err)
|
||||
}
|
||||
if len(res) >= 1 {
|
||||
t.Fatalf("failed to search definition with addon filter applied: %s", "too many results returned")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
Copyright 2022 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package addon
|
||||
|
||||
import "strings"
|
||||
|
||||
// AddonSecPrefix is the prefix for secret of addons
|
||||
const AddonSecPrefix = "addon-secret-"
|
||||
|
||||
// Addon2SecName returns the secret name that contains addon arguments
|
||||
func Addon2SecName(addonName string) string {
|
||||
if addonName == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
return AddonSecPrefix + addonName
|
||||
}
|
||||
|
||||
// AddonAppPrefix is the prefix for corresponding Application of an addon
|
||||
const AddonAppPrefix = "addon-"
|
||||
|
||||
// Addon2AppName return the app name that represents the addon
|
||||
func Addon2AppName(addonName string) string {
|
||||
if addonName == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
return AddonAppPrefix + addonName
|
||||
}
|
||||
|
||||
// AppName2Addon converts an addon app name to the actual addon name
|
||||
// If it is not a addon app name, empty string is returned.
|
||||
func AppName2Addon(appName string) string {
|
||||
if !strings.HasPrefix(appName, AddonAppPrefix) {
|
||||
return ""
|
||||
}
|
||||
|
||||
return appName[len(AddonAppPrefix):]
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
Copyright 2022 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package addon
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAddon2AppName(t *testing.T) {
|
||||
assert.Equal(t, Addon2AppName(""), "")
|
||||
assert.Equal(t, Addon2AppName("fluxcd"), AddonAppPrefix+"fluxcd")
|
||||
}
|
||||
|
||||
func TestAddon2SecName(t *testing.T) {
|
||||
assert.Equal(t, Addon2SecName(""), "")
|
||||
assert.Equal(t, Addon2SecName("fluxcd"), AddonSecPrefix+"fluxcd")
|
||||
}
|
||||
|
||||
func TestAppName2Addon(t *testing.T) {
|
||||
assert.Equal(t, AppName2Addon("some"), "")
|
||||
assert.Equal(t, AppName2Addon(""), "")
|
||||
assert.Equal(t, AppName2Addon(AddonAppPrefix+"fluxcd"), "fluxcd")
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
Copyright 2022 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package filters
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/addon"
|
||||
)
|
||||
|
||||
// Filter is used to filter Unstructured objects. It is basically a func(unstructured.Unstructured) bool.
|
||||
type Filter func(unstructured.Unstructured) bool
|
||||
|
||||
// Apply applies all the provided filters to a given object.
|
||||
// Then returns if the object is filtered out or not.
|
||||
// Returns true if this object is kept, otherwise false.
|
||||
func Apply(obj unstructured.Unstructured, filters ...Filter) bool {
|
||||
// Apply all filters
|
||||
for _, filter := range filters {
|
||||
// If filtered out by one of the filters
|
||||
if !filter(obj) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// All filters have kept this item
|
||||
return true
|
||||
}
|
||||
|
||||
// ApplyToList applies all the provided filters to a UnstructuredList.
|
||||
// It only keeps items that pass all the filters.
|
||||
func ApplyToList(list unstructured.UnstructuredList, filters ...Filter) unstructured.UnstructuredList {
|
||||
filteredList := unstructured.UnstructuredList{Object: list.Object}
|
||||
|
||||
// Apply filters to each item in the list
|
||||
for _, u := range list.Items {
|
||||
kept := Apply(u, filters...)
|
||||
if !kept {
|
||||
continue
|
||||
}
|
||||
|
||||
// Item that passed filters
|
||||
filteredList.Items = append(filteredList.Items, u)
|
||||
}
|
||||
|
||||
return filteredList
|
||||
}
|
||||
|
||||
// KeepAll returns a filter that keeps everything
|
||||
func KeepAll() Filter {
|
||||
return func(unstructured.Unstructured) bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// KeepNone returns a filter that filters out everything
|
||||
func KeepNone() Filter {
|
||||
return func(unstructured.Unstructured) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// ByOwnerAddon returns a filter that filters out what does not belong to the owner addon.
|
||||
// Empty addon name will keep everything.
|
||||
func ByOwnerAddon(addonName string) Filter {
|
||||
if addonName == "" {
|
||||
// Empty addon name, just keep everything, no further action needed
|
||||
return KeepAll()
|
||||
}
|
||||
|
||||
// Filter by which addon installed it by owner reference
|
||||
// only keep the ones that belong to the addon
|
||||
return func(obj unstructured.Unstructured) bool {
|
||||
ownerRefs := obj.GetOwnerReferences()
|
||||
isOwnedBy := false
|
||||
for _, ownerRef := range ownerRefs {
|
||||
if ownerRef.Name == addon.Addon2AppName(addonName) {
|
||||
isOwnedBy = true
|
||||
break
|
||||
}
|
||||
}
|
||||
return isOwnedBy
|
||||
}
|
||||
}
|
||||
|
||||
// ByName returns a filter that matches the given name.
|
||||
// Empty name will keep everything.
|
||||
func ByName(name string) Filter {
|
||||
// Keep everything
|
||||
if name == "" {
|
||||
return KeepAll()
|
||||
}
|
||||
|
||||
// Filter by name
|
||||
return func(obj unstructured.Unstructured) bool {
|
||||
return obj.GetName() == name
|
||||
}
|
||||
}
|
||||
|
||||
// ByAppliedWorkload returns a filter that only keeps trait definitions that applies to the given workload.
|
||||
// Empty workload name will keep everything.
|
||||
func ByAppliedWorkload(workload string) Filter {
|
||||
// Keep everything
|
||||
if workload == "" {
|
||||
return KeepAll()
|
||||
}
|
||||
|
||||
return func(obj unstructured.Unstructured) bool {
|
||||
traitDef := &v1beta1.TraitDefinition{}
|
||||
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, traitDef); err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Search for provided workload
|
||||
// If the trait definitions applies to the given workload, then it is kept.
|
||||
for _, w := range traitDef.Spec.AppliesToWorkloads {
|
||||
if w == workload || w == "*" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
Copyright 2022 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package filters
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gotest.tools/assert"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
addonutil "github.com/oam-dev/kubevela/pkg/utils/addon"
|
||||
)
|
||||
|
||||
func TestApply(t *testing.T) {
|
||||
// Any of the filters rejected
|
||||
assert.Equal(t, false, Apply(unstructured.Unstructured{},
|
||||
KeepAll(),
|
||||
KeepNone(),
|
||||
))
|
||||
|
||||
// All filters kept
|
||||
assert.Equal(t, true, Apply(unstructured.Unstructured{},
|
||||
KeepAll(),
|
||||
KeepAll(),
|
||||
))
|
||||
}
|
||||
|
||||
func TestApplyToList(t *testing.T) {
|
||||
list := unstructured.UnstructuredList{Items: []unstructured.Unstructured{
|
||||
unstructured.Unstructured{},
|
||||
unstructured.Unstructured{},
|
||||
}}
|
||||
|
||||
list.Items[0].SetName("name")
|
||||
|
||||
filtered := ApplyToList(list, KeepAll(), ByName("name"))
|
||||
assert.Equal(t, len(filtered.Items), 1)
|
||||
}
|
||||
|
||||
func TestKeepAll(t *testing.T) {
|
||||
f := KeepAll()
|
||||
assert.Equal(t, true, f(unstructured.Unstructured{}))
|
||||
}
|
||||
|
||||
func TestKeepNone(t *testing.T) {
|
||||
f := KeepNone()
|
||||
assert.Equal(t, false, f(unstructured.Unstructured{}))
|
||||
}
|
||||
|
||||
func TestByOwnerAddon(t *testing.T) {
|
||||
// Test with empty addon name
|
||||
f := ByOwnerAddon("")
|
||||
assert.Equal(t, true, f(unstructured.Unstructured{}))
|
||||
|
||||
f = ByOwnerAddon("addon-name")
|
||||
|
||||
// Test with empty owner refs
|
||||
u := unstructured.Unstructured{}
|
||||
assert.Equal(t, false, f(u))
|
||||
|
||||
// Test with right owner refs
|
||||
u.SetOwnerReferences([]v1.OwnerReference{{
|
||||
Name: addonutil.Addon2AppName("addon-name"),
|
||||
}})
|
||||
assert.Equal(t, true, f(u))
|
||||
|
||||
// Test with wrong owner refs
|
||||
u.SetOwnerReferences([]v1.OwnerReference{{
|
||||
Name: "addon-name-2",
|
||||
}})
|
||||
assert.Equal(t, false, f(u))
|
||||
}
|
||||
|
||||
func TestByName(t *testing.T) {
|
||||
// Test with empty name
|
||||
f := ByName("")
|
||||
assert.Equal(t, true, f(unstructured.Unstructured{}))
|
||||
|
||||
f = ByName("name")
|
||||
|
||||
// Test with empty name
|
||||
u := unstructured.Unstructured{}
|
||||
assert.Equal(t, false, f(u))
|
||||
|
||||
// Test with right name
|
||||
u.SetName("name")
|
||||
assert.Equal(t, true, f(u))
|
||||
|
||||
// Test with wrong name
|
||||
u.SetName("name-2")
|
||||
assert.Equal(t, false, f(u))
|
||||
}
|
||||
|
||||
func TestByAppliedWorkload(t *testing.T) {
|
||||
// Test with empty workload
|
||||
f := ByAppliedWorkload("")
|
||||
assert.Equal(t, true, f(unstructured.Unstructured{}))
|
||||
|
||||
f = ByAppliedWorkload("workload")
|
||||
|
||||
// Test with AppliesToWorkloads=*
|
||||
trait := v1beta1.TraitDefinition{}
|
||||
trait.Spec.AppliesToWorkloads = []string{"*"}
|
||||
u, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&trait)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, true, f(unstructured.Unstructured{Object: u}))
|
||||
|
||||
// Test with AppliesToWorkloads=workload
|
||||
trait = v1beta1.TraitDefinition{}
|
||||
trait.Spec.AppliesToWorkloads = []string{"workload"}
|
||||
u, err = runtime.DefaultUnstructuredConverter.ToUnstructured(&trait)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, true, f(unstructured.Unstructured{Object: u}))
|
||||
|
||||
// Test with AppliesToWorkloads=wrong
|
||||
trait = v1beta1.TraitDefinition{}
|
||||
trait.Spec.AppliesToWorkloads = []string{"wrong"}
|
||||
u, err = runtime.DefaultUnstructuredConverter.ToUnstructured(&trait)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, false, f(unstructured.Unstructured{Object: u}))
|
||||
|
||||
// Test not a definition
|
||||
assert.Equal(t, false, f(unstructured.Unstructured{}))
|
||||
}
|
||||
@@ -45,6 +45,7 @@ import (
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
pkgaddon "github.com/oam-dev/kubevela/pkg/addon"
|
||||
addonutil "github.com/oam-dev/kubevela/pkg/utils/addon"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/apply"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
cmdutil "github.com/oam-dev/kubevela/pkg/utils/util"
|
||||
@@ -203,7 +204,7 @@ Enable addon for specific clusters, (local means control plane):
|
||||
// AdditionalEndpointPrinter will print endpoints
|
||||
func AdditionalEndpointPrinter(ctx context.Context, c common.Args, k8sClient client.Client, name string, isUpgrade bool) {
|
||||
fmt.Printf("Please access %s from the following endpoints:\n", name)
|
||||
err := printAppEndpoints(ctx, pkgaddon.Convert2AppName(name), types.DefaultKubeVelaNS, Filter{}, c, true)
|
||||
err := printAppEndpoints(ctx, addonutil.Addon2AppName(name), types.DefaultKubeVelaNS, Filter{}, c, true)
|
||||
if err != nil {
|
||||
fmt.Println("Get application endpoints error:", err)
|
||||
return
|
||||
@@ -567,8 +568,8 @@ func statusAddon(name string, ioStreams cmdutil.IOStreams, cmd *cobra.Command, c
|
||||
fmt.Print(statusString)
|
||||
|
||||
if status.AddonPhase != statusEnabled && status.AddonPhase != statusDisabled {
|
||||
fmt.Printf("diagnose addon info from application %s", pkgaddon.Convert2AppName(name))
|
||||
err := printAppStatus(context.Background(), k8sClient, ioStreams, pkgaddon.Convert2AppName(name), types.DefaultKubeVelaNS, cmd, c)
|
||||
fmt.Printf("diagnose addon info from application %s", addonutil.Addon2AppName(name))
|
||||
err := printAppStatus(context.Background(), k8sClient, ioStreams, addonutil.Addon2AppName(name), types.DefaultKubeVelaNS, cmd, c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -869,7 +870,7 @@ func waitApplicationRunning(k8sClient client.Client, addonName string) error {
|
||||
defer spinner.Stop()
|
||||
|
||||
for {
|
||||
err := k8sClient.Get(ctx, types2.NamespacedName{Name: pkgaddon.Convert2AppName(addonName), Namespace: types.DefaultKubeVelaNS}, &app)
|
||||
err := k8sClient.Get(ctx, types2.NamespacedName{Name: addonutil.Addon2AppName(addonName), Namespace: types.DefaultKubeVelaNS}, &app)
|
||||
if err != nil {
|
||||
return client.IgnoreNotFound(err)
|
||||
}
|
||||
@@ -881,7 +882,7 @@ func waitApplicationRunning(k8sClient client.Client, addonName string) error {
|
||||
applySpinnerNewSuffix(spinner, fmt.Sprintf("Waiting addon application running. It is now in phase: %s (timeout %d/%d seconds)...",
|
||||
phase, timeConsumed, int(timeout.Seconds())))
|
||||
if timeConsumed > int(timeout.Seconds()) {
|
||||
return errors.Errorf("Enabling timeout, please run \"vela status %s -n vela-system\" to check the status of the addon", pkgaddon.Convert2AppName(addonName))
|
||||
return errors.Errorf("Enabling timeout, please run \"vela status %s -n vela-system\" to check the status of the addon", addonutil.Addon2AppName(addonName))
|
||||
}
|
||||
time.Sleep(trackInterval)
|
||||
}
|
||||
|
||||
+42
-4
@@ -50,7 +50,9 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/cue/model/sets"
|
||||
"github.com/oam-dev/kubevela/pkg/cue/packages"
|
||||
pkgdef "github.com/oam-dev/kubevela/pkg/definition"
|
||||
addonutil "github.com/oam-dev/kubevela/pkg/utils/addon"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/filters"
|
||||
"github.com/oam-dev/kubevela/references/plugins"
|
||||
)
|
||||
|
||||
@@ -384,7 +386,7 @@ func generateTerraformTypedComponentDefinition(cmd *cobra.Command, name, kind, p
|
||||
}
|
||||
|
||||
func getSingleDefinition(cmd *cobra.Command, definitionName string, client client.Client, definitionType string, namespace string) (*pkgdef.Definition, error) {
|
||||
definitions, err := pkgdef.SearchDefinition(definitionName, client, definitionType, namespace)
|
||||
definitions, err := pkgdef.SearchDefinition(client, definitionType, namespace, filters.ByName(definitionName))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -526,11 +528,18 @@ func NewDefinitionListCommand(c common.Args) *cobra.Command {
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to get `%s`", Namespace)
|
||||
}
|
||||
addonName, err := cmd.Flags().GetString("from")
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to get `%s`", "from")
|
||||
}
|
||||
k8sClient, err := c.GetClient()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to get k8s client")
|
||||
}
|
||||
definitions, err := pkgdef.SearchDefinition("*", k8sClient, definitionType, namespace)
|
||||
definitions, err := pkgdef.SearchDefinition(k8sClient,
|
||||
definitionType,
|
||||
namespace,
|
||||
filters.ByOwnerAddon(addonName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -538,14 +547,42 @@ func NewDefinitionListCommand(c common.Args) *cobra.Command {
|
||||
cmd.Println("No definition found.")
|
||||
return nil
|
||||
}
|
||||
// Determine if there is a definition in the list from some addons
|
||||
// This is used to tell if we want the SOURCE-ADDON column
|
||||
showSourceAddon := false
|
||||
for _, def := range definitions {
|
||||
ownerRef := def.GetOwnerReferences()
|
||||
if len(ownerRef) > 0 && strings.HasPrefix(ownerRef[0].Name, addonutil.AddonAppPrefix) {
|
||||
showSourceAddon = true
|
||||
break
|
||||
}
|
||||
}
|
||||
table := newUITable()
|
||||
table.AddRow("NAME", "TYPE", "NAMESPACE", "DESCRIPTION")
|
||||
|
||||
// We only include SOURCE-ADDON if there is at least one definition from an addon
|
||||
if showSourceAddon {
|
||||
table.AddRow("NAME", "TYPE", "NAMESPACE", "SOURCE-ADDON", "DESCRIPTION")
|
||||
} else {
|
||||
table.AddRow("NAME", "TYPE", "NAMESPACE", "DESCRIPTION")
|
||||
}
|
||||
|
||||
for _, definition := range definitions {
|
||||
desc := ""
|
||||
if annotations := definition.GetAnnotations(); annotations != nil {
|
||||
desc = annotations[pkgdef.DescriptionKey]
|
||||
}
|
||||
table.AddRow(definition.GetName(), definition.GetKind(), definition.GetNamespace(), desc)
|
||||
|
||||
// Do not show SOURCE-ADDON column
|
||||
if !showSourceAddon {
|
||||
table.AddRow(definition.GetName(), definition.GetKind(), definition.GetNamespace(), desc)
|
||||
continue
|
||||
}
|
||||
|
||||
sourceAddon := ""
|
||||
if len(definition.GetOwnerReferences()) > 0 {
|
||||
sourceAddon = strings.TrimPrefix(definition.GetOwnerReferences()[0].Name, "addon-")
|
||||
}
|
||||
table.AddRow(definition.GetName(), definition.GetKind(), definition.GetNamespace(), sourceAddon, desc)
|
||||
}
|
||||
cmd.Println(table)
|
||||
return nil
|
||||
@@ -553,6 +590,7 @@ func NewDefinitionListCommand(c common.Args) *cobra.Command {
|
||||
}
|
||||
cmd.Flags().StringP(FlagType, "t", "", "Specify which definition type to list. If empty, all types will be searched. Valid types: "+strings.Join(pkgdef.ValidDefinitionTypes(), ", "))
|
||||
cmd.Flags().StringP(Namespace, "n", "", "Specify which namespace to list. If empty, all namespaces will be searched.")
|
||||
cmd.Flags().String("from", "", "Filter definitions by which addon installed them.")
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ import (
|
||||
common3 "github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
pkgdef "github.com/oam-dev/kubevela/pkg/definition"
|
||||
addonutil "github.com/oam-dev/kubevela/pkg/utils/addon"
|
||||
common2 "github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
)
|
||||
|
||||
@@ -59,12 +60,16 @@ func initCommand(cmd *cobra.Command) {
|
||||
}
|
||||
|
||||
func createTrait(c common2.Args, t *testing.T) string {
|
||||
return createTraitWithOwnerAddon(c, "", t)
|
||||
}
|
||||
|
||||
func createTraitWithOwnerAddon(c common2.Args, addonName string, t *testing.T) string {
|
||||
traitName := fmt.Sprintf("my-trait-%d", time.Now().UnixNano())
|
||||
createNamespacedTrait(c, traitName, VelaTestNamespace, t)
|
||||
createNamespacedTrait(c, traitName, VelaTestNamespace, addonName, t)
|
||||
return traitName
|
||||
}
|
||||
|
||||
func createNamespacedTrait(c common2.Args, name string, ns string, t *testing.T) {
|
||||
func createNamespacedTrait(c common2.Args, name string, ns string, ownerAddon string, t *testing.T) {
|
||||
traitName := fmt.Sprintf("my-trait-%d", time.Now().UnixNano())
|
||||
client, err := c.GetClient()
|
||||
if err != nil {
|
||||
@@ -77,6 +82,9 @@ func createNamespacedTrait(c common2.Args, name string, ns string, t *testing.T)
|
||||
Annotations: map[string]string{
|
||||
pkgdef.DescriptionKey: "My test-trait " + traitName,
|
||||
},
|
||||
OwnerReferences: []v1.OwnerReference{{
|
||||
Name: addonutil.Addon2AppName(ownerAddon),
|
||||
}},
|
||||
},
|
||||
Spec: v1beta1.TraitDefinitionSpec{
|
||||
Schematic: &common3.Schematic{CUE: &common3.CUE{Template: "parameter: {}"}},
|
||||
@@ -393,7 +401,7 @@ func TestNewDefinitionGetCommand(t *testing.T) {
|
||||
// test multi trait
|
||||
cmd = NewDefinitionGetCommand(c)
|
||||
initCommand(cmd)
|
||||
createNamespacedTrait(c, traitName, "default", t)
|
||||
createNamespacedTrait(c, traitName, "default", "", t)
|
||||
cmd.SetArgs([]string{traitName})
|
||||
if err := cmd.Execute(); err == nil {
|
||||
t.Fatalf("expect found multiple traits error, but not found")
|
||||
@@ -433,6 +441,13 @@ func TestNewDefinitionListCommand(t *testing.T) {
|
||||
if err := cmd.Execute(); err != nil {
|
||||
t.Fatalf("no trait found should not return error, err: %v", err)
|
||||
}
|
||||
// with addon filter
|
||||
cmd = NewDefinitionListCommand(c)
|
||||
initCommand(cmd)
|
||||
cmd.SetArgs([]string{"--from", "non-existent-addon"})
|
||||
if err := cmd.Execute(); err != nil {
|
||||
t.Fatalf("applying addon filter should not return error, err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewDefinitionEditCommand(t *testing.T) {
|
||||
@@ -451,7 +466,7 @@ func TestNewDefinitionEditCommand(t *testing.T) {
|
||||
// test no change
|
||||
cmd = NewDefinitionEditCommand(c)
|
||||
initCommand(cmd)
|
||||
createNamespacedTrait(c, traitName, "default", t)
|
||||
createNamespacedTrait(c, traitName, "default", "", t)
|
||||
if err := os.Setenv("EDITOR", "sed -i -e 's/test-trait-test/TestTrait/g'"); err != nil {
|
||||
t.Fatalf("failed to set editor env: %v", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user