From 356db2dc9791fab614ef72038f1d206ee31ee4cd Mon Sep 17 00:00:00 2001 From: qiaozp <47812250+chivalryq@users.noreply.github.com> Date: Wed, 11 Aug 2021 11:49:44 +0800 Subject: [PATCH] Add add-on input go-template implementation (#2049) * add addon parameter template implementation * add test * fix test * fix helm version parameter --- charts/vela-core/templates/addons/fluxcd.yaml | 14 +-- e2e/addon/addon_test.go | 99 ++++++++++++++++--- references/cli/addon.go | 77 +++++++++++---- vela-templates/addons/auto-gen/fluxcd.yaml | 14 +-- .../fluxcd/definitions/helm-release.yaml | 4 +- 5 files changed, 159 insertions(+), 49 deletions(-) diff --git a/charts/vela-core/templates/addons/fluxcd.yaml b/charts/vela-core/templates/addons/fluxcd.yaml index f34887ec4..a2cae45aa 100644 --- a/charts/vela-core/templates/addons/fluxcd.yaml +++ b/charts/vela-core/templates/addons/fluxcd.yaml @@ -5940,13 +5940,13 @@ data: +usage=The interval at which to check for repository and relese updates.\n\tpullInterval: *\"5m\" | string\n\t// +usage=1.The relative path to helm chart for git source. 2. chart name for helm resource\n\tchart: - string\n\t// +usage=Chart version\n\tversion?: string\n\t// +usage=The - Git reference to checkout and monitor for changes, defaults to master - branch.\n\tbranch: *\"master\" | string\n\t// +usage=The name of - the secret containing authentication credentials for the Helm repository.\n\tsecretRef?: - string\n\t// +usage=The namespace for helm chart\n\ttargetNamespace?: - string\n\t// +usage=Chart version\n\tvalue?: #nestedmap\n}\n\n#nestedmap: - {\n\t...\n}\n" + string\n\t// +usage=Chart version\n\tversion: *\"*\" | string\n\t// + +usage=The Git reference to checkout and monitor for changes, defaults + to master branch.\n\tbranch: *\"master\" | string\n\t// +usage=The + name of the secret containing authentication credentials for the + Helm repository.\n\tsecretRef?: string\n\t// +usage=The namespace + for helm chart\n\ttargetNamespace?: string\n\t// +usage=Chart version\n\tvalue?: + #nestedmap\n}\n\n#nestedmap: {\n\t...\n} \n" workload: type: autodetects.core.oam.dev type: raw diff --git a/e2e/addon/addon_test.go b/e2e/addon/addon_test.go index a4adba880..296c0429e 100644 --- a/e2e/addon/addon_test.go +++ b/e2e/addon/addon_test.go @@ -32,44 +32,68 @@ import ( var _ = Describe("Addon Test", func() { args := common.Args{} - var cm v1.ConfigMap + var cmSimpleAddon v1.ConfigMap + var cmInputAddon v1.ConfigMap Context("Prepare test addon", func() { - It("apply test addon", func() { - Expect(yaml.Unmarshal([]byte(test_addon), &cm)).Should(BeNil()) - k8sClient, err := args.GetClient() - Expect(err).Should(BeNil()) - err = k8sClient.Create(context.Background(), &cm) + k8sClient, err := args.GetClient() + Expect(err).Should(BeNil()) + It("Apply test addon", func() { + Expect(yaml.Unmarshal([]byte(test_addon), &cmSimpleAddon)).Should(BeNil()) + err = k8sClient.Create(context.Background(), &cmSimpleAddon) + Expect(err).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{})) + }) + It("Apply test input addon", func() { + Expect(yaml.Unmarshal([]byte(test_input_addon), &cmInputAddon)).Should(BeNil()) + err = k8sClient.Create(context.Background(), &cmInputAddon) Expect(err).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{})) }) }) - Context("list addons", func() { - It("list all addon", func() { + Context("List addons", func() { + It("List all addon", func() { output, err := e2e.Exec("vela addon list") Expect(err).NotTo(HaveOccurred()) Expect(output).To(ContainSubstring("test-addon")) }) }) - Context("enable addon", func() { - It("enable addon fluxcd", func() { + Context("Enable addon", func() { + It("Enable addon fluxcd", func() { output, err := e2e.Exec("vela addon enable test-addon") Expect(err).NotTo(HaveOccurred()) Expect(output).To(ContainSubstring("Successfully enable addon")) }) }) - Context("disable addon", func() { - It("disable addon fluxcd", func() { + Context("Disable addon", func() { + It("Disable addon fluxcd", func() { output, err := e2e.LongTimeExec("vela addon disable test-addon", 600*time.Second) Expect(err).NotTo(HaveOccurred()) Expect(output).To(ContainSubstring("Successfully disable addon")) }) }) + Context("Test addon receive input", func() { + It("Enable addon with input", func() { + output, err := e2e.LongTimeExec("vela addon enable test-input-addon repoUrl=https://charts.bitnami.com/bitnami chart=redis", 300*time.Second) + Expect(err).NotTo(HaveOccurred()) + Expect(output).To(ContainSubstring("Successfully enable addon")) + }) + }) + + Context("Disable addon", func() { + It("Disable addon fluxcd", func() { + output, err := e2e.LongTimeExec("vela addon disable test-input-addon", 600*time.Second) + Expect(err).NotTo(HaveOccurred()) + Expect(output).To(ContainSubstring("Successfully disable addon")) + }) + }) + Context("Clean test environment", func() { - It("Delete test addon", func() { + It("Delete test addon and test-input addon", func() { k8sClient, err := args.GetClient() Expect(err).Should(BeNil()) - err = k8sClient.Delete(context.Background(), &cm) + err = k8sClient.Delete(context.Background(), &cmSimpleAddon) + Expect(err).Should(BeNil()) + err = k8sClient.Delete(context.Background(), &cmInputAddon) Expect(err).Should(BeNil()) }) }) @@ -112,3 +136,50 @@ data: - name: test-addon-pod-container image: nginx ` + +var test_input_addon = ` +kind: ConfigMap +metadata: + annotations: + addons.oam.dev/description: This is a test addon for test addon input + labels: + addons.oam.dev/type: test + name: test-input-addon + namespace: vela-system +apiVersion: v1 +data: + initializer: | + apiVersion: core.oam.dev/v1beta1 + kind: Initializer + metadata: + annotations: + addons.oam.dev/description: This is a test addon for test addon input + name: test-input-addon + namespace: vela-system + spec: + appTemplate: + spec: + components: + - name: test-chart + properties: + chart: {{ index .Args "chart" }} + repoType: helm + repoUrl: {{ index .Args "repoUrl" }} + type: helm + status: + rollout: + batchRollingState: "" + currentBatch: 0 + lastTargetAppRevision: "" + rollingState: "" + upgradedReadyReplicas: 0 + upgradedReplicas: 0 + dependsOn: + - ref: + apiVersion: core.oam.dev/v1beta1 + kind: Initializer + name: fluxcd + namespace: vela-system + status: + observedGeneration: 0 +` diff --git a/references/cli/addon.go b/references/cli/addon.go index 0c09b4b20..29d0dfce6 100644 --- a/references/cli/addon.go +++ b/references/cli/addon.go @@ -17,8 +17,11 @@ limitations under the License. package cli import ( + "bytes" "context" "fmt" + "strings" + "text/template" "time" "github.com/gosuri/uitable" @@ -111,7 +114,11 @@ func NewAddonEnableCommand(ioStream cmdutil.IOStreams) *cobra.Command { } } name := args[0] - err := enableAddon(name) + addonArgs, err := parseToMap(args[1:]) + if err != nil { + return err + } + err = enableAddon(name, addonArgs) if err != nil { return err } @@ -120,6 +127,22 @@ func NewAddonEnableCommand(ioStream cmdutil.IOStreams) *cobra.Command { } } +func parseToMap(args []string) (map[string]string, error) { + res := map[string]string{} + for _, pair := range args { + line := strings.Split(pair, "=") + if len(line) != 2 { + return nil, fmt.Errorf("parameter format should be foo=bar, %s not match", pair) + } + k := strings.TrimSpace(line[0]) + v := strings.TrimSpace(line[1]) + if k != "" && v != "" { + res[k] = v + } + } + return res, nil +} + // NewAddonDisableCommand create addon disable command func NewAddonDisableCommand(ioStream cmdutil.IOStreams) *cobra.Command { return &cobra.Command{ @@ -159,7 +182,7 @@ func listAddons() error { return nil } -func enableAddon(name string) error { +func enableAddon(name string, args map[string]string) error { repo, err := NewAddonRepo() if err != nil { return err @@ -168,6 +191,7 @@ func enableAddon(name string) error { if err != nil { return err } + addon.setArgs(args) err = addon.enable() if err != nil { return err @@ -199,7 +223,7 @@ func disableAddon(name string) error { func newAddon(data *v1.ConfigMap) *Addon { description := data.ObjectMeta.Annotations[DescAnnotation] a := Addon{name: data.Name, description: description, initYaml: data.Data["initializer"]} - init, _ := a.getInitializer() + init, _ := a.renderInitializer() a.addonNamespace = init.GetNamespace() return &a } @@ -258,14 +282,16 @@ type Addon struct { addonNamespace string // addonNamespace is where Initializer will be apply description string initYaml string - initializer *unstructured.Unstructured - gvk *schema.GroupVersionKind + // Args is map for renderInitializer + Args map[string]string + initializer *unstructured.Unstructured + gvk *schema.GroupVersionKind } func (a *Addon) getGVK() (*schema.GroupVersionKind, error) { if a.gvk == nil { if a.initializer == nil { - _, err := a.getInitializer() + _, err := a.renderInitializer() if err != nil { return nil, err } @@ -276,25 +302,34 @@ func (a *Addon) getGVK() (*schema.GroupVersionKind, error) { return a.gvk, nil } -func (a *Addon) getInitializer() (*unstructured.Unstructured, error) { - if a.initializer == nil { - res := a.initYaml - obj := &unstructured.Unstructured{} - dec := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme) - _, gvk, err := dec.Decode([]byte(res), nil, obj) - if err != nil { - return nil, err - } - a.initializer = obj - a.gvk = gvk +func (a *Addon) renderInitializer() (*unstructured.Unstructured, error) { + if a.Args == nil { + a.Args = map[string]string{} } + t, err := template.New("addon-template").Parse(a.initYaml) + if err != nil { + return nil, errors.Wrap(err, "parsing addon initializer template error") + } + buf := bytes.Buffer{} + err = t.Execute(&buf, a) + if err != nil { + return nil, errors.Wrap(err, "initializer template render fail") + } + dec := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme) + obj := &unstructured.Unstructured{} + _, gvk, err := dec.Decode(buf.Bytes(), nil, obj) + if err != nil { + return nil, err + } + a.initializer = obj + a.gvk = gvk return a.initializer, nil } func (a *Addon) enable() error { applicator := apply.NewAPIApplicator(clt) ctx := context.Background() - obj, err := a.getInitializer() + obj, err := a.renderInitializer() if err != nil { return err } @@ -349,7 +384,7 @@ func (a *Addon) disable() error { if err != nil { return err } - obj, err := a.getInitializer() + obj, err := a.renderInitializer() if err != nil { return err } @@ -392,3 +427,7 @@ func (a *Addon) getStatus() string { } return statusInstalled } + +func (a *Addon) setArgs(args map[string]string) { + a.Args = args +} diff --git a/vela-templates/addons/auto-gen/fluxcd.yaml b/vela-templates/addons/auto-gen/fluxcd.yaml index a947cddbd..e9566bab6 100644 --- a/vela-templates/addons/auto-gen/fluxcd.yaml +++ b/vela-templates/addons/auto-gen/fluxcd.yaml @@ -5937,13 +5937,13 @@ spec: +usage=The interval at which to check for repository and relese updates.\n\tpullInterval: *\"5m\" | string\n\t// +usage=1.The relative path to helm chart for git source. 2. chart name for helm resource\n\tchart: - string\n\t// +usage=Chart version\n\tversion?: string\n\t// +usage=The - Git reference to checkout and monitor for changes, defaults to master - branch.\n\tbranch: *\"master\" | string\n\t// +usage=The name of - the secret containing authentication credentials for the Helm repository.\n\tsecretRef?: - string\n\t// +usage=The namespace for helm chart\n\ttargetNamespace?: - string\n\t// +usage=Chart version\n\tvalue?: #nestedmap\n}\n\n#nestedmap: - {\n\t...\n}\n" + string\n\t// +usage=Chart version\n\tversion: *\"*\" | string\n\t// + +usage=The Git reference to checkout and monitor for changes, defaults + to master branch.\n\tbranch: *\"master\" | string\n\t// +usage=The + name of the secret containing authentication credentials for the + Helm repository.\n\tsecretRef?: string\n\t// +usage=The namespace + for helm chart\n\ttargetNamespace?: string\n\t// +usage=Chart version\n\tvalue?: + #nestedmap\n}\n\n#nestedmap: {\n\t...\n} \n" workload: type: autodetects.core.oam.dev type: raw diff --git a/vela-templates/addons/fluxcd/definitions/helm-release.yaml b/vela-templates/addons/fluxcd/definitions/helm-release.yaml index 60a4a6cd9..b9c59beaa 100644 --- a/vela-templates/addons/fluxcd/definitions/helm-release.yaml +++ b/vela-templates/addons/fluxcd/definitions/helm-release.yaml @@ -82,7 +82,7 @@ spec: // +usage=1.The relative path to helm chart for git source. 2. chart name for helm resource chart: string // +usage=Chart version - version?: string + version: *"*" | string // +usage=The Git reference to checkout and monitor for changes, defaults to master branch. branch: *"master" | string // +usage=The name of the secret containing authentication credentials for the Helm repository. @@ -95,4 +95,4 @@ spec: #nestedmap: { ... - } + } \ No newline at end of file