Compare commits

...

17 Commits

Author SHA1 Message Date
github-actions[bot]
8c9d0ae314 Chore: refactor addon enable with package (#4468)
Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
(cherry picked from commit 702fa36621)

Co-authored-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
2022-07-26 15:23:33 +08:00
Somefive
b6c024c205 Feat: add featuregates to disallow url in ref-objects (#4466)
Signed-off-by: Somefive <yd219913@alibaba-inc.com>
2022-07-26 13:37:11 +08:00
github-actions[bot]
3ab0b503c5 Fix: docker file fail to build for vela cli (#4465)
Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
(cherry picked from commit 62fcb152e2)

Co-authored-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
2022-07-25 22:45:13 +08:00
github-actions[bot]
85e168fea7 Fix: The apply failure error is ignored when the workflow is executed (#4462)
Signed-off-by: yangsoon <songyang.song@alibaba-inc.com>
(cherry picked from commit b1d8e6c88b)

Co-authored-by: yangsoon <songyang.song@alibaba-inc.com>
2022-07-25 22:18:53 +08:00
github-actions[bot]
189d74e87b Feat: delete svc flag from vela delete cli cmd (#4463)
Signed-off-by: suxiang <704427617@qq.com>
(cherry picked from commit 8d1a87083d)

Co-authored-by: suxiang <704427617@qq.com>
2022-07-25 22:18:29 +08:00
github-actions[bot]
8ec0209026 Feat: support vela addon enable with package (#4458)
Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
(cherry picked from commit 863d6161b1)

Co-authored-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
2022-07-25 22:16:50 +08:00
github-actions[bot]
c3a7209fa7 [Backport release-1.5] Feat: make addon init use the latest CUE addon template (#4456)
* Feat: make addon init use the latest CUE addon template

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

* Refactor: simplify init cmd

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

* Feat: ignore metadata

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

* Feat: remove status

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

* do not marshal to application

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

* Feat: only look for output field

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

* Feat: use global constant

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

* Test: update tests according to changes

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

Co-authored-by: Charlie Chiang <charlie_c_0129@outlook.com>
2022-07-25 21:34:22 +08:00
github-actions[bot]
564cba9aac [Backport release-1.5] Feat: support outputs for addon (#4457)
* support outputs for addon

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
(cherry picked from commit 123b6b5648)

* fix comments

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

fix comments

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
(cherry picked from commit 56132504f7)

Co-authored-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
2022-07-25 21:34:04 +08:00
github-actions[bot]
f7c21df915 [Backport release-1.5] Feat: support to query the alias of the project member (#4454)
* Feat: support to query the alias of the project member

Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
(cherry picked from commit 1cc82e324a)

* Fix: optimize the e2e test case

Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
(cherry picked from commit a45be41240)

Co-authored-by: barnettZQG <barnett.zqg@gmail.com>
2022-07-25 21:30:46 +08:00
github-actions[bot]
0b9c7f66c0 Feat: adapt vela port-forward with the velaql (#4453)
Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
(cherry picked from commit 50f902e483)

Co-authored-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
2022-07-25 21:29:00 +08:00
github-actions[bot]
54867c50d8 Fix: the history applications are repeatedly synchronized (#4452)
Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
(cherry picked from commit de80fbcf40)

Co-authored-by: barnettZQG <barnett.zqg@gmail.com>
2022-07-25 21:26:59 +08:00
github-actions[bot]
16d7a4b4f4 Revert "Chore: upgrade kind in tests, fix flaky test (#4105)" (#4451)
This reverts commit 8aaf526877.

(cherry picked from commit 3f9529dd34)

Co-authored-by: Jianbo Sun <wonderflow@icloud.com>
2022-07-25 19:09:12 +08:00
github-actions[bot]
35ae4e5ef5 [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>
2022-07-25 17:02:32 +08:00
github-actions[bot]
853f44cf61 [Backport release-1.5] Fix: support to test authentication with dex (#4440)
* Fix: support to test login with dex

Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
(cherry picked from commit 8d0d20fd60)

* Fix: support to update the user when the login mode is dex

Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
(cherry picked from commit 442d4601e9)

* Fix: systemInfoService is nil

Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
(cherry picked from commit 392637e69d)

Co-authored-by: barnettZQG <barnett.zqg@gmail.com>
2022-07-25 15:53:22 +08:00
github-actions[bot]
ca2a90a097 [Backport release-1.5] Feat: support to init the roles of the user who login by dex (#4436)
* Feat: support to init the roles of the user which login by dex

Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
(cherry picked from commit 9a255cdbc8)

* Fix: add the comment

Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
(cherry picked from commit 538bf2a08b)

* Fix: make the sub string to lower

Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
(cherry picked from commit 01160614e8)

Co-authored-by: barnettZQG <barnett.zqg@gmail.com>
2022-07-25 11:14:06 +08:00
github-actions[bot]
d110e97d68 [Backport release-1.5] Chore: update description of policy/workflowstep definition (#4435)
* Chore: update description of policy definition

Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
(cherry picked from commit 679ee6d8cd)

* Fix: support workflow step generation for doc

Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
(cherry picked from commit 418cd20315)

* Chore: refactor package refereces/plugins to references/docgen

Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
(cherry picked from commit 346c3b8f2b)

* Chore: add examples of def docs for workflow step

Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
(cherry picked from commit ea5d9b9076)

* Feat: refine workflow description

Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
(cherry picked from commit bdfc66c65e)

* Chore: refine the workflow step definition

Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
(cherry picked from commit 249af896b3)

* Chore: update workflow step definition

Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
(cherry picked from commit 3aa5569dd5)

Co-authored-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
2022-07-25 10:48:00 +08:00
github-actions[bot]
741559c8e0 Feat: support vela def apply for directory (#4432)
Co-authored-by: ivyilike <pww123@cmbchina.com>

Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
(cherry picked from commit b62ebb05d4)

Co-authored-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
2022-07-24 16:37:04 +08:00
245 changed files with 2400 additions and 948 deletions

View File

@@ -19,9 +19,9 @@ env:
# Common versions
GO_VERSION: '1.17'
GOLANGCI_VERSION: 'v1.38'
KIND_VERSION: 'v0.14.0'
KIND_IMAGE_VERSION: '[\"v1.20.15\"]'
KIND_IMAGE_VERSIONS: '[\"v1.18.20\",\"v1.20.15\",\"v1.22.9\"]'
KIND_VERSION: 'v0.7.0'
KIND_IMAGE_VERSION: '[\"v1.20.7\"]'
KIND_IMAGE_VERSIONS: '[\"v1.18.20\",\"v1.20.7\",\"v1.22.7\"]'
jobs:

View File

@@ -17,9 +17,9 @@ env:
# Common versions
GO_VERSION: '1.17'
GOLANGCI_VERSION: 'v1.38'
KIND_VERSION: 'v0.14.0'
KIND_IMAGE_VERSION: '[\"v1.20.15\"]'
KIND_IMAGE_VERSIONS: '[\"v1.18.20\",\"v1.20.15\",\"v1.22.9\"]'
KIND_VERSION: 'v0.7.0'
KIND_IMAGE_VERSION: '[\"v1.20.7\"]'
KIND_IMAGE_VERSIONS: '[\"v1.18.20\",\"v1.20.7\",\"v1.22.7\"]'
jobs:

View File

@@ -17,9 +17,9 @@ env:
# Common versions
GO_VERSION: '1.17'
GOLANGCI_VERSION: 'v1.38'
KIND_VERSION: 'v0.14.0'
KIND_IMAGE_VERSION: '[\"v1.20.15\"]'
KIND_IMAGE_VERSIONS: '[\"v1.18.20\",\"v1.20.15\",\"v1.22.9\"]'
KIND_VERSION: 'v0.7.0'
KIND_IMAGE_VERSION: '[\"v1.20.7\"]'
KIND_IMAGE_VERSIONS: '[\"v1.18.20\",\"v1.20.7\",\"v1.22.7\"]'
jobs:

View File

@@ -17,9 +17,9 @@ env:
# Common versions
GO_VERSION: '1.17'
GOLANGCI_VERSION: 'v1.38'
KIND_VERSION: 'v0.14.0'
KIND_IMAGE_VERSION: '[\"v1.20.15\"]'
KIND_IMAGE_VERSIONS: '[\"v1.18.20\",\"v1.20.15\",\"v1.22.9\"]'
KIND_VERSION: 'v0.7.0'
KIND_IMAGE_VERSION: '[\"v1.20.7\"]'
KIND_IMAGE_VERSIONS: '[\"v1.18.20\",\"v1.20.7\",\"v1.22.7\"]'
jobs:

View File

@@ -15,7 +15,7 @@ env:
# Common versions
GO_VERSION: '1.17'
GOLANGCI_VERSION: 'v1.38'
KIND_VERSION: 'v0.14.0'
KIND_VERSION: 'v0.7.0'
jobs:

View File

@@ -15,7 +15,7 @@ env:
# Common versions
GO_VERSION: '1.17'
GOLANGCI_VERSION: 'v1.38'
KIND_VERSION: 'v0.14.0'
KIND_VERSION: 'v0.7.0'
jobs:

2
.gitignore vendored
View File

@@ -1,4 +1,4 @@
# Binaries for programs and plugins
# Binaries for programs and docgen
*.exe
*.exe~
*.dll

View File

@@ -39,5 +39,5 @@ RUN apk add --no-cache ca-certificates bash expat
WORKDIR /
ARG TARGETARCH
COPY --from=builder /workspace/vela-${TARGETARCH} /vela
ENTRYPOINT ["/vela"]
COPY --from=builder /workspace/vela-${TARGETARCH} /bin/vela
ENTRYPOINT ["/bin/vela"]

View File

@@ -6,6 +6,7 @@ metadata:
annotations:
definition.oam.dev/description: Apply components of an application in parallel for your workflow steps
labels:
custom.definition.oam.dev/deprecated: "true"
custom.definition.oam.dev/ui-hidden: "true"
name: apply-application-in-parallel
namespace: {{ include "systemDefinitionNamespace" . }}

View File

@@ -4,8 +4,9 @@ apiVersion: core.oam.dev/v1beta1
kind: WorkflowStepDefinition
metadata:
annotations:
definition.oam.dev/description: Apply application for your workflow steps
definition.oam.dev/description: Apply application for your workflow steps, it has no arguments, should be used for custom steps before or after application applied.
labels:
custom.definition.oam.dev/deprecated: "true"
custom.definition.oam.dev/ui-hidden: "true"
name: apply-application
namespace: {{ include "systemDefinitionNamespace" . }}

View File

@@ -22,9 +22,9 @@ spec:
cluster: parameter.cluster
}
parameter: {
// +usage=Specify the value of the object
// +usage=Specify Kubernetes native resource object to be applied
value: {...}
// +usage=Specify the cluster of the object
// +usage=The cluster you want to apply the resource to, default is the current control plane cluster
cluster: *"" | string
}

View File

@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
kind: PolicyDefinition
metadata:
annotations:
definition.oam.dev/description: Configure the behaviour of preventing configuration drift.
definition.oam.dev/description: Allow configuration drift for applied resources, delivery the resource without continuously reconciliation.
name: apply-once
namespace: {{ include "systemDefinitionNamespace" . }}
spec:

View File

@@ -6,6 +6,7 @@ metadata:
annotations:
definition.oam.dev/description: Apply remaining components and traits
labels:
custom.definition.oam.dev/deprecated: "true"
custom.definition.oam.dev/ui-hidden: "true"
name: apply-remaining
namespace: {{ include "systemDefinitionNamespace" . }}

View File

@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
kind: WorkflowStepDefinition
metadata:
annotations:
definition.oam.dev/description: check or install depends-on Application
definition.oam.dev/description: Wait for the specified Application to complete.
labels:
custom.definition.oam.dev/ui-hidden: "true"
name: depends-on-app

View File

@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
kind: WorkflowStepDefinition
metadata:
annotations:
definition.oam.dev/description: Deploy cloud resource and bind secret to clusters
definition.oam.dev/description: Deploy cloud resource and deliver secret to multi clusters.
name: deploy-cloud-resource
namespace: {{ include "systemDefinitionNamespace" . }}
spec:

View File

@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
kind: WorkflowStepDefinition
metadata:
annotations:
definition.oam.dev/description: Deploy components with policies.
definition.oam.dev/description: A powerful and unified deploy step for components multi-cluster delivery with policies.
name: deploy
namespace: {{ include "systemDefinitionNamespace" . }}
spec:
@@ -21,9 +21,9 @@ spec:
ignoreTerraformComponent: parameter.ignoreTerraformComponent
}
parameter: {
//+usage=If set false, the workflow will be suspend before this step.
//+usage=If set to false, the workflow will suspend automatically before this step, default to be true.
auto: *true | bool
//+usage=Declare the policies used for this step.
//+usage=Declare the policies that used for this deployment. If not specified, the components will be deployed to the hub cluster.
policies?: [...string]
//+usage=Maximum number of concurrent delivered components.
parallelism: *5 | int

View File

@@ -6,6 +6,7 @@ metadata:
annotations:
definition.oam.dev/description: Deploy env binding component to target env
labels:
custom.definition.oam.dev/deprecated: "true"
custom.definition.oam.dev/ui-hidden: "true"
name: deploy2env
namespace: {{ include "systemDefinitionNamespace" . }}

View File

@@ -6,6 +6,7 @@ metadata:
annotations:
definition.oam.dev/description: Deploy application to runtime clusters
labels:
custom.definition.oam.dev/deprecated: "true"
custom.definition.oam.dev/ui-hidden: "true"
name: deploy2runtime
namespace: {{ include "systemDefinitionNamespace" . }}

View File

@@ -22,7 +22,7 @@ spec:
traits?: [...{
type: string
properties?: {...}
// +usage=Specify if the trait shoued be remove, default false
// +usage=Specify if the trait should be remove, default false
disable: *false | bool
}]
}

View File

@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
kind: WorkflowStepDefinition
metadata:
annotations:
definition.oam.dev/description: Export data to config map for your workflow steps
definition.oam.dev/description: Export data to specified Kubernetes ConfigMap in your workflow.
labels:
custom.definition.oam.dev/ui-hidden: "true"
name: export2config

View File

@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
kind: WorkflowStepDefinition
metadata:
annotations:
definition.oam.dev/description: Export data to secret for your workflow steps
definition.oam.dev/description: Export data to Kubernetes Secret in your workflow.
labels:
custom.definition.oam.dev/ui-hidden: "true"
name: export2secret
@@ -46,7 +46,7 @@ spec:
type?: string
// +usage=Specify the data of secret
data: {}
// +usage=Specify the cluster of the config map
// +usage=Specify the cluster of the secret
cluster: *"" | string
}

View File

@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
kind: WorkflowStepDefinition
metadata:
annotations:
definition.oam.dev/description: Send message to webhook
definition.oam.dev/description: Send notifications to Email, DingTalk, Slack, Lark or webhook in your workflow.
name: notification
namespace: {{ include "systemDefinitionNamespace" . }}
spec:
@@ -17,9 +17,11 @@ spec:
)
parameter: {
// +usage=Please fulfill its url and message if you want to send Lark messages
lark?: {
// +usage=Specify the the lark url, you can either sepcify it in value or use secretRef
url: {
// +usage=the url address content in string
value: string
} | {
secretRef: {
@@ -29,7 +31,7 @@ spec:
key: string
}
}
// +useage=Specify the message that you want to sent
// +usage=Specify the message that you want to sent, refer to [Lark messaging](https://open.feishu.cn/document/ukTMukTMukTM/ucTM5YjL3ETO24yNxkjN#8b0f2a1b).
message: {
// +usage=msg_type can be text, post, image, interactive, share_chat, share_user, audio, media, file, sticker
msg_type: string
@@ -37,10 +39,11 @@ spec:
content: string
}
}
// +usage=Please fulfill its url and message if you want to send DingTalk messages
dingding?: {
// +usage=Specify the the dingding url, you can either sepcify it in value or use secretRef
url: {
// +usage=the url address content in string
value: string
} | {
secretRef: {
@@ -50,8 +53,9 @@ spec:
key: string
}
}
// +useage=Specify the message that you want to sent
// +usage=Specify the message that you want to sent, refer to [dingtalk messaging](https://developers.dingtalk.com/document/robots/custom-robot-access/title-72m-8ag-pqw)
message: {
// +usage=Specify the message content of dingtalk notification
text?: *null | {
content: string
}
@@ -93,10 +97,11 @@ spec:
}
}
}
// +usage=Please fulfill its url and message if you want to send Slack messages
slack?: {
// +usage=Specify the the slack url, you can either sepcify it in value or use secretRef
url: {
// +usage=the url address content in string
value: string
} | {
secretRef: {
@@ -106,8 +111,9 @@ spec:
key: string
}
}
// +useage=Specify the message that you want to sent
// +usage=Specify the message that you want to sent, refer to [slack messaging](https://api.slack.com/reference/messaging/payload)
message: {
// +usage=Specify the message text for slack notification
text: string
blocks?: *null | [...block]
attachments?: *null | {
@@ -115,10 +121,11 @@ spec:
color?: string
}
thread_ts?: string
mrkdwn?: *true | bool
// +usage=Specify the message text format in markdown for slack notification
mrkdwn?: *true | bool
}
}
// +usage=Please fulfill its from, to and content if you want to send email
email?: {
// +usage=Specify the email info that you want to send from
from: {
@@ -128,6 +135,7 @@ spec:
alias?: string
// +usage=Specify the password of the email, you can either sepcify it in value or use secretRef
password: {
// +usage=the password content in string
value: string
} | {
secretRef: {

View File

@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
kind: PolicyDefinition
metadata:
annotations:
definition.oam.dev/description: Override configuration when deploying resources
definition.oam.dev/description: Describe the configuration to override when deploying resources, it only works with specified `deploy` step in workflow.
name: override
namespace: {{ include "systemDefinitionNamespace" . }}
spec:
@@ -16,11 +16,15 @@ spec:
name?: string
// +usage=Specify the type of the patch component.
type?: string
// +usage=Specify the properties to override.
properties?: {...}
// +usage=Specify the traits to override.
traits?: [...{
// +usage=Specify the type of the trait to be patched.
type: string
// +usage=Specify the properties to override.
properties?: {...}
// +usage=Specify if the trait shoued be remove, default false
// +usage=Specify if the trait should be remove, default false
disable: *false | bool
}]
}

View File

@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
kind: WorkflowStepDefinition
metadata:
annotations:
definition.oam.dev/description: Read objects for your workflow steps
definition.oam.dev/description: Read Kubernetes objects from cluster for your workflow steps
labels:
custom.definition.oam.dev/ui-hidden: "true"
name: read-object
@@ -50,15 +50,15 @@ spec:
}
}
parameter: {
// +usage=Specify the apiVersion of the object, defaults to core.oam.dev/v1beta1
// +usage=Specify the apiVersion of the object, defaults to 'core.oam.dev/v1beta1'
apiVersion?: string
// +usage=Specify the kind of the object, defaults to Application
kind?: string
// +usage=Specify the name of the object
name: string
// +usage=Specify the namespace of the object
namespace?: string
// +usage=Specify the cluster of the object
// +usage=The namespace of the resource you want to read
namespace?: *"default" | string
// +usage=The cluster you want to apply the resource to, default is the current control plane cluster
cluster: *"" | string
}

View File

@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
kind: WorkflowStepDefinition
metadata:
annotations:
definition.oam.dev/description: step group
definition.oam.dev/description: A special step that you can declare 'subSteps' in it, 'subSteps' is an array containing any step type whose valid parameters do not include the `step-group` step type itself. The sub steps were executed in parallel.
labels:
custom.definition.oam.dev/ui-hidden: "true"
name: step-group
@@ -13,6 +13,6 @@ spec:
schematic:
cue:
template: |
// no parameters
parameter: {}
// no parameters, the nop only to make the template not empty or it's invalid
nop: {}

View File

@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
kind: WorkflowStepDefinition
metadata:
annotations:
definition.oam.dev/description: Suspend your workflow
definition.oam.dev/description: Suspend the current workflow, it can be resumed by 'vela workflow resume' command.
name: suspend
namespace: {{ include "systemDefinitionNamespace" . }}
spec:

View File

@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
kind: PolicyDefinition
metadata:
annotations:
definition.oam.dev/description: Determining the destination where components should be deployed to.
definition.oam.dev/description: Describe the destination where components should be deployed to.
name: topology
namespace: {{ include "systemDefinitionNamespace" . }}
spec:

View File

@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
kind: WorkflowStepDefinition
metadata:
annotations:
definition.oam.dev/description: Send webhook request to the url
definition.oam.dev/description: Send a request to the specified Webhook URL. If no request body is specified, the current Application body will be sent by default.
name: webhook
namespace: {{ include "systemDefinitionNamespace" . }}
spec:

View File

@@ -13,6 +13,12 @@ spec:
properties:
image: {{ .Values.imageRegistry }}{{ .Values.test.app.repository }}:{{ .Values.test.app.tag }}
port: 8000
traits:
- type: ingress
properties:
domain: testsvc.example.com
http:
"/": 8000
---
apiVersion: v1
kind: Pod
@@ -46,6 +52,12 @@ spec:
kubectl -n {{ include "systemDefinitionNamespace" . }} wait --for=condition=available deployments helm-test-express-server --timeout 3m
echo "deployment being available"
# wait for ingress being created
while ! [ `kubectl -n {{ include "systemDefinitionNamespace" . }} get ing helm-test-express-server | grep -v NAME | wc -l` = 1 ]; do
echo "waiting for ingress being created"
sleep 1
done
echo "Application and its components are created"

View File

@@ -6,6 +6,7 @@ metadata:
annotations:
definition.oam.dev/description: Apply components of an application in parallel for your workflow steps
labels:
custom.definition.oam.dev/deprecated: "true"
custom.definition.oam.dev/ui-hidden: "true"
name: apply-application-in-parallel
namespace: {{ include "systemDefinitionNamespace" . }}

View File

@@ -4,8 +4,9 @@ apiVersion: core.oam.dev/v1beta1
kind: WorkflowStepDefinition
metadata:
annotations:
definition.oam.dev/description: Apply application for your workflow steps
definition.oam.dev/description: Apply application for your workflow steps, it has no arguments, should be used for custom steps before or after application applied.
labels:
custom.definition.oam.dev/deprecated: "true"
custom.definition.oam.dev/ui-hidden: "true"
name: apply-application
namespace: {{ include "systemDefinitionNamespace" . }}

View File

@@ -22,9 +22,9 @@ spec:
cluster: parameter.cluster
}
parameter: {
// +usage=Specify the value of the object
// +usage=Specify Kubernetes native resource object to be applied
value: {...}
// +usage=Specify the cluster of the object
// +usage=The cluster you want to apply the resource to, default is the current control plane cluster
cluster: *"" | string
}

View File

@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
kind: PolicyDefinition
metadata:
annotations:
definition.oam.dev/description: Configure the behaviour of preventing configuration drift.
definition.oam.dev/description: Allow configuration drift for applied resources, delivery the resource without continuously reconciliation.
name: apply-once
namespace: {{ include "systemDefinitionNamespace" . }}
spec:

View File

@@ -6,6 +6,7 @@ metadata:
annotations:
definition.oam.dev/description: Apply remaining components and traits
labels:
custom.definition.oam.dev/deprecated: "true"
custom.definition.oam.dev/ui-hidden: "true"
name: apply-remaining
namespace: {{ include "systemDefinitionNamespace" . }}

View File

@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
kind: WorkflowStepDefinition
metadata:
annotations:
definition.oam.dev/description: check or install depends-on Application
definition.oam.dev/description: Wait for the specified Application to complete.
labels:
custom.definition.oam.dev/ui-hidden: "true"
name: depends-on-app

View File

@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
kind: WorkflowStepDefinition
metadata:
annotations:
definition.oam.dev/description: Deploy cloud resource and bind secret to clusters
definition.oam.dev/description: Deploy cloud resource and deliver secret to multi clusters.
name: deploy-cloud-resource
namespace: {{ include "systemDefinitionNamespace" . }}
spec:

View File

@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
kind: WorkflowStepDefinition
metadata:
annotations:
definition.oam.dev/description: Deploy components with policies.
definition.oam.dev/description: A powerful and unified deploy step for components multi-cluster delivery with policies.
name: deploy
namespace: {{ include "systemDefinitionNamespace" . }}
spec:
@@ -21,9 +21,9 @@ spec:
ignoreTerraformComponent: parameter.ignoreTerraformComponent
}
parameter: {
//+usage=If set false, the workflow will be suspend before this step.
//+usage=If set to false, the workflow will suspend automatically before this step, default to be true.
auto: *true | bool
//+usage=Declare the policies used for this step.
//+usage=Declare the policies that used for this deployment. If not specified, the components will be deployed to the hub cluster.
policies?: [...string]
//+usage=Maximum number of concurrent delivered components.
parallelism: *5 | int

View File

@@ -6,6 +6,7 @@ metadata:
annotations:
definition.oam.dev/description: Deploy application to runtime clusters
labels:
custom.definition.oam.dev/deprecated: "true"
custom.definition.oam.dev/ui-hidden: "true"
name: deploy2runtime
namespace: {{ include "systemDefinitionNamespace" . }}

View File

@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
kind: WorkflowStepDefinition
metadata:
annotations:
definition.oam.dev/description: Export data to config map for your workflow steps
definition.oam.dev/description: Export data to specified Kubernetes ConfigMap in your workflow.
labels:
custom.definition.oam.dev/ui-hidden: "true"
name: export2config

View File

@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
kind: WorkflowStepDefinition
metadata:
annotations:
definition.oam.dev/description: Export data to secret for your workflow steps
definition.oam.dev/description: Export data to Kubernetes Secret in your workflow.
labels:
custom.definition.oam.dev/ui-hidden: "true"
name: export2secret
@@ -46,7 +46,7 @@ spec:
type?: string
// +usage=Specify the data of secret
data: {}
// +usage=Specify the cluster of the config map
// +usage=Specify the cluster of the secret
cluster: *"" | string
}

View File

@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
kind: WorkflowStepDefinition
metadata:
annotations:
definition.oam.dev/description: Send message to webhook
definition.oam.dev/description: Send notifications to Email, DingTalk, Slack, Lark or webhook in your workflow.
name: notification
namespace: {{ include "systemDefinitionNamespace" . }}
spec:
@@ -17,9 +17,11 @@ spec:
)
parameter: {
// +usage=Please fulfill its url and message if you want to send Lark messages
lark?: {
// +usage=Specify the the lark url, you can either sepcify it in value or use secretRef
url: {
// +usage=the url address content in string
value: string
} | {
secretRef: {
@@ -29,7 +31,7 @@ spec:
key: string
}
}
// +useage=Specify the message that you want to sent
// +usage=Specify the message that you want to sent, refer to [Lark messaging](https://open.feishu.cn/document/ukTMukTMukTM/ucTM5YjL3ETO24yNxkjN#8b0f2a1b).
message: {
// +usage=msg_type can be text, post, image, interactive, share_chat, share_user, audio, media, file, sticker
msg_type: string
@@ -37,10 +39,11 @@ spec:
content: string
}
}
// +usage=Please fulfill its url and message if you want to send DingTalk messages
dingding?: {
// +usage=Specify the the dingding url, you can either sepcify it in value or use secretRef
url: {
// +usage=the url address content in string
value: string
} | {
secretRef: {
@@ -50,8 +53,9 @@ spec:
key: string
}
}
// +useage=Specify the message that you want to sent
// +usage=Specify the message that you want to sent, refer to [dingtalk messaging](https://developers.dingtalk.com/document/robots/custom-robot-access/title-72m-8ag-pqw)
message: {
// +usage=Specify the message content of dingtalk notification
text?: *null | {
content: string
}
@@ -93,10 +97,11 @@ spec:
}
}
}
// +usage=Please fulfill its url and message if you want to send Slack messages
slack?: {
// +usage=Specify the the slack url, you can either sepcify it in value or use secretRef
url: {
// +usage=the url address content in string
value: string
} | {
secretRef: {
@@ -106,8 +111,9 @@ spec:
key: string
}
}
// +useage=Specify the message that you want to sent
// +usage=Specify the message that you want to sent, refer to [slack messaging](https://api.slack.com/reference/messaging/payload)
message: {
// +usage=Specify the message text for slack notification
text: string
blocks?: *null | [...block]
attachments?: *null | {
@@ -115,10 +121,11 @@ spec:
color?: string
}
thread_ts?: string
mrkdwn?: *true | bool
// +usage=Specify the message text format in markdown for slack notification
mrkdwn?: *true | bool
}
}
// +usage=Please fulfill its from, to and content if you want to send email
email?: {
// +usage=Specify the email info that you want to send from
from: {
@@ -128,6 +135,7 @@ spec:
alias?: string
// +usage=Specify the password of the email, you can either sepcify it in value or use secretRef
password: {
// +usage=the password content in string
value: string
} | {
secretRef: {

View File

@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
kind: PolicyDefinition
metadata:
annotations:
definition.oam.dev/description: Override configuration when deploying resources
definition.oam.dev/description: Describe the configuration to override when deploying resources, it only works with specified `deploy` step in workflow.
name: override
namespace: {{ include "systemDefinitionNamespace" . }}
spec:
@@ -16,11 +16,15 @@ spec:
name?: string
// +usage=Specify the type of the patch component.
type?: string
// +usage=Specify the properties to override.
properties?: {...}
// +usage=Specify the traits to override.
traits?: [...{
// +usage=Specify the type of the trait to be patched.
type: string
// +usage=Specify the properties to override.
properties?: {...}
// +usage=Specify if the trait shoued be remove, default false
// +usage=Specify if the trait should be remove, default false
disable: *false | bool
}]
}

View File

@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
kind: WorkflowStepDefinition
metadata:
annotations:
definition.oam.dev/description: Read objects for your workflow steps
definition.oam.dev/description: Read Kubernetes objects from cluster for your workflow steps
labels:
custom.definition.oam.dev/ui-hidden: "true"
name: read-object
@@ -50,15 +50,15 @@ spec:
}
}
parameter: {
// +usage=Specify the apiVersion of the object, defaults to core.oam.dev/v1beta1
// +usage=Specify the apiVersion of the object, defaults to 'core.oam.dev/v1beta1'
apiVersion?: string
// +usage=Specify the kind of the object, defaults to Application
kind?: string
// +usage=Specify the name of the object
name: string
// +usage=Specify the namespace of the object
namespace?: string
// +usage=Specify the cluster of the object
// +usage=The namespace of the resource you want to read
namespace?: *"default" | string
// +usage=The cluster you want to apply the resource to, default is the current control plane cluster
cluster: *"" | string
}

View File

@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
kind: WorkflowStepDefinition
metadata:
annotations:
definition.oam.dev/description: step group
definition.oam.dev/description: A special step that you can declare 'subSteps' in it, 'subSteps' is an array containing any step type whose valid parameters do not include the `step-group` step type itself. The sub steps were executed in parallel.
labels:
custom.definition.oam.dev/ui-hidden: "true"
name: step-group
@@ -13,6 +13,6 @@ spec:
schematic:
cue:
template: |
// no parameters
parameter: {}
// no parameters, the nop only to make the template not empty or it's invalid
nop: {}

View File

@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
kind: WorkflowStepDefinition
metadata:
annotations:
definition.oam.dev/description: Suspend your workflow
definition.oam.dev/description: Suspend the current workflow, it can be resumed by 'vela workflow resume' command.
name: suspend
namespace: {{ include "systemDefinitionNamespace" . }}
spec:

View File

@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
kind: PolicyDefinition
metadata:
annotations:
definition.oam.dev/description: Determining the destination where components should be deployed to.
definition.oam.dev/description: Describe the destination where components should be deployed to.
name: topology
namespace: {{ include "systemDefinitionNamespace" . }}
spec:

View File

@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
kind: WorkflowStepDefinition
metadata:
annotations:
definition.oam.dev/description: Send webhook request to the url
definition.oam.dev/description: Send a request to the specified Webhook URL. If no request body is specified, the current Application body will be sent by default.
name: webhook
namespace: {{ include "systemDefinitionNamespace" . }}
spec:

View File

@@ -41,26 +41,28 @@ var _ = Describe("Test Kubectl Plugin", func() {
Eventually(func() error {
err := k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: componentDefName}, &cd)
return err
}, 5*time.Second).Should(BeNil())
}, 5*time.Second, time.Second).Should(BeNil())
var td v1beta1.TraitDefinition
Eventually(func() error {
err := k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: traitDefName}, &td)
return err
}, 5*time.Second).Should(BeNil())
}, 5*time.Second, time.Second).Should(BeNil())
By("dry-run application")
err := os.WriteFile("dry-run-app.yaml", []byte(application), 0644)
Expect(err).NotTo(HaveOccurred())
output, err := e2e.Exec("kubectl-vela dry-run -f dry-run-app.yaml -n vela-system")
Expect(err).NotTo(HaveOccurred())
Expect(output).Should(ContainSubstring(dryRunResult))
Eventually(func() string {
output, _ := e2e.Exec("kubectl-vela dry-run -f dry-run-app.yaml -n vela-system")
return output
}, 10*time.Second, time.Second).Should(ContainSubstring(dryRunResult))
})
It("Test dry-run application use definitions in local", func() {
output, err := e2e.Exec("kubectl-vela dry-run -f dry-run-app.yaml -d definitions")
Expect(output).Should(ContainSubstring(dryRunResult))
Expect(err).NotTo(HaveOccurred())
Eventually(func() string {
output, _ := e2e.Exec("kubectl-vela dry-run -f dry-run-app.yaml -d definitions")
return output
}, 10*time.Second, time.Second).Should(ContainSubstring(dryRunResult))
})
})
@@ -156,7 +158,7 @@ var _ = Describe("Test Kubectl Plugin", func() {
tdName := "annotations"
output, err := e2e.Exec(fmt.Sprintf("kubectl-vela show %s -n default", tdName))
Expect(err).NotTo(HaveOccurred())
Expect(output).Should(ContainSubstring("map[string](null|string)"))
Expect(output).Should(ContainSubstring("map[string]:(null|string)"))
})
It("Test show webservice def with cue ignore annotation ", func() {
tdName := "webservice"
@@ -983,8 +985,8 @@ var showCdResult = `# Specification
| NAME | DESCRIPTION | TYPE | REQUIRED | DEFAULT |
+---------+--------------------------------------------------------------------------------------------------+----------+----------+---------+
| cmd | Commands to run in the container. | []string | false | |
| count | specify number of tasks to run in parallel. | int | true | 1 |
| restart | Define the job restart policy, the value can only be Never or OnFailure. By default, it's Never. | string | true | Never |
| count | specify number of tasks to run in parallel. | int | false | 1 |
| restart | Define the job restart policy, the value can only be Never or OnFailure. By default, it's Never. | string | false | Never |
| image | Which image would you like to use for your service. | string | true | |
+---------+--------------------------------------------------------------------------------------------------+----------+----------+---------+

View File

@@ -25,7 +25,7 @@ import (
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/hack/docgen/def/mods"
"github.com/oam-dev/kubevela/pkg/utils/common"
"github.com/oam-dev/kubevela/references/plugins"
"github.com/oam-dev/kubevela/references/docgen"
)
func main() {
@@ -45,7 +45,7 @@ func main() {
flag.Parse()
if *i18nfile != "" {
plugins.LoadI18nData(*i18nfile)
docgen.LoadI18nData(*i18nfile)
}
if *tp == "" && (*defdir != "" || *path != "") {
@@ -54,19 +54,22 @@ func main() {
}
fmt.Printf("creating docs with args path=%s, location=%s, defdir=%s, type=%s.\n", *path, *location, *defdir, *tp)
switch types.CapType(*tp) {
case types.TypeComponentDefinition, "component":
case types.TypeComponentDefinition, "component", "comp":
mods.ComponentDef(ctx, c, path, location, *defdir)
case types.TypeTrait:
mods.TraitDef(ctx, c, path, location, *defdir)
case types.TypePolicy:
mods.PolicyDef(ctx, c, path, location, *defdir)
case types.TypeWorkflowStep:
case types.TypeWorkflowStep, "workflow", "wf":
mods.WorkflowDef(ctx, c, path, location, *defdir)
default:
case "":
mods.ComponentDef(ctx, c, path, location, *defdir)
mods.TraitDef(ctx, c, path, location, *defdir)
mods.PolicyDef(ctx, c, path, location, *defdir)
mods.WorkflowDef(ctx, c, path, location, *defdir)
default:
fmt.Printf("type %s not supported\n", *tp)
os.Exit(1)
}
}

View File

@@ -26,7 +26,7 @@ import (
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/utils/common"
"github.com/oam-dev/kubevela/references/plugins"
"github.com/oam-dev/kubevela/references/docgen"
)
const (
@@ -62,7 +62,7 @@ func ComponentDef(ctx context.Context, c common.Args, path, location *string, de
if defdir == "" {
defdir = ComponentDefDir
}
ref := &plugins.MarkdownReference{
ref := &docgen.MarkdownReference{
AllInOne: true,
Filter: func(capability types.Capability) bool {
if capability.Type != types.TypeComponentDefinition || capability.Category != types.CUECategory {
@@ -86,12 +86,12 @@ func ComponentDef(ctx context.Context, c common.Args, path, location *string, de
},
CustomDocHeader: CustomComponentHeaderEN,
}
ref.Remote = &plugins.FromCluster{Namespace: types.DefaultKubeVelaNS}
ref.Remote = &docgen.FromCluster{Namespace: types.DefaultKubeVelaNS}
if *path != "" {
ref.I18N = &plugins.En
ref.I18N = &docgen.En
if strings.Contains(*location, "zh") || strings.Contains(*location, "chinese") {
ref.I18N = &plugins.Zh
ref.I18N = &docgen.Zh
ref.CustomDocHeader = CustomComponentHeaderZH
}
if err := ref.GenerateReferenceDocs(ctx, c, *path); err != nil {
@@ -101,7 +101,7 @@ func ComponentDef(ctx context.Context, c common.Args, path, location *string, de
fmt.Printf("component reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), *path)
}
if *location == "" || *location == "en" {
ref.I18N = &plugins.En
ref.I18N = &docgen.En
if err := ref.GenerateReferenceDocs(ctx, c, ComponentDefRefPath); err != nil {
fmt.Println(err)
os.Exit(1)
@@ -109,7 +109,7 @@ func ComponentDef(ctx context.Context, c common.Args, path, location *string, de
fmt.Printf("component reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), ComponentDefRefPath)
}
if *location == "" || *location == "zh" {
ref.I18N = &plugins.Zh
ref.I18N = &docgen.Zh
ref.CustomDocHeader = CustomComponentHeaderZH
if err := ref.GenerateReferenceDocs(ctx, c, ComponentDefRefPathZh); err != nil {
fmt.Println(err)

View File

@@ -26,7 +26,7 @@ import (
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/utils/common"
"github.com/oam-dev/kubevela/references/plugins"
"github.com/oam-dev/kubevela/references/docgen"
)
const (
@@ -62,7 +62,7 @@ func PolicyDef(ctx context.Context, c common.Args, path, location *string, defdi
if defdir == "" {
defdir = PolicyDefDir
}
ref := &plugins.MarkdownReference{
ref := &docgen.MarkdownReference{
AllInOne: true,
Filter: func(capability types.Capability) bool {
if capability.Type != types.TypePolicy || capability.Category != types.CUECategory {
@@ -86,11 +86,11 @@ func PolicyDef(ctx context.Context, c common.Args, path, location *string, defdi
},
CustomDocHeader: CustomPolicyHeaderEN,
}
ref.Remote = &plugins.FromCluster{Namespace: types.DefaultKubeVelaNS}
ref.Remote = &docgen.FromCluster{Namespace: types.DefaultKubeVelaNS}
if *path != "" {
ref.I18N = &plugins.En
ref.I18N = &docgen.En
if strings.Contains(*location, "zh") || strings.Contains(*location, "chinese") {
ref.I18N = &plugins.Zh
ref.I18N = &docgen.Zh
ref.CustomDocHeader = CustomPolicyHeaderZH
}
if err := ref.GenerateReferenceDocs(ctx, c, *path); err != nil {
@@ -100,7 +100,7 @@ func PolicyDef(ctx context.Context, c common.Args, path, location *string, defdi
fmt.Printf("policy reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), *path)
}
if *location == "" || *location == "en" {
ref.I18N = &plugins.En
ref.I18N = &docgen.En
if err := ref.GenerateReferenceDocs(ctx, c, PolicyDefRefPath); err != nil {
fmt.Println(err)
os.Exit(1)
@@ -108,7 +108,7 @@ func PolicyDef(ctx context.Context, c common.Args, path, location *string, defdi
fmt.Printf("policy reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), PolicyDefRefPath)
}
if *location == "" || *location == "zh" {
ref.I18N = &plugins.Zh
ref.I18N = &docgen.Zh
ref.CustomDocHeader = CustomPolicyHeaderZH
if err := ref.GenerateReferenceDocs(ctx, c, PolicyDefRefPathZh); err != nil {
fmt.Println(err)

View File

@@ -26,7 +26,7 @@ import (
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/utils/common"
"github.com/oam-dev/kubevela/references/plugins"
"github.com/oam-dev/kubevela/references/docgen"
)
const (
@@ -62,7 +62,7 @@ func TraitDef(ctx context.Context, c common.Args, path, location *string, defdir
if defdir == "" {
defdir = TraitDefDir
}
ref := &plugins.MarkdownReference{
ref := &docgen.MarkdownReference{
AllInOne: true,
Filter: func(capability types.Capability) bool {
if capability.Type != types.TypeTrait || capability.Category != types.CUECategory {
@@ -86,12 +86,12 @@ func TraitDef(ctx context.Context, c common.Args, path, location *string, defdir
},
CustomDocHeader: CustomTraitHeaderEN,
}
ref.Remote = &plugins.FromCluster{Namespace: types.DefaultKubeVelaNS}
ref.Remote = &docgen.FromCluster{Namespace: types.DefaultKubeVelaNS}
if *path != "" {
ref.I18N = &plugins.En
ref.I18N = &docgen.En
if strings.Contains(*location, "zh") || strings.Contains(*location, "chinese") {
ref.I18N = &plugins.Zh
ref.I18N = &docgen.Zh
ref.CustomDocHeader = CustomTraitHeaderZH
}
if err := ref.GenerateReferenceDocs(ctx, c, *path); err != nil {
@@ -101,7 +101,7 @@ func TraitDef(ctx context.Context, c common.Args, path, location *string, defdir
fmt.Printf("trait reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), *path)
}
if *location == "" || *location == "en" {
ref.I18N = &plugins.En
ref.I18N = &docgen.En
if err := ref.GenerateReferenceDocs(ctx, c, TraitDefRefPath); err != nil {
fmt.Println(err)
os.Exit(1)
@@ -109,7 +109,7 @@ func TraitDef(ctx context.Context, c common.Args, path, location *string, defdir
fmt.Printf("trait reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), TraitDefRefPath)
}
if *location == "" || *location == "zh" {
ref.I18N = &plugins.Zh
ref.I18N = &docgen.Zh
ref.CustomDocHeader = CustomTraitHeaderZH
if err := ref.GenerateReferenceDocs(ctx, c, TraitDefRefPathZh); err != nil {
fmt.Println(err)

View File

@@ -26,7 +26,7 @@ import (
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/utils/common"
"github.com/oam-dev/kubevela/references/plugins"
"github.com/oam-dev/kubevela/references/docgen"
)
const (
@@ -62,7 +62,7 @@ func WorkflowDef(ctx context.Context, c common.Args, path, location *string, def
if defdir == "" {
defdir = WorkflowDefDir
}
ref := &plugins.MarkdownReference{
ref := &docgen.MarkdownReference{
AllInOne: true,
Filter: func(capability types.Capability) bool {
@@ -70,7 +70,7 @@ func WorkflowDef(ctx context.Context, c common.Args, path, location *string, def
return false
}
if capability.Labels != nil && (capability.Labels[types.LabelDefinitionHidden] == "true" || capability.Labels[types.LabelDefinitionDeprecated] == "true") {
if capability.Labels != nil && capability.Labels[types.LabelDefinitionDeprecated] == "true" {
return false
}
// only print capability which contained in cue def
@@ -88,12 +88,12 @@ func WorkflowDef(ctx context.Context, c common.Args, path, location *string, def
},
CustomDocHeader: CustomWorkflowHeaderEN,
}
ref.Remote = &plugins.FromCluster{Namespace: types.DefaultKubeVelaNS}
ref.Remote = &docgen.FromCluster{Namespace: types.DefaultKubeVelaNS}
if *path != "" {
ref.I18N = &plugins.En
ref.I18N = &docgen.En
if strings.Contains(*location, "zh") || strings.Contains(*location, "chinese") {
ref.I18N = &plugins.Zh
ref.I18N = &docgen.Zh
ref.CustomDocHeader = CustomWorkflowHeaderZH
}
if err := ref.GenerateReferenceDocs(ctx, c, *path); err != nil {
@@ -103,7 +103,7 @@ func WorkflowDef(ctx context.Context, c common.Args, path, location *string, def
fmt.Printf("workflow reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), *path)
}
if *location == "" || *location == "en" {
ref.I18N = &plugins.En
ref.I18N = &docgen.En
if err := ref.GenerateReferenceDocs(ctx, c, WorkflowDefRefPath); err != nil {
fmt.Println(err)
os.Exit(1)
@@ -111,7 +111,7 @@ func WorkflowDef(ctx context.Context, c common.Args, path, location *string, def
fmt.Printf("workflow reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), WorkflowDefRefPath)
}
if *location == "" || *location == "zh" {
ref.I18N = &plugins.Zh
ref.I18N = &docgen.Zh
ref.CustomDocHeader = CustomWorkflowHeaderZH
if err := ref.GenerateReferenceDocs(ctx, c, WorkflowDefRefPathZh); err != nil {
fmt.Println(err)

View File

@@ -25,7 +25,7 @@ import (
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/utils/common"
"github.com/oam-dev/kubevela/references/plugins"
"github.com/oam-dev/kubevela/references/docgen"
)
const (
@@ -36,7 +36,7 @@ const (
)
func main() {
ref := &plugins.MarkdownReference{}
ref := &docgen.MarkdownReference{}
ctx := context.Background()
c, err := common.InitBaseRestConfig()
@@ -44,7 +44,7 @@ func main() {
fmt.Println(err)
os.Exit(1)
}
ref.Remote = &plugins.FromCluster{Namespace: types.DefaultKubeVelaNS}
ref.Remote = &docgen.FromCluster{Namespace: types.DefaultKubeVelaNS}
ref.Filter = func(capability types.Capability) bool {
if capability.Labels != nil && capability.Labels[types.LabelDefinitionHidden] == "true" {
return false
@@ -58,13 +58,13 @@ func main() {
flag.Parse()
if *i18nfile != "" {
plugins.LoadI18nData(*i18nfile)
docgen.LoadI18nData(*i18nfile)
}
if *path != "" {
ref.I18N = &plugins.En
ref.I18N = &docgen.En
if strings.Contains(*location, "zh") || strings.Contains(*location, "chinese") {
ref.I18N = &plugins.Zh
ref.I18N = &docgen.Zh
}
if err := ref.GenerateReferenceDocs(ctx, c, *path); err != nil {
fmt.Println(err)
@@ -72,13 +72,13 @@ func main() {
}
fmt.Printf("terraform reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), *path)
}
ref.I18N = &plugins.En
ref.I18N = &docgen.En
if err := ref.GenerateReferenceDocs(ctx, c, KubeVelaIOTerraformPath); err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Printf("terraform reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), KubeVelaIOTerraformPath)
ref.I18N = &plugins.Zh
ref.I18N = &docgen.Zh
if err := ref.GenerateReferenceDocs(ctx, c, KubeVelaIOTerraformPathZh); err != nil {
fmt.Println(err)
os.Exit(1)

View File

@@ -85,7 +85,7 @@ e2e-rollout-test:
.PHONY: e2e-multicluster-test
e2e-multicluster-test:
go test -v -coverpkg=./... -timeout=20m -coverprofile=/tmp/e2e_multicluster_test.out ./test/e2e-multicluster-test
go test -v -coverpkg=./... -coverprofile=/tmp/e2e_multicluster_test.out ./test/e2e-multicluster-test
@$(OK) tests pass
.PHONY: e2e-cleanup

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
@@ -1049,11 +1052,10 @@ func (h *Installer) createOrUpdate(app *v1beta1.Application) error {
}
func (h *Installer) dispatchAddonResource(addon *InstallPackage) error {
app, err := RenderApp(h.ctx, addon, h.cli, h.args)
app, auxiliaryOutputs, err := RenderApp(h.ctx, addon, h.cli, h.args)
if err != nil {
return errors.Wrap(err, "render addon application fail")
}
appName, err := determineAddonAppName(h.ctx, h.cli, h.addon.Name)
if err != nil {
return err
@@ -1109,6 +1111,14 @@ func (h *Installer) dispatchAddonResource(addon *InstallPackage) error {
}
}
for _, o := range auxiliaryOutputs {
addOwner(o, app)
err = h.apply.Apply(h.ctx, o, apply.DisableUpdateAnnotation())
if err != nil {
return err
}
}
if h.args != nil && len(h.args) > 0 {
sec := RenderArgsSecret(addon, h.args)
addOwner(sec, app)

View File

@@ -307,7 +307,7 @@ var _ = Describe("Test render addon with specified clusters", func() {
args := map[string]interface{}{
"clusters": []string{"add-c1", "ne"},
}
_, err := RenderApp(ctx, i, k8sClient, args)
_, _, err := RenderApp(ctx, i, k8sClient, args)
Expect(err.Error()).Should(BeEquivalentTo("cluster ne not exist"))
})
It("test render normal addon with specified clusters", func() {
@@ -317,7 +317,7 @@ var _ = Describe("Test render addon with specified clusters", func() {
args := map[string]interface{}{
"clusters": []string{"add-c1", "add-c2"},
}
ap, err := RenderApp(ctx, i, k8sClient, args)
ap, _, err := RenderApp(ctx, i, k8sClient, args)
Expect(err).Should(BeNil())
Expect(ap.Spec.Policies).Should(BeEquivalentTo([]v1beta1.AppPolicy{{Name: specifyAddonClustersTopologyPolicy,
Type: v1alpha12.TopologyPolicyType,

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
@@ -259,7 +281,7 @@ func TestRender(t *testing.T) {
func TestRenderApp(t *testing.T) {
addon := baseAddon
app, err := RenderApp(ctx, &addon, nil, map[string]interface{}{})
app, _, err := RenderApp(ctx, &addon, nil, map[string]interface{}{})
assert.NoError(t, err, "render app fail")
// definition should not be rendered
assert.Equal(t, len(app.Spec.Components), 1)
@@ -268,7 +290,7 @@ func TestRenderApp(t *testing.T) {
func TestRenderAppWithNeedNamespace(t *testing.T) {
addon := baseAddon
addon.NeedNamespace = append(addon.NeedNamespace, types.DefaultKubeVelaNS, "test-ns2")
app, err := RenderApp(ctx, &addon, nil, map[string]interface{}{})
app, _, err := RenderApp(ctx, &addon, nil, map[string]interface{}{})
assert.NoError(t, err, "render app fail")
assert.Equal(t, len(app.Spec.Components), 2)
for _, c := range app.Spec.Components {
@@ -289,7 +311,7 @@ func TestRenderDeploy2RuntimeAddon(t *testing.T) {
assert.Equal(t, def.GetAPIVersion(), "core.oam.dev/v1beta1")
assert.Equal(t, def.GetKind(), "TraitDefinition")
app, err := RenderApp(ctx, &addonDeployToRuntime, nil, map[string]interface{}{})
app, _, err := RenderApp(ctx, &addonDeployToRuntime, nil, map[string]interface{}{})
assert.NoError(t, err)
policies := app.Spec.Policies
assert.True(t, len(policies) == 1)
@@ -309,7 +331,7 @@ func TestRenderDefinitions(t *testing.T) {
assert.Equal(t, def.GetAPIVersion(), "core.oam.dev/v1beta1")
assert.Equal(t, def.GetKind(), "TraitDefinition")
app, err := RenderApp(ctx, &addonDeployToRuntime, nil, map[string]interface{}{})
app, _, err := RenderApp(ctx, &addonDeployToRuntime, nil, map[string]interface{}{})
assert.NoError(t, err)
// addon which app work on no-runtime-cluster mode workflow is nil
assert.Nil(t, app.Spec.Workflow)
@@ -346,7 +368,7 @@ func TestRenderK8sObjects(t *testing.T) {
RuntimeCluster: false,
}
app, err := RenderApp(ctx, &addonMultiYaml, nil, map[string]interface{}{})
app, _, err := RenderApp(ctx, &addonMultiYaml, nil, map[string]interface{}{})
assert.NoError(t, err)
assert.Equal(t, len(app.Spec.Components), 1)
comp := app.Spec.Components[0]
@@ -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

@@ -50,7 +50,7 @@ const (
enabling = "enabling"
// disabling indicates the addon related app is deleting
disabling = "disabling"
// suspend indicates the addon related app is suspend
// suspend indicates the addon related app is suspended
suspend = "suspend"
)

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

@@ -28,19 +28,16 @@ import (
"cuelang.org/go/cue/format"
"cuelang.org/go/encoding/gocode/gocodec"
"github.com/fatih/color"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/klog/v2"
"sigs.k8s.io/yaml"
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/utils"
)
const (
// AddonNameRegex is the regex to validate addon names
AddonNameRegex = `^[a-z\d]+(-[a-z\d]+)*$`
AddonNameRegex = `^[a-z\d]+(-[a-z\d]+)*$`
// helmComponentDependency is the dependent addon of Helm Component
helmComponentDependency = "fluxcd"
)
@@ -54,13 +51,16 @@ type InitCmd struct {
Path string
Overwrite bool
RefObjURLs []string
AppTmpl v1beta1.Application
Metadata Meta
Readme string
Resources []ElementFile
Schemas []ElementFile
Views []ElementFile
Definitions []ElementFile
// We use string instead of v1beta1.Application is because
// the cue formatter is having some problems: it will keep
// TypeMeta (instead of inlined).
AppTmpl string
Metadata Meta
Readme string
Resources []ElementFile
Schemas []ElementFile
Views []ElementFile
Definitions []ElementFile
}
// CreateScaffold creates an addon scaffold
@@ -152,9 +152,6 @@ func (cmd *InitCmd) createSamples() {
cmd.Resources = append(cmd.Resources, ElementFile{
Data: resourceTemplate,
Name: "myresource.cue",
}, ElementFile{
Data: parameterTemplate,
Name: "parameter.cue",
})
// Sample schema
cmd.Schemas = append(cmd.Schemas, ElementFile{
@@ -173,21 +170,8 @@ func (cmd *InitCmd) createRequiredFiles() {
// README.md
cmd.Readme = strings.ReplaceAll(readmeTemplate, "ADDON_NAME", cmd.AddonName)
// template.yaml
cmd.AppTmpl = v1beta1.Application{
TypeMeta: v1.TypeMeta{
APIVersion: v1beta1.SchemeGroupVersion.String(),
Kind: "Application",
},
ObjectMeta: v1.ObjectMeta{
Name: cmd.AddonName,
Namespace: types.DefaultKubeVelaNS,
},
Spec: v1beta1.ApplicationSpec{
// Prevent nulls after serialization
Components: []common.ApplicationComponent{},
},
}
// template.cue
cmd.AppTmpl = appTemplate
// metadata.yaml
cmd.Metadata = Meta{
@@ -341,6 +325,9 @@ func (cmd *InitCmd) writeFiles() error {
files = append(files, ElementFile{
Name: ReadmeFileName,
Data: cmd.Readme,
}, ElementFile{
Data: parameterTemplate,
Name: GlobalParameterFileName,
})
for _, v := range cmd.Resources {
@@ -368,14 +355,10 @@ func (cmd *InitCmd) writeFiles() error {
})
}
// Prepare template.yaml
tmplBytes, err := yaml.Marshal(cmd.AppTmpl)
if err != nil {
return err
}
// Prepare template.cue
files = append(files, ElementFile{
Data: string(tmplBytes),
Name: TemplateFileName,
Data: cmd.AppTmpl,
Name: AppTemplateCueFileName,
})
// Prepare metadata.yaml
@@ -502,7 +485,7 @@ output: {
`
parameterTemplate = `// parameter.cue is used to store addon parameters.
//
// You can use these parameters in other resources by 'parameter.myparam'
// You can use these parameters in template.cue or in resources/ by 'parameter.myparam'
//
// For example, you can use parameters to allow the user to customize
// container images, ports, and etc.
@@ -519,5 +502,19 @@ parameter: {
label: MyParam
validate:
required: true
`
appTemplate = `package main
output: {
apiVersion: "core.oam.dev/v1beta1"
kind: "Application"
metadata: {
name: "my-addon-name"
namespace: "vela-system"
}
spec: {
components: []
policies: []
}
}
`
)

View File

@@ -23,7 +23,10 @@ import (
"path"
"strings"
"cuelang.org/go/cue/parser"
"github.com/pkg/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -35,6 +38,7 @@ import (
"github.com/oam-dev/kubevela/pkg/cue/model/value"
"github.com/oam-dev/kubevela/pkg/multicluster"
"github.com/oam-dev/kubevela/pkg/oam"
"github.com/oam-dev/kubevela/pkg/oam/util"
addonutil "github.com/oam-dev/kubevela/pkg/utils/addon"
)
@@ -42,6 +46,7 @@ const (
specifyAddonClustersTopologyPolicy = "deploy-addon-to-specified-clusters"
addonAllClusterPolicy = "deploy-addon-to-all-clusters"
renderOutputCuePath = "output"
renderAuxiliaryOutputsPath = "outputs"
)
type addonCueTemplateRender struct {
@@ -49,32 +54,42 @@ type addonCueTemplateRender struct {
inputArgs map[string]interface{}
}
// This func can be used for addon render, supporting render app template and component.
// Please notice the result will be stored in object parameter, so object must be a pointer type
func (a addonCueTemplateRender) toObject(cueTemplate string, object interface{}) error {
func (a addonCueTemplateRender) formatContext() (string, error) {
args := a.inputArgs
if args == nil {
args = map[string]interface{}{}
}
bt, err := json.Marshal(args)
if err != nil {
return err
return "", err
}
paramFile := fmt.Sprintf("%s: %s", cuemodel.ParameterFieldName, string(bt))
var contextFile = strings.Builder{}
// user custom parameter but be the first data and generated data should be appended at last
// in case the user defined data has packages
contextFile.WriteString(a.addon.Parameters + "\n")
// addon metadata context
metadataJSON, err := json.Marshal(a.addon.Meta)
if err != nil {
return err
return "", err
}
contextFile.WriteString(fmt.Sprintf("context: metadata: %s\n", string(metadataJSON)))
// parameter definition
contextFile.WriteString(paramFile + "\n")
// user custom parameter
contextFile.WriteString(a.addon.Parameters + "\n")
v, err := value.NewValue(contextFile.String(), nil, "")
return contextFile.String(), nil
}
// This func can be used for addon render component.
// Please notice the result will be stored in object parameter, so object must be a pointer type
func (a addonCueTemplateRender) toObject(cueTemplate string, path string, object interface{}) error {
contextFile, err := a.formatContext()
if err != nil {
return err
}
v, err := value.NewValue(contextFile, nil, "")
if err != nil {
return err
}
@@ -82,25 +97,85 @@ func (a addonCueTemplateRender) toObject(cueTemplate string, object interface{})
if err != nil {
return err
}
outputContent, err := out.LookupValue(renderOutputCuePath)
outputContent, err := out.LookupValue(path)
if err != nil {
return err
}
return outputContent.UnmarshalTo(object)
}
// renderApp will render Application from CUE files
func (a addonCueTemplateRender) renderApp() (*v1beta1.Application, []*unstructured.Unstructured, error) {
var app v1beta1.Application
var outputs = map[string]interface{}{}
var res []*unstructured.Unstructured
contextFile, err := a.formatContext()
if err != nil {
return nil, nil, errors.Wrap(err, "format context for app render")
}
contextCue, err := parser.ParseFile("parameter.cue", contextFile, parser.ParseComments)
if err != nil {
return nil, nil, errors.Wrap(err, "parse parameter context")
}
if contextCue.PackageName() == "" {
contextFile = value.DefaultPackageHeader + contextFile
}
var files = []string{contextFile}
for _, cuef := range a.addon.CUETemplates {
files = append(files, cuef.Data)
}
// TODO(wonderflow): add package discover to support vela own packages if needed
v, err := value.NewValueWithMainAndFiles(a.addon.AppCueTemplate.Data, files, nil, "")
if err != nil {
return nil, nil, errors.Wrap(err, "load app template with CUE files")
}
outputContent, err := v.LookupValue(renderOutputCuePath)
if err != nil {
return nil, nil, errors.Wrap(err, "render app from output field from CUE")
}
err = outputContent.UnmarshalTo(&app)
if err != nil {
return nil, nil, errors.Wrap(err, "decode app from CUE")
}
auxiliaryContent, err := v.LookupValue(renderAuxiliaryOutputsPath)
if err != nil {
// no outputs defined in app template, return normal data
if isErrorCueRenderPathNotFound(err, renderAuxiliaryOutputsPath) {
return &app, res, nil
}
return nil, nil, errors.Wrap(err, "render app from output field from CUE")
}
err = auxiliaryContent.UnmarshalTo(&outputs)
if err != nil {
return nil, nil, errors.Wrap(err, "decode app from CUE")
}
for k, o := range outputs {
if ao, ok := o.(map[string]interface{}); ok {
auxO := &unstructured.Unstructured{Object: ao}
auxO.SetLabels(util.MergeMapOverrideWithDst(auxO.GetLabels(), map[string]string{oam.LabelAddonAuxiliaryName: k}))
res = append(res, auxO)
}
}
return &app, res, nil
}
// generateAppFramework generate application from yaml defined by template.yaml or cue file from template.cue
func generateAppFramework(addon *InstallPackage, parameters map[string]interface{}) (*v1beta1.Application, error) {
func generateAppFramework(addon *InstallPackage, parameters map[string]interface{}) (*v1beta1.Application, []*unstructured.Unstructured, error) {
if len(addon.AppCueTemplate.Data) != 0 && addon.AppTemplate != nil {
return nil, ErrBothCueAndYamlTmpl
return nil, nil, ErrBothCueAndYamlTmpl
}
var app *v1beta1.Application
var auxiliaryObjects []*unstructured.Unstructured
var err error
if len(addon.AppCueTemplate.Data) != 0 {
app, err = renderAppAccordingToCueTemplate(addon, parameters)
app, auxiliaryObjects, err = renderAppAccordingToCueTemplate(addon, parameters)
if err != nil {
return nil, err
return nil, nil, err
}
} else {
app = addon.AppTemplate
@@ -122,19 +197,20 @@ func generateAppFramework(addon *InstallPackage, parameters map[string]interface
}
app.Labels[oam.LabelAddonName] = addon.Name
app.Labels[oam.LabelAddonVersion] = addon.Version
return app, nil
for _, aux := range auxiliaryObjects {
aux.SetLabels(util.MergeMapOverrideWithDst(aux.GetLabels(), map[string]string{oam.LabelAddonName: addon.Name, oam.LabelAddonVersion: addon.Version}))
}
return app, auxiliaryObjects, nil
}
func renderAppAccordingToCueTemplate(addon *InstallPackage, args map[string]interface{}) (*v1beta1.Application, error) {
app := v1beta1.Application{}
func renderAppAccordingToCueTemplate(addon *InstallPackage, args map[string]interface{}) (*v1beta1.Application, []*unstructured.Unstructured, error) {
r := addonCueTemplateRender{
addon: addon,
inputArgs: args,
}
if err := r.toObject(addon.AppCueTemplate.Data, &app); err != nil {
return nil, err
}
return &app, nil
return r.renderApp()
}
// renderCompAccordingCUETemplate will return a component from cue template
@@ -145,8 +221,8 @@ func renderCompAccordingCUETemplate(cueTemplate ElementFile, addon *InstallPacka
addon: addon,
inputArgs: args,
}
if err := r.toObject(cueTemplate.Data, &comp); err != nil {
return nil, err
if err := r.toObject(cueTemplate.Data, renderOutputCuePath, &comp); err != nil {
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 {
@@ -157,19 +233,19 @@ func renderCompAccordingCUETemplate(cueTemplate ElementFile, addon *InstallPacka
}
// RenderApp render a K8s application
func RenderApp(ctx context.Context, addon *InstallPackage, k8sClient client.Client, args map[string]interface{}) (*v1beta1.Application, error) {
func RenderApp(ctx context.Context, addon *InstallPackage, k8sClient client.Client, args map[string]interface{}) (*v1beta1.Application, []*unstructured.Unstructured, error) {
if args == nil {
args = map[string]interface{}{}
}
app, err := generateAppFramework(addon, args)
app, auxiliaryObjects, err := generateAppFramework(addon, args)
if err != nil {
return nil, err
return nil, nil, err
}
app.Spec.Components = append(app.Spec.Components, renderNeededNamespaceAsComps(addon)...)
resources, err := renderResources(addon, args)
if err != nil {
return nil, err
return nil, nil, err
}
app.Spec.Components = append(app.Spec.Components, resources...)
@@ -177,10 +253,10 @@ func RenderApp(ctx context.Context, addon *InstallPackage, k8sClient client.Clie
// attach topology policy to application.
if checkNeedAttachTopologyPolicy(app, addon) {
if err := attachPolicyForLegacyAddon(ctx, app, addon, args, k8sClient); err != nil {
return nil, err
return nil, nil, err
}
}
return app, nil
return app, auxiliaryObjects, nil
}
func attachPolicyForLegacyAddon(ctx context.Context, app *v1beta1.Application, addon *InstallPackage, args map[string]interface{}, k8sClient client.Client) error {

View File

@@ -34,6 +34,116 @@ func TestRenderAppTemplate(t *testing.T) {
clusters?: [...string]
namespace: string
}`
resourceComponent1 := `
myref: {
type: "ref-objects"
properties: {
urls: ["https://hello.yaml"]
}
}
`
appTemplate := `output: {
apiVersion: "core.oam.dev/v1beta1"
kind: "Application"
metadata: {
name: "velaux"
namespace: "vela-system"
}
spec: {
components: [{
type: "k8s-objects"
name: "vela-namespace"
properties: objects: [{
apiVersion: "v1"
kind: "Namespace"
metadata: name: parameter.namespace
}]
},myref]
policies: [{
type: "shared-resource"
name: "namespace"
properties: rules: [{selector: resourceTypes: ["Namespace"]}]
}, {
type: "topology"
name: "deploy-topology"
properties: {
if parameter.clusters != _|_ {
clusters: parameter.clusters
}
if parameter.clusters == _|_ {
clusterLabelSelector: {}
}
namespace: parameter.namespace
}
}]
}
}`
addon := &InstallPackage{
Meta: Meta{
Name: "velaux",
DeployTo: &DeployTo{
RuntimeCluster: true,
},
},
Parameters: paraDefined,
CUETemplates: []ElementFile{{Data: resourceComponent1}},
AppCueTemplate: ElementFile{Data: appTemplate},
}
render := addonCueTemplateRender{
addon: addon,
inputArgs: map[string]interface{}{
"namespace": "vela-system",
},
}
app, _, err := render.renderApp()
assert.Equal(t, err.Error(), `load app template with CUE files: reference "myref" not found`)
assert.Nil(t, app)
addon.CUETemplates = []ElementFile{{Data: "package main\n" + resourceComponent1}}
app, _, err = render.renderApp()
assert.NoError(t, err)
assert.Equal(t, len(app.Spec.Components), 2)
str, err := json.Marshal(app.Spec.Components[0].Properties)
assert.NoError(t, err)
assert.True(t, strings.Contains(string(str), `{"name":"vela-system"}`))
str2, err := json.Marshal(app.Spec.Components[1].Properties)
assert.NoError(t, err)
assert.True(t, strings.Contains(string(str2), `{"urls":["https://hello.yaml"]}`))
assert.Equal(t, len(app.Spec.Policies), 2)
str, err = json.Marshal(app.Spec.Policies)
assert.NoError(t, err)
assert.True(t, strings.Contains(string(str), `"clusterLabelSelector":{}`))
addon.Parameters = "package newp\n" + paraDefined
addon.CUETemplates = []ElementFile{{Data: "package newp\n" + resourceComponent1}}
addon.AppCueTemplate = ElementFile{Data: "package newp\n" + appTemplate}
app, _, err = render.renderApp()
assert.NoError(t, err)
assert.Equal(t, len(app.Spec.Components), 2)
addon.CUETemplates = []ElementFile{{Data: "package main\n" + resourceComponent1}}
addon.Parameters = paraDefined
addon.AppCueTemplate = ElementFile{Data: appTemplate}
app, _, err = render.renderApp()
assert.NoError(t, err)
assert.Equal(t, len(app.Spec.Components), 2)
addon.CUETemplates = []ElementFile{{Data: "package hello\n" + resourceComponent1}}
addon.AppCueTemplate = ElementFile{Data: "package main\n" + appTemplate}
_, _, err = render.renderApp()
assert.Equal(t, err.Error(), `load app template with CUE files: reference "myref" not found`)
addon.CUETemplates = []ElementFile{{Data: "package hello\n" + resourceComponent1}}
addon.Parameters = paraDefined
addon.AppCueTemplate = ElementFile{Data: appTemplate}
_, _, err = render.renderApp()
assert.Equal(t, err.Error(), `load app template with CUE files: reference "myref" not found`)
}
func TestOutputsRender(t *testing.T) {
appTemplate := `output: {
apiVersion: "core.oam.dev/v1beta1"
kind: "Application"
@@ -69,7 +179,59 @@ func TestRenderAppTemplate(t *testing.T) {
}
}]
}
}`
},
outputs: configmap: {
apiVersion: "v1"
kind: "Configmap"
metadata: {
name: "test-cm"
namespace: "default"
}
data: parameter.data
}
`
paraDefined := `parameter: {
// +usage=The clusters to install
data: "myData"
}`
appTemplateNoOutputs := `output: {
apiVersion: "core.oam.dev/v1beta1"
kind: "Application"
metadata: {
name: "velaux"
namespace: "vela-system"
}
spec: {
components: [{
type: "k8s-objects"
name: "vela-namespace"
properties: objects: [{
apiVersion: "v1"
kind: "Namespace"
metadata: name: parameter.namespace
}]
}]
policies: [{
type: "shared-resource"
name: "namespace"
properties: rules: [{selector: resourceTypes: ["Namespace"]}]
}, {
type: "topology"
name: "deploy-topology"
properties: {
if parameter.clusters != _|_ {
clusters: parameter.clusters
}
if parameter.clusters == _|_ {
clusterLabelSelector: {}
}
namespace: parameter.namespace
}
}]
}
},
`
addon := &InstallPackage{
Meta: Meta{
Name: "velaux",
@@ -77,27 +239,32 @@ func TestRenderAppTemplate(t *testing.T) {
RuntimeCluster: true,
},
},
Parameters: paraDefined,
Parameters: paraDefined,
AppCueTemplate: ElementFile{Data: appTemplate},
}
render := addonCueTemplateRender{
addon: addon,
inputArgs: map[string]interface{}{
"namespace": "vela-system",
},
}
app := v1beta1.Application{}
err := render.toObject(appTemplate, &app)
app, auxdata, err := render.renderApp()
assert.NoError(t, err)
assert.Equal(t, len(app.Spec.Components), 1)
str, err := json.Marshal(app.Spec.Components[0].Properties)
assert.NoError(t, err)
assert.True(t, strings.Contains(string(str), `{"name":"vela-system"}`))
assert.Equal(t, len(app.Spec.Policies), 2)
str, err = json.Marshal(app.Spec.Policies)
assert.Equal(t, len(auxdata), 1)
auxStr, err := json.Marshal(auxdata[0])
assert.NoError(t, err)
assert.True(t, strings.Contains(string(auxStr), "myData"))
assert.True(t, strings.Contains(string(auxStr), "addons.oam.dev/auxiliary-name"))
assert.True(t, strings.Contains(string(auxStr), "configmap"))
// test no error when no outputs
addon.AppCueTemplate = ElementFile{Data: appTemplateNoOutputs}
_, _, err = render.renderApp()
assert.NoError(t, err)
assert.True(t, strings.Contains(string(str), `"clusterLabelSelector":{}`))
}
func TestAppComponentRender(t *testing.T) {
@@ -127,7 +294,7 @@ func TestAppComponentRender(t *testing.T) {
},
}
comp := common.ApplicationComponent{}
err := render.toObject(compTemplate, &comp)
err := render.toObject(compTemplate, renderOutputCuePath, &comp)
assert.NoError(t, err)
assert.Equal(t, comp.Name, "velaux")
assert.Equal(t, comp.Type, "webservice")
@@ -217,7 +384,7 @@ func TestGenerateAppFrameworkWithCue(t *testing.T) {
AppCueTemplate: ElementFile{Data: cueTemplate},
Parameters: paraDefined,
}
app, err := generateAppFramework(cueAddon, map[string]interface{}{
app, _, err := generateAppFramework(cueAddon, map[string]interface{}{
"namespace": "vela-system",
})
assert.NoError(t, err)
@@ -237,7 +404,7 @@ func TestGenerateAppFrameworkWithYamlTemplate(t *testing.T) {
Meta: Meta{Name: "velaux"},
AppTemplate: nil,
}
app, err := generateAppFramework(yamlAddon, nil)
app, _, err := generateAppFramework(yamlAddon, nil)
assert.NoError(t, err)
assert.Equal(t, app.Spec.Components != nil, true)
assert.Equal(t, len(app.Labels), 2)
@@ -246,20 +413,24 @@ func TestGenerateAppFrameworkWithYamlTemplate(t *testing.T) {
Meta: Meta{Name: "velaux"},
AppTemplate: &v1beta1.Application{},
}
app, err = generateAppFramework(noCompAddon, nil)
app, _, err = generateAppFramework(noCompAddon, nil)
assert.NoError(t, err)
assert.Equal(t, app.Spec.Components != nil, true)
assert.Equal(t, len(app.Labels), 2)
}
func TestRenderCueResourceError(t *testing.T) {
cueTemplate1 := `{
cueTemplate1 := `output: {
type: "webservice"
name: "velaux"
}`
cueTemplate2 := `output: {
type: "webservice"
name: "velaux"
name: "velaux2"
}`
cueTemplate3 := `nooutput: {
type: "webservice"
name: "velaux3"
}`
comp, err := renderResources(&InstallPackage{
CUETemplates: []ElementFile{
@@ -271,8 +442,12 @@ func TestRenderCueResourceError(t *testing.T) {
Data: cueTemplate2,
Name: "tmplaate2.cue",
},
{
Data: cueTemplate3,
Name: "tmplaate3.cue",
},
},
}, nil)
assert.NoError(t, err)
assert.Equal(t, len(comp), 1)
assert.Equal(t, len(comp), 2)
}

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"
}
}]
}
}

View File

@@ -24,16 +24,13 @@ import (
"path/filepath"
"strings"
errors "github.com/pkg/errors"
"helm.sh/helm/v3/pkg/chart"
"helm.sh/helm/v3/pkg/chartutil"
"sigs.k8s.io/yaml"
errors "github.com/pkg/errors"
"sigs.k8s.io/controller-runtime/pkg/client"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
rest "k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/pkg/definition"
@@ -279,23 +276,46 @@ func IsAddonDir(dirName string) (bool, error) {
return false, errors.Errorf("addon version is empty")
}
// Load template.yaml
templateYaml := filepath.Join(dirName, TemplateFileName)
if _, err := os.Stat(templateYaml); os.IsNotExist(err) {
return false, errors.Errorf("no %s exists in directory %q", TemplateFileName, dirName)
// Load template.yaml/cue
var errYAML error
var errCUE error
templateYAML := filepath.Join(dirName, TemplateFileName)
templateCUE := filepath.Join(dirName, AppTemplateCueFileName)
_, errYAML = os.Stat(templateYAML)
_, errCUE = os.Stat(templateCUE)
if os.IsNotExist(errYAML) && os.IsNotExist(errCUE) {
return false, fmt.Errorf("no %s or %s exists in directory %q", TemplateFileName, AppTemplateCueFileName, dirName)
}
templateYamlContent, err := ioutil.ReadFile(filepath.Clean(templateYaml))
if errYAML != nil && errCUE != nil {
return false, errors.Errorf("cannot stat %s or %s", TemplateFileName, AppTemplateCueFileName)
}
// template.cue have higher priority
if errCUE == nil {
templateContent, err := ioutil.ReadFile(filepath.Clean(templateCUE))
if err != nil {
return false, fmt.Errorf("cannot read %s: %w", AppTemplateCueFileName, err)
}
// Just look for `output` field is enough.
// No need to load the whole addon package to render the Application.
if !strings.Contains(string(templateContent), renderOutputCuePath) {
return false, fmt.Errorf("no %s field in %s", renderOutputCuePath, AppTemplateCueFileName)
}
return true, nil
}
// then check template.yaml
templateYamlContent, err := ioutil.ReadFile(filepath.Clean(templateYAML))
if err != nil {
return false, errors.Errorf("cannot read %s in directory %q", TemplateFileName, dirName)
}
// Check template.yaml contents
templateContent := new(v1beta1.Application)
if err := yaml.Unmarshal(templateYamlContent, &templateContent); err != nil {
template := new(v1beta1.Application)
if err := yaml.Unmarshal(templateYamlContent, &template); err != nil {
return false, err
}
if templateContent == nil {
return false, errors.Errorf("chart metadata (%s) missing", TemplateFileName)
if template == nil {
return false, errors.Errorf("template (%s) missing", TemplateFileName)
}
return true, nil
@@ -385,3 +405,7 @@ func generateAnnotation(meta *Meta) map[string]string {
}
return res
}
func isErrorCueRenderPathNotFound(err error, path string) bool {
return err.Error() == fmt.Sprintf("var(path=%s) not exist", path)
}

View File

@@ -23,19 +23,16 @@ import (
"strings"
"testing"
"helm.sh/helm/v3/pkg/chartutil"
"github.com/stretchr/testify/assert"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/stretchr/testify/assert"
"helm.sh/helm/v3/pkg/chartutil"
v1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"sigs.k8s.io/yaml"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
velatypes "github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/oam"
@@ -213,7 +210,7 @@ func TestIsAddonDir(t *testing.T) {
assert.Equal(t, isAddonDir, false)
assert.Contains(t, err.Error(), "addon version is empty")
// No template.yaml
// No metadata.yaml
meta = &Meta{
Name: "name",
Version: "1.0.0",
@@ -233,6 +230,13 @@ func TestIsAddonDir(t *testing.T) {
assert.Equal(t, isAddonDir, false)
assert.Contains(t, err.Error(), "missing")
// Empty template.cue
err = os.WriteFile(filepath.Join("testdata", "testaddon", AppTemplateCueFileName), []byte{}, 0644)
assert.NoError(t, err)
isAddonDir, err = IsAddonDir(filepath.Join("testdata", "testaddon"))
assert.Equal(t, isAddonDir, false)
assert.Contains(t, err.Error(), renderOutputCuePath)
// Pass all checks
cmd := InitCmd{
Path: filepath.Join("testdata", "testaddon2"),

View File

@@ -32,10 +32,17 @@ const (
// SystemInfo systemInfo model
type SystemInfo struct {
BaseModel
InstallID string `json:"installID"`
EnableCollection bool `json:"enableCollection"`
LoginType string `json:"loginType"`
StatisticInfo StatisticInfo `json:"statisticInfo,omitempty"`
InstallID string `json:"installID"`
EnableCollection bool `json:"enableCollection"`
StatisticInfo StatisticInfo `json:"statisticInfo,omitempty"`
LoginType string `json:"loginType"`
DexUserDefaultProjects []ProjectRef `json:"projects"`
}
// ProjectRef set the project name and roles
type ProjectRef struct {
Name string `json:"name"`
Roles []string `json:"roles"`
}
// UpdateDexConfig update dex config
@@ -50,6 +57,8 @@ type DexConfig struct {
Issuer string `json:"issuer"`
Web DexWeb `json:"web"`
Storage DexStorage `json:"storage"`
Telemetry Telemetry `json:"telemetry"`
Frontend WebConfig `json:"frontend"`
StaticClients []DexStaticClient `json:"staticClients"`
Connectors []map[string]interface{} `json:"connectors,omitempty"`
EnablePasswordDB bool `json:"enablePasswordDB"`
@@ -88,6 +97,26 @@ type DexStorageConfig struct {
// DexWeb dex web
type DexWeb struct {
HTTP string `json:"http"`
HTTPS string `json:"https"`
TLSCert string `json:"tlsCert"`
TLSKey string `json:"tlsKey"`
AllowedOrigins []string `json:"allowedOrigins"`
}
// WebConfig holds the server's frontend templates and asset configuration.
type WebConfig struct {
LogoURL string
// Defaults to "dex"
Issuer string
// Defaults to "light"
Theme string
}
// Telemetry is the config format for telemetry including the HTTP server config.
type Telemetry struct {
HTTP string `json:"http"`
}

View File

@@ -48,6 +48,7 @@ type User struct {
LastLoginTime time.Time `json:"lastLoginTime,omitempty"`
// UserRoles binding the platform level roles
UserRoles []string `json:"userRoles"`
DexSub string `json:"dexSub,omitempty"`
}
// TableName return custom table name
@@ -74,6 +75,9 @@ func (u *User) Index() map[string]string {
if u.Email != "" {
index["email"] = u.Email
}
if u.DexSub != "" {
index["dexSub"] = u.DexSub
}
return index
}

View File

@@ -23,6 +23,7 @@ import (
"fmt"
"net/http"
"reflect"
"strings"
"time"
"github.com/coreos/go-oidc"
@@ -68,10 +69,12 @@ type AuthenticationService interface {
}
type authenticationServiceImpl struct {
SysService SystemInfoService `inject:""`
UserService UserService `inject:""`
Store datastore.DataStore `inject:"datastore"`
KubeClient client.Client `inject:"kubeClient"`
SysService SystemInfoService `inject:""`
UserService UserService `inject:""`
ProjectService ProjectService `inject:""`
SystemInfoService SystemInfoService `inject:""`
Store datastore.DataStore `inject:"datastore"`
KubeClient client.Client `inject:"kubeClient"`
}
// NewAuthenticationService new authentication service
@@ -84,8 +87,10 @@ type authHandler interface {
}
type dexHandlerImpl struct {
idToken *oidc.IDToken
Store datastore.DataStore
idToken *oidc.IDToken
Store datastore.DataStore
projectService ProjectService
systemInfoService SystemInfoService
}
type localHandlerImpl struct {
@@ -124,8 +129,10 @@ func (a *authenticationServiceImpl) newDexHandler(ctx context.Context, req apisv
return nil, err
}
return &dexHandlerImpl{
idToken: idToken,
Store: a.Store,
idToken: idToken,
Store: a.Store,
projectService: a.ProjectService,
systemInfoService: a.SystemInfoService,
}, nil
}
@@ -150,13 +157,13 @@ func (a *authenticationServiceImpl) Login(ctx context.Context, loginReq apisv1.L
}
loginType := sysInfo.LoginType
switch loginType {
case model.LoginTypeDex:
switch {
case loginType == model.LoginTypeDex || (loginReq.Code != "" && loginReq.Username == ""):
handler, err = a.newDexHandler(ctx, loginReq)
if err != nil {
return nil, err
}
case model.LoginTypeLocal:
case loginType == model.LoginTypeLocal:
handler, err = a.newLocalHandler(loginReq)
if err != nil {
return nil, err
@@ -282,6 +289,11 @@ func generateDexConfig(ctx context.Context, kubeClient client.Client, update *mo
if len(update.StaticPasswords) > 0 {
dexConfig.StaticPasswords = update.StaticPasswords
}
// This is the title that the dex login page.
// It will be: Log in to KubeVela
if dexConfig.Frontend.Issuer == "" {
dexConfig.Frontend.Issuer = "KubeVela"
}
config, err := model.NewJSONStructByStruct(dexConfig)
if err != nil {
return err
@@ -317,7 +329,7 @@ func initDexConfig(ctx context.Context, kubeClient client.Client, velaAddress st
StaticClients: []model.DexStaticClient{
{
ID: "velaux",
Name: "Vela UX",
Name: "VelaUX",
Secret: "velaux-secret",
RedirectURIs: []string{fmt.Sprintf("%s/callback", velaAddress)},
},
@@ -397,9 +409,13 @@ func getDexConfig(ctx context.Context, kubeClient client.Client) (*model.DexConf
Namespace: velatypes.DefaultKubeVelaNS,
}, dexConfigSecret); err != nil {
if kerrors.IsNotFound(err) {
return nil, bcode.ErrDexConfigNotFound
dexConfigSecret, err = initDexConfig(ctx, kubeClient, "http://velaux.com")
if err != nil {
return nil, err
}
} else {
return nil, err
}
return nil, err
}
if dexConfigSecret.Data == nil {
return nil, bcode.ErrInvalidDexConfig
@@ -433,31 +449,68 @@ func (a *authenticationServiceImpl) GetLoginType(ctx context.Context) (*apisv1.G
func (d *dexHandlerImpl) login(ctx context.Context) (*apisv1.UserBase, error) {
var claims struct {
Email string `json:"email"`
Name string `json:"name"`
// Name End-User's full name in displayable form including all name parts, possibly including titles and suffixes, ordered according to the End-User's locale and preferences.
Name string `json:"name"`
// Subject - Identifier for the End-User at the Issuer.
Sub string `json:"sub"`
}
if err := d.idToken.Claims(&claims); err != nil {
return nil, err
}
user := &model.User{Email: claims.Email}
userBase := &apisv1.UserBase{Email: claims.Email, Name: claims.Name}
users, err := d.Store.List(ctx, user, &datastore.ListOptions{})
if err != nil {
return nil, err
var users []datastore.Entity
var err error
if claims.Email != "" {
user := &model.User{Email: claims.Email}
users, err = d.Store.List(ctx, user, &datastore.ListOptions{})
if err != nil {
return nil, err
}
}
if len(users) == 0 && claims.Sub != "" {
// Support query the user by the subject
user := &model.User{DexSub: claims.Sub}
users, err = d.Store.List(ctx, user, &datastore.ListOptions{})
if err != nil {
return nil, err
}
}
var userBase *apisv1.UserBase
if len(users) > 0 {
u := users[0].(*model.User)
u.LastLoginTime = time.Now()
u.DexSub = claims.Sub
if err := d.Store.Put(ctx, u); err != nil {
return nil, err
}
userBase.Name = u.Name
} else if err := d.Store.Add(ctx, &model.User{
Email: claims.Email,
Name: claims.Name,
LastLoginTime: time.Now(),
}); err != nil {
return nil, err
userBase = convertUserBase(u)
} else {
user := &model.User{
Email: claims.Email,
Name: strings.ToLower(claims.Sub),
DexSub: claims.Sub,
Alias: claims.Name,
LastLoginTime: time.Now(),
}
if err := d.Store.Add(ctx, user); err != nil {
log.Logger.Errorf("failed to save the user from the dex: %s", err.Error())
return nil, err
}
systemInfo, err := d.systemInfoService.GetSystemInfo(ctx)
if err != nil {
log.Logger.Errorf("failed to get the system info %s", err.Error())
}
if systemInfo != nil {
for _, project := range systemInfo.DexUserDefaultProjects {
_, err := d.projectService.AddProjectUser(ctx, project.Name, apisv1.AddProjectUserRequest{
UserName: claims.Sub,
UserRoles: project.Roles,
})
if err != nil {
log.Logger.Errorf("failed to add a user to project %s", err.Error())
}
}
}
userBase = convertUserBase(user)
}
return userBase, nil

View File

@@ -19,6 +19,7 @@ package service
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"reflect"
"strconv"
@@ -45,10 +46,11 @@ import (
var _ = Describe("Test authentication service functions", func() {
var (
authService *authenticationServiceImpl
userService *userServiceImpl
sysService *systemInfoServiceImpl
ds datastore.DataStore
authService *authenticationServiceImpl
userService *userServiceImpl
sysService *systemInfoServiceImpl
projectService ProjectService
ds datastore.DataStore
)
BeforeEach(func() {
@@ -59,42 +61,87 @@ var _ = Describe("Test authentication service functions", func() {
authService = &authenticationServiceImpl{KubeClient: k8sClient, Store: ds}
sysService = &systemInfoServiceImpl{Store: ds, KubeClient: k8sClient}
userService = &userServiceImpl{Store: ds, SysService: sysService}
projectService = NewTestProjectService(ds, k8sClient)
})
It("Test Dex login", func() {
testIDToken := &oidc.IDToken{}
sub := "248289761001"
patch := ApplyMethod(reflect.TypeOf(testIDToken), "Claims", func(_ *oidc.IDToken, v interface{}) error {
return json.Unmarshal([]byte(`{"email":"test@test.com","name":"test"}`), v)
return json.Unmarshal([]byte(fmt.Sprintf(`{"email":"test@test.com", "name":"show name", "sub": "%s"}`, sub)), v)
})
defer patch.Reset()
err := sysService.Init(context.TODO())
Expect(err).Should(BeNil())
err = userService.Init(context.TODO())
Expect(err).Should(BeNil())
err = projectService.Init(context.TODO())
Expect(err).Should(BeNil())
_, err = sysService.UpdateSystemInfo(context.TODO(), apisv1.SystemInfoRequest{
LoginType: "local",
DexUserDefaultProjects: []model.ProjectRef{{
Name: "default",
Roles: []string{"app-developer"},
}},
})
Expect(err).Should(BeNil())
dexHandler := dexHandlerImpl{
idToken: testIDToken,
Store: ds,
idToken: testIDToken,
Store: ds,
projectService: projectService,
systemInfoService: sysService,
}
resp, err := dexHandler.login(context.Background())
Expect(err).Should(BeNil())
Expect(resp.Email).Should(Equal("test@test.com"))
Expect(resp.Name).Should(Equal("test"))
Expect(resp.Name).Should(Equal(sub))
Expect(resp.Alias).Should(Equal("show name"))
projects, err := projectService.ListUserProjects(context.TODO(), sub)
Expect(err).Should(BeNil())
Expect(len(projects)).Should(Equal(1))
user := &model.User{
Name: "test",
Name: sub,
}
err = ds.Get(context.Background(), user)
Expect(err).Should(BeNil())
Expect(user.Email).Should(Equal("test@test.com"))
existUser := &model.User{
Name: "test",
Name: sub,
}
err = ds.Delete(context.Background(), existUser)
Expect(err).Should(BeNil())
existUser.Name = "exist-user"
existUser.Email = "test@test.com"
existUser = &model.User{
Name: "exist-user",
Email: "test@test.com",
}
err = ds.Add(context.Background(), existUser)
Expect(err).Should(BeNil())
resp, err = dexHandler.login(context.Background())
Expect(err).Should(BeNil())
Expect(resp.Email).Should(Equal("test@test.com"))
Expect(resp.Name).Should(Equal("exist-user"))
err = ds.Delete(context.Background(), existUser)
Expect(err).Should(BeNil())
existUser = &model.User{
Name: "zhangsan",
Email: "test2@test.com",
DexSub: sub,
}
err = ds.Add(context.Background(), existUser)
Expect(err).Should(BeNil())
resp, err = dexHandler.login(context.Background())
Expect(err).Should(BeNil())
Expect(resp.Email).Should(Equal("test2@test.com"))
Expect(resp.Name).Should(Equal("zhangsan"))
})
It("Test local login", func() {

View File

@@ -383,9 +383,28 @@ func (p *projectServiceImpl) ListProjectUser(ctx context.Context, projectName st
if err != nil {
return nil, err
}
var usernames []string
for _, entity := range entities {
usernames = append(usernames, entity.(*model.ProjectUser).Username)
}
var userMap = make(map[string]*model.User, len(usernames))
if len(usernames) > 0 {
users, _ := p.Store.List(ctx, &model.User{}, &datastore.ListOptions{
FilterOptions: datastore.FilterOptions{
In: []datastore.InQueryOption{
{Key: "name", Values: usernames},
},
},
})
for i := range users {
user := users[i].(*model.User)
userMap[user.Name] = user
}
}
var res apisv1.ListProjectUsersResponse
for _, entity := range entities {
res.Users = append(res.Users, ConvertProjectUserModel2Base(entity.(*model.ProjectUser)))
projectUser := entity.(*model.ProjectUser)
res.Users = append(res.Users, ConvertProjectUserModel2Base(projectUser, userMap[projectUser.Username]))
}
count, err := p.Store.Count(ctx, &projectUser, nil)
if err != nil {
@@ -400,7 +419,7 @@ func (p *projectServiceImpl) AddProjectUser(ctx context.Context, projectName str
if err != nil {
return nil, err
}
_, err = p.UserService.GetUser(ctx, req.UserName)
user, err := p.UserService.GetUser(ctx, req.UserName)
if err != nil {
return nil, err
}
@@ -428,7 +447,7 @@ func (p *projectServiceImpl) AddProjectUser(ctx context.Context, projectName str
}
return nil, err
}
return ConvertProjectUserModel2Base(&projectUser), nil
return ConvertProjectUserModel2Base(&projectUser, user), nil
}
func (p *projectServiceImpl) DeleteProjectUser(ctx context.Context, projectName string, userName string) error {
@@ -454,6 +473,10 @@ func (p *projectServiceImpl) UpdateProjectUser(ctx context.Context, projectName
if err != nil {
return nil, err
}
user, err := p.UserService.GetUser(ctx, userName)
if err != nil {
return nil, err
}
// check user roles
for _, role := range req.UserRoles {
var projectUser = model.Role{
@@ -481,7 +504,7 @@ func (p *projectServiceImpl) UpdateProjectUser(ctx context.Context, projectName
if err := p.Store.Put(ctx, &projectUser); err != nil {
return nil, err
}
return ConvertProjectUserModel2Base(&projectUser), nil
return ConvertProjectUserModel2Base(&projectUser, user), nil
}
func (p *projectServiceImpl) GetConfigs(ctx context.Context, projectName, configType string) ([]*apisv1.Config, error) {
@@ -592,13 +615,16 @@ func ConvertProjectModel2Base(project *model.Project, owner *model.User) *apisv1
}
// ConvertProjectUserModel2Base convert project user model to base struct
func ConvertProjectUserModel2Base(user *model.ProjectUser) *apisv1.ProjectUserBase {
func ConvertProjectUserModel2Base(user *model.ProjectUser, userModel *model.User) *apisv1.ProjectUserBase {
base := &apisv1.ProjectUserBase{
UserName: user.Username,
UserRoles: user.UserRoles,
CreateTime: user.CreateTime,
UpdateTime: user.UpdateTime,
}
if userModel != nil {
base.UserAlias = userModel.Alias
}
return base
}

View File

@@ -227,6 +227,11 @@ var _ = Describe("Test project service functions", func() {
UserRoles: []string{"project-admin"},
})
Expect(err).Should(BeNil())
users, err := projectService.ListProjectUser(context.TODO(), "test-project", 0, 0)
Expect(err).Should(BeNil())
Expect(len(users.Users)).Should(Equal(1))
Expect(users.Users[0].UserAlias).Should(Equal("Administrator"))
})
It("Test Update project user function", func() {

View File

@@ -113,7 +113,8 @@ func (u systemInfoServiceImpl) UpdateSystemInfo(ctx context.Context, sysInfo v1.
CreateTime: info.CreateTime,
UpdateTime: time.Now(),
},
StatisticInfo: info.StatisticInfo,
StatisticInfo: info.StatisticInfo,
DexUserDefaultProjects: sysInfo.DexUserDefaultProjects,
}
if sysInfo.LoginType == model.LoginTypeDex {
@@ -166,9 +167,10 @@ func (u systemInfoServiceImpl) Init(ctx context.Context) error {
func convertInfoToBase(info *model.SystemInfo) v1.SystemInfo {
return v1.SystemInfo{
PlatformID: info.InstallID,
EnableCollection: info.EnableCollection,
LoginType: info.LoginType,
InstallTime: info.CreateTime,
PlatformID: info.InstallID,
EnableCollection: info.EnableCollection,
LoginType: info.LoginType,
InstallTime: info.CreateTime,
DexUserDefaultProjects: info.DexUserDefaultProjects,
}
}

View File

@@ -192,18 +192,17 @@ func (u *userServiceImpl) UpdateUser(ctx context.Context, user *model.User, req
if err != nil {
return nil, err
}
if sysInfo.LoginType == model.LoginTypeDex {
return nil, bcode.ErrUserCannotModified
}
if req.Alias != "" {
user.Alias = req.Alias
}
if req.Password != "" {
hash, err := GeneratePasswordHash(req.Password)
if err != nil {
return nil, err
if sysInfo.LoginType != model.LoginTypeDex {
if req.Password != "" {
hash, err := GeneratePasswordHash(req.Password)
if err != nil {
return nil, err
}
user.Password = hash
}
user.Password = hash
}
if req.Email != "" {
if user.Email != "" {
@@ -211,6 +210,7 @@ func (u *userServiceImpl) UpdateUser(ctx context.Context, user *model.User, req
}
user.Email = req.Email
}
// TODO: validate the roles, they must be platform roles
if req.Roles != nil {
user.UserRoles = *req.Roles

View File

@@ -82,8 +82,13 @@ func (v *velaQLServiceImpl) QueryView(ctx context.Context, velaQL string) (*apis
return nil, bcode.ErrParseQuery2Json
}
if strings.Contains(velaQL, "collect-logs") {
enc, _ := base64.StdEncoding.DecodeString(resp["logs"].(string))
resp["logs"] = string(enc)
logs, ok := resp["logs"].(string)
if ok {
enc, _ := base64.StdEncoding.DecodeString(logs)
resp["logs"] = string(enc)
} else {
resp["logs"] = ""
}
}
return &resp, err
}

View File

@@ -77,6 +77,11 @@ func (c *CR2UX) shouldSync(ctx context.Context, targetApp *v1beta1.Application,
}
}
// if no LabelSourceOfTruth label, it means the app is existing ones, check the existing labels and annotations
if _, appName := targetApp.Annotations[oam.AnnotationAppName]; appName {
return false
}
key := formatAppComposedName(targetApp.Name, targetApp.Namespace)
cachedData, ok := c.cache.Load(key)
if ok {

View File

@@ -27,35 +27,8 @@ import (
"github.com/oam-dev/kubevela/pkg/apiserver/domain/service"
"github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore"
"github.com/oam-dev/kubevela/pkg/apiserver/utils/log"
"github.com/oam-dev/kubevela/pkg/oam"
)
// CheckSoTFromCR will check the source of truth of the application
func CheckSoTFromCR(targetApp *v1beta1.Application) string {
if sot := targetApp.Annotations[model.LabelSourceOfTruth]; sot != "" {
return sot
}
// if no LabelSourceOfTruth label, it means the app is existing ones, check the existing labels and annotations
if _, appName := targetApp.Annotations[oam.AnnotationAppName]; appName {
return model.FromUX
}
// no labels mean it's created by K8s resources.
return model.FromCR
}
// CheckSoTFromAppMeta will check the source of truth marked in datastore
func (c *CR2UX) CheckSoTFromAppMeta(ctx context.Context, appName, namespace string, sotFromCR string) string {
app, _, err := c.getApp(ctx, appName, namespace)
if err != nil {
return sotFromCR
}
if app.Labels == nil || app.Labels[model.LabelSourceOfTruth] == "" {
return sotFromCR
}
return app.Labels[model.LabelSourceOfTruth]
}
// getApp will return the app and appname if exists
func (c *CR2UX) getApp(ctx context.Context, name, namespace string) (*model.Application, string, error) {
alreadyCreated := &model.Application{Name: formatAppComposedName(name, namespace)}

View File

@@ -1194,10 +1194,11 @@ type SystemInfoResponse struct {
// SystemInfo system info
type SystemInfo struct {
PlatformID string `json:"platformID"`
EnableCollection bool `json:"enableCollection"`
LoginType string `json:"loginType"`
InstallTime time.Time `json:"installTime,omitempty"`
PlatformID string `json:"platformID"`
EnableCollection bool `json:"enableCollection"`
LoginType string `json:"loginType" validate:"oneof=dex local"`
InstallTime time.Time `json:"installTime,omitempty"`
DexUserDefaultProjects []model.ProjectRef `json:"dexUserDefaultProjects,omitempty"`
}
// StatisticInfo generated by cronJob running in backend
@@ -1214,9 +1215,10 @@ type StatisticInfo struct {
// SystemInfoRequest request by update SystemInfo
type SystemInfoRequest struct {
EnableCollection bool `json:"enableCollection"`
LoginType string `json:"loginType"`
VelaAddress string `json:"velaAddress,omitempty"`
EnableCollection bool `json:"enableCollection"`
LoginType string `json:"loginType"`
VelaAddress string `json:"velaAddress,omitempty"`
DexUserDefaultProjects []model.ProjectRef `json:"dexUserDefaultProjects,omitempty"`
}
// SystemVersion contains KubeVela version
@@ -1273,6 +1275,7 @@ type DetailUserResponse struct {
// ProjectUserBase project user base
type ProjectUserBase struct {
UserName string `json:"name"`
UserAlias string `json:"alias"`
UserRoles []string `json:"userRoles"`
CreateTime time.Time `json:"createTime"`
UpdateTime time.Time `json:"updateTime"`

View File

@@ -28,6 +28,7 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
ktypes "k8s.io/apimachinery/pkg/types"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
@@ -38,6 +39,7 @@ import (
"github.com/oam-dev/kubevela/pkg/component"
"github.com/oam-dev/kubevela/pkg/cue/definition"
"github.com/oam-dev/kubevela/pkg/cue/packages"
"github.com/oam-dev/kubevela/pkg/features"
monitorContext "github.com/oam-dev/kubevela/pkg/monitor/context"
"github.com/oam-dev/kubevela/pkg/monitor/metrics"
"github.com/oam-dev/kubevela/pkg/oam"
@@ -350,6 +352,9 @@ func (p *Parser) parseReferredObjects(ctx context.Context, af *Appfile) error {
}
af.ReferredObjects = component.AppendUnstructuredObjects(af.ReferredObjects, objs...)
}
if utilfeature.DefaultMutableFeatureGate.Enabled(features.DisableReferObjectsFromURL) && len(spec.URLs) > 0 {
return fmt.Errorf("referring objects from url is disabled")
}
for _, url := range spec.URLs {
objs, err := utilscommon.HTTPGetKubernetesObjects(ctx, url)
if err != nil {

View File

@@ -72,14 +72,14 @@ type Template struct {
func LoadTemplate(ctx context.Context, dm discoverymapper.DiscoveryMapper, cli client.Reader, capName string, capType types.CapType) (*Template, error) {
// Application Controller only load template from ComponentDefinition and TraitDefinition
switch capType {
case types.TypeComponentDefinition:
case types.TypeComponentDefinition, types.TypeWorkload:
cd := new(v1beta1.ComponentDefinition)
err := oamutil.GetCapabilityDefinition(ctx, cli, cd, capName)
if err != nil {
if kerrors.IsNotFound(err) {
wd := new(v1beta1.WorkloadDefinition)
if err := oamutil.GetDefinition(ctx, cli, wd, capName); err != nil {
return nil, errors.WithMessagef(err, "LoadTemplate from workloadDefinition [%s] ", capName)
return nil, errors.WithMessagef(err, "load template from component definition [%s] ", capName)
}
tmpl, err := newTemplateOfWorkloadDefinition(wd)
if err != nil {
@@ -87,7 +87,7 @@ func LoadTemplate(ctx context.Context, dm discoverymapper.DiscoveryMapper, cli c
}
gvk, err := oamutil.GetGVKFromDefinition(dm, wd.Spec.Reference)
if err != nil {
return nil, errors.WithMessagef(err, "Get GVK from workload definition [%s]", capName)
return nil, errors.WithMessagef(err, "get group version kind from component definition [%s]", capName)
}
tmpl.Reference = common.WorkloadTypeDescriptor{
Definition: common.WorkloadGVK{
@@ -100,7 +100,7 @@ func LoadTemplate(ctx context.Context, dm discoverymapper.DiscoveryMapper, cli c
}
return tmpl, nil
}
return nil, errors.WithMessagef(err, "LoadTemplate from ComponentDefinition [%s] ", capName)
return nil, errors.WithMessagef(err, "load template from component definition [%s] ", capName)
}
tmpl, err := newTemplateOfCompDefinition(cd)
if err != nil {
@@ -112,7 +112,7 @@ func LoadTemplate(ctx context.Context, dm discoverymapper.DiscoveryMapper, cli c
td := new(v1beta1.TraitDefinition)
err := oamutil.GetCapabilityDefinition(ctx, cli, td, capName)
if err != nil {
return nil, errors.WithMessagef(err, "LoadTemplate [%s] ", capName)
return nil, errors.WithMessagef(err, "load template from trait definition [%s] ", capName)
}
tmpl, err := newTemplateOfTraitDefinition(td)
if err != nil {
@@ -123,7 +123,7 @@ func LoadTemplate(ctx context.Context, dm discoverymapper.DiscoveryMapper, cli c
d := new(v1beta1.PolicyDefinition)
err := oamutil.GetCapabilityDefinition(ctx, cli, d, capName)
if err != nil {
return nil, errors.WithMessagef(err, "LoadTemplate [%s] ", capName)
return nil, errors.WithMessagef(err, "load template from policy definition [%s] ", capName)
}
tmpl, err := newTemplateOfPolicyDefinition(d)
if err != nil {
@@ -134,7 +134,7 @@ func LoadTemplate(ctx context.Context, dm discoverymapper.DiscoveryMapper, cli c
d := new(v1beta1.WorkflowStepDefinition)
err := oamutil.GetCapabilityDefinition(ctx, cli, d, capName)
if err != nil {
return nil, errors.WithMessagef(err, "LoadTemplate [%s] ", capName)
return nil, errors.WithMessagef(err, "load template from workflow step definition [%s] ", capName)
}
tmpl, err := newTemplateOfWorkflowStepDefinition(d)
if err != nil {
@@ -143,8 +143,6 @@ func LoadTemplate(ctx context.Context, dm discoverymapper.DiscoveryMapper, cli c
return tmpl, nil
case types.TypeScope:
// TODO: add scope template support
default:
return nil, fmt.Errorf("kind(%s) of %s not supported", capType, capName)
}
return nil, fmt.Errorf("kind(%s) of %s not supported", capType, capName)
}
@@ -161,7 +159,7 @@ func LoadTemplateFromRevision(capName string, capType types.CapType, apprev *v1b
if !ok {
wd, ok := apprev.Spec.WorkloadDefinitions[capName]
if !ok {
return nil, errors.Errorf("ComponentDefinition [%s] not found in app revision %s", capName, apprev.Name)
return nil, errors.Errorf("component definition [%s] not found in app revision %s", capName, apprev.Name)
}
tmpl, err := newTemplateOfWorkloadDefinition(&wd)
if err != nil {
@@ -169,7 +167,7 @@ func LoadTemplateFromRevision(capName string, capType types.CapType, apprev *v1b
}
gvk, err := oamutil.GetGVKFromDefinition(dm, wd.Spec.Reference)
if err != nil {
return nil, errors.WithMessagef(err, "Get GVK from workload definition [%s]", capName)
return nil, errors.WithMessagef(err, "Get group version kind from component definition [%s]", capName)
}
tmpl.Reference = common.WorkloadTypeDescriptor{
Definition: common.WorkloadGVK{

View File

@@ -29,6 +29,9 @@ import (
"github.com/oam-dev/kubevela/pkg/cue/packages"
)
// ErrParameterNotExist represents the parameter field is not exist in CUE template
var ErrParameterNotExist = errors.New("parameter not exist")
// GetParameters get parameter from cue template
func GetParameters(templateStr string, pd *packages.PackageDiscover) ([]types.Parameter, error) {
var template *cue.Instance
@@ -66,7 +69,7 @@ func GetParameters(templateStr string, pd *packages.PackageDiscover) ([]types.Pa
}
}
if !found {
return nil, errors.New("arguments not exist")
return nil, ErrParameterNotExist
}
arguments, err := paraDef.Value.Struct()
if err != nil {

View File

@@ -36,6 +36,9 @@ import (
"github.com/oam-dev/kubevela/pkg/stdlib"
)
// DefaultPackageHeader describes the default package header for CUE files.
const DefaultPackageHeader = "package main\n"
// Value is an object with cue.runtime and vendors
type Value struct {
v cue.Value
@@ -75,6 +78,50 @@ func (val *Value) UnmarshalTo(x interface{}) error {
return json.Unmarshal(data, x)
}
// NewValueWithMainAndFiles new a value from main and appendix files
func NewValueWithMainAndFiles(main string, slaveFiles []string, pd *packages.PackageDiscover, tagTempl string, opts ...func(*ast.File) error) (*Value, error) {
builder := &build.Instance{}
mainFile, err := parser.ParseFile("main.cue", main, parser.ParseComments)
if err != nil {
return nil, errors.Wrap(err, "parse main file")
}
if mainFile.PackageName() == "" {
// add a default package main if not exist
mainFile, err = parser.ParseFile("main.cue", DefaultPackageHeader+main, parser.ParseComments)
if err != nil {
return nil, errors.Wrap(err, "parse main file with added package main header")
}
}
for _, opt := range opts {
if err := opt(mainFile); err != nil {
return nil, errors.Wrap(err, "run option func for main file")
}
}
if err := builder.AddSyntax(mainFile); err != nil {
return nil, errors.Wrap(err, "add main file to CUE builder")
}
for idx, sf := range slaveFiles {
cueSF, err := parser.ParseFile("sf-"+strconv.Itoa(idx)+".cue", sf, parser.ParseComments)
if err != nil {
return nil, errors.Wrap(err, "parse added file "+strconv.Itoa(idx)+" \n"+sf)
}
if cueSF.PackageName() != mainFile.PackageName() {
continue
}
for _, opt := range opts {
if err := opt(cueSF); err != nil {
return nil, errors.Wrap(err, "run option func for files")
}
}
if err := builder.AddSyntax(cueSF); err != nil {
return nil, errors.Wrap(err, "add slave files to CUE builder")
}
}
return newValue(builder, pd, tagTempl)
}
// NewValue new a value
func NewValue(s string, pd *packages.PackageDiscover, tagTempl string, opts ...func(*ast.File) error) (*Value, error) {
builder := &build.Instance{}
@@ -91,6 +138,10 @@ func NewValue(s string, pd *packages.PackageDiscover, tagTempl string, opts ...f
if err := builder.AddSyntax(file); err != nil {
return nil, err
}
return newValue(builder, pd, tagTempl)
}
func newValue(builder *build.Instance, pd *packages.PackageDiscover, tagTempl string) (*Value, error) {
addImports := func(inst *build.Instance) error {
if pd != nil {
pd.ImportBuiltinPackagesFor(inst)

View File

@@ -40,6 +40,8 @@ const (
// LegacyResourceOwnerValidation if enabled, the resource dispatch will allow existing resource not to have owner
// application and the current application will take over it
LegacyResourceOwnerValidation featuregate.Feature = "LegacyResourceOwnerValidation"
// DisableReferObjectsFromURL if set, the url ref objects will be disallowed
DisableReferObjectsFromURL featuregate.Feature = "DisableReferObjectsFromURL"
// Edge Features
@@ -55,6 +57,7 @@ var defaultFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{
EnableSuspendOnFailure: {Default: false, PreRelease: featuregate.Alpha},
LegacyComponentRevision: {Default: false, PreRelease: featuregate.Alpha},
LegacyResourceOwnerValidation: {Default: false, PreRelease: featuregate.Alpha},
DisableReferObjectsFromURL: {Default: false, PreRelease: featuregate.Alpha},
AuthenticateApplication: {Default: false, PreRelease: featuregate.Alpha},
}

View File

@@ -69,6 +69,9 @@ const (
// LabelAddonName indicates the name of the corresponding Addon
LabelAddonName = "addons.oam.dev/name"
// LabelAddonAuxiliaryName indicates the name of the auxiliary resource in addon app template
LabelAddonAuxiliaryName = "addons.oam.dev/auxiliary-name"
// LabelAddonVersion indicates the version of the corresponding installed Addon
LabelAddonVersion = "addons.oam.dev/version"

View File

@@ -271,7 +271,7 @@ func GetCUEParameterValue(cueStr string, pd *packages.PackageDiscover) (cue.Valu
}
}
if !found {
return cue.Value{}, errors.New("parameter not exist")
return cue.Value{}, velacue.ErrParameterNotExist
}
arguments := paraDef.Value
@@ -394,8 +394,7 @@ func clusterObjectReferenceTypeFilterGenerator(allowedKinds ...string) clusterOb
}
var isWorkloadClusterObjectReferenceFilter = clusterObjectReferenceTypeFilterGenerator("Deployment", "StatefulSet", "CloneSet", "Job", "Configuration")
var isPortForwardEndpointClusterObjectReferenceFilter = clusterObjectReferenceTypeFilterGenerator("Deployment",
"StatefulSet", "CloneSet", "Job", "Service", "HelmRelease")
var resourceNameClusterObjectReferenceFilter = func(resourceName []string) clusterObjectReferenceFilter {
return func(reference common.ClusterObjectReference) bool {
if len(resourceName) == 0 {
@@ -533,14 +532,6 @@ func AskToChooseOneEnvResource(app *v1beta1.Application, resourceName ...string)
return askToChooseOneResource(app, filters...)
}
// AskToChooseOnePortForwardEndpoint will ask user to select one applied resource as port forward endpoint
func AskToChooseOnePortForwardEndpoint(app *v1beta1.Application, resourceName ...string) (*common.ClusterObjectReference, error) {
filters := []clusterObjectReferenceFilter{isPortForwardEndpointClusterObjectReferenceFilter}
_resourceName := removeEmptyString(resourceName)
filters = append(filters, resourceNameClusterObjectReferenceFilter(_resourceName))
return askToChooseOneResource(app, filters...)
}
func askToChooseOneInApplication(category string, options []string) (decision string, err error) {
if len(options) == 0 {
return "", fmt.Errorf("no %s exists in the application", category)

View File

@@ -47,6 +47,9 @@ type ServiceEndpoint struct {
// String return endpoint URL
func (s *ServiceEndpoint) String() string {
if s.Endpoint.Host == "" && s.Endpoint.Port == 0 {
return "-"
}
protocol := strings.ToLower(string(s.Endpoint.Protocol))
if s.Endpoint.AppProtocol != nil && *s.Endpoint.AppProtocol != "" {
protocol = *s.Endpoint.AppProtocol

View File

@@ -134,7 +134,7 @@ func (h *provider) ApplyInParallel(ctx wfContext.Context, v *value.Value, act ty
deployCtx := multicluster.ContextWithClusterName(context.Background(), cluster)
deployCtx = auth.ContextWithUserInfo(deployCtx, h.app)
if err = h.apply(deployCtx, cluster, common.WorkflowResourceCreator, workloads...); err != nil {
return v.FillObject(err, "err")
return err
}
return nil
}

View File

@@ -19,7 +19,7 @@ package template
import (
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/utils/common"
"github.com/oam-dev/kubevela/references/plugins"
"github.com/oam-dev/kubevela/references/docgen"
)
// Manager defines a manager for template
@@ -30,7 +30,7 @@ type Manager interface {
// Load will load all installed capabilities and create a manager
func Load(namespace string, c common.Args) (Manager, error) {
caps, err := plugins.LoadAllInstalledCapability(namespace, c)
caps, err := docgen.LoadAllInstalledCapability(namespace, c)
if err != nil {
return nil, err
}

View File

@@ -393,15 +393,8 @@ func NewAddonStatusCommand(c common.Args, ioStream cmdutil.IOStreams) *cobra.Com
// NewAddonInitCommand creates an addon scaffold
func NewAddonInitCommand() *cobra.Command {
var (
helmRepoURL string
chartName string
chartVersion string
urls []string
path string
noSample bool
overwrite bool
)
var path string
initCmd := pkgaddon.InitCmd{}
cmd := &cobra.Command{
Use: "init",
@@ -438,29 +431,21 @@ func NewAddonInitCommand() *cobra.Command {
return fmt.Errorf("addon name or path should not be empty")
}
initCmd := pkgaddon.InitCmd{
AddonName: addonName,
HelmChartName: chartName,
HelmChartVersion: chartVersion,
HelmRepoURL: helmRepoURL,
Path: addonPath,
RefObjURLs: urls,
NoSamples: noSample,
Overwrite: overwrite,
}
initCmd.AddonName = addonName
initCmd.Path = addonPath
return initCmd.CreateScaffold()
},
}
f := cmd.Flags()
f.StringVar(&helmRepoURL, "helm-repo", "", "URL that points to a Helm repo")
f.StringVar(&chartName, "chart", "", "Helm Chart name")
f.StringVar(&chartVersion, "chart-version", "", "version of the Chart")
f.StringVar(&initCmd.HelmRepoURL, "helm-repo", "", "URL that points to a Helm repo")
f.StringVar(&initCmd.HelmChartName, "chart", "", "Helm Chart name")
f.StringVar(&initCmd.HelmChartVersion, "chart-version", "", "version of the Chart")
f.StringVarP(&path, "path", "p", "", "path to the addon directory (default is ./<addon-name>)")
f.StringArrayVarP(&urls, "url", "u", []string{}, "add URL resources using ref-object component")
f.BoolVarP(&noSample, "no-samples", "", false, "do not generate sample files")
f.BoolVarP(&overwrite, "force", "f", false, "overwrite existing addon files")
f.StringArrayVarP(&initCmd.RefObjURLs, "url", "u", []string{}, "add URL resources using ref-object component")
f.BoolVarP(&initCmd.NoSamples, "no-samples", "", false, "do not generate sample files")
f.BoolVarP(&initCmd.Overwrite, "force", "f", false, "overwrite existing addon files")
return cmd
}

Some files were not shown because too many files have changed in this diff Show More