Compare commits

..

28 Commits

Author SHA1 Message Date
github-actions[bot]
b1cc06b0f3 Feat: support dry-run with cue format definition (#5080)
Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
(cherry picked from commit ce75a33633)

Co-authored-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
2022-11-16 18:11:58 +08:00
github-actions[bot]
ed9d53b448 Feat: add print message example (#5079)
Signed-off-by: FogDong <dongtianxin.tx@alibaba-inc.com>
(cherry picked from commit ee2b854c80)

Co-authored-by: FogDong <dongtianxin.tx@alibaba-inc.com>
2022-11-16 16:38:06 +08:00
github-actions[bot]
ad83e59865 [Backport release-1.6] Feat: add apply component definition for docs (#5076)
* Feat: add apply component definition for docs

Signed-off-by: FogDong <dongtianxin.tx@alibaba-inc.com>
(cherry picked from commit d31dbecb76)

* Feat: add apply component definition for docs

Signed-off-by: FogDong <dongtianxin.tx@alibaba-inc.com>
(cherry picked from commit 454edb05ff)

Co-authored-by: FogDong <dongtianxin.tx@alibaba-inc.com>
2022-11-16 15:45:49 +08:00
github-actions[bot]
b62eeca3f9 [Backport release-1.6] Fix: code vulnerability (#5075)
* Fix: code vulnerability

Signed-off-by: qiaozp <qiaozhongpei.qzp@alibaba-inc.com>
(cherry picked from commit d47019de35)

* lint

Signed-off-by: qiaozp <qiaozhongpei.qzp@alibaba-inc.com>
(cherry picked from commit 7a51a1f22a)

* imports

Signed-off-by: qiaozp <qiaozhongpei.qzp@alibaba-inc.com>
(cherry picked from commit 8040fe63ce)

* use space

Signed-off-by: Qiaozp <qiaozhongpei.qzp@alibaba-inc.com>
(cherry picked from commit 80d16b480c)

* reuse sanitize function

Signed-off-by: Qiaozp <qiaozhongpei.qzp@alibaba-inc.com>
(cherry picked from commit 75695440b1)

Co-authored-by: qiaozp <qiaozhongpei.qzp@alibaba-inc.com>
2022-11-16 15:45:25 +08:00
github-actions[bot]
5d9757fcb8 Feat: support vela up --wait and --timeout (#5074)
Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
(cherry picked from commit f81f26f66b)

Co-authored-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
2022-11-16 10:01:14 +08:00
github-actions[bot]
4d653951a1 add tests (#5068)
Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
(cherry picked from commit 7080d7ae31)

Co-authored-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
2022-11-15 13:17:06 +08:00
github-actions[bot]
bcda4976a9 [Backport release-1.6] Fix: Failed to get detail policy for application (#5049)
* Fix: Failed to get detail policy for application

Signed-off-by: wuzhongjian <wuzhongjian_yewu@cmss.chinamobile.com>
(cherry picked from commit 50f63bf8bc)

* Fix: Failed to get detail policy for application

Signed-off-by: wuzhongjian <wuzhongjian_yewu@cmss.chinamobile.com>
(cherry picked from commit 8c70f067fc)

Co-authored-by: wuzhongjian <wuzhongjian_yewu@cmss.chinamobile.com>
2022-11-10 21:50:22 +08:00
github-actions[bot]
a01d0e773a Fix: add debug for workflowrun and support debug sub steps (#5042)
Signed-off-by: FogDong <dongtianxin.tx@alibaba-inc.com>
(cherry picked from commit 5749babe71)

Co-authored-by: FogDong <dongtianxin.tx@alibaba-inc.com>
2022-11-10 11:04:47 +08:00
github-actions[bot]
f0e3304c17 [Backport release-1.6] Fix: fix the stuck problem and use LRU cache to promote the speed of loading topology (#5036)
* Fix: fix the stuck problem and use lru cache to promote the speed of loading topology

Signed-off-by: HanMengnan <1448189829@qq.com>
(cherry picked from commit 8395fe56b2)

* Fix: reuse existing lru golang library

Signed-off-by: HanMengnan <1448189829@qq.com>
(cherry picked from commit d69018f71e)

Co-authored-by: HanMengnan <1448189829@qq.com>
2022-11-09 16:09:46 +08:00
github-actions[bot]
e9f1e21d55 Feat: support webservice containing duplicate port with different protocol (#5035)
Signed-off-by: Somefive <yd219913@alibaba-inc.com>
(cherry picked from commit a8653e5d1c)

Co-authored-by: Somefive <yd219913@alibaba-inc.com>
2022-11-09 13:15:17 +08:00
github-actions[bot]
de127b7311 Fix: higher version contraint in install command (#5033)
Signed-off-by: Qiaozp <qiaozhongpei.qzp@alibaba-inc.com>
(cherry picked from commit a7b30ce104)

Co-authored-by: Qiaozp <qiaozhongpei.qzp@alibaba-inc.com>
2022-11-09 11:26:33 +08:00
github-actions[bot]
9f0558c62e Fix: temporary fix comments in comprehesions for env trait (#5025)
Signed-off-by: FogDong <dongtianxin.tx@alibaba-inc.com>
(cherry picked from commit b9d380142d)

Co-authored-by: FogDong <dongtianxin.tx@alibaba-inc.com>
2022-11-09 11:22:41 +08:00
github-actions[bot]
0f547fa158 Feat: add scope label in workflow step defs (#5023)
Signed-off-by: FogDong <dongtianxin.tx@alibaba-inc.com>
(cherry picked from commit 2d02a7ac20)

Co-authored-by: FogDong <dongtianxin.tx@alibaba-inc.com>
2022-11-09 11:21:59 +08:00
github-actions[bot]
84155d06fb [Backport release-1.6] Fix: remove the reference of v1beta1 ingress (#5028)
* try to remove the v1beta1 ingress

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

* fix tests

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

Co-authored-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
2022-11-09 11:20:14 +08:00
github-actions[bot]
bc7e31f979 use vi as ingress's target apiVersion (#5022)
Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>

fix test

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

fix test

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

revert test

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

add tests

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

Co-authored-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
2022-11-08 12:12:05 +08:00
github-actions[bot]
f406936dce Fix: vela status will always get external ip first (#5018)
Signed-off-by: Qiaozp <qiaozhongpei.qzp@alibaba-inc.com>
(cherry picked from commit f1d81feece)

Co-authored-by: Qiaozp <qiaozhongpei.qzp@alibaba-inc.com>
2022-11-07 18:55:07 +08:00
github-actions[bot]
c2ecc71941 Fix: vela cluster join reports 'resource name may not be empty' error (#5015)
Signed-off-by: wuzhongjian <wuzhongjian_yewu@cmss.chinamobile.com>
(cherry picked from commit ce4cad677d)

Co-authored-by: wuzhongjian <wuzhongjian_yewu@cmss.chinamobile.com>
2022-11-07 16:14:47 +08:00
github-actions[bot]
c1efd3f056 Fix: fix problem of loading pods of cronjob (#5013)
Signed-off-by: HanMengnan <1448189829@qq.com>
(cherry picked from commit c074c558b6)

Co-authored-by: HanMengnan <1448189829@qq.com>
2022-11-07 16:12:52 +08:00
github-actions[bot]
7002182072 [Backport release-1.6] Fix: remove duplicate mock server in CI (#5012)
* Fix: not killing mock server process every time

Signed-off-by: qiaozp <qiaozhongpei.qzp@alibaba-inc.com>
(cherry picked from commit abb13b9ab4)

* remove redundent mock server

Signed-off-by: qiaozp <qiaozhongpei.qzp@alibaba-inc.com>
(cherry picked from commit b1672d1bf3)

Co-authored-by: qiaozp <qiaozhongpei.qzp@alibaba-inc.com>
2022-11-07 11:06:00 +08:00
github-actions[bot]
554a06e35e [Backport release-1.6] Fix: forbid 302 request to avoid SSRF (#5004)
* fix helm chart list endpoint SSRF CVE

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

* revert error log

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

* change with const value

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

fix ci

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

Co-authored-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
2022-11-04 20:17:15 +08:00
github-actions[bot]
4ffb7e6707 Chore: update the API schema (#4997)
Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
(cherry picked from commit 64eb622f12)

Co-authored-by: barnettZQG <barnett.zqg@gmail.com>
2022-11-04 13:10:56 +08:00
github-actions[bot]
caeb334340 [Backport release-1.6] Fix: test ci windows (#4991)
* Fix: test ci windows

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

* Fix: rename add exe suffix for command

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

* Fix: trim extra commands

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

Co-authored-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
2022-11-03 15:43:31 +08:00
github-actions[bot]
275b61d427 Fix: app contains app override the child app with parent app label (#4988)
Signed-off-by: Somefive <yd219913@alibaba-inc.com>
(cherry picked from commit c39e6adc50)

Co-authored-by: Somefive <yd219913@alibaba-inc.com>
2022-11-02 22:39:57 +08:00
github-actions[bot]
11904a6f60 [Backport release-1.6] Fix: fix filepath for windows (#4987)
* Fix: fix filepath for windows

Signed-off-by: FogDong <dongtianxin.tx@alibaba-inc.com>
(cherry picked from commit ef5b4fbe38)

* test ci

Signed-off-by: FogDong <dongtianxin.tx@alibaba-inc.com>
(cherry picked from commit fb367d3722)

Co-authored-by: FogDong <dongtianxin.tx@alibaba-inc.com>
2022-11-02 22:39:29 +08:00
github-actions[bot]
4b4e4f8530 [Backport release-1.6] Fix: enhance the default permissions (#4977)
* Fix: enhance the default permissions

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

* Fix: unit test error

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

Co-authored-by: barnettZQG <barnett.zqg@gmail.com>
2022-11-01 20:51:33 +08:00
github-actions[bot]
0121e8b6ef Feat: allowing restart a compeleted workflow (#4976)
Signed-off-by: FogDong <dongtianxin.tx@alibaba-inc.com>
(cherry picked from commit 74eea5bed2)

Co-authored-by: FogDong <dongtianxin.tx@alibaba-inc.com>
2022-11-01 20:35:36 +08:00
github-actions[bot]
382510aa67 [Backport release-1.6] Fix: mongodb can not decode runtime.Object (#4974)
* Fix: mongodb can not decode runtime.Object

Signed-off-by: Qiaozp <qiaozhongpei.qzp@alibaba-inc.com>
(cherry picked from commit bc43762f86)

* reuse struct

Signed-off-by: Qiaozp <qiaozhongpei.qzp@alibaba-inc.com>
(cherry picked from commit fa8a6335dc)

* fix no output when filter

Signed-off-by: Qiaozp <qiaozhongpei.qzp@alibaba-inc.com>
(cherry picked from commit 4b9e016b01)

Co-authored-by: Qiaozp <qiaozhongpei.qzp@alibaba-inc.com>
2022-11-01 17:25:55 +08:00
github-actions[bot]
7ae7d2a5ef [Backport release-1.6] Fix: grant the permission to read the ConfigMap in the vela-system namesapce (#4970)
* Fix: grant the permission to read the ConfigMap in the vela-system namespace

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

* Fix: change the test

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

Co-authored-by: barnettZQG <barnett.zqg@gmail.com>
2022-11-01 10:00:04 +08:00
111 changed files with 2068 additions and 697 deletions

View File

@@ -141,3 +141,35 @@ jobs:
- name: Cleanup binary
run: make build-cleanup
check-windows:
runs-on: windows-latest
needs: detect-noop
if: needs.detect-noop.outputs.noop != 'true'
steps:
- name: Checkout
uses: actions/checkout@v2
with:
submodules: true
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: ${{ env.GO_VERSION }}
- name: Cache Go Dependencies
uses: actions/cache@v2
with:
path: .work/pkg
key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-pkg-
- name: Run Build CLI
run: make vela-cli
- name: Run CLI for version
shell: cmd
run: |
move .\bin\vela .\bin\vela.exe
.\bin\vela.exe version

View File

@@ -189,8 +189,6 @@ type Status struct {
type ApplicationPhase string
const (
// ApplicationRollingOut means the app is in the middle of rolling out
ApplicationRollingOut ApplicationPhase = "rollingOut"
// ApplicationStarting means the app is preparing for reconcile
ApplicationStarting ApplicationPhase = "starting"
// ApplicationRendering means the app is rendering
@@ -205,8 +203,6 @@ const (
ApplicationWorkflowTerminated ApplicationPhase = "workflowTerminated"
// ApplicationWorkflowFailed means the app's workflow is failed
ApplicationWorkflowFailed ApplicationPhase = "workflowFailed"
// ApplicationWorkflowFinished means the app's workflow is finished
ApplicationWorkflowFinished ApplicationPhase = "workflowFinished"
// ApplicationRunning means the app finished rendering and applied result to the cluster
ApplicationRunning ApplicationPhase = "running"
// ApplicationUnhealthy means the app finished rendering and applied result to the cluster, but still unhealthy

View File

@@ -64,6 +64,8 @@ const (
LabelDefinitionDeprecated = "custom.definition.oam.dev/deprecated"
// LabelDefinitionHidden is the label which describe whether the capability is hidden by UI
LabelDefinitionHidden = "custom.definition.oam.dev/ui-hidden"
// LabelDefinitionScope is the label which describe whether the capability's scope
LabelDefinitionScope = "custom.definition.oam.dev/scope"
// LabelNodeRoleGateway gateway role of node
LabelNodeRoleGateway = "node-role.kubernetes.io/gateway"
// LabelNodeRoleWorker worker role of node

View File

@@ -7,6 +7,7 @@ metadata:
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/scope: Application
custom.definition.oam.dev/ui-hidden: "true"
name: apply-application-in-parallel
namespace: {{ include "systemDefinitionNamespace" . }}

View File

@@ -7,6 +7,7 @@ metadata:
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/scope: Application
custom.definition.oam.dev/ui-hidden: "true"
name: apply-application
namespace: {{ include "systemDefinitionNamespace" . }}

View File

@@ -0,0 +1,23 @@
# Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file.
# Definition source cue file: vela-templates/definitions/internal/apply-component.cue
apiVersion: core.oam.dev/v1beta1
kind: WorkflowStepDefinition
metadata:
annotations:
definition.oam.dev/description: Apply a specific component and its corresponding traits in application
labels:
custom.definition.oam.dev/scope: Application
custom.definition.oam.dev/ui-hidden: "true"
name: apply-component
namespace: {{ include "systemDefinitionNamespace" . }}
spec:
schematic:
cue:
template: |
parameter: {
// +usage=Specify the component name to apply
component: string
// +usage=Specify the cluster
cluster: *"" | string
}

View File

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

View File

@@ -5,6 +5,8 @@ kind: WorkflowStepDefinition
metadata:
annotations:
definition.oam.dev/description: Deploy cloud resource and deliver secret to multi clusters.
labels:
custom.definition.oam.dev/scope: Application
name: deploy-cloud-resource
namespace: {{ include "systemDefinitionNamespace" . }}
spec:

View File

@@ -5,6 +5,8 @@ kind: WorkflowStepDefinition
metadata:
annotations:
definition.oam.dev/description: A powerful and unified deploy step for components multi-cluster delivery with policies.
labels:
custom.definition.oam.dev/scope: Application
name: deploy
namespace: {{ include "systemDefinitionNamespace" . }}
spec:

View File

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

View File

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

View File

@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
kind: WorkflowStepDefinition
metadata:
annotations:
definition.oam.dev/description: pring message in workflow status
definition.oam.dev/description: print message in workflow step status
labels:
custom.definition.oam.dev/ui-hidden: "true"
name: print-message-in-status

View File

@@ -5,6 +5,8 @@ kind: WorkflowStepDefinition
metadata:
annotations:
definition.oam.dev/description: Sync secrets created by terraform component to runtime clusters so that runtime clusters can share the created cloud resource.
labels:
custom.definition.oam.dev/scope: Application
name: share-cloud-resource
namespace: {{ include "systemDefinitionNamespace" . }}
spec:

View File

@@ -13,6 +13,7 @@ spec:
template: |
import (
"strconv"
"strings"
)
mountsArray: [
@@ -167,7 +168,11 @@ spec:
name: v.name
}
if v.name == _|_ {
name: "port-" + strconv.FormatInt(v.port, 10)
_name: "port-" + strconv.FormatInt(v.port, 10)
name: *_name | string
if v.protocol != "TCP" {
name: _name + "-" + strings.ToLower(v.protocol)
}
}
}}]
}
@@ -283,11 +288,18 @@ spec:
name: v.name
}
if v.name == _|_ {
name: "port-" + strconv.FormatInt(v.port, 10)
_name: "port-" + strconv.FormatInt(v.port, 10)
name: *_name | string
if v.protocol != "TCP" {
name: _name + "-" + strings.ToLower(v.protocol)
}
}
if v.nodePort != _|_ && parameter.exposeType == "NodePort" {
nodePort: v.nodePort
}
if v.protocol != _|_ {
protocol: v.protocol
}
},
]
outputs: {

View File

@@ -116,6 +116,39 @@ subjects:
name: {{ include "kubevela.serviceAccountName" . }}
---
# permissions to read the view of VelaQL, schemas, and templates.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: {{ include "kubevela.fullname" . }}:template-reader-role
rules:
- apiGroups:
- ""
resources:
- configmaps
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- configmaps/status
verbs:
- get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: {{ include "kubevela.fullname" . }}:template-reader-binding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: {{ include "kubevela.fullname" . }}:template-reader-role
subjects:
- kind: Group
name: template-reader
---
apiVersion: apps/v1
kind: Deployment
metadata:

View File

@@ -7,6 +7,7 @@ metadata:
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/scope: Application
custom.definition.oam.dev/ui-hidden: "true"
name: apply-application-in-parallel
namespace: {{ include "systemDefinitionNamespace" . }}

View File

@@ -7,6 +7,7 @@ metadata:
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/scope: Application
custom.definition.oam.dev/ui-hidden: "true"
name: apply-application
namespace: {{ include "systemDefinitionNamespace" . }}

View File

@@ -0,0 +1,23 @@
# Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file.
# Definition source cue file: vela-templates/definitions/internal/apply-component.cue
apiVersion: core.oam.dev/v1beta1
kind: WorkflowStepDefinition
metadata:
annotations:
definition.oam.dev/description: Apply a specific component and its corresponding traits in application
labels:
custom.definition.oam.dev/scope: Application
custom.definition.oam.dev/ui-hidden: "true"
name: apply-component
namespace: {{ include "systemDefinitionNamespace" . }}
spec:
schematic:
cue:
template: |
parameter: {
// +usage=Specify the component name to apply
component: string
// +usage=Specify the cluster
cluster: *"" | string
}

View File

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

View File

@@ -5,6 +5,8 @@ kind: WorkflowStepDefinition
metadata:
annotations:
definition.oam.dev/description: Deploy cloud resource and deliver secret to multi clusters.
labels:
custom.definition.oam.dev/scope: Application
name: deploy-cloud-resource
namespace: {{ include "systemDefinitionNamespace" . }}
spec:

View File

@@ -5,6 +5,8 @@ kind: WorkflowStepDefinition
metadata:
annotations:
definition.oam.dev/description: A powerful and unified deploy step for components multi-cluster delivery with policies.
labels:
custom.definition.oam.dev/scope: Application
name: deploy
namespace: {{ include "systemDefinitionNamespace" . }}
spec:

View File

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

View File

@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
kind: WorkflowStepDefinition
metadata:
annotations:
definition.oam.dev/description: pring message in workflow status
definition.oam.dev/description: print message in workflow step status
labels:
custom.definition.oam.dev/ui-hidden: "true"
name: print-message-in-status

View File

@@ -5,6 +5,8 @@ kind: WorkflowStepDefinition
metadata:
annotations:
definition.oam.dev/description: Sync secrets created by terraform component to runtime clusters so that runtime clusters can share the created cloud resource.
labels:
custom.definition.oam.dev/scope: Application
name: share-cloud-resource
namespace: {{ include "systemDefinitionNamespace" . }}
spec:

View File

@@ -13,6 +13,7 @@ spec:
template: |
import (
"strconv"
"strings"
)
mountsArray: [
@@ -167,7 +168,11 @@ spec:
name: v.name
}
if v.name == _|_ {
name: "port-" + strconv.FormatInt(v.port, 10)
_name: "port-" + strconv.FormatInt(v.port, 10)
name: *_name | string
if v.protocol != "TCP" {
name: _name + "-" + strings.ToLower(v.protocol)
}
}
}}]
}
@@ -283,11 +288,18 @@ spec:
name: v.name
}
if v.name == _|_ {
name: "port-" + strconv.FormatInt(v.port, 10)
_name: "port-" + strconv.FormatInt(v.port, 10)
name: *_name | string
if v.protocol != "TCP" {
name: _name + "-" + strings.ToLower(v.protocol)
}
}
if v.nodePort != _|_ && parameter.exposeType == "NodePort" {
nodePort: v.nodePort
}
if v.protocol != _|_ {
protocol: v.protocol
}
},
]
outputs: {

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -34,4 +34,30 @@ entries:
urls:
- http://127.0.0.1:9098/helm/vela-workflow-v0.3.1.tgz
version: v0.3.1
foo:
- created: "2022-10-29T09:11:16.865230605Z"
description: Vela test addon named foo
home: https://www.foo.com/icon
icon: https://www.foo.com
name: foo
urls:
- http://127.0.0.1:9098/helm/foo-v1.0.0.tgz
version: v1.0.0
bar:
- created: "2022-10-29T09:11:16.865230605Z"
description: Vela test addon named bar
home: https://www.bar.com/icon
icon: https://www.bar.com
name: foo
urls:
- http://127.0.0.1:9098/helm/bar-v1.0.0.tgz
version: v1.0.0
- created: "2022-10-29T09:11:16.865230605Z"
description: Vela test addon named bar
home: https://www.bar.com/icon
icon: https://www.bar.com
name: foo
urls:
- http://127.0.0.1:9098/helm/bar-v2.0.0.tgz
version: v2.0.0
generated: "2022-06-15T13:17:04.733573+08:00"

View File

@@ -131,6 +131,24 @@ var helmHandler http.HandlerFunc = func(rw http.ResponseWriter, req *http.Reques
_, _ = rw.Write([]byte(err.Error()))
}
rw.Write(file)
case strings.Contains(req.URL.Path, "foo-v1.0.0.tgz"):
file, err := os.ReadFile("./e2e/addon/mock/testrepo/helm-repo/foo-v1.0.0.tgz")
if err != nil {
_, _ = rw.Write([]byte(err.Error()))
}
rw.Write(file)
case strings.Contains(req.URL.Path, "bar-v1.0.0.tgz"):
file, err := os.ReadFile("./e2e/addon/mock/testrepo/helm-repo/bar-v1.0.0.tgz")
if err != nil {
_, _ = rw.Write([]byte(err.Error()))
}
rw.Write(file)
case strings.Contains(req.URL.Path, "bar-v2.0.0.tgz"):
file, err := os.ReadFile("./e2e/addon/mock/testrepo/helm-repo/bar-v2.0.0.tgz")
if err != nil {
_, _ = rw.Write([]byte(err.Error()))
}
rw.Write(file)
}
}

View File

@@ -47,6 +47,9 @@ var (
appbasicJsonAppFile = `{"name":"app-basic","services":{"app-basic":{"type":"webservice","image":"nginx:1.9.4","ports":[{port: 80, expose: true}]}}}`
appbasicAddTraitJsonAppFile = `{"name":"app-basic","services":{"app-basic":{"type":"webservice","image":"nginx:1.9.4","ports":[{port: 80, expose: true}],"scaler":{"replicas":2}}}}`
velaQL = "test-component-pod-view{appNs=default,appName=nginx-vela,name=nginx}"
waitAppfileToSuccess = `{"name":"app-wait-success","services":{"app-basic1":{"type":"webservice","image":"nginx:1.9.4","ports":[{port: 80, expose: true}]}}}`
waitAppfileToFail = `{"name":"app-wait-fail","services":{"app-basic2":{"type":"webservice","image":"nginx:fail","ports":[{port: 80, expose: true}]}}}`
)
var _ = ginkgo.Describe("Test Vela Application", func() {
@@ -75,6 +78,9 @@ var _ = ginkgo.Describe("Test Vela Application", func() {
e2e.JsonAppFileContext("json appfile apply", testDeleteJsonAppFile)
VelaQLPodListContext("ql", velaQL)
e2e.JsonAppFileContextWithWait("json appfile apply with wait", waitAppfileToSuccess)
e2e.JsonAppFileContextWithTimeout("json appfile apply with wait but timeout", waitAppfileToFail, "3s")
})
var ApplicationStatusContext = func(context string, applicationName string, workloadType string) bool {
@@ -182,7 +188,7 @@ var ApplicationInitIntercativeCliContext = func(context string, appName string,
c.ExpectEOF()
})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
gomega.Expect(output).To(gomega.ContainSubstring("Checking Status"))
gomega.Expect(output).To(gomega.ContainSubstring("Waiting app to be healthy"))
})
})
}

View File

@@ -84,6 +84,30 @@ var (
})
}
JsonAppFileContextWithWait = func(context, jsonAppFile string) bool {
return ginkgo.Context(context, func() {
ginkgo.It("Start the application through the app file in JSON format.", func() {
writeStatus := os.WriteFile("vela.json", []byte(jsonAppFile), 0644)
gomega.Expect(writeStatus).NotTo(gomega.HaveOccurred())
output, err := Exec("vela up -f vela.json --wait")
gomega.Expect(err).NotTo(gomega.HaveOccurred())
gomega.Expect(output).To(gomega.ContainSubstring("Application Deployed Successfully!"))
})
})
}
JsonAppFileContextWithTimeout = func(context, jsonAppFile, duration string) bool {
return ginkgo.Context(context, func() {
ginkgo.It("Start the application through the app file in JSON format.", func() {
writeStatus := os.WriteFile("vela.json", []byte(jsonAppFile), 0644)
gomega.Expect(writeStatus).NotTo(gomega.HaveOccurred())
output, err := Exec("vela up -f vela.json --wait --timeout=" + duration)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
gomega.Expect(output).To(gomega.ContainSubstring("Timeout waiting Application to be healthy!"))
})
})
}
DeleteEnvFunc = func(context string, envName string) bool {
return ginkgo.Context(context, func() {
ginkgo.It("should print env does not exist message", func() {

4
go.mod
View File

@@ -50,6 +50,7 @@ require (
github.com/gosuri/uilive v0.0.4
github.com/gosuri/uitable v0.0.4
github.com/hashicorp/go-version v1.3.0
github.com/hashicorp/golang-lru v0.5.4
github.com/hashicorp/hcl/v2 v2.9.1
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174
github.com/imdario/mergo v0.3.12
@@ -57,7 +58,7 @@ require (
github.com/koding/websocketproxy v0.0.0-20181220232114-7ed82d81a28c
github.com/kubevela/pkg v0.0.0-20221024115939-a103acee6db2
github.com/kubevela/prism v1.5.1-0.20220915071949-6bf3ad33f84f
github.com/kubevela/workflow v0.3.1
github.com/kubevela/workflow v0.3.3
github.com/kyokomi/emoji v2.2.4+incompatible
github.com/mitchellh/hashstructure/v2 v2.0.1
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd
@@ -341,6 +342,7 @@ require (
)
replace (
cuelang.org/go => github.com/kubevela/cue v0.4.4-0.20221107123854-a976b0e340be
github.com/docker/cli => github.com/docker/cli v20.10.9+incompatible
github.com/docker/docker => github.com/moby/moby v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible
github.com/wercker/stern => github.com/oam-dev/stern v1.13.2

9
go.sum
View File

@@ -73,8 +73,6 @@ cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq
collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE=
contrib.go.opencensus.io/exporter/ocagent v0.6.0/go.mod h1:zmKjrJcdo0aYcVS7bmEeSEBLPA9YJp5bjrofdU3pIXs=
contrib.go.opencensus.io/exporter/stackdriver v0.13.4/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc=
cuelang.org/go v0.5.0-alpha.1 h1:uftOYkiScCHPCQMF2dIwoyCIJsTAEONkFSA2GCm5xIc=
cuelang.org/go v0.5.0-alpha.1/go.mod h1:nxWFAPWKYvZJ+eYayxArWqKKjdBTeU1N52vJpML/c6w=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/AlecAivazis/survey/v2 v2.1.1 h1:LEMbHE0pLj75faaVEKClEX1TM4AJmmnOh9eimREzLWI=
github.com/AlecAivazis/survey/v2 v2.1.1/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
@@ -1182,6 +1180,7 @@ github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
@@ -1330,12 +1329,14 @@ github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kubevela/cue v0.4.4-0.20221107123854-a976b0e340be h1:0xj/Rh4yVy54mUD2nLmAuN1AYgBkkHxBh4PoLGbIg5g=
github.com/kubevela/cue v0.4.4-0.20221107123854-a976b0e340be/go.mod h1:Ya12qn7FZc+LSN0qgEhzEpnzQsvnGHVgoDrqe9i3eNg=
github.com/kubevela/pkg v0.0.0-20221024115939-a103acee6db2 h1:C3cAfrxst1+dIWgLLhUQt1TQvEEpp1UTq9ZQB2xKbeI=
github.com/kubevela/pkg v0.0.0-20221024115939-a103acee6db2/go.mod h1:TgIGEB/r0NOy63Jzem7WsL3AIr34l+ClH9dmPqcZ4d4=
github.com/kubevela/prism v1.5.1-0.20220915071949-6bf3ad33f84f h1:1lUtU1alPThdcsn4MI6XjPb7eJLuZPpmlEdgjtnUMKw=
github.com/kubevela/prism v1.5.1-0.20220915071949-6bf3ad33f84f/go.mod h1:m724/7ANnB/iukyHW20+DicpeJMEC/JA0ZhgsHY10MA=
github.com/kubevela/workflow v0.3.1 h1:R2h6bZbcBSF1OswF0LtLIGn+X+fS0xPOoYgWgOPn1Ig=
github.com/kubevela/workflow v0.3.1/go.mod h1:5jfZ8T1m/En44wDGRf2YqCSlODfEnAV+9PnzoLoDlFs=
github.com/kubevela/workflow v0.3.3 h1:NSbQGcABWJIzUV5wfWFJsrO/ffZ4mCVfofUtUHCTojQ=
github.com/kubevela/workflow v0.3.3/go.mod h1:5jfZ8T1m/En44wDGRf2YqCSlODfEnAV+9PnzoLoDlFs=
github.com/kulti/thelper v0.4.0/go.mod h1:vMu2Cizjy/grP+jmsvOFDx1kYP6+PD1lqg4Yu5exl2U=
github.com/kunwardeep/paralleltest v1.0.3/go.mod h1:vLydzomDFpk7yu5UX02RmP0H8QfRPOV/oFhWN85Mjb4=
github.com/kylelemons/godebug v0.0.0-20160406211939-eadb3ce320cb/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=

View File

@@ -6,8 +6,8 @@ e2e-setup-core-pre-hook:
e2e-setup-core-post-hook:
kubectl wait --for=condition=Available deployment/kubevela-vela-core -n vela-system --timeout=180s
helm install kruise https://github.com/openkruise/charts/releases/download/kruise-1.1.0/kruise-1.1.0.tgz --set featureGates="PreDownloadImageForInPlaceUpdate=true" --set daemon.socketLocation=/run/k3s/containerd/
kill -9 $(lsof -it:9098) || true
go run ./e2e/addon/mock &
sleep 15
bin/vela addon enable ./e2e/addon/mock/testdata/fluxcd
bin/vela addon enable ./e2e/addon/mock/testdata/rollout
bin/vela addon enable ./e2e/addon/mock/testdata/terraform
@@ -82,14 +82,9 @@ e2e-api-test:
ginkgo -v -skipPackage capability,setup,application -r e2e
ginkgo -v -r e2e/application
ADDONSERVER = $(shell pgrep vela_addon_mock_server)
.PHONY: e2e-apiserver-test
e2e-apiserver-test:
pkill vela_addon_mock_server || true
go run ./e2e/addon/mock/vela_addon_mock_server.go &
sleep 15
go test -v -coverpkg=./... -coverprofile=/tmp/e2e_apiserver_test.out ./test/e2e-apiserver-test
@$(OK) tests pass

View File

@@ -1043,7 +1043,7 @@ func (h *Installer) installDependency(addon *InstallPackage) error {
continue
}
// always install addon's latest version
depAddon, err := h.loadInstallPackage(dep.Name, "")
depAddon, err := h.loadInstallPackage(dep.Name, dep.Version)
if err != nil {
return err
}

View File

@@ -105,7 +105,8 @@ type DeployTo struct {
// Dependency defines the other addons it depends on
type Dependency struct {
Name string `json:"name,omitempty"`
Name string `json:"name,omitempty"`
Version string `json:"version,omitempty"`
}
// ElementFile can be addon's definition or addon's component

View File

@@ -23,6 +23,7 @@ import (
"sort"
"github.com/Masterminds/semver/v3"
"github.com/pkg/errors"
"helm.sh/helm/v3/pkg/chart/loader"
"helm.sh/helm/v3/pkg/repo"
@@ -146,7 +147,7 @@ func (i versionedRegistry) loadAddon(ctx context.Context, name, version string)
sort.Sort(sort.Reverse(versions))
addonVersion, availableVersions := chooseVersion(version, versions)
if addonVersion == nil {
return nil, fmt.Errorf("specified version %s not exist", version)
return nil, errors.Errorf("specified version %s not exist", utils.Sanitize(version))
}
for _, chartURL := range addonVersion.URLs {
if !utils.IsValidURL(chartURL) {

View File

@@ -247,6 +247,9 @@ var RevisionStatusTerminated = "terminated"
// RevisionStatusRollback event status rollback
var RevisionStatusRollback = "rollback"
// WorkflowStepPhaseStopped is the stopped phase
var WorkflowStepPhaseStopped workflowv1alpha1.WorkflowStepPhase = "stopped"
// ApplicationRevision be created when an application initiates deployment and describes the phased version of the application.
type ApplicationRevision struct {
BaseModel

View File

@@ -27,10 +27,18 @@ func init() {
RegisterModel(&Pipeline{})
}
// Structs copied from workflow/api/v1alpha1/types.go
// WorkflowSpec defines workflow steps and other attributes
type WorkflowSpec struct {
Mode *v1alpha1.WorkflowExecuteMode `json:"mode,omitempty"`
Steps []WorkflowStep `json:"steps,omitempty"`
}
// Pipeline is the model of pipeline
type Pipeline struct {
BaseModel
Spec v1alpha1.WorkflowSpec
Spec WorkflowSpec
Name string `json:"name"`
Project string `json:"project"`
Alias string `json:"alias"`

View File

@@ -24,6 +24,7 @@ import (
"github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore"
"github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode"
"github.com/oam-dev/kubevela/pkg/apiserver/utils/log"
"github.com/oam-dev/kubevela/pkg/utils"
)
// ListApplicationPolicies query the application policies
@@ -83,7 +84,7 @@ func ListApplicationCommonPolicies(ctx context.Context, store datastore.DataStor
// DeleteApplicationEnvPolicies delete the policies via app name and env name
func DeleteApplicationEnvPolicies(ctx context.Context, store datastore.DataStore, app *model.Application, envName string) error {
log.Logger.Debugf("clear the policies via app name %s and env name %s", app.PrimaryKey(), envName)
log.Logger.Debugf("clear the policies via app name %s and env name %s", app.PrimaryKey(), utils.Sanitize(envName))
policies, err := ListApplicationEnvPolicies(ctx, store, app, envName)
if err != nil {
return err

View File

@@ -231,6 +231,7 @@ func (c *cloudShellServiceImpl) prepareKubeConfig(ctx context.Context) error {
var groups []string
for _, p := range projects {
permissions, err := c.RBACService.GetUserPermissions(ctx, user, p.Name, false)
// The kubernetes permission set is generated based on simple rules, but this is not completely strict.
var readOnly bool
if err != nil {
log.Logger.Errorf("failed to get the user permissions %s", err.Error())
@@ -239,7 +240,7 @@ func (c *cloudShellServiceImpl) prepareKubeConfig(ctx context.Context) error {
readOnly = checkReadOnly(p.Name, permissions)
}
if readOnly {
groupName, err := c.managePrivilegesForProjectRead(ctx, p.Name, true)
groupName, err := c.managePrivilegesForProject(ctx, p, true)
if err != nil {
log.Logger.Errorf("failed to privileges the user %s", err.Error())
}
@@ -247,9 +248,16 @@ func (c *cloudShellServiceImpl) prepareKubeConfig(ctx context.Context) error {
groups = append(groups, groupName)
}
} else {
groups = append(groups, utils.KubeVelaProjectGroupPrefix+p.Name)
groupName, err := c.managePrivilegesForProject(ctx, p, false)
if err != nil {
log.Logger.Errorf("failed to privileges the user %s", err.Error())
}
if groupName != "" {
groups = append(groups, groupName)
}
}
}
groups = append(groups, utils.TemplateReaderGroup)
if utils.StringsContain(user.UserRoles, "admin") {
groups = append(groups, utils.KubeVelaAdminGroupPrefix+"admin")
@@ -375,8 +383,9 @@ func checkReadOnly(projectName string, permissions []*model.Permission) bool {
return !ra.Match(permissions)
}
// managePrivilegesForProjectRead grant the read privileges for a project
func (c *cloudShellServiceImpl) managePrivilegesForProjectRead(ctx context.Context, projectName string, readOnly bool) (string, error) {
// managePrivilegesForProject grant the privileges for a project
func (c *cloudShellServiceImpl) managePrivilegesForProject(ctx context.Context, project *apisv1.ProjectBase, readOnly bool) (string, error) {
projectName := project.Name
targets, err := c.TargetService.ListTargets(ctx, 0, 0, projectName)
if err != nil {
log.Logger.Infof("failed to list the targets by the project name %s :%s", projectName, err.Error())
@@ -392,7 +401,14 @@ func (c *cloudShellServiceImpl) managePrivilegesForProjectRead(ctx context.Conte
for _, e := range envs.Envs {
authPDs = append(authPDs, &auth.ApplicationPrivilege{Cluster: kubevelatypes.ClusterLocalName, Namespace: e.Namespace, ReadOnly: readOnly})
}
// The namespace of the environment: Application and WorkflowRun
authPDs = append(authPDs, &auth.ApplicationPrivilege{Cluster: kubevelatypes.ClusterLocalName, Namespace: project.Namespace, ReadOnly: readOnly})
groupName := utils.KubeVelaProjectReadGroupPrefix + projectName
if !readOnly {
groupName = utils.KubeVelaProjectGroupPrefix + projectName
}
identity := &auth.Identity{Groups: []string{groupName}}
writer := &bytes.Buffer{}
if err := auth.GrantPrivileges(ctx, c.KubeClient, authPDs, identity, writer, auth.WithReplace); err != nil {

View File

@@ -48,6 +48,7 @@ var _ = Describe("Test cloudshell service function", func() {
cloudShellService *cloudShellServiceImpl
userService *userServiceImpl
projectService *projectServiceImpl
envService *envServiceImpl
err error
database string
)
@@ -56,7 +57,7 @@ var _ = Describe("Test cloudshell service function", func() {
database = "cloudshell-test-kubevela"
ds, err = NewDatastore(datastore.Config{Type: "kubeapi", Database: database})
Expect(err).Should(Succeed())
envService := &envServiceImpl{
envService = &envServiceImpl{
Store: ds,
KubeClient: k8sClient,
}
@@ -100,14 +101,13 @@ var _ = Describe("Test cloudshell service function", func() {
}
})
It("test prepareKubeConfig", func() {
It("Test prepareKubeConfig", func() {
err = userService.Init(context.TODO())
Expect(err).Should(BeNil())
err = projectService.Init(context.TODO())
Expect(err).Should(BeNil())
By("test the developer users")
_, err = userService.CreateUser(context.TODO(), apisv1.CreateUserRequest{Name: "test-dev", Password: "test"})
Expect(err).Should(BeNil())
@@ -171,17 +171,37 @@ var _ = Describe("Test cloudshell service function", func() {
err = cloudShellService.prepareKubeConfig(ctx)
Expect(err).Should(BeNil())
var cm corev1.ConfigMap
err = k8sClient.Get(context.TODO(), types.NamespacedName{Namespace: kubevelatypes.DefaultKubeVelaNS, Name: makeUserConfigName("admin-test")}, &cm)
checkConfig := func() {
var cm corev1.ConfigMap
err = k8sClient.Get(context.TODO(), types.NamespacedName{Namespace: kubevelatypes.DefaultKubeVelaNS, Name: makeUserConfigName("admin-test")}, &cm)
Expect(err).Should(BeNil())
Expect(len(cm.Data["identity"]) > 0).Should(BeTrue())
var identity auth.Identity
err = yaml.Unmarshal([]byte(cm.Data["identity"]), &identity)
Expect(err).Should(BeNil())
Expect(utils.StringsContain(identity.Groups, utils.KubeVelaAdminGroupPrefix+"admin")).Should(BeTrue())
Expect(utils.StringsContain(identity.Groups, utils.TemplateReaderGroup)).Should(BeTrue())
}
checkConfig()
By("Test other projects")
_, err = projectService.CreateProject(ctx, apisv1.CreateProjectRequest{Name: "cloudshell"})
Expect(err).Should(BeNil())
Expect(len(cm.Data["identity"]) > 0).Should(BeTrue())
var identity auth.Identity
err = yaml.Unmarshal([]byte(cm.Data["identity"]), &identity)
_, err = envService.CreateEnv(ctx, apisv1.CreateEnvRequest{Name: "cloudshell-env", Project: "cloudshell"})
Expect(err).Should(BeNil())
Expect(utils.StringsContain(identity.Groups, utils.KubeVelaAdminGroupPrefix+"admin")).Should(BeTrue())
err = cloudShellService.prepareKubeConfig(ctx)
Expect(err).Should(BeNil())
err = k8sClient.Get(context.Background(), types.NamespacedName{Name: "kubevela:writer:application:binding", Namespace: "cloudshell-env"}, &rb)
Expect(err).Should(BeNil())
Expect(rb.Subjects[0].Name).Should(Equal(utils.KubeVelaProjectGroupPrefix + "cloudshell"))
checkConfig()
})
It("test prepare", func() {
It("Test prepare", func() {
By("Test with not CRD")
_, err = userService.CreateUser(context.TODO(), apisv1.CreateUserRequest{Name: "test", Password: "test"})
Expect(err).Should(BeNil())

View File

@@ -69,6 +69,7 @@ type DefinitionQueryOption struct {
AppliedWorkloads string `json:"appliedWorkloads"`
OwnerAddon string `json:"sourceAddon"`
QueryAll bool `json:"queryAll"`
Scope string `json:"scope"`
}
// String return cache key string
@@ -109,6 +110,19 @@ func (d *definitionServiceImpl) listDefinitions(ctx context.Context, list *unstr
},
},
}
if ops.Scope != "" {
var filterScope string
if ops.Scope == "Application" {
filterScope = "WorkflowRun"
} else {
filterScope = "Application"
}
matchLabels.MatchExpressions = append(matchLabels.MatchExpressions, metav1.LabelSelectorRequirement{
Key: types.LabelDefinitionScope,
Operator: metav1.LabelSelectorOpNotIn,
Values: []string{filterScope},
})
}
if !ops.QueryAll {
matchLabels.MatchExpressions = append(matchLabels.MatchExpressions, metav1.LabelSelectorRequirement{
Key: types.LabelDefinitionHidden,

View File

@@ -112,6 +112,11 @@ var _ = Describe("Test namespace service functions", func() {
Expect(wfstep[0].WorkflowStep.Schematic).ShouldNot(BeNil())
Expect(wfstep[0].Alias).Should(Equal("test-alias"))
wfstep, err = definitionService.ListDefinitions(context.TODO(), DefinitionQueryOption{Type: "workflowstep", Scope: "WorkflowRun"})
Expect(err).Should(BeNil())
// the definition should be filtered
Expect(cmp.Diff(len(wfstep), 1)).Should(BeEmpty())
step, err = ioutil.ReadFile("./testdata/apply-application-hide.yaml")
Expect(err).Should(Succeed())
var sd2 v1beta1.WorkflowStepDefinition

View File

@@ -210,7 +210,7 @@ func (p pipelineServiceImpl) ListPipelines(ctx context.Context, req apis.ListPip
info, err = p.getPipelineInfo(ctx, pipeline, projectMap[pipeline.Project].Namespace)
if err != nil {
// Since we are listing pipelines. We should not return directly if we cannot get pipeline info
log.Logger.Errorf("get pipeline %s/%s info error: %v", pipeline.Project, pipeline.Name, err)
log.Logger.Errorf("get pipeline %s/%s info error: %v", pipeline.Project, pkgutils.Sanitize(pipeline.Name), err)
continue
}
}
@@ -248,7 +248,7 @@ func (p pipelineServiceImpl) GetPipeline(ctx context.Context, name string, getIn
if getInfo {
in, err := p.getPipelineInfo(ctx, pipeline, project.Namespace)
if err != nil {
log.Logger.Errorf("get pipeline %s/%s info error: %v", pipeline.Project, pipeline.Name, err)
log.Logger.Errorf("get pipeline %s/%s info error: %v", pipeline.Project, pkgutils.Sanitize(pipeline.Name), err)
return nil, bcode.ErrGetPipelineInfo
}
if in != nil {
@@ -673,13 +673,17 @@ func getResourceLogs(ctx context.Context, config *rest.Config, cli client.Client
}
break
}
shouldPrint := true
if len(filters) != 0 {
for _, f := range filters {
if strings.Contains(s, f) {
buf.WriteString(s)
if !strings.Contains(s, f) {
shouldPrint = false
}
}
}
if shouldPrint {
buf.WriteString(s)
}
}
if readErr != nil {
errPrint(buf, "error in copy information from APIServer to buffer: %s", err.Error())
@@ -705,6 +709,48 @@ func getResourceLogs(ctx context.Context, config *rest.Config, cli client.Client
return logBuilder.String(), nil
}
func pipelineStep2WorkflowStep(step model.WorkflowStep) v1alpha1.WorkflowStep {
res := v1alpha1.WorkflowStep{
WorkflowStepBase: v1alpha1.WorkflowStepBase{
Name: step.Name,
Type: step.Type,
Meta: step.Meta,
If: step.If,
Timeout: step.Timeout,
DependsOn: step.DependsOn,
Inputs: step.Inputs,
Outputs: step.Outputs,
Properties: step.Properties.RawExtension(),
},
SubSteps: make([]v1alpha1.WorkflowStepBase, 0),
}
for _, subStep := range step.SubSteps {
res.SubSteps = append(res.SubSteps, v1alpha1.WorkflowStepBase{
Name: subStep.Name,
Type: subStep.Type,
Meta: subStep.Meta,
If: subStep.If,
Timeout: subStep.Timeout,
DependsOn: subStep.DependsOn,
Inputs: subStep.Inputs,
Outputs: subStep.Outputs,
Properties: subStep.Properties.RawExtension(),
})
}
return res
}
func pipelineSpec2WorkflowSpec(spec model.WorkflowSpec) *v1alpha1.WorkflowSpec {
res := &v1alpha1.WorkflowSpec{
Mode: spec.Mode,
Steps: make([]v1alpha1.WorkflowStep, 0),
}
for _, step := range spec.Steps {
res.Steps = append(res.Steps, pipelineStep2WorkflowStep(step))
}
return res
}
// RunPipeline will run a pipeline
func (p pipelineServiceImpl) RunPipeline(ctx context.Context, pipeline apis.PipelineBase, req apis.RunPipelineRequest) (*apis.PipelineRun, error) {
if err := checkRunMode(&req.Mode); err != nil {
@@ -714,9 +760,10 @@ func (p pipelineServiceImpl) RunPipeline(ctx context.Context, pipeline apis.Pipe
run := v1alpha1.WorkflowRun{}
version := utils.GenerateVersion("")
name := fmt.Sprintf("%s-%s", pipeline.Name, version)
s := pipeline.Spec
run.Name = name
run.Namespace = project.GetNamespace()
run.Spec.WorkflowSpec = &pipeline.Spec
run.Spec.WorkflowSpec = pipelineSpec2WorkflowSpec(s)
run.Spec.Mode = &req.Mode
run.SetLabels(map[string]string{
@@ -884,7 +931,7 @@ func (p pipelineRunServiceImpl) GetPipelineRun(ctx context.Context, meta apis.Pi
}
return nil, err
}
run.Spec.WorkflowSpec = &pipeline.Spec
run.Spec.WorkflowSpec = pipelineSpec2WorkflowSpec(pipeline.Spec)
}
return workflowRun2PipelineRun(run, project, p.ContextService)
@@ -984,7 +1031,7 @@ func (c contextServiceImpl) CreateContext(ctx context.Context, projectName, pipe
}
}
if _, ok := modelCtx.Contexts[context.Name]; ok {
log.Logger.Errorf("context %s already exists", context.Name)
log.Logger.Errorf("context %s already exists", pkgutils.Sanitize(context.Name))
return nil, bcode.ErrContextAlreadyExist
}
modelCtx.Contexts[context.Name] = context.Values
@@ -1239,7 +1286,7 @@ func (p pipelineRunServiceImpl) terminatePipelineRun(ctx context.Context, run *v
}
func checkPipelineSpec(spec v1alpha1.WorkflowSpec) error {
func checkPipelineSpec(spec model.WorkflowSpec) error {
if len(spec.Steps) == 0 {
return bcode.ErrNoSteps
}

View File

@@ -22,7 +22,6 @@ import (
"github.com/kubevela/workflow/api/v1alpha1"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"k8s.io/apimachinery/pkg/runtime"
"github.com/oam-dev/kubevela/pkg/apiserver/domain/model"
"github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore"
@@ -68,10 +67,12 @@ var _ = Describe("Test pipeline service functions", func() {
})
It("Test create pipeline", func() {
rawProps := []byte(`{"url":"https://api.github.com/repos/kubevela/kubevela"}`)
testPipelineSteps := []v1alpha1.WorkflowStep{
props := model.JSONStruct{
"url": "https://api.github.com/repos/kubevela/kubevela",
}
testPipelineSteps := []model.WorkflowStep{
{
SubSteps: []v1alpha1.WorkflowStepBase{
SubSteps: []model.WorkflowStepBase{
{
Name: "request",
Type: "request",
@@ -81,12 +82,10 @@ var _ = Describe("Test pipeline service functions", func() {
Name: "stars",
},
},
Properties: &runtime.RawExtension{
Raw: rawProps,
},
Properties: &props,
},
},
WorkflowStepBase: v1alpha1.WorkflowStepBase{
WorkflowStepBase: model.WorkflowStepBase{
Name: "step-group",
Type: "step-group",
},
@@ -96,7 +95,7 @@ var _ = Describe("Test pipeline service functions", func() {
By("create pipeline with sub-steps")
pipeline, err := pipelineService.CreatePipeline(ctx, apisv1.CreatePipelineRequest{
Name: pipelineName,
Spec: v1alpha1.WorkflowSpec{
Spec: model.WorkflowSpec{
Steps: testPipelineSteps,
},
})

View File

@@ -75,29 +75,19 @@ func (p *projectServiceImpl) Init(ctx context.Context) error {
// the default env and default target both using the `default` namespace in control plane cluster
func (p *projectServiceImpl) InitDefaultProjectEnvTarget(ctx context.Context, defaultNamespace string) error {
var project = model.Project{}
entities, err := p.Store.List(ctx, &project, &datastore.ListOptions{FilterOptions: datastore.FilterOptions{
IsNotExist: []datastore.IsNotExistQueryOption{
{
Key: "owner",
},
},
}})
entities, err := p.Store.List(ctx, &project, &datastore.ListOptions{FilterOptions: datastore.FilterOptions{}})
if err != nil {
return fmt.Errorf("initialize project failed %w", err)
}
if len(entities) > 0 {
for _, project := range entities {
pro := project.(*model.Project)
var init = pro.Owner == ""
pro.Owner = model.DefaultAdminUserName
if err := p.Store.Put(ctx, pro); err != nil {
return err
}
// owner is empty, it is old data
if init {
if err := p.RbacService.InitDefaultRoleAndUsersForProject(ctx, pro); err != nil {
return fmt.Errorf("init default role and users for project %s failure %w", pro.Name, err)
}
if err := p.RbacService.SyncDefaultRoleAndUsersForProject(ctx, pro); err != nil {
return fmt.Errorf("fail to sync the default role and users for the project %s %w", pro.Name, err)
}
}
return nil
@@ -305,7 +295,6 @@ func (p *projectServiceImpl) DeleteProject(ctx context.Context, name string) err
// CreateProject create project
func (p *projectServiceImpl) CreateProject(ctx context.Context, req apisv1.CreateProjectRequest) (*apisv1.ProjectBase, error) {
exist, err := p.Store.IsExist(ctx, &model.Project{Name: req.Name})
if err != nil {
log.Logger.Errorf("check project name is exist failure %s", err.Error())
@@ -344,8 +333,8 @@ func (p *projectServiceImpl) CreateProject(ctx context.Context, req apisv1.Creat
return nil, err
}
if err := p.RbacService.InitDefaultRoleAndUsersForProject(ctx, newProject); err != nil {
log.Logger.Errorf("init default role and users for project failure %s", err.Error())
if err := p.RbacService.SyncDefaultRoleAndUsersForProject(ctx, newProject); err != nil {
log.Logger.Errorf("fail to sync the default role and users for the project: %s", err.Error())
}
return ConvertProjectModel2Base(newProject, user), nil

View File

@@ -90,10 +90,10 @@ var defaultProjectPermissionTemplate = []*model.PermissionTemplate{
Scope: "project",
},
{
Name: "configuration-read",
Alias: "Environment Management",
Name: "config-management",
Alias: "Config Management",
Resources: []string{"project:{projectName}/config:*", "project:{projectName}/provider:*"},
Actions: []string{"list", "detail"},
Actions: []string{"*"},
Effect: "Allow",
Scope: "project",
},
@@ -405,7 +405,7 @@ type RBACService interface {
ListPermissions(ctx context.Context, projectName string) ([]apisv1.PermissionBase, error)
CreatePermission(ctx context.Context, projectName string, req apisv1.CreatePermissionRequest) (*apisv1.PermissionBase, error)
DeletePermission(ctx context.Context, projectName, permName string) error
InitDefaultRoleAndUsersForProject(ctx context.Context, project *model.Project) error
SyncDefaultRoleAndUsersForProject(ctx context.Context, project *model.Project) error
Init(ctx context.Context) error
}
@@ -857,7 +857,17 @@ func (p *rbacServiceImpl) CreatePermission(ctx context.Context, projectName stri
return assembler.ConvertPermission2DTO(&permission), nil
}
func (p *rbacServiceImpl) InitDefaultRoleAndUsersForProject(ctx context.Context, project *model.Project) error {
func (p *rbacServiceImpl) SyncDefaultRoleAndUsersForProject(ctx context.Context, project *model.Project) error {
permissions, err := p.ListPermissions(ctx, project.Name)
if err != nil {
return err
}
var permissionMap = map[string]apisv1.PermissionBase{}
for i, per := range permissions {
permissionMap[per.Name] = permissions[i]
}
var batchData []datastore.Entity
for _, permissionTemp := range defaultProjectPermissionTemplate {
var rra = RequestResourceAction{}
@@ -871,39 +881,52 @@ func (p *rbacServiceImpl) InitDefaultRoleAndUsersForProject(ctx context.Context,
})
formattedResource = append(formattedResource, rra.GetResource().String())
}
batchData = append(batchData, &model.Permission{
permission := &model.Permission{
Name: permissionTemp.Name,
Alias: permissionTemp.Alias,
Project: project.Name,
Resources: formattedResource,
Actions: permissionTemp.Actions,
Effect: permissionTemp.Effect,
})
}
batchData = append(batchData, &model.Role{
Name: "app-developer",
Alias: "App Developer",
Permissions: []string{"project-view", "app-management", "env-management", "configuration-read"},
Project: project.Name,
}, &model.Role{
Name: "project-admin",
Alias: "Project Admin",
Permissions: []string{"project-view", "app-management", "env-management", "role-management", "pipeline-management", "configuration-read"},
Project: project.Name,
}, &model.Role{
Name: "project-viewer",
Alias: "Project Viewer",
Permissions: []string{"project-view"},
Project: project.Name,
})
if project.Owner != "" {
var projectUser = &model.ProjectUser{
ProjectName: project.Name,
UserRoles: []string{"project-admin"},
Username: project.Owner,
}
batchData = append(batchData, projectUser)
if perm, exist := permissionMap[permissionTemp.Name]; exist {
if !utils.EqualSlice(perm.Resources, permissionTemp.Resources) || utils.EqualSlice(perm.Actions, permissionTemp.Actions) {
if err := p.Store.Put(ctx, permission); err != nil {
return err
}
}
continue
}
batchData = append(batchData, permission)
}
if len(permissions) == 0 {
batchData = append(batchData, &model.Role{
Name: "app-developer",
Alias: "App Developer",
Permissions: []string{"project-view", "app-management", "env-management", "config-management", "pipeline-management"},
Project: project.Name,
}, &model.Role{
Name: "project-admin",
Alias: "Project Admin",
Permissions: []string{"project-view", "app-management", "env-management", "pipeline-management", "config-management", "role-management"},
Project: project.Name,
}, &model.Role{
Name: "project-viewer",
Alias: "Project Viewer",
Permissions: []string{"project-view"},
Project: project.Name,
})
if project.Owner != "" {
var projectUser = &model.ProjectUser{
ProjectName: project.Name,
UserRoles: []string{"project-admin"},
Username: project.Owner,
}
batchData = append(batchData, projectUser)
}
}
return p.Store.BatchAdd(ctx, batchData)
}

View File

@@ -189,7 +189,7 @@ var _ = Describe("Test rbac service", func() {
err = ds.Add(context.TODO(), &model.Project{Name: "init-test", Owner: "test-user"})
Expect(err).Should(BeNil())
err = rbacService.InitDefaultRoleAndUsersForProject(context.TODO(), &model.Project{Name: "init-test"})
err = rbacService.SyncDefaultRoleAndUsersForProject(context.TODO(), &model.Project{Name: "init-test"})
Expect(err).Should(BeNil())
roles, err := rbacService.ListRole(context.TODO(), "init-test", 0, 0)

View File

@@ -8,6 +8,8 @@ metadata:
definition.oam.dev/alias: test-alias
name: apply-application
namespace: vela-system
labels:
custom.definition.oam.dev/scope: Application
spec:
schematic:
cue:

View File

@@ -52,6 +52,9 @@ func guaranteePolicyNotExist(c []string, policy string) ([]string, bool) {
// extractPolicyListAndProperty can extract policy from string-format properties, and return
// map-format properties in order to further update operation.
func extractPolicyListAndProperty(property string) ([]string, map[string]interface{}, error) {
if len(property) == 0 {
return nil, nil, nil
}
content := map[string]interface{}{}
err := json.Unmarshal([]byte(property), &content)
if err != nil {

View File

@@ -208,6 +208,14 @@ func TestExtractPolicyListAndProperty(t *testing.T) {
noError bool
}{noError: false},
},
{
input: ``,
res: struct {
policies []string
properties map[string]interface{}
noError bool
}{policies: nil, properties: nil, noError: true},
},
}
for _, testCase := range testCases {
policy, properties, err := extractPolicyListAndProperty(testCase.input)

View File

@@ -623,7 +623,7 @@ func resetRevisionsAndRecords(ctx context.Context, ds datastore.DataStore, appNa
record.Finished = "true"
for i, step := range record.Steps {
if step.Phase == workflowv1alpha1.WorkflowStepPhaseRunning {
record.Steps[i].Phase = workflowv1alpha1.WorkflowStepPhaseStopped
record.Steps[i].Phase = model.WorkflowStepPhaseStopped
}
}
if err := ds.Put(ctx, record); err != nil {

View File

@@ -586,7 +586,7 @@ var _ = Describe("Test workflow service functions", func() {
Expect(err).Should(BeNil())
Expect(record.Status).Should(Equal(model.RevisionStatusTerminated))
Expect(record.Finished).Should(Equal("true"))
Expect(record.Steps[1].Phase).Should(Equal(workflowv1alpha1.WorkflowStepPhaseStopped))
Expect(record.Steps[1].Phase).Should(Equal(model.WorkflowStepPhaseStopped))
})
It("Test deleting workflow", func() {

View File

@@ -51,6 +51,7 @@ func (d *definitionAPIInterface) GetWebServiceRoute() *restful.WebService {
Param(ws.QueryParameter("queryAll", "query all definitions include hidden in UI").DataType("boolean").DefaultValue("false")).
Param(ws.QueryParameter("appliedWorkload", "if specified, query the trait definition applied to the workload").DataType("string")).
Param(ws.QueryParameter("ownerAddon", "query by which addon created the definition").DataType("string")).
Param(ws.QueryParameter("scope", "query by the specified scope like WorkflowRun or Application").DataType("string")).
Returns(200, "OK", apis.ListDefinitionResponse{}).
Writes(apis.ListDefinitionResponse{}).Do(returns200, returns500))
@@ -97,6 +98,7 @@ func (d *definitionAPIInterface) listDefinitions(req *restful.Request, res *rest
Type: req.QueryParameter("type"),
AppliedWorkloads: req.QueryParameter("appliedWorkload"),
OwnerAddon: req.QueryParameter("ownerAddon"),
Scope: req.QueryParameter("scope"),
QueryAll: queryAll,
})
if err != nil {

View File

@@ -793,7 +793,7 @@ type ProjectBase struct {
CreateTime time.Time `json:"createTime"`
UpdateTime time.Time `json:"updateTime"`
Owner NameAlias `json:"owner,omitempty"`
Namespace string `json:"-"`
Namespace string `json:"namespace"`
}
// CreateProjectRequest create project request body
@@ -1564,7 +1564,7 @@ type PipelineMeta struct {
// PipelineBase is the base info of pipeline
type PipelineBase struct {
PipelineMeta `json:",inline"`
Spec workflowv1alpha1.WorkflowSpec `json:"spec"`
Spec model.WorkflowSpec `json:"spec"`
}
// RunStatInfo is the pipeline run statistics info
@@ -1583,10 +1583,10 @@ type RunStat struct {
// CreatePipelineRequest is the request body of creating pipeline
type CreatePipelineRequest struct {
Name string `json:"name" validate:"checkname"`
Alias string `json:"alias" validate:"checkalias" optional:"true"`
Description string `json:"description" optional:"true"`
Spec workflowv1alpha1.WorkflowSpec `json:"spec"`
Name string `json:"name" validate:"checkname"`
Alias string `json:"alias" validate:"checkalias" optional:"true"`
Description string `json:"description" optional:"true"`
Spec model.WorkflowSpec `json:"spec"`
}
// PipelineMetaResponse is the response body contains PipelineMeta
@@ -1615,9 +1615,9 @@ type PipelineListItem struct {
// UpdatePipelineRequest is the request body of updating pipeline
type UpdatePipelineRequest struct {
Alias string `json:"alias" validate:"checkalias" optional:"true"`
Description string `json:"description" optional:"true"`
Spec workflowv1alpha1.WorkflowSpec `json:"spec" optional:"true"`
Alias string `json:"alias" validate:"checkalias" optional:"true"`
Description string `json:"description" optional:"true"`
Spec model.WorkflowSpec `json:"spec" optional:"true"`
}
// GetPipelineResponse is the response body of getting pipeline

View File

@@ -38,6 +38,9 @@ const KubeVelaProjectReadGroupPrefix = "kubevela:project-ro:"
// KubeVelaAdminGroupPrefix the prefix kubevela admin
const KubeVelaAdminGroupPrefix = "kubevela:admin:"
// TemplateReaderGroup This group includes the permission that read the ConfigMap in the vela-system namespace.
const TemplateReaderGroup = "template-reader"
// ContextWithUserInfo extract user from context (parse username and project) for impersonation
func ContextWithUserInfo(ctx context.Context) context.Context {
if !features.APIServerFeatureGate.Enabled(features.APIServerEnableImpersonation) {

View File

@@ -329,7 +329,7 @@ func (af *Appfile) generateAndFilterCommonLabels(compName string) map[string]str
oam.LabelAppComponent: compName,
}
// merge application's all labels
finalLabels := util.MergeMapOverrideWithDst(Labels, af.AppLabels)
finalLabels := util.MergeMapOverrideWithDst(af.AppLabels, Labels)
filterLabels, ok := af.AppAnnotations[oam.AnnotationFilterLabelKeys]
if ok {
filter(finalLabels, strings.Split(filterLabels, ","))

View File

@@ -338,7 +338,7 @@ func (a *ApplicationPrivilege) GetRoles() []client.Object {
Rules: []rbacv1.PolicyRule{
{
APIGroups: []string{"core.oam.dev"},
Resources: []string{"applications", "policies", "workflows"},
Resources: []string{"applications", "policies", "workflows", "workflowruns"},
Verbs: verbs,
},
},

View File

@@ -192,7 +192,7 @@ var _ = Describe("Test Application Controller", func() {
},
{
Type: "env",
Properties: &runtime.RawExtension{Raw: []byte("{\"env\":{\"thirdKey\":\"thirdValue\"}}")},
Properties: &runtime.RawExtension{Raw: []byte("{\"env\":{\"firstKey\":\"newValue\"}}")},
},
}
@@ -3757,11 +3757,11 @@ var _ = Describe("Test Application Controller", func() {
By("Check debug Config Map is created")
debugCM := &corev1.ConfigMap{}
Expect(k8sClient.Get(ctx, types.NamespacedName{
Name: debug.GenerateContextName(app.Name, "step1"),
Name: debug.GenerateContextName(app.Name, "step1", string(app.UID)),
Namespace: "default",
}, debugCM)).Should(BeNil())
Expect(k8sClient.Get(ctx, types.NamespacedName{
Name: debug.GenerateContextName(app.Name, "step2-sub1"),
Name: debug.GenerateContextName(app.Name, "step2-sub1", string(app.UID)),
Namespace: "default",
}, debugCM)).Should(BeNil())
@@ -3770,7 +3770,7 @@ var _ = Describe("Test Application Controller", func() {
testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey})
updatedCM := &corev1.ConfigMap{}
Expect(k8sClient.Get(ctx, types.NamespacedName{
Name: debug.GenerateContextName(app.Name, "step1"),
Name: debug.GenerateContextName(app.Name, "step1", string(app.UID)),
Namespace: "default",
}, updatedCM)).Should(BeNil())

View File

@@ -162,16 +162,13 @@ func needRestart(app *v1beta1.Application, revName string) (string, bool) {
}
func generateWorkflowInstance(af *appfile.Appfile, app *v1beta1.Application, appRev string) *wfTypes.WorkflowInstance {
anno := make(map[string]string)
if af.Debug {
anno[wfTypes.AnnotationWorkflowRunDebug] = "true"
}
instance := &wfTypes.WorkflowInstance{
WorkflowMeta: wfTypes.WorkflowMeta{
Name: af.Name,
Namespace: af.Namespace,
Annotations: app.Annotations,
Labels: app.Labels,
UID: app.UID,
ChildOwnerReferences: []metav1.OwnerReference{
{
APIVersion: v1beta1.SchemeGroupVersion.String(),
@@ -237,6 +234,7 @@ func generateWorkflowInstance(af *appfile.Appfile, app *v1beta1.Application, app
func convertStepProperties(step *workflowv1alpha1.WorkflowStep, app *v1beta1.Application) error {
o := struct {
Component string `json:"component"`
Cluster string `json:"cluster"`
}{}
js, err := common.RawExtensionPointer{RawExtension: step.Properties}.MarshalJSON()
if err != nil {
@@ -262,6 +260,9 @@ func convertStepProperties(step *workflowv1alpha1.WorkflowStep, app *v1beta1.App
if parameterKey != "" && !strings.HasPrefix(parameterKey, "properties") && !strings.HasPrefix(parameterKey, "traits[") {
parameterKey = "properties." + parameterKey
}
if parameterKey != "" {
parameterKey = "value." + parameterKey
}
step.Inputs[index].ParameterKey = parameterKey
}
step.Outputs = append(step.Outputs, c.Outputs...)
@@ -269,7 +270,11 @@ func convertStepProperties(step *workflowv1alpha1.WorkflowStep, app *v1beta1.App
c.Inputs = nil
c.Outputs = nil
c.DependsOn = nil
step.Properties = util.Object2RawExtension(c)
stepProperties := map[string]interface{}{
"value": c,
"cluster": o.Cluster,
}
step.Properties = util.Object2RawExtension(stepProperties)
return nil
}
}

View File

@@ -410,7 +410,7 @@ func JoinClusterByKubeConfig(ctx context.Context, cli client.Client, kubeconfigP
}
}
if cfg, ok := ctx.Value(KubeConfigContext).(*rest.Config); ok {
if err = SetClusterVersionInfo(ctx, cfg, clusterName); err != nil {
if err = SetClusterVersionInfo(ctx, cfg, clusterConfig.ClusterName); err != nil {
return nil, err
}
}

View File

@@ -80,9 +80,15 @@ import (
var (
// Scheme defines the default KubeVela schema
Scheme = k8sruntime.NewScheme()
// forbidRedirectFunc general check func for http redirect response
forbidRedirectFunc = func(req *http.Request, via []*http.Request) error {
return errors.New("got a redirect response which is forbidden")
}
//nolint:gosec
// insecureHTTPClient insecure http client
insecureHTTPClient = &http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}}
insecureHTTPClient = &http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}, CheckRedirect: forbidRedirectFunc}
// forbidRedirectClient is a http client forbid redirect http request
forbidRedirectClient = &http.Client{CheckRedirect: forbidRedirectFunc}
)
const (
@@ -170,7 +176,7 @@ func HTTPGetResponse(ctx context.Context, url string, opts *HTTPOption) (*http.R
if err != nil {
return nil, err
}
httpClient := http.DefaultClient
httpClient := forbidRedirectClient
if opts != nil && len(opts.Username) != 0 && len(opts.Password) != 0 {
req.SetBasicAuth(opts.Username, opts.Password)
}
@@ -198,7 +204,7 @@ func HTTPGetResponse(ctx context.Context, url string, opts *HTTPOption) (*http.R
}
tr.TLSClientConfig = tlsConfig
defer tr.CloseIdleConnections()
httpClient = &http.Client{Transport: &tr}
httpClient = &http.Client{Transport: &tr, CheckRedirect: forbidRedirectFunc}
}
return httpClient.Do(req)
}

View File

@@ -26,6 +26,7 @@ import (
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
"time"
@@ -223,6 +224,25 @@ func TestHttpGetCaFile(t *testing.T) {
}
}
func TestHttpGetForbidRedirect(t *testing.T) {
var ctx = context.Background()
testServer := &http.Server{Addr: ":19090"}
http.HandleFunc("/redirect", func(writer http.ResponseWriter, request *http.Request) {
http.Redirect(writer, request, "http://192.168.1.1", http.StatusFound)
})
go func() {
err := testServer.ListenAndServe()
assert.NoError(t, err)
}()
time.Sleep(time.Millisecond)
_, err := HTTPGetWithOption(ctx, "http://127.0.0.1:19090/redirect", nil)
assert.Error(t, err)
assert.True(t, strings.Contains(err.Error(), "got a redirect response which is forbidden"))
}
func TestGetCUEParameterValue(t *testing.T) {
type want struct {
err error

View File

@@ -225,7 +225,7 @@ func (h *Helper) GetIndexInfo(repoURL string, skipCache bool, opts *common.HTTPO
}
i := &repo.IndexFile{}
if err := yaml.UnmarshalStrict(body, i); err != nil {
return nil, fmt.Errorf("parse index file from %s failure %w", repoURL, err)
return nil, fmt.Errorf("parse index file from %s failure", repoURL)
}
if h.cache != nil {

View File

@@ -24,6 +24,7 @@ import (
"time"
corev1 "k8s.io/api/core/v1"
v1 "k8s.io/api/networking/v1"
networkv1beta1 "k8s.io/api/networking/v1beta1"
kerrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -108,7 +109,7 @@ func getServiceEndpoints(ctx context.Context, cli client.Client, gvk schema.Grou
switch gvk.Kind {
case "Ingress":
if gvk.Group == networkv1beta1.GroupName && (gvk.Version == "v1beta1" || gvk.Version == "v1") {
var ingress networkv1beta1.Ingress
var ingress v1.Ingress
ingress.SetGroupVersionKind(gvk)
if err := findResource(ctx, cli, &ingress, name, namespace, cluster); err != nil {
klog.Error(err, fmt.Sprintf("find v1 Ingress %s/%s from cluster %s failure", name, namespace, cluster))
@@ -232,7 +233,7 @@ func generatorFromService(service corev1.Service, selectorNodeIP func() string,
return serviceEndpoints
}
func generatorFromIngress(ingress networkv1beta1.Ingress, cluster, component string) (serviceEndpoints []querytypes.ServiceEndpoint) {
func generatorFromIngress(ingress v1.Ingress, cluster, component string) (serviceEndpoints []querytypes.ServiceEndpoint) {
getAppProtocol := func(host string) string {
if len(ingress.Spec.TLS) > 0 {
for _, tls := range ingress.Spec.TLS {
@@ -407,26 +408,10 @@ func selectorNodeIP(ctx context.Context, clusterName string, client client.Clien
workerNodes = append(workerNodes, nodes.Items[i])
}
}
if gatewayNode == nil && len(workerNodes) > 0 {
gatewayNode = &workerNodes[0]
}
if gatewayNode == nil {
gatewayNode = &nodes.Items[0]
}
if gatewayNode != nil {
var addressMap = make(map[corev1.NodeAddressType]string)
for _, address := range gatewayNode.Status.Addresses {
addressMap[address.Type] = address.Address
}
// first get external ip
if ip, exist := addressMap[corev1.NodeExternalIP]; exist {
return ip
}
if ip, exist := addressMap[corev1.NodeInternalIP]; exist {
return ip
}
return selectGatewayIP([]corev1.Node{*gatewayNode})
}
return ""
return selectGatewayIP(workerNodes)
}
// judgeAppProtocol RFC-6335 and http://www.iana.org/assignments/service-names).
@@ -444,3 +429,23 @@ func judgeAppProtocol(port int32) string {
return ""
}
}
// selectGatewayIP will choose one gateway IP from all nodes, it will pick up external IP first. If there isn't any, it will pick the first node's internal IP.
func selectGatewayIP(nodes []corev1.Node) string {
if len(nodes) == 0 {
return ""
}
var addressMaps = make([]map[corev1.NodeAddressType]string, 0)
for _, node := range nodes {
var addressMap = make(map[corev1.NodeAddressType]string)
for _, address := range node.Status.Addresses {
addressMap[address.Type] = address.Address
}
// first get external ip
if ip, exist := addressMap[corev1.NodeExternalIP]; exist {
return ip
}
addressMaps = append(addressMaps, addressMap)
}
return addressMaps[0][corev1.NodeInternalIP]
}

View File

@@ -18,15 +18,19 @@ package query
import (
"context"
"fmt"
"time"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
v1 "k8s.io/api/networking/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/intstr"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
monitorContext "github.com/kubevela/pkg/monitor/context"
"github.com/kubevela/workflow/pkg/cue/model/value"
@@ -39,7 +43,7 @@ import (
querytypes "github.com/oam-dev/kubevela/pkg/velaql/providers/query/types"
)
var _ = Describe("Test Query Provider", func() {
var _ = Describe("Test query endpoints", func() {
BeforeEach(func() {
})
@@ -268,5 +272,87 @@ var _ = Describe("Test Query Provider", func() {
}
Expect(edps).Should(BeEquivalentTo(urls))
})
It("Test select gateway IP", func() {
node1 := corev1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "node-with-external-ip",
},
Status: corev1.NodeStatus{
Addresses: []corev1.NodeAddress{
{Type: corev1.NodeExternalIP, Address: "node1-external-ip"},
{Type: corev1.NodeInternalIP, Address: "node1-internal-ip"},
},
},
}
node2 := corev1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "node-without-external-ip",
},
Status: corev1.NodeStatus{
Addresses: []corev1.NodeAddress{
{Type: corev1.NodeInternalIP, Address: "node2-internal-ip"},
},
},
}
testCases := []struct {
nodes []corev1.Node
wantIP string
}{
{
nodes: []corev1.Node{node1, node2},
wantIP: "node1-external-ip",
},
{
nodes: []corev1.Node{node2},
wantIP: "node2-internal-ip",
},
{
nodes: []corev1.Node{},
wantIP: "",
},
}
for _, tc := range testCases {
gotIP := selectGatewayIP(tc.nodes)
Expect(gotIP).Should(BeEquivalentTo(tc.wantIP))
}
})
})
})
var _ = Describe("Test get ingress endpoint", func() {
It("Test get ingress endpoint with different apiVersion", func() {
ingress1 := v1.Ingress{}
Expect(yaml.Unmarshal([]byte(ingressYaml1), &ingress1)).Should(BeNil())
err := k8sClient.Create(ctx, &ingress1)
Expect(err).Should(BeNil())
gvk := schema.GroupVersionKind{Group: "networking.k8s.io", Version: "v1", Kind: "Ingress"}
Eventually(func() error {
eps := getServiceEndpoints(ctx, k8sClient, gvk, ingress1.Name, ingress1.Namespace, "", "", nil)
if len(eps) != 1 {
return fmt.Errorf("result length missmatch")
}
return nil
}, 2*time.Second, 500*time.Millisecond).Should(BeNil())
})
})
var ingressYaml1 = `
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-1
namespace: default
spec:
rules:
- http:
paths:
- path: /testpath
pathType: Prefix
backend:
service:
name: test
port:
number: 80
`

View File

@@ -26,7 +26,7 @@ import (
. "github.com/onsi/gomega"
v1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
networkv1beta1 "k8s.io/api/networking/v1beta1"
networkv1 "k8s.io/api/networking/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
@@ -743,25 +743,29 @@ options: {
}
}
var prefixbeta = networkv1beta1.PathTypePrefix
var prefixbeta = networkv1.PathTypePrefix
testIngress := []client.Object{
&networkv1beta1.Ingress{
&networkv1.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "ingress-http",
Namespace: "default",
},
Spec: networkv1beta1.IngressSpec{
Rules: []networkv1beta1.IngressRule{
Spec: networkv1.IngressSpec{
Rules: []networkv1.IngressRule{
{
Host: "ingress.domain",
IngressRuleValue: networkv1beta1.IngressRuleValue{
HTTP: &networkv1beta1.HTTPIngressRuleValue{
Paths: []networkv1beta1.HTTPIngressPath{
IngressRuleValue: networkv1.IngressRuleValue{
HTTP: &networkv1.HTTPIngressRuleValue{
Paths: []networkv1.HTTPIngressPath{
{
Path: "/",
Backend: networkv1beta1.IngressBackend{
ServiceName: "clusterip",
ServicePort: intstr.FromInt(80),
Backend: networkv1.IngressBackend{
Service: &networkv1.IngressServiceBackend{
Name: "clusterip",
Port: networkv1.ServiceBackendPort{
Number: 80,
},
},
},
PathType: &prefixbeta,
},
@@ -772,28 +776,32 @@ options: {
},
},
},
&networkv1beta1.Ingress{
&networkv1.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "ingress-https",
Namespace: "default",
},
Spec: networkv1beta1.IngressSpec{
TLS: []networkv1beta1.IngressTLS{
Spec: networkv1.IngressSpec{
TLS: []networkv1.IngressTLS{
{
SecretName: "https-secret",
},
},
Rules: []networkv1beta1.IngressRule{
Rules: []networkv1.IngressRule{
{
Host: "ingress.domain.https",
IngressRuleValue: networkv1beta1.IngressRuleValue{
HTTP: &networkv1beta1.HTTPIngressRuleValue{
Paths: []networkv1beta1.HTTPIngressPath{
IngressRuleValue: networkv1.IngressRuleValue{
HTTP: &networkv1.HTTPIngressRuleValue{
Paths: []networkv1.HTTPIngressPath{
{
Path: "/",
Backend: networkv1beta1.IngressBackend{
ServiceName: "clusterip",
ServicePort: intstr.FromInt(80),
Backend: networkv1.IngressBackend{
Service: &networkv1.IngressServiceBackend{
Name: "clusterip",
Port: networkv1.ServiceBackendPort{
Number: 80,
},
},
},
PathType: &prefixbeta,
},
@@ -804,36 +812,44 @@ options: {
},
},
},
&networkv1beta1.Ingress{
&networkv1.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "ingress-paths",
Namespace: "default",
},
Spec: networkv1beta1.IngressSpec{
TLS: []networkv1beta1.IngressTLS{
Spec: networkv1.IngressSpec{
TLS: []networkv1.IngressTLS{
{
SecretName: "https-secret",
},
},
Rules: []networkv1beta1.IngressRule{
Rules: []networkv1.IngressRule{
{
Host: "ingress.domain.path",
IngressRuleValue: networkv1beta1.IngressRuleValue{
HTTP: &networkv1beta1.HTTPIngressRuleValue{
Paths: []networkv1beta1.HTTPIngressPath{
IngressRuleValue: networkv1.IngressRuleValue{
HTTP: &networkv1.HTTPIngressRuleValue{
Paths: []networkv1.HTTPIngressPath{
{
Path: "/test",
Backend: networkv1beta1.IngressBackend{
ServiceName: "clusterip",
ServicePort: intstr.FromInt(80),
Backend: networkv1.IngressBackend{
Service: &networkv1.IngressServiceBackend{
Name: "clusterip",
Port: networkv1.ServiceBackendPort{
Number: 80,
},
},
},
PathType: &prefixbeta,
},
{
Path: "/test2",
Backend: networkv1beta1.IngressBackend{
ServiceName: "clusterip",
ServicePort: intstr.FromInt(80),
Backend: networkv1.IngressBackend{
Service: &networkv1.IngressServiceBackend{
Name: "clusterip",
Port: networkv1.ServiceBackendPort{
Number: 80,
},
},
},
PathType: &prefixbeta,
},
@@ -844,7 +860,7 @@ options: {
},
},
},
&networkv1beta1.Ingress{
&networkv1.Ingress{
TypeMeta: metav1.TypeMeta{
APIVersion: "networking.k8s.io/v1beta1",
},
@@ -856,18 +872,22 @@ options: {
"helm.toolkit.fluxcd.io/namespace": "default",
},
},
Spec: networkv1beta1.IngressSpec{
Rules: []networkv1beta1.IngressRule{
Spec: networkv1.IngressSpec{
Rules: []networkv1.IngressRule{
{
Host: "ingress.domain.helm",
IngressRuleValue: networkv1beta1.IngressRuleValue{
HTTP: &networkv1beta1.HTTPIngressRuleValue{
Paths: []networkv1beta1.HTTPIngressPath{
IngressRuleValue: networkv1.IngressRuleValue{
HTTP: &networkv1.HTTPIngressRuleValue{
Paths: []networkv1.HTTPIngressPath{
{
Path: "/",
Backend: networkv1beta1.IngressBackend{
ServiceName: "clusterip",
ServicePort: intstr.FromInt(80),
Backend: networkv1.IngressBackend{
Service: &networkv1.IngressServiceBackend{
Name: "clusterip",
Port: networkv1.ServiceBackendPort{
Number: 80,
},
},
},
PathType: &prefixbeta,
},

View File

@@ -213,7 +213,7 @@ func init() {
listOptions: cronJobLabelListOption,
},
}),
GroupResourceType: GroupResourceType{Group: "batch/v1", Kind: "CronJob"},
GroupResourceType: GroupResourceType{Group: "batch", Kind: "CronJob"},
},
)
}

View File

@@ -1460,6 +1460,19 @@ var _ = Describe("unit-test to e2e test", func() {
Expect(len(tn)).Should(BeEquivalentTo(2))
Expect(len(tn[0].LeafNodes)).Should(BeEquivalentTo(1))
Expect(len(tn[1].LeafNodes)).Should(BeEquivalentTo(1))
tn, err = iterateListSubResources(ctx, "", k8sClient, types.ResourceTreeNode{
Cluster: "",
Namespace: "test-namespace",
Name: "cronjob1",
APIVersion: "batch/v1",
Kind: "CronJob",
}, 1, func(node types.ResourceTreeNode) bool {
return true
})
Expect(err).Should(BeNil())
Expect(len(tn)).Should(BeEquivalentTo(1))
Expect(len(tn[0].LeafNodes)).Should(BeEquivalentTo(0))
})
It("test provider handler func", func() {

View File

@@ -5,7 +5,8 @@ import (
oam: op.oam
// apply component and traits
apply: oam.#ApplyComponent & {
value: parameter
value: parameter.value
cluster: parameter.cluster
}
if apply.output != _|_ {
@@ -15,4 +16,7 @@ if apply.output != _|_ {
if apply.outputs != _|_ {
outputs: apply.outputs
}
parameter: {...}
parameter: {
value: {...}
cluster: *"" | string
}

View File

@@ -385,7 +385,7 @@ func NewCreateConfigCommand(f velacmd.Factory, streams util.IOStreams) *cobra.Co
vela config create test-config --template=image-registry -f config.yaml
# Generate a config without the template
vela config create --name test-vela -f config.yaml
vela config create test-vela -f config.yaml
`))
cmd := &cobra.Command{

View File

@@ -38,21 +38,22 @@ import (
"github.com/kubevela/workflow/pkg/cue/packages"
"github.com/kubevela/workflow/pkg/debug"
"github.com/kubevela/workflow/pkg/tasks/custom"
wfTypes "github.com/kubevela/workflow/pkg/types"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/appfile/dryrun"
"github.com/oam-dev/kubevela/pkg/oam"
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
"github.com/oam-dev/kubevela/pkg/utils/common"
cmdutil "github.com/oam-dev/kubevela/pkg/utils/util"
"github.com/oam-dev/kubevela/references/appfile"
)
type debugOpts struct {
step string
focus string
errMsg string
opts []string
errMap map[string]string
// TODO: (fog) add watch flag
// watch bool
}
@@ -61,25 +62,32 @@ type debugOpts struct {
func NewDebugCommand(c common.Args, ioStreams cmdutil.IOStreams) *cobra.Command {
ctx := context.Background()
dOpts := &debugOpts{}
wargs := &WorkflowArgs{
Args: c,
Writer: ioStreams.Out,
}
cmd := &cobra.Command{
Use: "debug",
Aliases: []string{"debug"},
Short: "Debug running application",
Long: "Debug running application with debug policy.",
Example: `vela debug <application-name>`,
PreRun: wargs.checkDebugMode(),
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("must specify application name")
}
namespace, err := GetFlagNamespaceOrEnv(cmd, c)
if err != nil {
if err := wargs.getWorkflowInstance(ctx, cmd, args); err != nil {
return err
}
app, err := appfile.LoadApplication(namespace, args[0], c)
if err != nil {
return err
if wargs.Type == instanceTypeWorkflowRun {
return fmt.Errorf("please use `vela workflow debug <name>` instead")
}
return dOpts.debugApplication(ctx, c, app, ioStreams)
if wargs.App == nil {
return fmt.Errorf("application %s not found", args[0])
}
return dOpts.debugApplication(ctx, wargs, c, ioStreams)
},
}
addNamespaceAndEnvArg(cmd)
@@ -88,7 +96,8 @@ func NewDebugCommand(c common.Args, ioStreams cmdutil.IOStreams) *cobra.Command
return cmd
}
func (d *debugOpts) debugApplication(ctx context.Context, c common.Args, app *v1beta1.Application, ioStreams cmdutil.IOStreams) error {
func (d *debugOpts) debugApplication(ctx context.Context, wargs *WorkflowArgs, c common.Args, ioStreams cmdutil.IOStreams) error {
app := wargs.App
cli, err := c.GetClient()
if err != nil {
return err
@@ -101,60 +110,64 @@ func (d *debugOpts) debugApplication(ctx context.Context, c common.Args, app *v1
if err != nil {
return err
}
d.opts = wargs.getWorkflowSteps()
d.errMap = wargs.ErrMap
if app.Spec.Workflow != nil && len(app.Spec.Workflow.Steps) > 0 {
return d.debugWorkflow(ctx, wargs, cli, pd, ioStreams)
}
s, opts, errMap := d.getDebugOptions(app)
if s == "workflow steps" {
if d.step == "" {
prompt := &survey.Select{
Message: fmt.Sprintf("Select the %s to debug:", s),
Options: opts,
}
var step string
err := survey.AskOne(prompt, &step, survey.WithValidator(survey.Required))
if err != nil {
return fmt.Errorf("failed to select %s: %w", s, err)
}
d.step = unwrapStepName(step)
d.errMsg = errMap[d.step]
}
// debug workflow steps
rawValue, data, err := d.getDebugRawValue(ctx, cli, pd, app)
if err != nil {
if data != "" {
ioStreams.Info(color.RedString("%s%s", emojiFail, err.Error()))
ioStreams.Info(color.GreenString("Original Data in Debug:\n"), data)
return nil
}
return err
}
if err := d.handleCueSteps(rawValue, ioStreams); err != nil {
ioStreams.Info(color.RedString("%s%s", emojiFail, err.Error()))
ioStreams.Info(color.GreenString("Original Data in Debug:\n"), data)
return nil
}
} else {
// dry run components
dm, err := discoverymapper.New(config)
if err != nil {
return err
}
dryRunOpt := dryrun.NewDryRunOption(cli, config, dm, pd, []oam.Object{}, false)
comps, _, err := dryRunOpt.ExecuteDryRun(ctx, app)
if err != nil {
ioStreams.Info(color.RedString("%s%s", emojiFail, err.Error()))
return nil
}
if err := d.debugComponents(opts, comps, ioStreams); err != nil {
return err
}
dm, err := discoverymapper.New(config)
if err != nil {
return err
}
dryRunOpt := dryrun.NewDryRunOption(cli, config, dm, pd, []oam.Object{}, false)
comps, _, err := dryRunOpt.ExecuteDryRun(ctx, app)
if err != nil {
ioStreams.Info(color.RedString("%s%s", emojiFail, err.Error()))
return nil
}
if err := d.debugComponents(comps, ioStreams); err != nil {
return err
}
return nil
}
func (d *debugOpts) debugComponents(compList []string, comps []*types.ComponentManifest, ioStreams cmdutil.IOStreams) error {
opts := compList
func (d *debugOpts) debugWorkflow(ctx context.Context, wargs *WorkflowArgs, cli client.Client, pd *packages.PackageDiscover, ioStreams cmdutil.IOStreams) error {
if d.step == "" {
prompt := &survey.Select{
Message: "Select the workflow step to debug:",
Options: d.opts,
}
var step string
err := survey.AskOne(prompt, &step, survey.WithValidator(survey.Required))
if err != nil {
return fmt.Errorf("failed to select workflow step: %w", err)
}
d.step = unwrapStepName(step)
d.errMsg = d.errMap[d.step]
}
// debug workflow steps
rawValue, data, err := d.getDebugRawValue(ctx, cli, pd, wargs.WorkflowInstance)
if err != nil {
if data != "" {
ioStreams.Info(color.RedString("%s%s", emojiFail, err.Error()))
ioStreams.Info(color.GreenString("Original Data in Debug:\n"), data)
return nil
}
return err
}
if err := d.handleCueSteps(rawValue, ioStreams); err != nil {
ioStreams.Info(color.RedString("%s%s", emojiFail, err.Error()))
ioStreams.Info(color.GreenString("Original Data in Debug:\n"), data)
return nil
}
return nil
}
func (d *debugOpts) debugComponents(comps []*types.ComponentManifest, ioStreams cmdutil.IOStreams) error {
opts := d.opts
all := color.YellowString("all fields")
exit := color.CyanString("exit debug mode")
opts = append(opts, all, exit)
@@ -184,7 +197,7 @@ func (d *debugOpts) debugComponents(compList []string, comps []*types.ComponentM
break
}
if step == all {
for _, step := range compList {
for _, step := range d.opts {
step = unwrapStepName(step)
if err := renderComponents(step, components[step], traits[step], ioStreams); err != nil {
return err
@@ -217,34 +230,6 @@ func renderComponents(compName string, comp *unstructured.Unstructured, traits [
return nil
}
func (d *debugOpts) getDebugOptions(app *v1beta1.Application) (string, []string, map[string]string) {
s := "components"
stepList := make([]string, 0)
if app.Spec.Workflow != nil && len(app.Spec.Workflow.Steps) > 0 {
s = "workflow steps"
}
errMap := make(map[string]string)
switch {
case app.Status.Workflow != nil:
for _, step := range app.Status.Workflow.Steps {
stepName := wrapStepName(step.StepStatus)
if strings.HasPrefix(stepName, emojiFail) {
errMap[step.Name] = step.Message
}
stepList = append(stepList, stepName)
}
case app.Spec.Workflow != nil && len(app.Spec.Workflow.Steps) > 0:
for _, step := range app.Spec.Workflow.Steps {
stepList = append(stepList, step.Name)
}
default:
for _, c := range app.Spec.Components {
stepList = append(stepList, c.Name)
}
}
return s, stepList, errMap
}
func wrapStepName(step workflowv1alpha1.StepStatus) string {
var stepName string
switch step.Phase {
@@ -276,10 +261,20 @@ func unwrapStepName(step string) string {
}
}
func (d *debugOpts) getDebugRawValue(ctx context.Context, cli client.Client, pd *packages.PackageDiscover, app *v1beta1.Application) (*value.Value, string, error) {
func (d *debugOpts) getDebugRawValue(ctx context.Context, cli client.Client, pd *packages.PackageDiscover, instance *wfTypes.WorkflowInstance) (*value.Value, string, error) {
debugCM := &corev1.ConfigMap{}
if err := cli.Get(ctx, client.ObjectKey{Name: debug.GenerateContextName(app.Name, d.step), Namespace: app.Namespace}, debugCM); err != nil {
return nil, "", fmt.Errorf("failed to get debug configmap, please make sure your application have the debug policy, you can add the debug policy by using `vela up -f <app.yaml> --debug`: %w", err)
if err := cli.Get(ctx, client.ObjectKey{Name: debug.GenerateContextName(instance.Name, d.step, string(instance.UID)), Namespace: instance.Namespace}, debugCM); err != nil {
for _, step := range instance.Status.Steps {
if step.Name == d.step && (step.Type == wfTypes.WorkflowStepTypeSuspend || step.Type == wfTypes.WorkflowStepTypeStepGroup) {
return nil, "", fmt.Errorf("no debug data for a suspend or step-group step, please choose another step")
}
for _, sub := range step.SubStepsStatus {
if sub.Name == d.step && sub.Type == wfTypes.WorkflowStepTypeSuspend {
return nil, "", fmt.Errorf("no debug data for a suspend step, please choose another step")
}
}
}
return nil, "", fmt.Errorf("failed to get debug configmap, please make sure the you're in the debug mode`: %w", err)
}
if debugCM.Data == nil || debugCM.Data["debug"] == "" {

View File

@@ -49,8 +49,10 @@ func TestDebugApplicationWithWorkflow(t *testing.T) {
Name: "no-debug-config-map",
Namespace: "default",
},
Spec: workflowSpec,
Status: common.AppStatus{},
Spec: workflowSpec,
Status: common.AppStatus{
Workflow: &common.WorkflowStatus{},
},
},
step: "test-wf1",
focus: "test",
@@ -61,13 +63,16 @@ func TestDebugApplicationWithWorkflow(t *testing.T) {
ObjectMeta: metav1.ObjectMeta{
Name: "config-map-no-data",
Namespace: "default",
UID: "12345",
},
Spec: workflowSpec,
Status: common.AppStatus{
Workflow: &common.WorkflowStatus{},
},
Spec: workflowSpec,
Status: common.AppStatus{},
},
cm: &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "config-map-no-data-test-wf1-debug",
Name: "config-map-no-data-test-wf1-debug-12345",
Namespace: "default",
},
},
@@ -80,13 +85,16 @@ func TestDebugApplicationWithWorkflow(t *testing.T) {
ObjectMeta: metav1.ObjectMeta{
Name: "config-map-error-data",
Namespace: "default",
UID: "12345",
},
Spec: workflowSpec,
Status: common.AppStatus{
Workflow: &common.WorkflowStatus{},
},
Spec: workflowSpec,
Status: common.AppStatus{},
},
cm: &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "config-map-error-data-test-wf1-debug",
Name: "config-map-error-data-test-wf1-debug-12345",
Namespace: "default",
},
Data: map[string]string{
@@ -100,13 +108,16 @@ func TestDebugApplicationWithWorkflow(t *testing.T) {
ObjectMeta: metav1.ObjectMeta{
Name: "success",
Namespace: "default",
UID: "12345",
},
Spec: workflowSpec,
Status: common.AppStatus{
Workflow: &common.WorkflowStatus{},
},
Spec: workflowSpec,
Status: common.AppStatus{},
},
cm: &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "success-test-wf1-debug",
Name: "success-test-wf1-debug-12345",
Namespace: "default",
},
Data: map[string]string{
@@ -131,7 +142,9 @@ test: test
Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)},
}},
},
Status: common.AppStatus{},
Status: common.AppStatus{
Workflow: &common.WorkflowStatus{},
},
},
step: "test-component",
},
@@ -140,7 +153,6 @@ test: test
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
r := require.New(t)
d := &debugOpts{
step: tc.step,
focus: tc.focus,
@@ -151,7 +163,14 @@ test: test
err := client.Create(ctx, tc.cm)
r.NoError(err)
}
err = d.debugApplication(ctx, c, tc.app, ioStream)
wargs := &WorkflowArgs{
Args: c,
Type: instanceTypeApplication,
App: tc.app,
}
err = wargs.generateWorkflowInstance(ctx, client)
r.NoError(err)
err = d.debugApplication(ctx, wargs, c, ioStream)
if tc.expectedErr != "" {
r.Contains(err.Error(), tc.expectedErr)
return

View File

@@ -22,6 +22,7 @@ import (
"encoding/json"
"os"
"path/filepath"
"strings"
"github.com/pkg/errors"
"github.com/spf13/cobra"
@@ -32,6 +33,7 @@ import (
corev1beta1 "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/appfile/dryrun"
pkgdef "github.com/oam-dev/kubevela/pkg/definition"
"github.com/oam-dev/kubevela/pkg/oam"
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
oamutil "github.com/oam-dev/kubevela/pkg/oam/util"
@@ -101,7 +103,7 @@ func DryRunApplication(cmdOption *DryRunCmdOptions, c common.Args, namespace str
objs := []oam.Object{}
if cmdOption.DefinitionFile != "" {
objs, err = ReadObjectsFromFile(cmdOption.DefinitionFile)
objs, err = ReadDefinitionsFromFile(cmdOption.DefinitionFile)
if err != nil {
return buff, err
}
@@ -160,15 +162,37 @@ func DryRunApplication(cmdOption *DryRunCmdOptions, c common.Args, namespace str
return buff, nil
}
// ReadObjectsFromFile will read objects from file or dir in the format of yaml
func ReadObjectsFromFile(path string) ([]oam.Object, error) {
func readObj(path string) (oam.Object, error) {
switch {
case strings.HasSuffix(path, ".cue"):
def := pkgdef.Definition{Unstructured: unstructured.Unstructured{}}
defBytes, err := os.ReadFile(filepath.Clean(path))
if err != nil {
return nil, err
}
if err := def.FromCUEString(string(defBytes), nil); err != nil {
return nil, errors.Wrapf(err, "failed to parse CUE for definition")
}
obj := &unstructured.Unstructured{Object: def.UnstructuredContent()}
return obj, nil
default:
obj := &unstructured.Unstructured{}
err := common.ReadYamlToObject(path, obj)
if err != nil {
return nil, err
}
return obj, nil
}
}
// ReadDefinitionsFromFile will read objects from file or dir in the format of yaml
func ReadDefinitionsFromFile(path string) ([]oam.Object, error) {
fi, err := os.Stat(path)
if err != nil {
return nil, err
}
if !fi.IsDir() {
obj := &unstructured.Unstructured{}
err = common.ReadYamlToObject(path, obj)
obj, err := readObj(path)
if err != nil {
return nil, err
}
@@ -186,11 +210,10 @@ func ReadObjectsFromFile(path string) ([]oam.Object, error) {
continue
}
fileType := filepath.Ext(fi.Name())
if fileType != ".yaml" && fileType != ".yml" {
if fileType != ".yaml" && fileType != ".yml" && fileType != ".cue" {
continue
}
obj := &unstructured.Unstructured{}
err = common.ReadYamlToObject(filepath.Join(path, fi.Name()), obj)
obj, err := readObj(filepath.Join(path, fi.Name()))
if err != nil {
return nil, err
}

View File

@@ -74,6 +74,20 @@ var _ = Describe("Test dry run with policy", func() {
Expect(buff.String()).Should(ContainSubstring("- image: oamdev/hello-world:v2\n name: server-v2"))
})
It("Test dry run with cue component format", func() {
c := common2.Args{}
c.SetConfig(cfg)
c.SetClient(k8sClient)
opt := DryRunCmdOptions{ApplicationFile: "test-data/dry-run/app.yaml", DefinitionFile: "test-data/dry-run/my-comp.cue", OfflineMode: true}
buff, err := DryRunApplication(&opt, c, "")
Expect(err).Should(BeNil())
Expect(buff.String()).Should(ContainSubstring("name: hello-world"))
Expect(buff.String()).Should(ContainSubstring("kind: Deployment"))
Expect(buff.String()).Should(ContainSubstring("name: hello-world-service"))
Expect(buff.String()).Should(ContainSubstring("kind: Service"))
})
})
var plcapp = `apiVersion: core.oam.dev/v1beta1

View File

@@ -22,6 +22,7 @@ import (
"fmt"
"os"
"strconv"
"time"
"cuelang.org/go/cue"
"github.com/AlecAivazis/survey/v2"
@@ -111,11 +112,11 @@ func NewInitCommand(c common2.Args, order string, ioStreams cmdutil.IOStreams) *
if err != nil {
return err
}
deployStatus, err := printTrackingDeployStatus(c, o.IOStreams, o.appName, o.Namespace)
deployStatus, err := printTrackingDeployStatus(c, o.IOStreams, o.appName, o.Namespace, 300*time.Second)
if err != nil {
return err
}
if deployStatus != compStatusDeployed {
if deployStatus != appDeployedHealthy {
return nil
}
return printAppStatus(context.Background(), newClient, ioStreams, o.appName, o.Namespace, cmd, c, false)

View File

@@ -43,7 +43,7 @@ import (
)
// defaultConstraint
const defaultConstraint = ">= 1.19, <= 1.22"
const defaultConstraint = ">= 1.19, <= 1.24"
const kubevelaInstallerHelmRepoURL = "https://charts.kubevela.net/core/"

View File

@@ -104,7 +104,7 @@ func LiveDiffApplication(cmdOption *LiveDiffCmdOptions, c common.Args) (bytes.Bu
}
objs := []oam.Object{}
if cmdOption.DefinitionFile != "" {
objs, err = ReadObjectsFromFile(cmdOption.DefinitionFile)
objs, err = ReadDefinitionsFromFile(cmdOption.DefinitionFile)
if err != nil {
return buff, err
}

View File

@@ -50,11 +50,11 @@ func newUITable() *uitable.Table {
}
func newTrackingSpinnerWithDelay(suffix string, interval time.Duration) *spinner.Spinner {
suffixColor := color.New(color.Bold, color.FgGreen)
suffixColor := color.New(color.Bold, color.FgWhite)
return spinner.New(
spinner.CharSets[14],
interval,
spinner.WithColor("green"),
spinner.WithColor("white"),
spinner.WithHiddenCursor(true),
spinner.WithSuffix(suffixColor.Sprintf(" %s", suffix)))
}

View File

@@ -78,26 +78,18 @@ type WorkloadHealthCondition = v1alpha2.WorkloadHealthCondition
// ScopeHealthCondition holds health condition of a scope
type ScopeHealthCondition = v1alpha2.ScopeHealthCondition
// CompStatus represents the status of a component during "vela init"
type CompStatus int
// AppDeployStatus represents the status of application during "vela init" and "vela up --wait"
type AppDeployStatus int
// Enums of CompStatus
// Enums of ApplicationStatus
const (
compStatusDeploying CompStatus = iota
compStatusDeployFail
compStatusDeployed
compStatusUnknown
)
// Error msg used in `status` command
const (
// ErrNotLoadAppConfig display the error message load
ErrNotLoadAppConfig = "cannot load the application"
appDeployFail AppDeployStatus = iota
appDeployedHealthy
appDeployError
)
const (
trackingInterval time.Duration = 1 * time.Second
deployTimeout time.Duration = 10 * time.Second
trackingInterval = 1 * time.Second
)
// NewAppStatusCommand creates `status` command for showing status
@@ -402,59 +394,47 @@ func loopCheckStatus(c client.Client, ioStreams cmdutil.IOStreams, appName strin
return nil
}
func printTrackingDeployStatus(c common.Args, ioStreams cmdutil.IOStreams, appName string, namespace string) (CompStatus, error) {
sDeploy := newTrackingSpinnerWithDelay("Checking Status ...", trackingInterval)
func printTrackingDeployStatus(c common.Args, ioStreams cmdutil.IOStreams, appName, namespace string, timeout time.Duration) (AppDeployStatus, error) {
sDeploy := newTrackingSpinnerWithDelay("Waiting app to be healthy ...", trackingInterval)
sDeploy.Start()
defer sDeploy.Stop()
startTime := time.Now()
TrackDeployLoop:
for {
time.Sleep(trackingInterval)
deployStatus, failMsg, err := TrackDeployStatus(c, appName, namespace)
deployStatus, err := TrackDeployStatus(c, appName, namespace)
if err != nil {
return compStatusUnknown, err
return appDeployError, err
}
switch deployStatus {
case compStatusDeploying:
case commontypes.ApplicationStarting, commontypes.ApplicationRendering, commontypes.ApplicationPolicyGenerating, commontypes.ApplicationRunningWorkflow, commontypes.ApplicationUnhealthy:
if time.Now().After(startTime.Add(timeout)) {
ioStreams.Info(red.Sprintf("\n%s Timeout waiting Application to be healthy!", emojiFail))
return appDeployFail, nil
}
continue
case compStatusDeployed:
case commontypes.ApplicationWorkflowSuspending, commontypes.ApplicationRunning:
ioStreams.Info(green.Sprintf("\n%sApplication Deployed Successfully!", emojiSucceed))
break TrackDeployLoop
case compStatusDeployFail:
ioStreams.Info(red.Sprintf("\n%sApplication Failed to Deploy!", emojiFail))
ioStreams.Info(red.Sprintf("Reason: %s", failMsg))
return compStatusDeployFail, nil
default:
continue
case commontypes.ApplicationWorkflowTerminated, commontypes.ApplicationWorkflowFailed:
ioStreams.Info(red.Sprintf("\n%sApplication Deployment Failed!", emojiFail))
ioStreams.Info(red.Sprintf("Please run the following command to check details: \n vela status %s -n %s\n", appName, namespace))
return appDeployFail, nil
case commontypes.ApplicationDeleting:
ioStreams.Info(red.Sprintf("\n%sApplication is in the deleting process!", emojiFail))
return appDeployFail, nil
}
}
return compStatusDeployed, nil
return appDeployedHealthy, nil
}
// TrackDeployStatus will only check AppConfig is deployed successfully,
func TrackDeployStatus(c common.Args, appName string, namespace string) (CompStatus, string, error) {
func TrackDeployStatus(c common.Args, appName string, namespace string) (commontypes.ApplicationPhase, error) {
appObj, err := appfile.LoadApplication(namespace, appName, c)
if err != nil {
return compStatusUnknown, "", err
return "", err
}
if appObj == nil {
return compStatusUnknown, "", errors.New(ErrNotLoadAppConfig)
}
condition := appObj.Status.Conditions
if len(condition) < 1 {
return compStatusDeploying, "", nil
}
// If condition is true, we can regard appConfig is deployed successfully
if appObj.Status.Phase == commontypes.ApplicationRunning {
return compStatusDeployed, "", nil
}
// if not found workload status in AppConfig
// then use age to check whether the workload controller is running
if time.Since(appObj.GetCreationTimestamp().Time) > deployTimeout {
return compStatusDeployFail, condition[0].Message, nil
}
return compStatusDeploying, "", nil
return appObj.Status.Phase, nil
}
func getHealthStatusColor(s bool) *color.Color {

View File

@@ -0,0 +1,8 @@
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: vela-app
spec:
components:
- name: express-server
type: my-comp

View File

@@ -0,0 +1,50 @@
"my-comp": {
annotations: {}
attributes: workload: definition: {
apiVersion: "apps/v1"
kind: "Deployment"
}
description: "My component."
labels: {}
type: "component"
}
template: {
output: {
metadata: name: "hello-world"
spec: {
replicas: 1
selector: matchLabels: "app.kubernetes.io/name": "hello-world"
template: {
metadata: labels: "app.kubernetes.io/name": "hello-world"
spec: containers: [{
name: "hello-world"
image: "somefive/hello-world"
ports: [{
name: "http"
containerPort: 80
protocol: "TCP"
}]
}]
}
}
apiVersion: "apps/v1"
kind: "Deployment"
}
outputs: "hello-world-service": {
metadata: name: "hello-world-service"
spec: {
ports: [{
name: "http"
protocol: "TCP"
port: 80
targetPort: 8080
}]
selector: app: "hello-world"
type: "LoadBalancer"
}
apiVersion: "v1"
kind: "Service"
}
parameter: {}
}

View File

@@ -130,9 +130,7 @@ func (a *App) Refresh() {
log.Printf("SystemInfo updater canceled!")
return
case <-time.After(delay):
a.QueueUpdateDraw(func() {
board.UpdateInfo(a.config.RestConfig)
})
board.UpdateInfo(a.config.RestConfig)
}
}
}()

View File

@@ -21,6 +21,7 @@ import (
"fmt"
"github.com/gdamore/tcell/v2"
lru "github.com/hashicorp/golang-lru"
"github.com/rivo/tview"
"github.com/oam-dev/kubevela/pkg/velaql/providers/query/types"
@@ -32,20 +33,31 @@ import (
// TopologyView display the resource topology of application
type TopologyView struct {
*tview.Grid
app *App
actions model.KeyActions
ctx context.Context
focusTopology bool
app *App
actions model.KeyActions
ctx context.Context
focusTopology bool
cache *lru.Cache
appTopologyInstance *TopologyTree
resourceTopologyInstance *TopologyTree
}
type topologyTree struct {
type cacheView struct {
appTopologyInstance *TopologyTree
resourceTopologyInstance *TopologyTree
}
// TopologyTree is the abstract of topology tree
type TopologyTree struct {
*tview.TreeView
}
const (
numberOfCacheView = 10
)
var (
topologyViewInstance = new(TopologyView)
appTopologyInstance = new(topologyTree)
resourceTopologyInstance = new(topologyTree)
topologyViewInstance = new(TopologyView)
)
// NewTopologyView return a new topology view
@@ -56,6 +68,10 @@ func NewTopologyView(ctx context.Context, app *App) model.View {
if topologyViewInstance.Grid == nil {
topologyViewInstance.Grid = tview.NewGrid()
topologyViewInstance.actions = make(model.KeyActions)
topologyViewInstance.cache, _ = lru.New(numberOfCacheView)
topologyViewInstance.appTopologyInstance = new(TopologyTree)
topologyViewInstance.resourceTopologyInstance = new(TopologyTree)
topologyViewInstance.Init()
}
return topologyViewInstance
@@ -74,13 +90,28 @@ func (v *TopologyView) Init() {
// Start the topology view
func (v *TopologyView) Start() {
appTopology := v.NewAppTopologyView()
resourceTopology := v.NewResourceTopologyView()
appName := v.ctx.Value(&model.CtxKeyAppName).(string)
namespace := v.ctx.Value(&model.CtxKeyNamespace).(string)
key := fmt.Sprintf("%s-%s", appName, namespace)
v.Grid.AddItem(appTopology, 0, 0, 1, 1, 0, 0, true)
v.Grid.AddItem(resourceTopology, 0, 1, 1, 1, 0, 0, true)
value, exist := v.cache.Get(key)
view, ok := value.(*cacheView)
if exist && ok {
v.appTopologyInstance = view.appTopologyInstance
v.resourceTopologyInstance = view.resourceTopologyInstance
} else {
v.resourceTopologyInstance = v.NewResourceTopologyView()
v.appTopologyInstance = v.NewAppTopologyView()
v.cache.Add(key, &cacheView{
resourceTopologyInstance: v.resourceTopologyInstance,
appTopologyInstance: v.appTopologyInstance,
})
}
v.app.SetFocus(appTopology)
v.Grid.AddItem(v.appTopologyInstance, 0, 0, 1, 1, 0, 0, true)
v.Grid.AddItem(v.resourceTopologyInstance, 0, 1, 1, 1, 0, 0, true)
v.app.SetFocus(v.appTopologyInstance)
}
// Stop the topology view
@@ -119,54 +150,49 @@ func (v *TopologyView) bindKeys() {
}
// NewResourceTopologyView return a new resource topology view
func (v *TopologyView) NewResourceTopologyView() tview.Primitive {
if resourceTopologyInstance.TreeView == nil {
resourceTopologyInstance.TreeView = tview.NewTreeView()
resourceTopologyInstance.SetGraphics(true)
resourceTopologyInstance.SetGraphicsColor(tcell.ColorCadetBlue)
resourceTopologyInstance.SetBorder(true)
resourceTopologyInstance.SetTitle(fmt.Sprintf("[ %s ]", "Resource"))
}
func (v *TopologyView) NewResourceTopologyView() *TopologyTree {
newTopology := new(TopologyTree)
appName := v.ctx.Value(&model.CtxKeyAppName).(string)
namespace := v.ctx.Value(&model.CtxKeyNamespace).(string)
newTopology.TreeView = tview.NewTreeView()
newTopology.SetGraphics(true)
newTopology.SetGraphicsColor(tcell.ColorCadetBlue)
newTopology.SetBorder(true)
newTopology.SetTitle(fmt.Sprintf("[ %s ]", "Resource"))
root := tview.NewTreeNode(component.EmojiFormat(fmt.Sprintf("%s (%s)", appName, namespace), "app")).SetSelectable(true)
resourceTopologyInstance.SetRoot(root)
newTopology.SetRoot(root)
resourceTree, err := model.ApplicationResourceTopology(v.app.client, appName, namespace)
if err != nil {
return resourceTopologyInstance
if err == nil {
for _, resource := range resourceTree {
root.AddChild(buildTopology(resource.ResourceTree))
}
}
for _, resource := range resourceTree {
root.AddChild(buildTopology(resource.ResourceTree))
}
return resourceTopologyInstance
return newTopology
}
// NewAppTopologyView return a new app topology view
func (v *TopologyView) NewAppTopologyView() tview.Primitive {
if appTopologyInstance.TreeView == nil {
appTopologyInstance.TreeView = tview.NewTreeView()
appTopologyInstance.SetGraphics(true)
appTopologyInstance.SetGraphicsColor(tcell.ColorCadetBlue)
appTopologyInstance.SetBorder(true)
appTopologyInstance.SetTitle(fmt.Sprintf("[ %s ]", "App"))
}
func (v *TopologyView) NewAppTopologyView() *TopologyTree {
newTopology := new(TopologyTree)
appName := v.ctx.Value(&model.CtxKeyAppName).(string)
namespace := v.ctx.Value(&model.CtxKeyNamespace).(string)
newTopology.TreeView = tview.NewTreeView()
newTopology.SetGraphics(true)
newTopology.SetGraphicsColor(tcell.ColorCadetBlue)
newTopology.SetBorder(true)
newTopology.SetTitle(fmt.Sprintf("[ %s ]", "App"))
root := tview.NewTreeNode(component.EmojiFormat(fmt.Sprintf("%s (%s)", appName, namespace), "app")).SetSelectable(true)
appTopologyInstance.SetRoot(root)
newTopology.SetRoot(root)
app, err := model.LoadApplication(v.app.client, appName, namespace)
if err != nil {
return appTopologyInstance
return newTopology
}
// workflow
workflowNode := tview.NewTreeNode(component.EmojiFormat("WorkFlow", "workflow")).SetSelectable(true)
root.AddChild(workflowNode)
@@ -206,15 +232,14 @@ func (v *TopologyView) NewAppTopologyView() tview.Primitive {
for _, policy := range app.Spec.Policies {
policyNode.AddChild(tview.NewTreeNode(policy.Name))
}
return appTopologyInstance
return newTopology
}
func (v *TopologyView) switchTopology(_ *tcell.EventKey) *tcell.EventKey {
if v.focusTopology {
v.app.SetFocus(appTopologyInstance)
v.app.SetFocus(v.appTopologyInstance)
} else {
v.app.SetFocus(resourceTopologyInstance)
v.app.SetFocus(v.resourceTopologyInstance)
}
v.focusTopology = !v.focusTopology
return nil

View File

@@ -61,10 +61,12 @@ func TestTopologyView(t *testing.T) {
})
t.Run("start", func(t *testing.T) {
appTopologyView := topologyView.NewAppTopologyView()
assert.Equal(t, appTopologyView.HasFocus(), false)
topologyView.Start()
assert.Equal(t, appTopologyView.HasFocus(), true)
assert.Equal(t, topologyView.appTopologyInstance.HasFocus(), true)
assert.Equal(t, topologyView.resourceTopologyInstance.HasFocus(), false)
topologyView.switchTopology(nil)
assert.Equal(t, topologyView.appTopologyInstance.HasFocus(), false)
assert.Equal(t, topologyView.resourceTopologyInstance.HasFocus(), true)
})
t.Run("stop", func(t *testing.T) {

View File

@@ -48,7 +48,7 @@ func (ps *PageStack) StackPop(old, new model.View) {
}
ps.app.QueueUpdateDraw(new.Start)
ps.app.SetFocus(new)
ps.app.QueueUpdate(old.Stop)
go old.Stop()
}
// StackPush change itself when accept "pop" notify from app's main view
@@ -56,6 +56,6 @@ func (ps *PageStack) StackPush(old, new model.View) {
ps.app.QueueUpdateDraw(new.Start)
ps.app.SetFocus(new)
if old != nil {
ps.app.QueueUpdate(old.Stop)
go old.Stop()
}
}

View File

@@ -18,6 +18,9 @@ package cli
import (
"context"
"fmt"
"os"
"time"
"github.com/pkg/errors"
"github.com/spf13/cobra"
@@ -53,6 +56,8 @@ type UpCommandOptions struct {
PublishVersion string
RevisionName string
Debug bool
Wait bool
WaitTimeout string
}
// Complete fill the args for vela up
@@ -211,34 +216,38 @@ func (opt *UpCommandOptions) deployApplicationFromFile(f velacmd.Factory, cmd *c
}
if common.IsAppfile(body) { // legacy compatibility
o := &common.AppfileOptions{Kubecli: cli, IO: ioStream, Namespace: opt.Namespace}
return o.Run(opt.File, o.Namespace, utilcommon.Args{Schema: utilcommon.Scheme})
}
var app v1beta1.Application
err = yaml.Unmarshal(body, &app)
if err != nil {
return errors.Wrap(err, "File format is illegal, only support vela appfile format or OAM Application object yaml")
}
if err = o.Run(opt.File, o.Namespace, utilcommon.Args{Schema: utilcommon.Scheme}); err != nil {
return err
}
opt.AppName = o.Name
} else {
var app v1beta1.Application
err = yaml.Unmarshal(body, &app)
if err != nil {
return errors.Wrap(err, "File format is illegal, only support vela appfile format or OAM Application object yaml")
}
// Override namespace if namespace flag is set. We should check if namespace is `default` or not
// since GetFlagNamespaceOrEnv returns default namespace when failed to get current env.
if opt.Namespace != "" && opt.Namespace != types.DefaultAppNamespace {
app.SetNamespace(opt.Namespace)
// Override namespace if namespace flag is set. We should check if namespace is `default` or not
// since GetFlagNamespaceOrEnv returns default namespace when failed to get current env.
if opt.Namespace != "" && opt.Namespace != types.DefaultAppNamespace {
app.SetNamespace(opt.Namespace)
}
if opt.PublishVersion != "" {
oam.SetPublishVersion(&app, opt.PublishVersion)
}
opt.AppName = app.Name
if opt.Debug {
app.Spec.Policies = append(app.Spec.Policies, v1beta1.AppPolicy{
Name: "debug",
Type: "debug",
})
}
err = common.ApplyApplication(app, ioStream, cli)
if err != nil {
return err
}
cmd.Printf("Application %s applied.\n", green.Sprintf("%s/%s", app.Namespace, app.Name))
}
if opt.PublishVersion != "" {
oam.SetPublishVersion(&app, opt.PublishVersion)
}
opt.AppName = app.Name
if opt.Debug {
app.Spec.Policies = append(app.Spec.Policies, v1beta1.AppPolicy{
Name: "debug",
Type: "debug",
})
}
err = common.ApplyApplication(app, ioStream, cli)
if err != nil {
return err
}
cmd.Printf("Application %s applied.\n", green.Sprintf("%s/%s", app.Namespace, app.Name))
return nil
}
@@ -276,7 +285,9 @@ var (
// NewUpCommand will create command for applying an AppFile
func NewUpCommand(f velacmd.Factory, order string, c utilcommon.Args, ioStream util.IOStreams) *cobra.Command {
o := &UpCommandOptions{}
o := &UpCommandOptions{
WaitTimeout: "300s",
}
cmd := &cobra.Command{
Use: "up",
DisableFlagsInUseLine: true,
@@ -301,10 +312,29 @@ func NewUpCommand(f velacmd.Factory, order string, c utilcommon.Args, ioStream u
cmdutil.CheckErr(o.Run(f, cmd))
if o.Debug {
dOpts := &debugOpts{}
cli := f.Client()
app := &v1beta1.Application{}
cmdutil.CheckErr(cli.Get(cmd.Context(), apitypes.NamespacedName{Name: o.AppName, Namespace: o.Namespace}, app))
cmdutil.CheckErr(dOpts.debugApplication(context.Background(), c, app, ioStream))
wargs := &WorkflowArgs{Args: c}
ctx := context.Background()
cmdutil.CheckErr(wargs.getWorkflowInstance(ctx, cmd, []string{o.AppName}))
if wargs.Type == instanceTypeWorkflowRun {
cmdutil.CheckErr(fmt.Errorf("please use `vela workflow debug <name>` instead"))
}
if wargs.App == nil {
cmdutil.CheckErr(fmt.Errorf("application %s not found", args[0]))
}
cmdutil.CheckErr(dOpts.debugApplication(ctx, wargs, c, ioStream))
}
if o.Wait {
dur, err := time.ParseDuration(o.WaitTimeout)
if err != nil {
cmdutil.CheckErr(fmt.Errorf("parse timeout duration err: %w", err))
}
status, err := printTrackingDeployStatus(c, ioStream, o.AppName, o.Namespace, dur)
if err != nil {
cmdutil.CheckErr(err)
}
if status != appDeployedHealthy {
os.Exit(1)
}
}
},
}
@@ -312,6 +342,8 @@ func NewUpCommand(f velacmd.Factory, order string, c utilcommon.Args, ioStream u
cmd.Flags().StringVarP(&o.PublishVersion, "publish-version", "v", o.PublishVersion, "The publish version for deploying application.")
cmd.Flags().StringVarP(&o.RevisionName, "revision", "r", o.RevisionName, "The revision to use for deploying the application, if empty, the current application configuration will be used.")
cmd.Flags().BoolVarP(&o.Debug, "debug", "", o.Debug, "Enable debug mode for application")
cmd.Flags().BoolVarP(&o.Wait, "wait", "w", o.Wait, "Wait app to be healthy until timout, if no timeout specified, the default duration is 300s.")
cmd.Flags().StringVarP(&o.WaitTimeout, "timeout", "", o.WaitTimeout, "Set the timout for wait app to be healthy, if not specified, the default duration is 300s.")
cmdutil.CheckErr(cmd.RegisterFlagCompletionFunc(
"revision",
func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {

View File

@@ -30,7 +30,7 @@ import (
. "github.com/onsi/gomega"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
networkv1beta1 "k8s.io/api/networking/v1beta1"
networkv1 "k8s.io/api/networking/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
pkgtypes "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
@@ -267,25 +267,29 @@ var _ = Describe("Test velaQL", func() {
Expect(err).Should(BeNil())
}
}
var prefixbeta = networkv1beta1.PathTypePrefix
var prefixbeta = networkv1.PathTypePrefix
testIngress := []client.Object{
&networkv1beta1.Ingress{
&networkv1.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "ingress-http",
Namespace: "default",
},
Spec: networkv1beta1.IngressSpec{
Rules: []networkv1beta1.IngressRule{
Spec: networkv1.IngressSpec{
Rules: []networkv1.IngressRule{
{
Host: "ingress.domain",
IngressRuleValue: networkv1beta1.IngressRuleValue{
HTTP: &networkv1beta1.HTTPIngressRuleValue{
Paths: []networkv1beta1.HTTPIngressPath{
IngressRuleValue: networkv1.IngressRuleValue{
HTTP: &networkv1.HTTPIngressRuleValue{
Paths: []networkv1.HTTPIngressPath{
{
Path: "/",
Backend: networkv1beta1.IngressBackend{
ServiceName: "clusterip",
ServicePort: intstr.FromInt(80),
Backend: networkv1.IngressBackend{
Service: &networkv1.IngressServiceBackend{
Name: "clusterip",
Port: networkv1.ServiceBackendPort{
Number: 80,
},
},
},
PathType: &prefixbeta,
},
@@ -296,28 +300,32 @@ var _ = Describe("Test velaQL", func() {
},
},
},
&networkv1beta1.Ingress{
&networkv1.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "ingress-https",
Namespace: "default",
},
Spec: networkv1beta1.IngressSpec{
TLS: []networkv1beta1.IngressTLS{
Spec: networkv1.IngressSpec{
TLS: []networkv1.IngressTLS{
{
SecretName: "https-secret",
},
},
Rules: []networkv1beta1.IngressRule{
Rules: []networkv1.IngressRule{
{
Host: "ingress.domain.https",
IngressRuleValue: networkv1beta1.IngressRuleValue{
HTTP: &networkv1beta1.HTTPIngressRuleValue{
Paths: []networkv1beta1.HTTPIngressPath{
IngressRuleValue: networkv1.IngressRuleValue{
HTTP: &networkv1.HTTPIngressRuleValue{
Paths: []networkv1.HTTPIngressPath{
{
Path: "/",
Backend: networkv1beta1.IngressBackend{
ServiceName: "clusterip",
ServicePort: intstr.FromInt(80),
Backend: networkv1.IngressBackend{
Service: &networkv1.IngressServiceBackend{
Name: "clusterip",
Port: networkv1.ServiceBackendPort{
Number: 80,
},
},
},
PathType: &prefixbeta,
},
@@ -328,36 +336,44 @@ var _ = Describe("Test velaQL", func() {
},
},
},
&networkv1beta1.Ingress{
&networkv1.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "ingress-paths",
Namespace: "default",
},
Spec: networkv1beta1.IngressSpec{
TLS: []networkv1beta1.IngressTLS{
Spec: networkv1.IngressSpec{
TLS: []networkv1.IngressTLS{
{
SecretName: "https-secret",
},
},
Rules: []networkv1beta1.IngressRule{
Rules: []networkv1.IngressRule{
{
Host: "ingress.domain.path",
IngressRuleValue: networkv1beta1.IngressRuleValue{
HTTP: &networkv1beta1.HTTPIngressRuleValue{
Paths: []networkv1beta1.HTTPIngressPath{
IngressRuleValue: networkv1.IngressRuleValue{
HTTP: &networkv1.HTTPIngressRuleValue{
Paths: []networkv1.HTTPIngressPath{
{
Path: "/test",
Backend: networkv1beta1.IngressBackend{
ServiceName: "clusterip",
ServicePort: intstr.FromInt(80),
Backend: networkv1.IngressBackend{
Service: &networkv1.IngressServiceBackend{
Name: "clusterip",
Port: networkv1.ServiceBackendPort{
Number: 80,
},
},
},
PathType: &prefixbeta,
},
{
Path: "/test2",
Backend: networkv1beta1.IngressBackend{
ServiceName: "clusterip",
ServicePort: intstr.FromInt(80),
Backend: networkv1.IngressBackend{
Service: &networkv1.IngressServiceBackend{
Name: "clusterip",
Port: networkv1.ServiceBackendPort{
Number: 80,
},
},
},
PathType: &prefixbeta,
},
@@ -368,7 +384,7 @@ var _ = Describe("Test velaQL", func() {
},
},
},
&networkv1beta1.Ingress{
&networkv1.Ingress{
TypeMeta: metav1.TypeMeta{
APIVersion: "networking.k8s.io/v1beta1",
},
@@ -380,18 +396,22 @@ var _ = Describe("Test velaQL", func() {
"helm.toolkit.fluxcd.io/namespace": "default",
},
},
Spec: networkv1beta1.IngressSpec{
Rules: []networkv1beta1.IngressRule{
Spec: networkv1.IngressSpec{
Rules: []networkv1.IngressRule{
{
Host: "ingress.domain.helm",
IngressRuleValue: networkv1beta1.IngressRuleValue{
HTTP: &networkv1beta1.HTTPIngressRuleValue{
Paths: []networkv1beta1.HTTPIngressPath{
IngressRuleValue: networkv1.IngressRuleValue{
HTTP: &networkv1.HTTPIngressRuleValue{
Paths: []networkv1.HTTPIngressPath{
{
Path: "/",
Backend: networkv1beta1.IngressBackend{
ServiceName: "clusterip",
ServicePort: intstr.FromInt(80),
Backend: networkv1.IngressBackend{
Service: &networkv1.IngressServiceBackend{
Name: "clusterip",
Port: networkv1.ServiceBackendPort{
Number: 80,
},
},
},
PathType: &prefixbeta,
},

View File

@@ -32,6 +32,7 @@ import (
wfTypes "github.com/kubevela/workflow/pkg/types"
wfUtils "github.com/kubevela/workflow/pkg/utils"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha1"
"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"
@@ -61,6 +62,7 @@ func NewWorkflowCommand(c common.Args, ioStreams cmdutil.IOStreams) *cobra.Comma
NewWorkflowRestartCommand(c, ioStreams, wargs),
NewWorkflowRollbackCommand(c, ioStreams, wargs),
NewWorkflowLogsCommand(c, ioStreams, wargs),
NewWorkflowDebugCommand(c, ioStreams, wargs),
)
return cmd
}
@@ -135,7 +137,6 @@ func NewWorkflowRestartCommand(c common.Args, ioStream cmdutil.IOStreams, wargs
Short: "Restart an application workflow.",
Long: "Restart an application workflow in cluster.",
Example: "vela workflow restart <application-name>",
PreRun: wargs.checkWorkflowNotComplete(),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
if err := wargs.getWorkflowInstance(ctx, cmd, args); err != nil {
@@ -195,6 +196,42 @@ func NewWorkflowLogsCommand(c common.Args, ioStream cmdutil.IOStreams, wargs *Wo
return cmd
}
// NewWorkflowDebugCommand create workflow debug command
func NewWorkflowDebugCommand(c common.Args, ioStream cmdutil.IOStreams, wargs *WorkflowArgs) *cobra.Command {
dOpts := &debugOpts{
step: wargs.StepName,
}
cmd := &cobra.Command{
Use: "debug",
Short: "Debug workflow steps",
Long: "Debug workflow steps",
Example: "vela workflow debug <workflow-name>",
PreRun: wargs.checkDebugMode(),
RunE: func(cmd *cobra.Command, args []string) error {
cli, err := c.GetClient()
if err != nil {
return err
}
pd, err := c.GetPackageDiscover()
if err != nil {
return err
}
ctx := context.Background()
if err := wargs.getWorkflowInstance(ctx, cmd, args); err != nil {
return err
}
dOpts.opts = wargs.getWorkflowSteps()
dOpts.errMap = wargs.ErrMap
return dOpts.debugWorkflow(ctx, wargs, cli, pd, ioStream)
},
}
cmd.Flags().StringVarP(&wargs.StepName, "step", "s", "", "specify the step name in the workflow")
cmd.Flags().StringVarP(&dOpts.focus, "focus", "f", "", "specify the focus value to debug, only valid for application with workflow")
cmd.Flags().StringVarP(&wargs.Type, "type", "t", "", "the type of the resource, support: [app, workflow]")
addNamespaceAndEnvArg(cmd)
return cmd
}
// WorkflowArgs is the args for workflow command
type WorkflowArgs struct {
Type string
@@ -204,6 +241,7 @@ type WorkflowArgs struct {
Writer io.Writer
Args common.Args
StepName string
ErrMap map[string]string
App *v1beta1.Application
WorkflowRun *workflowv1alpha1.WorkflowRun
WorkflowInstance *wfTypes.WorkflowInstance
@@ -266,18 +304,26 @@ func (w *WorkflowArgs) getWorkflowInstance(ctx context.Context, cmd *cobra.Comma
}
func (w *WorkflowArgs) generateWorkflowInstance(ctx context.Context, cli client.Client) error {
debug := false
switch w.Type {
case instanceTypeApplication:
if w.App.Status.Workflow == nil {
return fmt.Errorf("the workflow in application %s is not start", w.App.Name)
}
for _, policy := range w.App.Spec.Policies {
if policy.Type == v1alpha1.DebugPolicyType {
debug = true
break
}
}
status := w.App.Status.Workflow
w.WorkflowInstance = &wfTypes.WorkflowInstance{
WorkflowMeta: wfTypes.WorkflowMeta{
Name: w.App.Name,
Namespace: w.App.Namespace,
UID: w.App.UID,
},
Steps: w.App.Spec.Workflow.Steps,
Debug: debug,
Status: workflowv1alpha1.WorkflowRunStatus{
Phase: status.Phase,
Message: status.Message,
@@ -291,6 +337,9 @@ func (w *WorkflowArgs) generateWorkflowInstance(ctx context.Context, cli client.
EndTime: status.EndTime,
},
}
if w.App.Spec.Workflow != nil {
w.WorkflowInstance.Steps = w.App.Spec.Workflow.Steps
}
w.Operator = operation.NewApplicationWorkflowOperator(cli, w.Writer, w.App)
w.ControllerLabels = map[string]string{"app.kubernetes.io/name": "vela-core"}
case instanceTypeWorkflowRun:
@@ -304,13 +353,20 @@ func (w *WorkflowArgs) generateWorkflowInstance(ctx context.Context, cli client.
} else {
steps = w.WorkflowRun.Spec.WorkflowSpec.Steps
}
if w.WorkflowRun.Annotations != nil {
if d, ok := w.WorkflowRun.Annotations[wfTypes.AnnotationWorkflowRunDebug]; ok && d == "true" {
debug = true
}
}
w.WorkflowInstance = &wfTypes.WorkflowInstance{
WorkflowMeta: wfTypes.WorkflowMeta{
Name: w.WorkflowRun.Name,
Namespace: w.WorkflowRun.Namespace,
UID: w.WorkflowRun.UID,
},
Steps: steps,
Status: w.WorkflowRun.Status,
Debug: debug,
}
w.Operator = wfUtils.NewWorkflowRunOperator(cli, w.Writer, w.WorkflowRun)
w.ControllerLabels = map[string]string{"app.kubernetes.io/name": "vela-workflow"}
@@ -322,7 +378,7 @@ func (w *WorkflowArgs) generateWorkflowInstance(ctx context.Context, cli client.
func (w *WorkflowArgs) printStepLogs(ctx context.Context, cli client.Client, ioStreams cmdutil.IOStreams) error {
if w.StepName == "" {
if err := w.selectWorkflowStep(); err != nil {
if err := w.selectWorkflowStep("Select a step to show logs:"); err != nil {
return err
}
}
@@ -361,20 +417,34 @@ func (w *WorkflowArgs) printStepLogs(ctx context.Context, cli client.Client, ioS
return nil
}
func (w *WorkflowArgs) selectWorkflowStep() error {
func (w *WorkflowArgs) getWorkflowSteps() []string {
if w.ErrMap == nil {
w.ErrMap = make(map[string]string)
}
stepsKey := make([]string, 0)
for _, step := range w.WorkflowInstance.Status.Steps {
stepsKey = append(stepsKey, wrapStepName(step.StepStatus))
if step.Phase == workflowv1alpha1.WorkflowStepPhaseFailed {
w.ErrMap[step.Name] = step.Message
}
for _, sub := range step.SubStepsStatus {
stepsKey = append(stepsKey, fmt.Sprintf(" %s", wrapStepName(sub)))
if sub.Phase == workflowv1alpha1.WorkflowStepPhaseFailed {
w.ErrMap[step.Name] = sub.Message
}
}
}
return stepsKey
}
func (w *WorkflowArgs) selectWorkflowStep(msg string) error {
stepsKey := w.getWorkflowSteps()
if len(stepsKey) == 0 {
return fmt.Errorf("workflow is not start")
}
prompt := &survey.Select{
Message: "Select a step to show logs:",
Message: msg,
Options: stepsKey,
}
var stepName string
@@ -446,3 +516,21 @@ func (w *WorkflowArgs) checkWorkflowNotComplete() func(cmd *cobra.Command, args
}
}
}
func (w *WorkflowArgs) checkDebugMode() func(cmd *cobra.Command, args []string) {
return func(cmd *cobra.Command, args []string) {
if err := w.getWorkflowInstance(context.Background(), cmd, args); err != nil {
return
}
if !w.WorkflowInstance.Debug {
msg := ""
if w.Type == instanceTypeApplication {
msg = "please make sure your application have the debug policy, you can add the debug policy by using `vela up -f <app.yaml> --debug"
} else {
msg = "please make sure your workflow have the debug annotation [workflowrun.oam.dev/debug:true] then re-run the workflow"
}
cmd.Printf("workflow %s is not in debug mode, %s", w.WorkflowInstance.Name, msg)
os.Exit(1)
}
}
}

View File

@@ -61,6 +61,7 @@ type AppfileOptions struct {
Kubecli client.Client
IO cmdutil.IOStreams
Namespace string
Name string
}
// BuildResult is the export struct from AppFile yaml or AppFile object
@@ -380,7 +381,7 @@ func (o *AppfileOptions) BaseAppFileRun(result *BuildResult, args common.Args) e
return err
}
result.application.Spec.Components = kubernetesComponent
o.Name = result.application.Name
o.IO.Infof("\nApplying application ...\n")
return o.ApplyApp(result.application, result.scopes)
}

View File

@@ -266,7 +266,7 @@ func GetWorkflowSteps(ctx context.Context, namespace string, c common.Args) ([]t
for _, def := range workflowStepDefs.Items {
tmp, err := GetCapabilityByWorkflowStepDefinitionObject(def, pd)
if err != nil {
templateErrors = append(templateErrors, err)
templateErrors = append(templateErrors, errors.WithMessage(err, def.Name))
continue
}
templates = append(templates, *tmp)

View File

@@ -0,0 +1,27 @@
```yaml
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: first-vela-workflow
namespace: default
spec:
components:
- name: express-server
type: webservice
properties:
image: oamdev/hello-world
port: 8000
traits:
- type: ingress
properties:
domain: testsvc.example.com
http:
/: 8000
workflow:
steps:
- name: express-server
type: apply-component
properties:
component: express-server
# cluster: <your cluster name>
```

View File

@@ -0,0 +1,24 @@
```yaml
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: print-message-in-status
namespace: default
spec:
components:
- name: express-server
type: webservice
properties:
image: oamdev/hello-world
port: 8000
workflow:
steps:
- name: express-server
type: apply-component
properties:
component: express-server
- name: message
type: print-message-in-status
properties:
message: "All addons have been enabled successfully, you can use 'vela addon list' to check them."
```

View File

@@ -213,4 +213,23 @@ var _ = Describe("Test addon rest api", func() {
Expect(strings.Contains(errResponse.Message, "fail to install"))
})
})
Describe("Test addon dependency installed version", func() {
It("Test Operation of enabling foo addon will enable bar addon automatically", func() {
req := apisv1.EnableAddonRequest{}
res := post("/addons/foo/enable", req)
defer res.Body.Close()
var addon apisv1.AddonStatusResponse
Expect(decodeResponseBody(res, &addon)).Should(Succeed())
Expect(addon.Name).Should(BeEquivalentTo("foo"))
Eventually(func(g Gomega) {
status := get("/addons/bar/status")
var newaddonStatus apisv1.AddonStatusResponse
g.Expect(decodeResponseBody(status, &newaddonStatus)).Should(Succeed())
g.Expect(newaddonStatus.Name).Should(BeEquivalentTo("bar"))
g.Expect(newaddonStatus.InstalledVersion).Should(BeEquivalentTo("v1.0.0"))
}, 30*time.Second, 300*time.Millisecond).Should(Succeed())
})
})
})

View File

@@ -26,7 +26,6 @@ import (
"github.com/kubevela/workflow/api/v1alpha1"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"k8s.io/apimachinery/pkg/runtime"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/pkg/apiserver/domain/model"
@@ -35,13 +34,15 @@ import (
"github.com/oam-dev/kubevela/pkg/utils/common"
)
var testPipelineSteps []v1alpha1.WorkflowStep
var (
testPipelineSteps []model.WorkflowStep
props = model.JSONStruct{"url": "https://api.github.com/repos/kubevela/kubevela"}
)
func init() {
rawProps := []byte(`{"url":"https://api.github.com/repos/kubevela/kubevela"}`)
testPipelineSteps = []v1alpha1.WorkflowStep{
testPipelineSteps = []model.WorkflowStep{
{
WorkflowStepBase: v1alpha1.WorkflowStepBase{
WorkflowStepBase: model.WorkflowStepBase{
Name: "request",
Type: "request",
Outputs: v1alpha1.StepOutputs{
@@ -50,9 +51,7 @@ func init() {
Name: "stars",
},
},
Properties: &runtime.RawExtension{
Raw: rawProps,
},
Properties: &props,
},
},
}
@@ -93,7 +92,7 @@ var _ = Describe("Test the rest api about the pipeline", func() {
var req = apisv1.CreatePipelineRequest{
Name: pipelineName,
Description: description,
Spec: v1alpha1.WorkflowSpec{
Spec: model.WorkflowSpec{
Steps: testPipelineSteps,
},
}
@@ -150,10 +149,9 @@ var _ = Describe("Test the rest api about the pipeline", func() {
})
It("update pipeline", func() {
rawProps := []byte(`{"url":"https://api.github.com/repos/kubevela/kubevela"}`)
newSteps := make([]v1alpha1.WorkflowStep, 0)
newSteps = append(newSteps, v1alpha1.WorkflowStep{
SubSteps: []v1alpha1.WorkflowStepBase{
newSteps := make([]model.WorkflowStep, 0)
newSteps = append(newSteps, model.WorkflowStep{
SubSteps: []model.WorkflowStepBase{
{
Name: "request1",
Type: "request",
@@ -163,9 +161,7 @@ var _ = Describe("Test the rest api about the pipeline", func() {
Name: "stars",
},
},
Properties: &runtime.RawExtension{
Raw: rawProps,
},
Properties: &props,
},
{
Name: "request2",
@@ -176,18 +172,16 @@ var _ = Describe("Test the rest api about the pipeline", func() {
Name: "stars-copy",
},
},
Properties: &runtime.RawExtension{
Raw: rawProps,
},
Properties: &props,
},
},
WorkflowStepBase: v1alpha1.WorkflowStepBase{
WorkflowStepBase: model.WorkflowStepBase{
Name: "request-group",
Type: "step-group",
},
})
newSteps = append(newSteps, v1alpha1.WorkflowStep{
WorkflowStepBase: v1alpha1.WorkflowStepBase{
newSteps = append(newSteps, model.WorkflowStep{
WorkflowStepBase: model.WorkflowStepBase{
Name: "log",
Type: "log",
Inputs: v1alpha1.StepInputs{
@@ -200,7 +194,7 @@ var _ = Describe("Test the rest api about the pipeline", func() {
})
var req = apisv1.UpdatePipelineRequest{
Description: description,
Spec: v1alpha1.WorkflowSpec{
Spec: model.WorkflowSpec{
Steps: newSteps,
},
}
@@ -310,10 +304,10 @@ var _ = Describe("Test the rest api about the pipeline", func() {
It("stop pipeline", func() {
By("update pipeline so that it will run for a while")
var req = apisv1.UpdatePipelineRequest{
Spec: v1alpha1.WorkflowSpec{
Steps: []v1alpha1.WorkflowStep{
Spec: model.WorkflowSpec{
Steps: []model.WorkflowStep{
{
WorkflowStepBase: v1alpha1.WorkflowStepBase{
WorkflowStepBase: model.WorkflowStepBase{
Name: "request",
Type: "request",
Timeout: "20s",

View File

@@ -669,6 +669,21 @@ var _ = Describe("Test multicluster scenario", func() {
}, 20*time.Second).Should(Succeed())
})
It("Test application with apply-component and cluster", func() {
By("create application")
bs, err := os.ReadFile("./testdata/app/app-component-with-cluster.yaml")
Expect(err).Should(Succeed())
app := &v1beta1.Application{}
Expect(yaml.Unmarshal(bs, app)).Should(Succeed())
app.SetNamespace(testNamespace)
Expect(k8sClient.Create(hubCtx, app)).Should(Succeed())
Eventually(func(g Gomega) {
g.Expect(k8sClient.Get(hubCtx, client.ObjectKeyFromObject(app), app)).Should(Succeed())
g.Expect(app.Status.Phase).Should(Equal(common.ApplicationRunning))
}, 20*time.Second).Should(Succeed())
Expect(k8sClient.Get(workerCtx, client.ObjectKey{Namespace: testNamespace, Name: "component-cluster"}, &appsv1.Deployment{})).Should(Succeed())
})
It("Test application with component using cluster context", func() {
By("Create definition")
bs, err := os.ReadFile("./testdata/def/cluster-config.yaml")

View File

@@ -0,0 +1,20 @@
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: component-cluster
spec:
components:
- name: component-cluster
type: worker
properties:
image: busybox
cmd:
- sleep
- '1000000'
workflow:
steps:
- name: apply
type: apply-component
properties:
component: component-cluster
cluster: cluster-worker

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