Compare commits

...

7 Commits

Author SHA1 Message Date
wyike
47050c90b6 Fix: cli addon multicluster bug (#3547)
* fix addon multicluster bug

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>

delete useless test

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>

asd

asd

* fix test race condition

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
2022-03-29 23:03:57 +08:00
barnettZQG
685d73a20c Feat: support with metadata as context render cue file in the addon (#3549)
Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
2022-03-29 22:13:56 +08:00
Somefive
dde8a8e4c0 Fix: addon support empty components (#3542)
Signed-off-by: Somefive <yd219913@alibaba-inc.com>
2022-03-29 16:42:43 +08:00
Min Kim
943af3ddf6 bump cluster-gateway to v1.3.0 (#3540)
Signed-off-by: yue9944882 <291271447@qq.com>
2022-03-29 16:18:38 +08:00
Somefive
a5c2edf777 Feat: allow select resource by component name for gc policy (#3539)
Signed-off-by: Somefive <yd219913@alibaba-inc.com>
2022-03-29 14:00:50 +08:00
wyike
83d8022ce9 quick fix addon registry (#3537)
Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
2022-03-29 12:40:25 +08:00
Zheng Xi Zhou
6d63014c6f Feat: support config management (#3430)
* Feat: componentDefinitions for Config management

Added ComponentDefinitions for config management
- helm chart repository
- image registry
- Dex connector
- Terraform provider

Co-authored-by: Tianxin Dong <wuwuglu19@gmail.com>
Signed-off-by: Zheng Xi Zhou <zzxwill@gmail.com>

* Update vela-templates/definitions/internal/component/config-image-registry.cue

Co-authored-by: Jianbo Sun <wonderflow@icloud.com>

* address comments again

Signed-off-by: Zheng Xi Zhou <zzxwill@gmail.com>

Co-authored-by: Tianxin Dong <wuwuglu19@gmail.com>
Co-authored-by: Jianbo Sun <wonderflow@icloud.com>
2022-03-29 10:28:07 +08:00
29 changed files with 746 additions and 67 deletions

View File

@@ -46,10 +46,11 @@ type GarbageCollectPolicyRule struct {
// GarbageCollectPolicyRuleSelector select the targets of the rule
// if both traitTypes and componentTypes are specified, combination logic is OR
// if one resources are specified with conflict strategy, strategy as component go first.
// if one resource is specified with conflict strategies, strategy as component go first.
type GarbageCollectPolicyRuleSelector struct {
TraitTypes []string `json:"traitTypes"`
CompNames []string `json:"componentNames"`
CompTypes []string `json:"componentTypes"`
TraitTypes []string `json:"traitTypes"`
}
// GarbageCollectStrategy the strategy for target resource to recycle
@@ -68,27 +69,22 @@ const (
// FindStrategy find gc strategy for target resource
func (in GarbageCollectPolicySpec) FindStrategy(manifest *unstructured.Unstructured) *GarbageCollectStrategy {
for _, rule := range in.Rules {
var (
compType string
traitType string
)
if manifest.GetLabels() != nil {
traitType = manifest.GetLabels()[oam.TraitTypeLabel]
compType = manifest.GetLabels()[oam.WorkloadTypeLabel]
var compName, compType, traitType string
if labels := manifest.GetLabels(); labels != nil {
compName = labels[oam.LabelAppComponent]
compType = labels[oam.WorkloadTypeLabel]
traitType = labels[oam.TraitTypeLabel]
}
if compType != "" {
for _, _compType := range rule.Selector.CompTypes {
if _compType == compType {
return &rule.Strategy
}
match := func(src []string, val string) (found bool) {
for _, _val := range src {
found = found || _val == val
}
return val != "" && found
}
if traitType != "" {
for _, _traitType := range rule.Selector.TraitTypes {
if _traitType == traitType {
return &rule.Strategy
}
}
if match(rule.Selector.CompNames, compName) ||
match(rule.Selector.CompTypes, compType) ||
match(rule.Selector.TraitTypes, traitType) {
return &rule.Strategy
}
}
return nil

View File

@@ -32,7 +32,7 @@ func TestGarbageCollectPolicySpec_FindStrategy(t *testing.T) {
notFound bool
expectStrategy GarbageCollectStrategy
}{
"trait rule match": {
"trait type rule match": {
rules: []GarbageCollectPolicyRule{{
Selector: GarbageCollectPolicyRuleSelector{TraitTypes: []string{"a"}},
Strategy: GarbageCollectStrategyNever,
@@ -44,7 +44,7 @@ func TestGarbageCollectPolicySpec_FindStrategy(t *testing.T) {
}},
expectStrategy: GarbageCollectStrategyNever,
},
"trait rule mismatch": {
"trait type rule mismatch": {
rules: []GarbageCollectPolicyRule{{
Selector: GarbageCollectPolicyRuleSelector{TraitTypes: []string{"a"}},
Strategy: GarbageCollectStrategyNever,
@@ -52,7 +52,7 @@ func TestGarbageCollectPolicySpec_FindStrategy(t *testing.T) {
input: &unstructured.Unstructured{Object: map[string]interface{}{}},
notFound: true,
},
"trait rule multiple match": {
"trait type rule multiple match": {
rules: []GarbageCollectPolicyRule{{
Selector: GarbageCollectPolicyRuleSelector{TraitTypes: []string{"a"}},
Strategy: GarbageCollectStrategyOnAppDelete,
@@ -67,7 +67,7 @@ func TestGarbageCollectPolicySpec_FindStrategy(t *testing.T) {
}},
expectStrategy: GarbageCollectStrategyOnAppDelete,
},
"component rule match": {
"component type rule match": {
rules: []GarbageCollectPolicyRule{{
Selector: GarbageCollectPolicyRuleSelector{CompTypes: []string{"comp"}},
Strategy: GarbageCollectStrategyNever,
@@ -79,7 +79,7 @@ func TestGarbageCollectPolicySpec_FindStrategy(t *testing.T) {
}},
expectStrategy: GarbageCollectStrategyNever,
},
"rule match both component and trait, component first": {
"rule match both component type and trait type, component type first": {
rules: []GarbageCollectPolicyRule{
{
Selector: GarbageCollectPolicyRuleSelector{CompTypes: []string{"comp"}},
@@ -97,6 +97,18 @@ func TestGarbageCollectPolicySpec_FindStrategy(t *testing.T) {
}},
expectStrategy: GarbageCollectStrategyNever,
},
"component name rule match": {
rules: []GarbageCollectPolicyRule{{
Selector: GarbageCollectPolicyRuleSelector{CompNames: []string{"comp-name"}},
Strategy: GarbageCollectStrategyNever,
}},
input: &unstructured.Unstructured{Object: map[string]interface{}{
"metadata": map[string]interface{}{
"labels": map[string]interface{}{oam.LabelAppComponent: "comp-name"},
},
}},
expectStrategy: GarbageCollectStrategyNever,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {

View File

@@ -281,8 +281,8 @@ func (in *GarbageCollectPolicyRule) DeepCopy() *GarbageCollectPolicyRule {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *GarbageCollectPolicyRuleSelector) DeepCopyInto(out *GarbageCollectPolicyRuleSelector) {
*out = *in
if in.TraitTypes != nil {
in, out := &in.TraitTypes, &out.TraitTypes
if in.CompNames != nil {
in, out := &in.CompNames, &out.CompNames
*out = make([]string, len(*in))
copy(*out, *in)
}
@@ -291,6 +291,11 @@ func (in *GarbageCollectPolicyRuleSelector) DeepCopyInto(out *GarbageCollectPoli
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.TraitTypes != nil {
in, out := &in.TraitTypes, &out.TraitTypes
*out = make([]string, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GarbageCollectPolicyRuleSelector.

View File

@@ -86,7 +86,7 @@ helm install --create-namespace -n vela-system kubevela kubevela/vela-core --wai
| `multicluster.clusterGateway.replicaCount` | ClusterGateway replica count | `1` |
| `multicluster.clusterGateway.port` | ClusterGateway port | `9443` |
| `multicluster.clusterGateway.image.repository` | ClusterGateway image repository | `oamdev/cluster-gateway` |
| `multicluster.clusterGateway.image.tag` | ClusterGateway image tag | `v1.1.7` |
| `multicluster.clusterGateway.image.tag` | ClusterGateway image tag | `v1.3.0` |
| `multicluster.clusterGateway.image.pullPolicy` | ClusterGateway image pull policy | `IfNotPresent` |
| `multicluster.clusterGateway.resources.limits.cpu` | ClusterGateway cpu limit | `100m` |
| `multicluster.clusterGateway.resources.limits.memory` | ClusterGateway memory limit | `200Mi` |

View File

@@ -8,7 +8,7 @@ data:
"KubeVela":{
"name": "KubeVela",
"helm": {
"url": "https://addons.kubevela.net",
"url": "https://addons.kubevela.net"
}
}
}'

View File

@@ -0,0 +1,70 @@
# Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file.
# Definition source cue file: vela-templates/definitions/internal/config-dex-connector.cue
apiVersion: core.oam.dev/v1beta1
kind: ComponentDefinition
metadata:
annotations:
custom.definition.oam.dev/alias.config.oam.dev: Dex Connector
definition.oam.dev/description: Config information to authenticate Dex connectors
labels:
custom.definition.oam.dev/catalog.config.oam.dev: velacore-config
custom.definition.oam.dev/multi-cluster.config.oam.dev: "false"
custom.definition.oam.dev/type.config.oam.dev: dex-connector
name: config-dex-connector
namespace: {{ include "systemDefinitionNamespace" . }}
spec:
schematic:
cue:
template: |
output: {
apiVersion: "v1"
kind: "Secret"
metadata: {
name: parameter.name
namespace: context.namespace
labels: {
"config.oam.dev/catalog": "velacore-config"
"config.oam.dev/type": "dex-connector"
"config.oam.dev/multi-cluster": "false"
"config.oam.dev/identifier": parameter.name
"config.oam.dev/sub-type": parameter.type
}
}
type: "Opaque"
if parameter.type == "github" {
stringData: parameter.github
}
if parameter.type == "ldap" {
stringData: parameter.ldap
}
}
parameter: {
// +usage=Config type
type: "github" | "ldap"
github?: {
// +usage=GitHub client ID
clientID: string
// +usage=GitHub client secret
clientSecret: string
// +usage=GitHub call back URL
callbackURL: string
}
ldap?: {
host: string
insecureNoSSL: *true | bool
insecureSkipVerify: bool
startTLS: bool
usernamePrompt: string
userSearch: {
baseDN: string
username: string
idAttr: string
emailAttr: string
nameAttr: string
}
}
}
workload:
type: autodetects.core.oam.dev

View File

@@ -0,0 +1,69 @@
# Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file.
# Definition source cue file: vela-templates/definitions/internal/config-image-registry.cue
apiVersion: core.oam.dev/v1beta1
kind: ComponentDefinition
metadata:
annotations:
custom.definition.oam.dev/alias.config.oam.dev: Image Registry
definition.oam.dev/description: Config information to authenticate image registry
labels:
custom.definition.oam.dev/catalog.config.oam.dev: velacore-config
custom.definition.oam.dev/multi-cluster.config.oam.dev: "true"
custom.definition.oam.dev/type.config.oam.dev: image-registry
name: config-image-registry
namespace: {{ include "systemDefinitionNamespace" . }}
spec:
schematic:
cue:
template: |
import (
"encoding/base64"
"encoding/json"
)
output: {
apiVersion: "v1"
kind: "Secret"
metadata: {
name: context.name
namespace: context.namespace
labels: {
"config.oam.dev/catalog": "velacore-config"
"config.oam.dev/type": "image-registry"
"config.oam.dev/multi-cluster": "true"
"config.oam.dev/identifier": parameter.registry
"config.oam.dev/sub-type": "auth"
}
}
type: "kubernetes.io/dockerconfigjson"
stringData: {
if parameter.auth != _|_ {
".dockerconfigjson": json.Marshal({
auths: "\(parameter.registry)": {
username: parameter.auth.username
password: parameter.auth.password
if parameter.auth.email != _|_ {
email: parameter.auth.email
}
auth: base64.Encode(null, (parameter.auth.username + ":" + parameter.auth.password))
}
})
}
}
}
parameter: {
// +usage=Image registry FQDN
registry: string
// +usage=Authenticate the image registry
auth?: {
// +usage=Private Image registry username
username: string
// +usage=Private Image registry password
password: string
// +usage=Private Image registry email
email?: string
}
}
workload:
type: autodetects.core.oam.dev

View File

@@ -104,7 +104,7 @@ multicluster:
port: 9443
image:
repository: oamdev/cluster-gateway
tag: v1.1.7
tag: v1.3.0
pullPolicy: IfNotPresent
resources:
limits:

View File

@@ -105,7 +105,7 @@ helm install --create-namespace -n vela-system kubevela kubevela/vela-minimal --
| `multicluster.clusterGateway.replicaCount` | ClusterGateway replica count | `1` |
| `multicluster.clusterGateway.port` | ClusterGateway port | `9443` |
| `multicluster.clusterGateway.image.repository` | ClusterGateway image repository | `oamdev/cluster-gateway` |
| `multicluster.clusterGateway.image.tag` | ClusterGateway image tag | `v1.1.7` |
| `multicluster.clusterGateway.image.tag` | ClusterGateway image tag | `v1.3.0` |
| `multicluster.clusterGateway.image.pullPolicy` | ClusterGateway image pull policy | `IfNotPresent` |
| `multicluster.clusterGateway.resources.limits.cpu` | ClusterGateway cpu limit | `100m` |
| `multicluster.clusterGateway.resources.limits.memory` | ClusterGateway memory limit | `200Mi` |

View File

@@ -0,0 +1,70 @@
# Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file.
# Definition source cue file: vela-templates/definitions/internal/config-dex-connector.cue
apiVersion: core.oam.dev/v1beta1
kind: ComponentDefinition
metadata:
annotations:
custom.definition.oam.dev/alias.config.oam.dev: Dex Connector
definition.oam.dev/description: Config information to authenticate Dex connectors
labels:
custom.definition.oam.dev/catalog.config.oam.dev: velacore-config
custom.definition.oam.dev/multi-cluster.config.oam.dev: "false"
custom.definition.oam.dev/type.config.oam.dev: dex-connector
name: config-dex-connector
namespace: {{ include "systemDefinitionNamespace" . }}
spec:
schematic:
cue:
template: |
output: {
apiVersion: "v1"
kind: "Secret"
metadata: {
name: parameter.name
namespace: context.namespace
labels: {
"config.oam.dev/catalog": "velacore-config"
"config.oam.dev/type": "dex-connector"
"config.oam.dev/multi-cluster": "false"
"config.oam.dev/identifier": parameter.name
"config.oam.dev/sub-type": parameter.type
}
}
type: "Opaque"
if parameter.type == "github" {
stringData: parameter.github
}
if parameter.type == "ldap" {
stringData: parameter.ldap
}
}
parameter: {
// +usage=Config type
type: "github" | "ldap"
github?: {
// +usage=GitHub client ID
clientID: string
// +usage=GitHub client secret
clientSecret: string
// +usage=GitHub call back URL
callbackURL: string
}
ldap?: {
host: string
insecureNoSSL: *true | bool
insecureSkipVerify: bool
startTLS: bool
usernamePrompt: string
userSearch: {
baseDN: string
username: string
idAttr: string
emailAttr: string
nameAttr: string
}
}
}
workload:
type: autodetects.core.oam.dev

View File

@@ -0,0 +1,69 @@
# Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file.
# Definition source cue file: vela-templates/definitions/internal/config-image-registry.cue
apiVersion: core.oam.dev/v1beta1
kind: ComponentDefinition
metadata:
annotations:
custom.definition.oam.dev/alias.config.oam.dev: Image Registry
definition.oam.dev/description: Config information to authenticate image registry
labels:
custom.definition.oam.dev/catalog.config.oam.dev: velacore-config
custom.definition.oam.dev/multi-cluster.config.oam.dev: "true"
custom.definition.oam.dev/type.config.oam.dev: image-registry
name: config-image-registry
namespace: {{ include "systemDefinitionNamespace" . }}
spec:
schematic:
cue:
template: |
import (
"encoding/base64"
"encoding/json"
)
output: {
apiVersion: "v1"
kind: "Secret"
metadata: {
name: context.name
namespace: context.namespace
labels: {
"config.oam.dev/catalog": "velacore-config"
"config.oam.dev/type": "image-registry"
"config.oam.dev/multi-cluster": "true"
"config.oam.dev/identifier": parameter.registry
"config.oam.dev/sub-type": "auth"
}
}
type: "kubernetes.io/dockerconfigjson"
stringData: {
if parameter.auth != _|_ {
".dockerconfigjson": json.Marshal({
auths: "\(parameter.registry)": {
username: parameter.auth.username
password: parameter.auth.password
if parameter.auth.email != _|_ {
email: parameter.auth.email
}
auth: base64.Encode(null, (parameter.auth.username + ":" + parameter.auth.password))
}
})
}
}
}
parameter: {
// +usage=Image registry FQDN
registry: string
// +usage=Authenticate the image registry
auth?: {
// +usage=Private Image registry username
username: string
// +usage=Private Image registry password
password: string
// +usage=Private Image registry email
email?: string
}
}
workload:
type: autodetects.core.oam.dev

View File

@@ -107,7 +107,7 @@ multicluster:
port: 9443
image:
repository: oamdev/cluster-gateway
tag: v1.1.7
tag: v1.3.0
pullPolicy: IfNotPresent
resources:
limits:

View File

@@ -95,7 +95,32 @@ spec:
properties:
rules:
- selector:
componentTypes:
- webservice
strategy: never
componentTypes:
- webservice
strategy: never
```
A more straightforward way is to specify `compNames` to match specified components.
```yaml
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: create-ns-app
spec:
components:
- name: example-addon-namespace
type: k8s-objects
properties:
objects:
- apiVersion: v1
kind: Namespace
policies:
- name: garbage-collect
type: garbage-collect
properties:
rules:
- selector:
componentNames:
- example-addon-namespace
strategy: never
```

View File

@@ -0,0 +1,15 @@
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: config-dex-connector-dev
namespace: vela-system
spec:
components:
- name: dev
type: config-dex-connector
properties:
type: github
github:
clientID: "aa"
clientSecret: "bb"
callbackURL: "http://localhost:8080/callback"

View File

@@ -0,0 +1,102 @@
# How to store and use configurations
## General
- list all configuration types
```shell
$ vela components --label custom.definition.oam.dev/catalog.config.oam.dev=velacore-config
NAME DEFINITION
config-dex-connector autodetects.core.oam.dev
config-helm-repository autodetects.core.oam.dev
config-image-registry autodetects.core.oam.dev
terraform-azure autodetects.core.oam.dev
terraform-baidu autodetects.core.oam.dev
```
```json
# Get http://127.0.0.1:8000/api/v1/configs
[
{
"definitions": [
"config-dex-connector"
],
"name": "Dex Connectors",
"type": "dex-connector"
},
{
"definitions": [
"config-helm-repository"
],
"name": "Helm Repository",
"type": "helm-repository"
},
{
"definitions": [
"config-image-registry"
],
"name": "Image Registry",
"type": "image-registry"
},
null,
{
"definitions": [
"terraform-baidu"
],
"name": "Terraform Cloud Provider",
"type": "terraform-provider"
}
]
```
- list all configurations
```shell
$ kubectl get secret -n vela-system -l=config.oam.dev/catalog=velacore-config
NAME TYPE DATA AGE
image-registry-dev kubernetes.io/dockerconfigjson 1 3h51m
```
## Image registry
- Create a config for an image registry
```shell
$ vela up -f app-config-image-registry-account-auth.yaml
Applying an application in vela K8s object format...
I0323 10:45:25.347102 85930 apply.go:107] "creating object" name="config-image-registry-account-auth-dev" resource="core.oam.dev/v1beta1, Kind=Application"
✅ App has been deployed 🚀🚀🚀
Port forward: vela port-forward config-image-registry-account-auth-dev
SSH: vela exec config-image-registry-account-auth-dev
Logging: vela logs config-image-registry-account-auth-dev
App status: vela status config-image-registry-account-auth-dev
Endpoint: vela status config-image-registry-account-auth-dev
--endpoint%
$ kubectl get secret -n vela-system -l=config.oam.dev/catalog=velacore-config
NAME TYPE DATA AGE
image-registry-dev kubernetes.io/dockerconfigjson 1 77s
```
- Deliver the config secret to working cluster
```shell
$ vela cluster list
CLUSTER TYPE ENDPOINT ACCEPTED LABELS
local Internal - true
bj X509Certificate https://123.57.73.107:6443 true
$ vela up -f app-deliever-secret.yaml
```
- Deploy an application who needs to pull images from the private image registry
```shell
$ export KUBECONFIG=~/.kube/config-bj
$ kubectl get secret -n vela-system -l=config.oam.dev/catalog=velacore-config
NAME TYPE DATA AGE
image-registry-dev kubernetes.io/dockerconfigjson 1 120s
$ vela up -f app-validate-imagePullSecret.yaml
```

View File

@@ -0,0 +1,15 @@
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: config-image-registry-account-auth-dev
namespace: vela-system
spec:
components:
- name: account-auth
type: config-image-registry
properties:
registry: "registry.cn-beijing.aliyuncs.com"
auth:
username: "xxx"
password: "PfwrjwifjFaked"
email: "a@gmail.com"

View File

@@ -0,0 +1,23 @@
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: config-project1
namespace: vela-system
labels:
config.oam.dev/catalog: "velacore-config"
config.oam.dev/type: "helm-repository"
spec:
components:
- name: deliver-secret
type: ref-objects
properties:
objects:
- apiVersion: v1
kind: Secret
name: image-registry-dev
policies:
- type: topology
name: dev
properties:
clusters: ["bj"]
# namespaces: ["ns1"]

View File

@@ -0,0 +1,14 @@
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: app-sample
namespace: ns1
spec:
components:
- name: sample
type: webservice
properties:
image: registry.cn-beijing.aliyuncs.com/vela/nginx:latest
imagePullPolicy: Always
imagePullSecrets:
- image-registry-dev

View File

@@ -0,0 +1,14 @@
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: app-validate-image-pull-secret
namespace: vela-system
spec:
components:
- name: validate
type: webservice
properties:
image: registry.cn-beijing.aliyuncs.com/vela/nginx:latest
imagePullPolicy: Always
imagePullSecrets:
- image-registry-dev

View File

@@ -22,6 +22,8 @@ import (
"strings"
"time"
v1 "k8s.io/api/core/v1"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
apierrors "k8s.io/apimachinery/pkg/api/errors"
@@ -62,6 +64,7 @@ var _ = Describe("Addon Test", func() {
Expect(output).To(ContainSubstring("Successfully disable addon"))
Eventually(func(g Gomega) {
g.Expect(apierrors.IsNotFound(k8sClient.Get(context.Background(), types.NamespacedName{Name: "addon-test-addon", Namespace: "vela-system"}, &v1beta1.Application{}))).Should(BeTrue())
g.Expect(apierrors.IsNotFound(k8sClient.Get(context.Background(), types.NamespacedName{Name: "addon-secret-test-addon", Namespace: "vela-system"}, &v1.Secret{}))).Should(BeTrue())
}, 60*time.Second).Should(Succeed())
})

View File

@@ -567,7 +567,7 @@ func renderResources(addon *InstallPackage, args map[string]interface{}) ([]comm
}
for _, tmpl := range addon.CUETemplates {
comp, err := renderCUETemplate(tmpl, addon.Parameters, args)
comp, err := renderCUETemplate(tmpl, addon.Parameters, args, addon.Meta)
if err != nil {
return nil, NewAddonError(fmt.Sprintf("fail to render cue template %s", err.Error()))
}
@@ -589,6 +589,9 @@ func formatAppFramework(addon *InstallPackage) *v1beta1.Application {
},
}
}
if app.Spec.Components == nil {
app.Spec.Components = []common2.ApplicationComponent{}
}
app.Name = Convert2AppName(addon.Name)
// force override the namespace defined vela with DefaultVelaNS,this value can be modified by Env
app.SetNamespace(types.DefaultKubeVelaNS)
@@ -929,17 +932,28 @@ func renderSchemaConfigmap(elem ElementFile) (*unstructured.Unstructured, error)
}
// renderCUETemplate will return a component from cue template
func renderCUETemplate(elem ElementFile, parameters string, args map[string]interface{}) (*common2.ApplicationComponent, error) {
func renderCUETemplate(elem ElementFile, parameters string, args map[string]interface{}, metadata Meta) (*common2.ApplicationComponent, error) {
bt, err := json.Marshal(args)
if err != nil {
return nil, err
}
var contextFile = strings.Builder{}
var paramFile = cuemodel.ParameterFieldName + ": {}"
if string(bt) != "null" {
paramFile = fmt.Sprintf("%s: %s", cuemodel.ParameterFieldName, string(bt))
}
param := fmt.Sprintf("%s\n%s", paramFile, parameters)
v, err := value.NewValue(param, nil, "")
// addon metadata context
metadataJSON, err := json.Marshal(metadata)
if err != nil {
return nil, err
}
contextFile.WriteString(fmt.Sprintf("context: metadata: %s\n", string(metadataJSON)))
// parameter definition
contextFile.WriteString(paramFile + "\n")
// user custom parameter
contextFile.WriteString(parameters + "\n")
v, err := value.NewValue(contextFile.String(), nil, "")
if err != nil {
return nil, err
}

View File

@@ -880,3 +880,20 @@ func TestReadDefFile(t *testing.T) {
// verify
assert.True(t, len(uiData.Definitions) == 1)
}
func TestRenderCUETemplate(t *testing.T) {
fileDate, err := os.ReadFile("./testdata/example/resources/configmap.cue")
assert.NoError(t, err)
component, err := renderCUETemplate(ElementFile{Data: string(fileDate), Name: "configmap.cue"}, "{\"example\": \"\"}", map[string]interface{}{
"example": "render",
}, Meta{
Version: "1.0.1",
})
assert.NoError(t, err)
assert.True(t, component.Type == "raw")
var config = make(map[string]interface{})
err = json.Unmarshal(component.Properties.Raw, &config)
assert.NoError(t, err)
assert.True(t, component.Type == "raw")
assert.True(t, config["metadata"].(map[string]interface{})["labels"].(map[string]interface{})["version"] == "1.0.1")
}

View File

@@ -1,5 +1,5 @@
name: example
version: 1.0.0
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

View File

@@ -6,6 +6,9 @@ output: {
metadata: {
name: "exampleinput"
namespace: "default"
labels: {
version: context.metadata.version
}
}
data: input: parameter.example
}

View File

@@ -59,7 +59,7 @@ func returns500(b *restful.RouteBuilder) {
b.Returns(http.StatusInternalServerError, "Bummer, something went wrong", nil)
}
// Init init all webservice, pass in the required parameter object.
// Init inits all webservice, pass in the required parameter object.
// It can be implemented using the idea of dependency injection.
func Init(ctx context.Context, ds datastore.DataStore, addonCacheTime time.Duration, initDatabase bool) map[string]interface{} {
clusterUsecase := usecase.NewClusterUsecase(ds)

View File

@@ -73,6 +73,8 @@ const (
var forceDisable bool
var addonVersion string
var addonClusters string
// NewAddonCommand create `addon` command
func NewAddonCommand(c common.Args, order string, ioStreams cmdutil.IOStreams) *cobra.Command {
cmd := &cobra.Command{
@@ -140,6 +142,7 @@ Enable addon for specific clusters, (local means control plane):
if err != nil {
return err
}
addonArgs[types.ClustersArg] = transClusters(addonClusters)
config, err := c.GetConfig()
if err != nil {
return err
@@ -182,6 +185,7 @@ Enable addon for specific clusters, (local means control plane):
}
cmd.Flags().StringVarP(&addonVersion, "version", "v", "", "specify the addon version to enable")
cmd.Flags().StringVarP(&addonClusters, types.ClustersArg, "c", "", "specify the runtime-clusters to enable")
return cmd
}
@@ -239,6 +243,7 @@ Upgrade addon for specific clusters, (local means control plane):
if err != nil {
return err
}
addonArgs[types.ClustersArg] = transClusters(addonClusters)
addonOrDir := args[0]
var name string
if file, err := os.Stat(addonOrDir); err == nil {
@@ -287,22 +292,6 @@ func parseAddonArgsToMap(args []string) (map[string]interface{}, error) {
return nil, err
}
}
clusters, ok := res[types.ClustersArg]
if ok {
var clusterL []string
clusterList, ok := clusters.([]interface{})
if !ok {
return nil, errors.Errorf("you must specify string list for --clusters instead of %v", clusters)
}
for _, v := range clusterList {
val, strOk := v.(string)
if !strOk {
return nil, errors.Errorf("only string allowed in parameter --clusters list instead of %v", v)
}
clusterL = append(clusterL, strings.TrimSpace(val))
}
res[types.ClustersArg] = clusterL
}
return res, nil
}
@@ -555,6 +544,16 @@ func hasAddon(addons []*pkgaddon.UIData, name string) bool {
return false
}
func transClusters(cstr string) []string {
cstr = strings.TrimPrefix(strings.TrimSuffix(cstr, "}"), "{")
var clusterL []string
clusterList := strings.Split(cstr, ",")
for _, v := range clusterList {
clusterL = append(clusterL, strings.TrimSpace(v))
}
return clusterL
}
// TODO(wangyike) addon can support multi-tenancy, an addon can be enabled multi times and will create many times
// func checkWhetherTerraformProviderExist(ctx context.Context, k8sClient client.Client, addonName string, args map[string]string) (string, bool, error) {
// _, providerName := getTerraformProviderArgumentValue(addonName, args)

View File

@@ -65,16 +65,6 @@ func TestParseMap(t *testing.T) {
},
nilError: true,
},
{
args: []string{"clusters={c1, c2, c3}", "image.tag=1.1"},
res: map[string]interface{}{
"clusters": []string{"c1", "c2", "c3"},
"image": map[string]interface{}{
"tag": "1.1",
},
},
nilError: true,
},
}
for _, s := range testcase {
r, err := parseAddonArgsToMap(s.args)
@@ -138,3 +128,26 @@ func TestAddonUpgradeCmdWithErrLocalPath(t *testing.T) {
assert.Error(t, err, s.errMsg)
}
}
func TestTransCluster(t *testing.T) {
testcase := []struct {
str string
res []string
}{
{
str: "{cluster1, cluster2}",
res: []string{"cluster1", "cluster2"},
},
{
str: "{cluster1,cluster2}",
res: []string{"cluster1", "cluster2"},
},
{
str: "{cluster1, cluster2 }",
res: []string{"cluster1", "cluster2"},
},
}
for _, s := range testcase {
assert.DeepEqual(t, transClusters(s.str), s.res)
}
}

View File

@@ -0,0 +1,66 @@
"config-dex-connector": {
type: "component"
annotations: {
"alias.config.oam.dev": "Dex Connector"
}
labels: {
"catalog.config.oam.dev": "velacore-config"
"type.config.oam.dev": "dex-connector"
"multi-cluster.config.oam.dev": "false"
}
description: "Config information to authenticate Dex connectors"
attributes: workload: type: "autodetects.core.oam.dev"
}
template: {
output: {
apiVersion: "v1"
kind: "Secret"
metadata: {
name: parameter.name
namespace: context.namespace
labels: {
"config.oam.dev/catalog": "velacore-config"
"config.oam.dev/type": "dex-connector"
"config.oam.dev/multi-cluster": "false"
"config.oam.dev/identifier": parameter.name
"config.oam.dev/sub-type": parameter.type
}
}
type: "Opaque"
if parameter.type == "github" {
stringData: parameter.github
}
if parameter.type == "ldap" {
stringData: parameter.ldap
}
}
parameter: {
// +usage=Config type
type: "github" | "ldap"
github?: {
// +usage=GitHub client ID
clientID: string
// +usage=GitHub client secret
clientSecret: string
// +usage=GitHub call back URL
callbackURL: string
}
ldap?: {
host: string
insecureNoSSL: *true | bool
insecureSkipVerify: bool
startTLS: bool
usernamePrompt: string
userSearch: {
baseDN: string
username: string
idAttr: string
emailAttr: string
nameAttr: string
}
}
}
}

View File

@@ -0,0 +1,65 @@
import (
"encoding/base64"
"encoding/json"
)
"config-image-registry": {
type: "component"
annotations: {
"alias.config.oam.dev": "Image Registry"
}
labels: {
"catalog.config.oam.dev": "velacore-config"
"type.config.oam.dev": "image-registry"
"multi-cluster.config.oam.dev": "true"
}
description: "Config information to authenticate image registry"
attributes: workload: type: "autodetects.core.oam.dev"
}
template: {
output: {
apiVersion: "v1"
kind: "Secret"
metadata: {
name: context.name
namespace: context.namespace
labels: {
"config.oam.dev/catalog": "velacore-config"
"config.oam.dev/type": "image-registry"
"config.oam.dev/multi-cluster": "true"
"config.oam.dev/identifier": parameter.registry
"config.oam.dev/sub-type": "auth"
}
}
type: "kubernetes.io/dockerconfigjson"
stringData: {
if parameter.auth != _|_ {
".dockerconfigjson": json.Marshal({
"auths": "\(parameter.registry)": {
"username": parameter.auth.username
"password": parameter.auth.password
if parameter.auth.email != _|_ {
"email": parameter.auth.email
}
"auth": base64.Encode(null, (parameter.auth.username + ":" + parameter.auth.password))
}
})
}
}
}
parameter: {
// +usage=Image registry FQDN
registry: string
// +usage=Authenticate the image registry
auth?: {
// +usage=Private Image registry username
username: string
// +usage=Private Image registry password
password: string
// +usage=Private Image registry email
email?: string
}
}
}