diff --git a/pkg/apiserver/rest/usecase/addon.go b/pkg/apiserver/rest/usecase/addon.go index f536c2682..ac14a705d 100644 --- a/pkg/apiserver/rest/usecase/addon.go +++ b/pkg/apiserver/rest/usecase/addon.go @@ -18,6 +18,7 @@ package usecase import ( "context" + "encoding/json" "errors" "fmt" "sort" @@ -25,6 +26,8 @@ import ( "sync" "time" + k8stypes "k8s.io/apimachinery/pkg/types" + v1 "k8s.io/api/core/v1" errors2 "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -39,6 +42,7 @@ import ( "github.com/oam-dev/kubevela/pkg/apiserver/clients" "github.com/oam-dev/kubevela/pkg/apiserver/log" apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils" "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" "github.com/oam-dev/kubevela/pkg/oam" "github.com/oam-dev/kubevela/pkg/utils/apply" @@ -155,7 +159,9 @@ func (u *defaultAddonHandler) GetAddon(ctx context.Context, name string, registr if addon == nil { return nil, bcode.ErrAddonNotExist } - addon.UISchema = renderDefaultUISchema(addon.APISchema) + + addon.UISchema = renderAddonCustomUISchema(ctx, u.kubeClient, name, renderDefaultUISchema(addon.APISchema)) + a, err := AddonImpl2AddonRes(addon) if err != nil { return nil, err @@ -455,6 +461,29 @@ func convertAppStateToAddonPhase(state common2.ApplicationPhase) apis.AddonPhase } } +func renderAddonCustomUISchema(ctx context.Context, cli client.Client, addonName string, defaultSchema []*utils.UIParameter) []*utils.UIParameter { + var cm v1.ConfigMap + if err := cli.Get(ctx, k8stypes.NamespacedName{ + Namespace: types.DefaultKubeVelaNS, + Name: fmt.Sprintf("addon-uischema-%s", addonName), + }, &cm); err != nil { + if !errors2.IsNotFound(err) { + log.Logger.Errorf("find uischema configmap from cluster failure %s", err.Error()) + } + return defaultSchema + } + data, ok := cm.Data[types.UISchema] + if !ok { + return defaultSchema + } + schema := []*utils.UIParameter{} + if err := json.Unmarshal([]byte(data), &schema); err != nil { + log.Logger.Errorf("unmarshal ui schema failure %s", err.Error()) + return defaultSchema + } + return patchSchema(defaultSchema, schema) +} + // ConvertAddonRegistryModel2AddonRegistryMeta will convert from model to AddonRegistry func ConvertAddonRegistryModel2AddonRegistryMeta(r pkgaddon.Registry) apis.AddonRegistry { return apis.AddonRegistry{ diff --git a/pkg/apiserver/rest/usecase/addon_test.go b/pkg/apiserver/rest/usecase/addon_test.go new file mode 100644 index 000000000..39acc8d44 --- /dev/null +++ b/pkg/apiserver/rest/usecase/addon_test.go @@ -0,0 +1,106 @@ +/* +Copyright 2021 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 usecase + +import ( + "context" + "fmt" + "io/ioutil" + + "github.com/oam-dev/kubevela/pkg/oam/util" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/yaml" + + "github.com/oam-dev/kubevela/apis/types" + "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils" +) + +var _ = Describe("addon usecase test", func() { + var ctx context.Context + + BeforeEach(func() { + ctx = context.Background() + Expect(k8sClient.Create(ctx, &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: types.DefaultKubeVelaNS}})).Should(SatisfyAny(BeNil(), util.AlreadyExistMatcher{})) + }) + + It("Test render customize ui-schema", func() { + schemaData, err := ioutil.ReadFile("testdata/addon-uischema-test.yaml") + + addonName := "test" + Expect(err).Should(BeNil()) + jsonData, err := yaml.YAMLToJSON(schemaData) + Expect(err).Should(BeNil()) + cm := v1.ConfigMap{ + TypeMeta: metav1.TypeMeta{APIVersion: "v1", Kind: "ConfigMap"}, + ObjectMeta: metav1.ObjectMeta{Namespace: types.DefaultKubeVelaNS, Name: fmt.Sprintf("addon-uischema-%s", addonName)}, + Data: map[string]string{ + types.UISchema: string(jsonData), + }} + + Expect(k8sClient.Create(ctx, &cm)).Should(BeNil()) + defaultSchema := []*utils.UIParameter{ + { + JSONKey: "version", + Sort: 3, + }, + { + JSONKey: "domain", + Sort: 8, + }, + } + res := renderAddonCustomUISchema(ctx, k8sClient, addonName, defaultSchema) + Expect(len(res)).Should(BeEquivalentTo(2)) + for _, re := range res { + if re.JSONKey == "version" { + Expect(re.Validate.DefaultValue.(string)).Should(BeEquivalentTo("1.2.0-rc1")) + Expect(re.Sort).Should(BeEquivalentTo(1)) + } + if re.JSONKey == "domain" { + Expect(re.Sort).Should(BeEquivalentTo(9)) + } + } + }) + + It("Test render without ui-schema", func() { + addonName := "test-without-schema" + defaultSchema := []*utils.UIParameter{ + { + JSONKey: "version", + Sort: 3, + }, + { + JSONKey: "domain", + Sort: 8, + }, + } + res := renderAddonCustomUISchema(ctx, k8sClient, addonName, defaultSchema) + Expect(len(res)).Should(BeEquivalentTo(2)) + for _, re := range res { + if re.JSONKey == "version" { + Expect(re.Validate).Should(BeNil()) + Expect(re.Sort).Should(BeEquivalentTo(3)) + } + if re.JSONKey == "domain" { + Expect(re.Sort).Should(BeEquivalentTo(8)) + } + } + }) +}) diff --git a/pkg/apiserver/rest/usecase/testdata/addon-uischema-test.yaml b/pkg/apiserver/rest/usecase/testdata/addon-uischema-test.yaml new file mode 100644 index 000000000..b08de0f4c --- /dev/null +++ b/pkg/apiserver/rest/usecase/testdata/addon-uischema-test.yaml @@ -0,0 +1,18 @@ +- jsonKey: version + validate: + defaultValue: 1.2.0-rc1 + sort: 1 +- jsonKey: dbType + label: DBType + validate: + defaultValue: kubeapi + sort: 3 +- jsonKey: dbURL + label: DBURL + sort: 5 +- jsonKey: database + sort: 7 + validate: + defaultValue: kubevela +- jsonKey: domain + sort: 9