Compare commits

...

14 Commits

Author SHA1 Message Date
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
240 changed files with 2339 additions and 928 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

@@ -40,4 +40,5 @@ WORKDIR /
ARG TARGETARCH
COPY --from=builder /workspace/vela-${TARGETARCH} /vela
COPY /vela /bin/
ENTRYPOINT ["/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
@@ -1053,7 +1056,6 @@ func (h *Installer) dispatchAddonResource(addon *InstallPackage) error {
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
@@ -1081,6 +1083,12 @@ func (h *Installer) dispatchAddonResource(addon *InstallPackage) error {
return errors.Wrapf(err, "cannot pass definition to addon app's annotation")
}
var auxiliaryOutputs []*unstructured.Unstructured
auxiliaryOutputs, err = renderOutputs(addon, h.args)
if err != nil {
return err
}
if err = h.createOrUpdate(app); err != nil {
return err
}
@@ -1109,6 +1117,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

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

View File

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

View File

@@ -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,14 @@ parameter: {
label: MyParam
validate:
required: true
`
appTemplate = `output: {
apiVersion: "core.oam.dev/v1beta1"
kind: "Application"
spec: {
components: []
policies: []
}
}
`
)

View File

@@ -23,7 +23,9 @@ import (
"path"
"strings"
"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"
@@ -42,6 +44,7 @@ const (
specifyAddonClustersTopologyPolicy = "deploy-addon-to-specified-clusters"
addonAllClusterPolicy = "deploy-addon-to-all-clusters"
renderOutputCuePath = "output"
renderAuxiliaryOutputsPath = "outputs"
)
type addonCueTemplateRender struct {
@@ -49,32 +52,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,13 +95,42 @@ 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, error) {
var app v1beta1.Application
contextFile, err := a.formatContext()
if err != nil {
return nil, errors.Wrap(err, "format context for app render")
}
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.NewValueWithFiles(a.addon.AppCueTemplate.Data, files, nil, "")
if err != nil {
return nil, errors.Wrap(err, "load app template with CUE files")
}
outputContent, err := v.LookupValue(renderOutputCuePath)
if err != nil {
return nil, errors.Wrap(err, "render app from output field from CUE")
}
err = outputContent.UnmarshalTo(&app)
if err != nil {
return nil, errors.Wrap(err, "decode app from CUE")
}
return &app, 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) {
if len(addon.AppCueTemplate.Data) != 0 && addon.AppTemplate != nil {
@@ -126,15 +168,15 @@ func generateAppFramework(addon *InstallPackage, parameters map[string]interface
}
func renderAppAccordingToCueTemplate(addon *InstallPackage, args map[string]interface{}) (*v1beta1.Application, error) {
app := v1beta1.Application{}
r := addonCueTemplateRender{
addon: addon,
inputArgs: args,
}
if err := r.toObject(addon.AppCueTemplate.Data, &app); err != nil {
app, err := r.renderApp()
if err != nil {
return nil, err
}
return &app, nil
return app, nil
}
// renderCompAccordingCUETemplate will return a component from cue template
@@ -145,8 +187,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 {
@@ -275,6 +317,27 @@ func checkNeedAttachTopologyPolicy(app *v1beta1.Application, addon *InstallPacka
return true
}
func renderOutputs(addon *InstallPackage, args map[string]interface{}) ([]*unstructured.Unstructured, error) {
outputs := map[string]interface{}{}
r := addonCueTemplateRender{
addon: addon,
inputArgs: args,
}
if err := r.toObject(addon.AppCueTemplate.Data, renderAuxiliaryOutputsPath, &outputs); err != nil {
if isErrorCueRenderPathNotFound(err, renderAuxiliaryOutputsPath) {
return nil, nil
}
return nil, err
}
var res []*unstructured.Unstructured
for _, o := range outputs {
if ao, ok := o.(map[string]interface{}); ok {
res = append(res, &unstructured.Unstructured{Object: ao})
}
}
return res, nil
}
func isDeployToRuntime(addon *InstallPackage) bool {
if addon.DeployTo == nil {
return false

View File

@@ -34,6 +34,108 @@ 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.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.CUETemplates = []ElementFile{{Data: resourceComponent1}}
addon.AppCueTemplate = ElementFile{Data: "package main\n" + appTemplate}
app, err = render.renderApp()
assert.NoError(t, err)
assert.Equal(t, len(app.Spec.Components), 2)
addon.Parameters = "package main\n" + paraDefined
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 +171,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 +231,25 @@ func TestRenderAppTemplate(t *testing.T) {
RuntimeCluster: true,
},
},
Parameters: paraDefined,
Parameters: paraDefined,
AppCueTemplate: ElementFile{Data: appTemplate},
}
list, err := renderOutputs(addon, nil)
assert.NoError(t, err)
assert.Equal(t, true, len(list) == 1)
render := addonCueTemplateRender{
addon: addon,
inputArgs: map[string]interface{}{
"namespace": "vela-system",
addon = &InstallPackage{
Meta: Meta{
Name: "velaux",
DeployTo: &DeployTo{
RuntimeCluster: true,
},
},
Parameters: paraDefined,
AppCueTemplate: ElementFile{Data: appTemplateNoOutputs},
}
app := v1beta1.Application{}
err := render.toObject(appTemplate, &app)
_, err = renderOutputs(addon, nil)
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.NoError(t, err)
assert.True(t, strings.Contains(string(str), `"clusterLabelSelector":{}`))
}
func TestAppComponentRender(t *testing.T) {
@@ -127,7 +279,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")
@@ -253,13 +405,17 @@ func TestGenerateAppFrameworkWithYamlTemplate(t *testing.T) {
}
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 +427,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

@@ -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

@@ -75,6 +75,49 @@ func (val *Value) UnmarshalTo(x interface{}) error {
return json.Unmarshal(data, x)
}
// NewValueWithFiles new a value from main and appendix files
func NewValueWithFiles(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")
}
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 mainFile.PackageName() != "" && cueSF.PackageName() == "" {
cueSF, err = parser.ParseFile("sf-"+strconv.Itoa(idx)+".cue", "package "+mainFile.PackageName()+"\n"+sf, parser.ParseComments)
if err != nil {
return nil, errors.Wrap(err, "add package for added file")
}
}
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 +134,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

@@ -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
}

View File

@@ -25,7 +25,6 @@ import (
// constants used in `svc` command
const (
App = "app"
Service = "svc"
Namespace = "namespace"
// FlagDescription command flag to specify the description of the definition

View File

@@ -56,7 +56,7 @@ import (
addonutil "github.com/oam-dev/kubevela/pkg/utils/addon"
"github.com/oam-dev/kubevela/pkg/utils/common"
"github.com/oam-dev/kubevela/pkg/utils/filters"
cmdutil "github.com/oam-dev/kubevela/pkg/utils/util"
"github.com/oam-dev/kubevela/pkg/utils/util"
)
const (
@@ -67,7 +67,7 @@ const (
)
// DefinitionCommandGroup create the command group for `vela def` command to manage definitions
func DefinitionCommandGroup(c common.Args, order string, ioStreams cmdutil.IOStreams) *cobra.Command {
func DefinitionCommandGroup(c common.Args, order string, ioStreams util.IOStreams) *cobra.Command {
cmd := &cobra.Command{
Use: "def",
Short: "Manage Definitions",
@@ -82,7 +82,7 @@ func DefinitionCommandGroup(c common.Args, order string, ioStreams cmdutil.IOStr
NewDefinitionListCommand(c),
NewDefinitionEditCommand(c),
NewDefinitionRenderCommand(c),
NewDefinitionApplyCommand(c),
NewDefinitionApplyCommand(c, ioStreams),
NewDefinitionDelCommand(c),
NewDefinitionInitCommand(c),
NewDefinitionValidateCommand(c),
@@ -518,8 +518,8 @@ func NewDefinitionGetCommand(c common.Args) *cobra.Command {
}
// NewDefinitionGenDocCommand create the `vela def doc-gen` command to generate documentation of definitions
func NewDefinitionGenDocCommand(c common.Args, ioStreams cmdutil.IOStreams) *cobra.Command {
var path, location, i18nPath string
func NewDefinitionGenDocCommand(c common.Args, ioStreams util.IOStreams) *cobra.Command {
var docPath, location, i18nPath string
cmd := &cobra.Command{
Use: "doc-gen NAME",
Short: "Generate documentation for definitions",
@@ -539,11 +539,11 @@ func NewDefinitionGenDocCommand(c common.Args, ioStreams cmdutil.IOStreams) *cob
if err != nil {
return errors.Wrapf(err, "failed to get `%s`", Namespace)
}
return ShowReferenceMarkdown(context.Background(), c, ioStreams, args[0], path, location, i18nPath, namespace, 0)
return ShowReferenceMarkdown(context.Background(), c, ioStreams, args[0], docPath, location, i18nPath, namespace, 0)
},
}
cmd.Flags().StringVarP(&path, "path", "p", "", "Specify the path for of the doc generated from definition.")
cmd.Flags().StringVarP(&docPath, "path", "p", "", "Specify the path for of the doc generated from definition.")
cmd.Flags().StringVarP(&location, "location", "l", "", "specify the location for of the doc generated from definition, now supported options 'zh', 'en'. ")
cmd.Flags().StringP(Namespace, "n", types.DefaultKubeVelaNS, "Specify which namespace the definition locates.")
cmd.Flags().StringVarP(&i18nPath, "i18n", "", "https://kubevela.io/reference-i18n.json", "specify the location for of the doc generated from definition, now supported options 'zh', 'en'. ")
@@ -843,13 +843,15 @@ func NewDefinitionRenderCommand(c common.Args) *cobra.Command {
}
// NewDefinitionApplyCommand create the `vela def apply` command to help user apply local definitions to k8s
func NewDefinitionApplyCommand(c common.Args) *cobra.Command {
func NewDefinitionApplyCommand(c common.Args, streams util.IOStreams) *cobra.Command {
cmd := &cobra.Command{
Use: "apply DEFINITION.cue",
Short: "Apply X-Definition.",
Long: "Apply X-Definition from local storage to kubernetes cluster. It will apply file to vela-system namespace by default.",
Example: "# Command below will apply the local my-webservice.cue file to kubernetes vela-system namespace\n" +
"> vela def apply my-webservice.cue\n" +
"# Apply the local directory including all files(YAML and CUE definition) to kubernetes vela-system namespace\n" +
"> vela def apply def/\n" +
"# Command below will apply the ./defs/my-trait.cue file to kubernetes default namespace\n" +
"> vela def apply ./defs/my-trait.cue --namespace default" +
"# Command below will convert the ./defs/my-trait.cue file to kubernetes CRD object and print it without applying it to kubernetes\n" +
@@ -869,85 +871,100 @@ func NewDefinitionApplyCommand(c common.Args) *cobra.Command {
if err != nil {
return errors.Wrapf(err, "failed to get `%s`", Namespace)
}
config, err := c.GetConfig()
if err != nil {
return err
if len(args) < 1 {
return errors.New("you must specify the definition path, directory or URL")
}
k8sClient, err := c.GetClient()
if err != nil {
return errors.Wrapf(err, "failed to get k8s client")
}
defpath := args[0]
defBytes, err := utils.ReadRemoteOrLocalPath(defpath, false)
if err != nil {
return errors.Wrapf(err, "failed to get from %s", defpath)
}
def := pkgdef.Definition{Unstructured: unstructured.Unstructured{}}
switch {
case strings.HasSuffix(defpath, ".yaml") || strings.HasSuffix(defpath, ".yml"):
// In this case, it's not in cue format, it's a yaml
if err = def.FromYAML(defBytes); err != nil {
return errors.Wrapf(err, "failed to parse YAML to definition")
}
if dryRun {
return errors.New("dry-run will render CUE to YAML, while the input is already in yaml")
}
// YAML won't validate or format CUE schematic
op, err := utils.CreateOrUpdate(ctx, k8sClient, &def)
if err != nil {
return err
}
cmd.Printf("%s %s in namespace %s %s.\n", def.GetKind(), def.GetName(), def.GetNamespace(), op)
return nil
default:
if err := def.FromCUEString(string(defBytes), config); err != nil {
return errors.Wrapf(err, "failed to parse CUE for definition")
}
def.SetNamespace(namespace)
}
if dryRun {
s, err := prettyYAMLMarshal(def.Object)
if err != nil {
return errors.Wrapf(err, "failed to marshal CRD into YAML")
}
cmd.Print(s)
return nil
}
oldDef := pkgdef.Definition{Unstructured: unstructured.Unstructured{}}
oldDef.SetGroupVersionKind(def.GroupVersionKind())
err = k8sClient.Get(ctx, types2.NamespacedName{
Namespace: def.GetNamespace(),
Name: def.GetName(),
}, &oldDef)
if err != nil {
if errors2.IsNotFound(err) {
kind := def.GetKind()
if err = k8sClient.Create(ctx, &def); err != nil {
return errors.Wrapf(err, "failed to create new definition in kubernetes")
}
cmd.Printf("%s %s created in namespace %s.\n", kind, def.GetName(), def.GetNamespace())
return nil
}
return errors.Wrapf(err, "failed to check existence of target definition in kubernetes")
}
if err := oldDef.FromCUEString(string(defBytes), config); err != nil {
return errors.Wrapf(err, "failed to merge with existing definition")
}
if err = k8sClient.Update(ctx, &oldDef); err != nil {
return errors.Wrapf(err, "failed to update existing definition in kubernetes")
}
cmd.Printf("%s %s in namespace %s updated.\n", oldDef.GetKind(), oldDef.GetName(), oldDef.GetNamespace())
return nil
return defApplyAll(ctx, c, streams, namespace, args[0], dryRun)
},
}
cmd.Flags().BoolP(FlagDryRun, "", false, "only build definition from CUE into CRB object without applying it to kubernetes clusters")
cmd.Flags().StringP(Namespace, "n", types.DefaultKubeVelaNS, "Specify which namespace the definition locates.")
return cmd
}
func defApplyAll(ctx context.Context, c common.Args, io util.IOStreams, namespace, path string, dryRun bool) error {
files, err := utils.LoadDataFromPath(ctx, path, utils.IsJSONYAMLorCUEFile)
if err != nil {
return errors.Wrapf(err, "failed to get from %s", path)
}
for _, f := range files {
result, err := defApplyOne(ctx, c, namespace, f.Path, f.Data, dryRun)
if err != nil {
return err
}
io.Infonln(result)
}
return nil
}
func defApplyOne(ctx context.Context, c common.Args, namespace, defpath string, defBytes []byte, dryRun bool) (string, error) {
config, err := c.GetConfig()
if err != nil {
return "", err
}
k8sClient, err := c.GetClient()
if err != nil {
return "", errors.Wrapf(err, "failed to get k8s client")
}
def := pkgdef.Definition{Unstructured: unstructured.Unstructured{}}
switch {
case strings.HasSuffix(defpath, ".yaml") || strings.HasSuffix(defpath, ".yml"):
// In this case, it's not in cue format, it's a yaml
if err = def.FromYAML(defBytes); err != nil {
return "", errors.Wrapf(err, "failed to parse YAML to definition")
}
if dryRun {
return "", errors.New("dry-run will render CUE to YAML, while the input is already in yaml")
}
// YAML won't validate or format CUE schematic
op, err := utils.CreateOrUpdate(ctx, k8sClient, &def)
if err != nil {
return "", err
}
return fmt.Sprintf("%s %s in namespace %s %s.\n", def.GetKind(), def.GetName(), def.GetNamespace(), op), nil
default:
if err := def.FromCUEString(string(defBytes), config); err != nil {
return "", errors.Wrapf(err, "failed to parse CUE for definition")
}
def.SetNamespace(namespace)
}
if dryRun {
s, err := prettyYAMLMarshal(def.Object)
if err != nil {
return "", errors.Wrapf(err, "failed to marshal CRD into YAML")
}
return s, nil
}
oldDef := pkgdef.Definition{Unstructured: unstructured.Unstructured{}}
oldDef.SetGroupVersionKind(def.GroupVersionKind())
err = k8sClient.Get(ctx, types2.NamespacedName{
Namespace: def.GetNamespace(),
Name: def.GetName(),
}, &oldDef)
if err != nil {
if errors2.IsNotFound(err) {
kind := def.GetKind()
if err = k8sClient.Create(ctx, &def); err != nil {
return "", errors.Wrapf(err, "failed to create new definition in kubernetes")
}
return fmt.Sprintf("%s %s created in namespace %s.\n", kind, def.GetName(), def.GetNamespace()), nil
}
return "", errors.Wrapf(err, "failed to check existence of target definition in kubernetes")
}
if err := oldDef.FromCUEString(string(defBytes), config); err != nil {
return "", errors.Wrapf(err, "failed to merge with existing definition")
}
if err = k8sClient.Update(ctx, &oldDef); err != nil {
return "", errors.Wrapf(err, "failed to update existing definition in kubernetes")
}
return fmt.Sprintf("%s %s in namespace %s updated.\n", oldDef.GetKind(), oldDef.GetName(), oldDef.GetNamespace()), nil
}
// NewDefinitionDelCommand create the `vela def del` command to help user delete existing definitions conveniently
func NewDefinitionDelCommand(c common.Args) *cobra.Command {
cmd := &cobra.Command{

View File

@@ -17,6 +17,7 @@ limitations under the License.
package cli
import (
"bytes"
"context"
"fmt"
"io"
@@ -40,7 +41,7 @@ import (
pkgdef "github.com/oam-dev/kubevela/pkg/definition"
addonutil "github.com/oam-dev/kubevela/pkg/utils/addon"
common2 "github.com/oam-dev/kubevela/pkg/utils/common"
cmdutil "github.com/oam-dev/kubevela/pkg/utils/util"
"github.com/oam-dev/kubevela/pkg/utils/util"
)
const (
@@ -180,7 +181,7 @@ func removeDir(dirname string, t *testing.T) {
}
func TestNewDefinitionCommandGroup(t *testing.T) {
cmd := DefinitionCommandGroup(common2.Args{}, "", cmdutil.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr})
cmd := DefinitionCommandGroup(common2.Args{}, "", util.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr})
initCommand(cmd)
cmd.SetArgs([]string{"-h"})
if err := cmd.Execute(); err != nil {
@@ -460,7 +461,7 @@ func TestNewDefinitionGetCommand(t *testing.T) {
func TestNewDefinitionGenDocCommand(t *testing.T) {
c := initArgs()
cmd := NewDefinitionGenDocCommand(c, cmdutil.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr})
cmd := NewDefinitionGenDocCommand(c, util.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr})
assert.NotNil(t, cmd.Execute())
cmd.SetArgs([]string{"alibaba-xxxxxxx"})
@@ -559,8 +560,9 @@ func TestNewDefinitionRenderCommand(t *testing.T) {
func TestNewDefinitionApplyCommand(t *testing.T) {
c := initArgs()
ioStreams := util.IOStreams{In: os.Stdin, Out: bytes.NewBuffer(nil), ErrOut: bytes.NewBuffer(nil)}
// dry-run test
cmd := NewDefinitionApplyCommand(c)
cmd := NewDefinitionApplyCommand(c, ioStreams)
initCommand(cmd)
_, traitFilename := createLocalTrait(t)
defer removeFile(traitFilename, t)
@@ -569,7 +571,7 @@ func TestNewDefinitionApplyCommand(t *testing.T) {
t.Fatalf("unexpeced error when executing apply command: %v", err)
}
// normal test and reapply
cmd = NewDefinitionApplyCommand(c)
cmd = NewDefinitionApplyCommand(c, ioStreams)
initCommand(cmd)
cmd.SetArgs([]string{traitFilename})
for i := 0; i < 2; i++ {

View File

@@ -0,0 +1,60 @@
/*
Copyright 2022 The KubeVela Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cli
import (
"bytes"
"context"
"os"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/utils/common"
"github.com/oam-dev/kubevela/pkg/utils/util"
)
var _ = Describe("Test def apply cli", func() {
When("test vela def apply", func() {
It("should not have err and applied all def files", func() {
buffer := bytes.NewBuffer(nil)
ioStreams := util.IOStreams{In: os.Stdin, Out: buffer, ErrOut: buffer}
ctx := context.Background()
c := common.Args{}
c.SetConfig(cfg)
c.SetClient(k8sClient)
err := defApplyAll(ctx, c, ioStreams, types.DefaultKubeVelaNS, "./test-data/defapply", false)
Expect(err).Should(BeNil())
By("check component definition in YAML exist")
cml := v1beta1.ComponentDefinition{}
Expect(k8sClient.Get(ctx, client.ObjectKey{Name: "testdefyaml", Namespace: types.DefaultKubeVelaNS}, &cml)).Should(BeNil())
By("check trait definition in CUE exist")
traitd := v1beta1.TraitDefinition{}
Expect(k8sClient.Get(ctx, client.ObjectKey{Name: "testdefcue", Namespace: types.DefaultKubeVelaNS}, &traitd)).Should(BeNil())
})
})
})

View File

@@ -62,10 +62,6 @@ func NewDeleteCommand(c common2.Args, order string, ioStreams cmdutil.IOStreams)
return errors.New("must specify name for the app")
}
o.AppName = args[0]
svcname, err := cmd.Flags().GetString(Service)
if err != nil {
return err
}
wait, err := cmd.Flags().GetBool("wait")
if err != nil {
return err
@@ -77,34 +73,19 @@ func NewDeleteCommand(c common2.Args, order string, ioStreams cmdutil.IOStreams)
}
o.ForceDelete = force
userInput := NewUserInput()
if svcname == "" {
if !assumeYes {
userConfirmation := userInput.AskBool(fmt.Sprintf("Do you want to delete the application %s from namespace %s", o.AppName, o.Namespace), &UserInputOptions{assumeYes})
if !userConfirmation {
return fmt.Errorf("stopping Deleting")
}
if !assumeYes {
userConfirmation := userInput.AskBool(fmt.Sprintf("Do you want to delete the application %s from namespace %s", o.AppName, o.Namespace), &UserInputOptions{assumeYes})
if !userConfirmation {
return fmt.Errorf("stopping Deleting")
}
if err = o.DeleteApp(ioStreams); err != nil {
return err
}
ioStreams.Info(green.Sprintf("app \"%s\" deleted from namespace \"%s\"", o.AppName, o.Namespace))
} else {
if !assumeYes {
userConfirmation := userInput.AskBool(fmt.Sprintf("Do you want to delete the component %s from application %s in namespace %s", svcname, o.AppName, o.Namespace), &UserInputOptions{assumeYes})
if !userConfirmation {
return fmt.Errorf("stopping Deleting")
}
}
o.CompName = svcname
if err = o.DeleteComponent(ioStreams); err != nil {
return err
}
ioStreams.Info(green.Sprintf("component \"%s\" deleted from \"%s\"", o.CompName, o.AppName))
}
if err = o.DeleteApp(ioStreams); err != nil {
return err
}
ioStreams.Info(green.Sprintf("app \"%s\" deleted from namespace \"%s\"", o.AppName, o.Namespace))
return nil
}
cmd.PersistentFlags().StringP(Service, "", "", "delete only the specified service in this app")
cmd.PersistentFlags().BoolVarP(&o.Wait, "wait", "w", false, "wait util the application is deleted completely")
cmd.PersistentFlags().BoolVarP(&o.ForceDelete, "force", "f", false, "force to delete the application")
addNamespaceAndEnvArg(cmd)

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