mirror of
https://github.com/kubevela/kubevela.git
synced 2026-02-14 18:10:21 +00:00
Compare commits
14 Commits
v1.10.4
...
v1.5.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
85e168fea7 | ||
|
|
189d74e87b | ||
|
|
8ec0209026 | ||
|
|
c3a7209fa7 | ||
|
|
564cba9aac | ||
|
|
f7c21df915 | ||
|
|
0b9c7f66c0 | ||
|
|
54867c50d8 | ||
|
|
16d7a4b4f4 | ||
|
|
35ae4e5ef5 | ||
|
|
853f44cf61 | ||
|
|
ca2a90a097 | ||
|
|
d110e97d68 | ||
|
|
741559c8e0 |
6
.github/workflows/apiserver-test.yaml
vendored
6
.github/workflows/apiserver-test.yaml
vendored
@@ -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:
|
||||
|
||||
|
||||
6
.github/workflows/e2e-multicluster-test.yml
vendored
6
.github/workflows/e2e-multicluster-test.yml
vendored
@@ -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:
|
||||
|
||||
|
||||
6
.github/workflows/e2e-rollout-test.yml
vendored
6
.github/workflows/e2e-rollout-test.yml
vendored
@@ -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:
|
||||
|
||||
|
||||
6
.github/workflows/e2e-test.yml
vendored
6
.github/workflows/e2e-test.yml
vendored
@@ -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:
|
||||
|
||||
|
||||
2
.github/workflows/go.yml
vendored
2
.github/workflows/go.yml
vendored
@@ -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
.github/workflows/unit-test.yml
vendored
2
.github/workflows/unit-test.yml
vendored
@@ -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
2
.gitignore
vendored
@@ -1,4 +1,4 @@
|
||||
# Binaries for programs and plugins
|
||||
# Binaries for programs and docgen
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
|
||||
@@ -40,4 +40,5 @@ WORKDIR /
|
||||
|
||||
ARG TARGETARCH
|
||||
COPY --from=builder /workspace/vela-${TARGETARCH} /vela
|
||||
COPY /vela /bin/
|
||||
ENTRYPOINT ["/vela"]
|
||||
|
||||
@@ -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" . }}
|
||||
|
||||
@@ -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" . }}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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" . }}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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" . }}
|
||||
|
||||
@@ -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" . }}
|
||||
|
||||
@@ -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
|
||||
}]
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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
|
||||
}]
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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: {}
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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" . }}
|
||||
|
||||
@@ -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" . }}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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" . }}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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" . }}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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
|
||||
}]
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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: {}
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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 | |
|
||||
+---------+--------------------------------------------------------------------------------------------------+----------+----------+---------+
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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())
|
||||
})
|
||||
|
||||
|
||||
@@ -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: []
|
||||
}
|
||||
}
|
||||
`
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
12
pkg/addon/testdata/example-legacy/Chart.yaml
vendored
Normal file
12
pkg/addon/testdata/example-legacy/Chart.yaml
vendored
Normal 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
|
||||
62
pkg/addon/testdata/example-legacy/definitions/helm.yaml
vendored
Normal file
62
pkg/addon/testdata/example-legacy/definitions/helm.yaml
vendored
Normal 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
|
||||
23
pkg/addon/testdata/example-legacy/metadata.yaml
vendored
Normal file
23
pkg/addon/testdata/example-legacy/metadata.yaml
vendored
Normal 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
|
||||
19
pkg/addon/testdata/example-legacy/readme.md
vendored
Normal file
19
pkg/addon/testdata/example-legacy/readme.md
vendored
Normal 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**
|
||||
|
||||
|
||||
15
pkg/addon/testdata/example-legacy/resources/configmap.cue
vendored
Normal file
15
pkg/addon/testdata/example-legacy/resources/configmap.cue
vendored
Normal 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
|
||||
}
|
||||
}
|
||||
0
pkg/addon/testdata/example-legacy/resources/dummy.txt
vendored
Normal file
0
pkg/addon/testdata/example-legacy/resources/dummy.txt
vendored
Normal file
17
pkg/addon/testdata/example-legacy/resources/service/source-controller.yaml
vendored
Normal file
17
pkg/addon/testdata/example-legacy/resources/service/source-controller.yaml
vendored
Normal 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
|
||||
@@ -1,7 +1,7 @@
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: example
|
||||
name: example-legacy
|
||||
namespace: vela-system
|
||||
spec:
|
||||
workflow:
|
||||
3
pkg/addon/testdata/example/parameter.cue
vendored
Normal file
3
pkg/addon/testdata/example/parameter.cue
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
parameter: {
|
||||
example: string
|
||||
}
|
||||
27
pkg/addon/testdata/example/template.cue
vendored
Normal file
27
pkg/addon/testdata/example/template.cue
vendored
Normal 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"
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)}
|
||||
|
||||
@@ -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"`
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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++ {
|
||||
|
||||
60
references/cli/defapply_test.go
Normal file
60
references/cli/defapply_test.go
Normal 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())
|
||||
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -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
Reference in New Issue
Block a user