[Backport release-1.5] Fix: address failure when rendering addon API schemas (#4445)

* Fix: address failure when rendering addon API schemas

Signed-off-by: Charlie Chiang <charlie_c_0129@outlook.com>
(cherry picked from commit de7a64346c)

* Fix: address failure when rendering addon API schemas

Signed-off-by: Charlie Chiang <charlie_c_0129@outlook.com>
(cherry picked from commit 284e673bad)

* Test: add tests

Signed-off-by: Charlie Chiang <charlie_c_0129@outlook.com>
(cherry picked from commit 1c0653e449)

* Test: fix tests

Signed-off-by: Charlie Chiang <charlie_c_0129@outlook.com>
(cherry picked from commit 78104068b1)

* Test: fix tests

Signed-off-by: Charlie Chiang <charlie_c_0129@outlook.com>
(cherry picked from commit 0442f823c8)

Co-authored-by: Charlie Chiang <charlie_c_0129@outlook.com>
This commit is contained in:
github-actions[bot]
2022-07-25 17:02:32 +08:00
committed by GitHub
parent 853f44cf61
commit 35ae4e5ef5
16 changed files with 215 additions and 11 deletions

View File

@@ -306,13 +306,16 @@ func GetUIDataFromReader(r AsyncReader, meta *SourceMeta, opt ListOptions) (*UID
}
if opt.GetParameter && (len(addon.Parameters) != 0 || len(addon.GlobalParameters) != 0) {
if addon.GlobalParameters != "" {
if addon.Parameters != "" {
klog.Warning("both legacy parameter and global parameter are provided, but only global parameter will be used. Consider removing the legacy parameters.")
}
addon.Parameters = addon.GlobalParameters
}
err := genAddonAPISchema(addon)
if err != nil {
return nil, fmt.Errorf("fail to generate openAPIschema for addon %s : %w", meta.Name, err)
}
if len(addon.GlobalParameters) != 0 {
addon.Parameters = addon.GlobalParameters
}
}
addon.AvailableVersions = []string{addon.Version}
return addon, nil

View File

@@ -58,12 +58,20 @@ import (
var paths = []string{
"example/metadata.yaml",
"example/readme.md",
"example/template.yaml",
"example/template.cue",
"example/definitions/helm.yaml",
"example/resources/configmap.cue",
"example/resources/parameter.cue",
"example/parameter.cue",
"example/resources/service/source-controller.yaml",
"example-legacy/metadata.yaml",
"example-legacy/readme.md",
"example-legacy/template.yaml",
"example-legacy/definitions/helm.yaml",
"example-legacy/resources/configmap.cue",
"example-legacy/resources/parameter.cue",
"example-legacy/resources/service/source-controller.yaml",
"terraform/metadata.yaml",
"terraform-alibaba/metadata.yaml",
@@ -159,11 +167,25 @@ func testReaderFunc(t *testing.T, reader AsyncReader) {
assert.True(t, uiData.Parameters != "")
assert.True(t, len(uiData.Definitions) > 0)
testAddonName = "example-legacy"
for _, m := range registryMeta {
if m.Name == testAddonName {
testAddonMeta = m
break
}
}
assert.NoError(t, err)
uiData, err = GetUIDataFromReader(reader, &testAddonMeta, UIMetaOptions)
assert.NoError(t, err)
assert.Equal(t, uiData.Name, testAddonName)
assert.True(t, uiData.Parameters != "")
assert.True(t, len(uiData.Definitions) > 0)
// test get ui data
rName := "KubeVela"
uiDataList, err := ListAddonUIDataFromReader(reader, registryMeta, rName, UIMetaOptions)
assert.True(t, strings.Contains(err.Error(), "#parameter.example: preference mark not allowed at this position"))
assert.Equal(t, 4, len(uiDataList))
assert.Equal(t, 5, len(uiDataList))
assert.Equal(t, uiDataList[0].RegistryName, rName)
// test get install package
@@ -1151,13 +1173,13 @@ func TestCheckEnableAddonErrorWhenMissMatch(t *testing.T) {
func TestPackageAddon(t *testing.T) {
pwd, _ := os.Getwd()
validAddonDict := "./testdata/example"
validAddonDict := "./testdata/example-legacy"
archiver, err := PackageAddon(validAddonDict)
assert.NoError(t, err)
assert.Equal(t, filepath.Join(pwd, "example-1.0.1.tgz"), archiver)
assert.Equal(t, filepath.Join(pwd, "example-legacy-1.0.1.tgz"), archiver)
// Remove generated package after tests
defer func() {
_ = os.RemoveAll(filepath.Join(pwd, "example-1.0.1.tgz"))
_ = os.RemoveAll(filepath.Join(pwd, "example-legacy-1.0.1.tgz"))
}()
invalidAddonDict := "./testdata"

View File

@@ -141,6 +141,7 @@ var _ = Describe("test FindWholeAddonPackagesFromRegistry", func() {
cmYaml := strings.ReplaceAll(registryCmYaml, "TEST_SERVER_URL", server.URL)
cmYaml = strings.ReplaceAll(cmYaml, "KubeVela", "testreg")
Expect(yaml.Unmarshal([]byte(cmYaml), &cm)).Should(BeNil())
_ = k8sClient.Create(ctx, &cm)
Expect(k8sClient.Update(ctx, &cm)).Should(BeNil())
})

View File

@@ -146,7 +146,7 @@ func renderCompAccordingCUETemplate(cueTemplate ElementFile, addon *InstallPacka
inputArgs: args,
}
if err := r.toObject(cueTemplate.Data, &comp); err != nil {
return nil, err
return nil, fmt.Errorf("error rendering file %s: %w", cueTemplate.Name, err)
}
// If the name of component has been set, just keep it, otherwise will set with file name.
if len(comp.Name) == 0 {

View File

@@ -0,0 +1,12 @@
apiVersion: v2
appVersion: 1.0.1
description: Extended workload to do continuous and progressive delivery
home: https://fluxcd.io
icon: https://raw.githubusercontent.com/fluxcd/flux/master/docs/_files/weave-flux.png
keywords:
- extended_workload
- gitops
- only_example
name: example-legacy
type: library
version: 1.0.1

View File

@@ -0,0 +1,62 @@
apiVersion: core.oam.dev/v1beta1
kind: ComponentDefinition
metadata:
annotations:
definition.oam.dev/description: helm release is a group of K8s resources
from either git repository or helm repo
name: helm-example
namespace: vela-system
spec:
schematic:
cue:
template: "output: {\n\tapiVersion: \"source.toolkit.fluxcd.io/v1beta1\"\n\tmetadata:
{\n\t\tname: context.name\n\t}\n\tif parameter.repoType == \"git\" {\n\t\tkind:
\"GitRepository\"\n\t\tspec: {\n\t\t\turl: parameter.url\n\t\t\tif parameter.git.branch
!= _|_ {\n\t\t\t\tref: branch: parameter.git.branch\n\t\t\t}\n\t\t\t_secret\n\t\t\t_sourceCommonArgs\n\t\t}\n\t}\n\tif
parameter.repoType == \"oss\" {\n\t\tkind: \"Bucket\"\n\t\tspec: {\n\t\t\tendpoint:
\ parameter.url\n\t\t\tbucketName: parameter.oss.bucketName\n\t\t\tprovider:
\ parameter.oss.provider\n\t\t\tif parameter.oss.region != _|_ {\n\t\t\t\tregion:
parameter.oss.region\n\t\t\t}\n\t\t\t_secret\n\t\t\t_sourceCommonArgs\n\t\t}\n\t}\n\tif
parameter.repoType == \"helm\" {\n\t\tkind: \"HelmRepository\"\n\t\tspec:
{\n\t\t\turl: parameter.url\n\t\t\t_secret\n\t\t\t_sourceCommonArgs\n\t\t}\n\t}\n}\n\noutputs:
release: {\n\tapiVersion: \"helm.toolkit.fluxcd.io/v2beta1\"\n\tkind:
\ \"HelmRelease\"\n\tmetadata: {\n\t\tname: context.name\n\t}\n\tspec:
{\n\t\tinterval: parameter.pullInterval\n\t\tchart: {\n\t\t\tspec: {\n\t\t\t\tchart:
\ parameter.chart\n\t\t\t\tversion: parameter.version\n\t\t\t\tsourceRef:
{\n\t\t\t\t\tif parameter.repoType == \"git\" {\n\t\t\t\t\t\tkind: \"GitRepository\"\n\t\t\t\t\t}\n\t\t\t\t\tif
parameter.repoType == \"helm\" {\n\t\t\t\t\t\tkind: \"HelmRepository\"\n\t\t\t\t\t}\n\t\t\t\t\tif
parameter.repoType == \"oss\" {\n\t\t\t\t\t\tkind: \"Bucket\"\n\t\t\t\t\t}\n\t\t\t\t\tname:
\ context.name\n\t\t\t\t\tnamespace: context.namespace\n\t\t\t\t}\n\t\t\t\tinterval:
parameter.pullInterval\n\t\t\t}\n\t\t}\n\t\tif parameter.targetNamespace
!= _|_ {\n\t\t\ttargetNamespace: parameter.targetNamespace\n\t\t}\n\t\tif
parameter.releaseName != _|_ {\n\t\t\treleaseName: parameter.releaseName\n\t\t}\n\t\tif
parameter.values != _|_ {\n\t\t\tvalues: parameter.values\n\t\t}\n\t}\n}\n\n_secret:
{\n\tif parameter.secretRef != _|_ {\n\t\tsecretRef: {\n\t\t\tname:
parameter.secretRef\n\t\t}\n\t}\n}\n\n_sourceCommonArgs: {\n\tinterval:
parameter.pullInterval\n\tif parameter.timeout != _|_ {\n\t\ttimeout:
parameter.timeout\n\t}\n}\n\nparameter: {\n\trepoType: *\"helm\" | \"git\"
| \"oss\"\n\t// +usage=The interval at which to check for repository/bucket
and relese updates, default to 5m\n\tpullInterval: *\"5m\" | string\n\t//
+usage=The Git or Helm repository URL, OSS endpoint, accept HTTP/S or
SSH address as git url,\n\turl: string\n\t// +usage=The name of the
secret containing authentication credentials\n\tsecretRef?: string\n\t//
+usage=The timeout for operations like download index/clone repository,
optional\n\ttimeout?: string\n\n\tgit?: {\n\t\t// +usage=The Git reference
to checkout and monitor for changes, defaults to master branch\n\t\tbranch:
string\n\t}\n\toss?: {\n\t\t// +usage=The bucket's name, required if
repoType is oss\n\t\tbucketName: string\n\t\t// +usage=\"generic\" for
Minio, Amazon S3, Google Cloud Storage, Alibaba Cloud OSS, \"aws\" for
retrieve credentials from the EC2 service when credentials not specified,
default \"generic\"\n\t\tprovider: *\"generic\" | \"aws\"\n\t\t// +usage=The
bucket region, optional\n\t\tregion?: string\n\t}\n\n\t// +usage=1.The
relative path to helm chart for git/oss source. 2. chart name for helm
resource 3. relative path for chart package(e.g. ./charts/podinfo-1.2.3.tgz)\n\tchart:
string\n\t// +usage=Chart version\n\tversion: *\"*\" | string\n\t//
+usage=The namespace for helm chart, optional\n\ttargetNamespace?: string\n\t//
+usage=The release name\n\treleaseName?: string\n\t// +usage=Chart values\n\tvalues?:
#nestedmap\n}\n\n#nestedmap: {\n\t...\n}\n"
status:
healthPolicy: 'isHealth: len(context.outputs.release.status.conditions)
!= 0 && context.outputs.release.status.conditions[0]["status"]=="True"'
workload:
type: autodetects.core.oam.dev

View File

@@ -0,0 +1,23 @@
name: example-legacy
version: 1.0.1
description: Extended workload to do continuous and progressive delivery
icon: https://raw.githubusercontent.com/fluxcd/flux/master/docs/_files/weave-flux.png
url: https://fluxcd.io
tags:
- extended_workload
- gitops
- only_example
deployTo:
control_plane: true
runtime_cluster: false
dependencies: []
#- name: addon_name
# set invisible means this won't be list and will be enabled when depended on
# for example, terraform-alibaba depends on terraform which is invisible,
# when terraform-alibaba is enabled, terraform will be enabled automatically
# default: false
invisible: false

View File

@@ -0,0 +1,19 @@
# Example FluxCD Addon
This is an example addon based [FluxCD](https://fluxcd.io/)
## Directory Structure
- `template.yaml`: contains the basic app, you can add some component and workflow to meet your requirements. Other files
in `resources/` and `definitions/` will be rendered as Components and appended in `spec.components`
- `metadata.yaml`: contains addon metadata information.
- `definitions/`: contains the X-Definition yaml/cue files. These file will be rendered as KubeVela Component in `template.yaml`
- `resources/`:
- `parameter.cue` to expose parameters. It will be converted to JSON schema and rendered in UI forms.
- All other files will be rendered as KubeVela Components. It can be one of the two types:
- YAML file that contains only one resource. This will be rendered as a `raw` component
- CUE template file that can read user input as `parameter.XXX` as defined `parameter.cue`.
Basically the CUE template file will be combined with `parameter.cue` to render a resource.
**You can specify the type and trait in this format**

View File

@@ -0,0 +1,15 @@
output: {
type: "raw"
properties: {
apiVersion: "v1"
kind: "ConfigMap"
metadata: {
name: "exampleinput"
namespace: "default"
labels: {
version: context.metadata.version
}
}
data: input: parameter.example
}
}

View File

View File

@@ -0,0 +1,17 @@
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/instance: flux-system
control-plane: controller
name: source-controller
namespace: example-system
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: http
selector:
app: source-controller
type: ClusterIP

View File

@@ -1,7 +1,7 @@
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: example
name: example-legacy
namespace: vela-system
spec:
workflow:

View File

@@ -0,0 +1,3 @@
parameter: {
example: string
}

27
pkg/addon/testdata/example/template.cue vendored Normal file
View File

@@ -0,0 +1,27 @@
output: {
apiVersion: "core.oam.dev/v1beta1"
kind: "Application"
metadata: {
name: "example"
namespace: "vela-system"
}
spec: {
workflow: steps: [{
name: "apply-ns"
type: "apply-component"
properties: component: "ns-example-system"
}, {
name: "apply-resources"
type: "apply-remaining"
}]
components: [{
name: "ns-example-system"
type: "raw"
properties: {
apiVersion: "v1"
kind: "Namespace"
metadata: name: "example-system"
}
}]
}
}