Compare commits

..

13 Commits

Author SHA1 Message Date
Tianxin Dong
b24b52b651 Chore: update workflow vendor to fix certain bugs in 1.6 (#5254)
Signed-off-by: FogDong <dongtianxin.tx@alibaba-inc.com>

Signed-off-by: FogDong <dongtianxin.tx@alibaba-inc.com>
2023-01-03 16:12:19 +08:00
github-actions[bot]
ae91006a3d Fix:Dry-run from revision application,Problems caused by resource version lower than the current version (#5250)
Signed-off-by: old.prince <di7zhang@gmail.com>
(cherry picked from commit d5fcb04147)

Co-authored-by: oldprince <di7zhang@gmail.com>
2023-01-03 11:37:20 +08:00
Somefive
42d6791476 Fix: gc failure cause workflow restart not working properly (#5242)
Signed-off-by: Somefive <yd219913@alibaba-inc.com>

Signed-off-by: Somefive <yd219913@alibaba-inc.com>
2022-12-30 18:10:38 +08:00
github-actions[bot]
c85502e54a Fix: remove useless field when loading pod in top (#5230)
Signed-off-by: Qiaozp <qiaozhongpei.qzp@alibaba-inc.com>
(cherry picked from commit 8582ae7d93)

Co-authored-by: Qiaozp <qiaozhongpei.qzp@alibaba-inc.com>
2022-12-26 13:17:56 +08:00
github-actions[bot]
f9a6b22294 Fix: addon ls comman does not show the componentless application (#5204)
Signed-off-by: zhaohuihui <zhaohuihui_yewu@cmss.chinamobile.com>
(cherry picked from commit 50f6347050)

Co-authored-by: zhaohuihui <zhaohuihui_yewu@cmss.chinamobile.com>
2022-12-19 13:23:43 +08:00
github-actions[bot]
5085a99a12 [Backport release-1.6] Feat: rollout support statefulsets (#5203)
* rollout support workload

Signed-off-by: suwanliang_yewu <suwanliang_yewu@cmss.chinamobile.com>
(cherry picked from commit ab36f3b7c3)

* modify the error output

Signed-off-by: suwanliang_yewu <suwanliang_yewu@cmss.chinamobile.com>
(cherry picked from commit 1ed43cc282)

Co-authored-by: suwanliang_yewu <suwanliang_yewu@cmss.chinamobile.com>
2022-12-19 12:10:08 +08:00
github-actions[bot]
18639ccbae Fix: The error of cannot fetch addon package breaks the loop to continue looking for addon package in the remaining registries (#5188)
Signed-off-by: zhaohuihui <zhaohuihui_yewu@cmss.chinamobile.com>
(cherry picked from commit 9263633db7)

Co-authored-by: zhaohuihui <zhaohuihui_yewu@cmss.chinamobile.com>
2022-12-14 13:36:05 +08:00
github-actions[bot]
f36c8f8fbb [Backport release-1.6] Fix: check if enabling operation will uninstall controller from some clusters (#5181)
* check clusters parameter of addon will uninstall from clusters

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

* revert go mod

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

* fix comments

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

* skip verify version check

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

* small fix for error notice

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

Co-authored-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
2022-12-12 13:41:31 +08:00
github-actions[bot]
c55ac52c4d [Backport release-1.6] Feat: add multicluster test for vela logs (#5177)
* Feat: add multicluster test for vela logs

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

* reviewable

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

* fix test

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

* fix test

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

Co-authored-by: Qiaozp <qiaozhongpei.qzp@alibaba-inc.com>
2022-12-09 10:45:34 +08:00
github-actions[bot]
4d4ab9d098 Chore(deps): Bump github.com/containerd/containerd from 1.5.13 to 1.5.16 (#5170)
Bumps [github.com/containerd/containerd](https://github.com/containerd/containerd) from 1.5.13 to 1.5.16.
- [Release notes](https://github.com/containerd/containerd/releases)
- [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md)
- [Commits](https://github.com/containerd/containerd/compare/v1.5.13...v1.5.16)

---
updated-dependencies:
- dependency-name: github.com/containerd/containerd
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
(cherry picked from commit 6d49114177)

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-08 11:06:53 +08:00
Jianbo Sun
14dfca44b4 Feat: add options for addon parameter (#5166) (#5167)
* Feat: add options for addon parameter

Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>

* Fix: wrongly report disbaled when addon is not existed

Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>

Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>

Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
2022-12-07 19:29:16 +08:00
github-actions[bot]
44c6267b76 [Backport release-1.6] Fix: vela show docs can't generate composition type (#5165)
* Fix: vela show can not display or result

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

* Fix: vela show docs can't generate composition type

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

Co-authored-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
2022-12-06 16:15:03 +08:00
github-actions[bot]
9c81aeed4a [Backport release-1.6] Fix: CollectLogsInPod won't work in multicluster (#5164)
* Fix: CollectLogsInPod won't work in multicluster

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

* clean up

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

Co-authored-by: Qiaozp <qiaozhongpei.qzp@alibaba-inc.com>
2022-12-06 15:53:57 +08:00
31 changed files with 877 additions and 187 deletions

8
go.mod
View File

@@ -19,7 +19,7 @@ require (
github.com/briandowns/spinner v1.11.1
github.com/chartmuseum/helm-push v0.10.2
github.com/cloudtty/cloudtty v0.2.0
github.com/containerd/containerd v1.5.13
github.com/containerd/containerd v1.5.16
github.com/coreos/go-oidc v2.1.0+incompatible
github.com/coreos/prometheus-operator v0.41.1
github.com/crossplane/crossplane-runtime v0.14.1-0.20210722005935-0b469fcc77cd
@@ -58,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.3
github.com/kubevela/workflow v0.3.4-0.20230103040208-bca032d481ed
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
@@ -158,7 +158,7 @@ require (
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 // indirect
github.com/clbanning/mxj/v2 v2.5.5 // indirect
github.com/cockroachdb/apd/v2 v2.0.2 // indirect
github.com/containerd/continuity v0.1.0 // indirect
github.com/containerd/continuity v0.3.0 // indirect
github.com/coreos/go-semver v0.3.0 // indirect
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
@@ -230,7 +230,7 @@ require (
github.com/mattn/go-colorable v0.1.11 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect

14
go.sum
View File

@@ -448,16 +448,17 @@ github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoT
github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g=
github.com/containerd/containerd v1.5.2/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g=
github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c=
github.com/containerd/containerd v1.5.13 h1:XqvKw9i4P7/mFrC3TSM7yV5cwFZ9avXe6M3YANKnzEE=
github.com/containerd/containerd v1.5.13/go.mod h1:3AlCrzKROjIuP3JALsY14n8YtntaUDBu7vek+rPN5Vc=
github.com/containerd/containerd v1.5.16 h1:WsTS9tV0vQmRxkWAiiaoasHJ20jqVxVA15s93Bs4GIU=
github.com/containerd/containerd v1.5.16/go.mod h1:bVZZA+0blg2Lw6+I4xDml7L3gum0LsFKe3TnFELlSFw=
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo=
github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y=
github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ=
github.com/containerd/continuity v0.1.0 h1:UFRRY5JemiAhPZrr/uE0n8fMTLcZsUvySPr1+D7pgr8=
github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM=
github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg=
github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM=
github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0=
@@ -1336,8 +1337,8 @@ github.com/kubevela/pkg v0.0.0-20221024115939-a103acee6db2 h1:C3cAfrxst1+dIWgLLh
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.3 h1:NSbQGcABWJIzUV5wfWFJsrO/ffZ4mCVfofUtUHCTojQ=
github.com/kubevela/workflow v0.3.3/go.mod h1:5jfZ8T1m/En44wDGRf2YqCSlODfEnAV+9PnzoLoDlFs=
github.com/kubevela/workflow v0.3.4-0.20230103040208-bca032d481ed h1:VGeEvL/XOkwADhLS4Upz5zEM6Enjw4yx8JU3Z+UUqXw=
github.com/kubevela/workflow v0.3.4-0.20230103040208-bca032d481ed/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=
@@ -1438,8 +1439,9 @@ github.com/mattn/go-sqlite3 v1.14.8 h1:gDp86IdQsN/xWjIEmr9MF6o9mpksUgh0fu+9ByFxz
github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/mbilski/exhaustivestruct v1.2.0/go.mod h1:OeTBVxQWoEmB2J2JCHmXWPJ0aksxSUOUy+nvtVEfzXc=
github.com/mgechev/dots v0.0.0-20210922191527-e955255bf517/go.mod h1:KQ7+USdGKfpPjXk4Ga+5XxQM4Lm4e3gAogrreFAYpOg=
github.com/mgechev/revive v1.1.2/go.mod h1:bnXsMr+ZTH09V5rssEI+jHAZ4z+ZdyhgO/zsy3EhK+0=

View File

@@ -43,6 +43,9 @@ var (
// ErrBothCueAndYamlTmpl means yaml and cue app template are exist in addon
ErrBothCueAndYamlTmpl = NewAddonError("yaml and cue app template are exist in addon, should only keep one of them")
// ErrFetch means fetch addon package error(package not exist or parse archive error and so on)
ErrFetch = NewAddonError("cannot fetch addon package")
)
// WrapErrRateLimit return ErrRateLimit if is the situation, or return error directly

View File

@@ -280,8 +280,8 @@ func GetObservabilityAccessibilityInfo(ctx context.Context, k8sClient client.Cli
return domains, nil
}
// FindWholeAddonPackagesFromRegistry find addons' WholeInstallPackage from registries, empty registryName indicates matching all
func FindWholeAddonPackagesFromRegistry(ctx context.Context, k8sClient client.Client, addonNames []string, registryNames []string) ([]*WholeAddonPackage, error) {
// FindAddonPackagesDetailFromRegistry find addons' WholeInstallPackage from registries, empty registryName indicates matching all
func FindAddonPackagesDetailFromRegistry(ctx context.Context, k8sClient client.Client, addonNames []string, registryNames []string) ([]*WholeAddonPackage, error) {
var addons []*WholeAddonPackage
var registries []Registry

View File

@@ -29,21 +29,21 @@ import (
. "github.com/onsi/gomega"
)
var _ = Describe("test FindWholeAddonPackagesFromRegistry", func() {
var _ = Describe("test FindAddonPackagesDetailFromRegistry", func() {
Describe("when no registry is added, no matter what you do, it will just return error", func() {
Context("when empty addonNames and registryNames is supplied", func() {
It("should return error", func() {
_, err := FindWholeAddonPackagesFromRegistry(context.Background(), k8sClient, []string{}, []string{})
_, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{}, []string{})
Expect(err).To(HaveOccurred())
})
It("should return error", func() {
_, err := FindWholeAddonPackagesFromRegistry(context.Background(), k8sClient, nil, nil)
_, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, nil, nil)
Expect(err).To(HaveOccurred())
})
})
Context("when non-empty addonNames and registryNames is supplied", func() {
It("should return error saying ErrRegistryNotExist", func() {
_, err := FindWholeAddonPackagesFromRegistry(context.Background(), k8sClient, []string{"fluxcd"}, []string{"some-registry"})
_, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{"fluxcd"}, []string{"some-registry"})
Expect(errors.Is(err, ErrRegistryNotExist)).To(BeTrue())
})
})
@@ -70,18 +70,18 @@ var _ = Describe("test FindWholeAddonPackagesFromRegistry", func() {
Context("when empty addonNames and registryNames is supplied", func() {
It("should return error, empty addonNames are not allowed", func() {
_, err := FindWholeAddonPackagesFromRegistry(context.Background(), k8sClient, []string{}, []string{"KubeVela"})
_, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{}, []string{"KubeVela"})
Expect(err).To(HaveOccurred())
})
It("should return error, empty addonNames are not allowed", func() {
_, err := FindWholeAddonPackagesFromRegistry(context.Background(), k8sClient, nil, []string{"KubeVela"})
_, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, nil, []string{"KubeVela"})
Expect(err).To(HaveOccurred())
})
})
Context("one existing addon name provided", func() {
It("should return one valid result, matching all registries", func() {
res, err := FindWholeAddonPackagesFromRegistry(context.Background(), k8sClient, []string{"velaux"}, nil)
res, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{"velaux"}, nil)
Expect(err).To(Succeed())
Expect(res).To(HaveLen(1))
Expect(res[0].Name).To(Equal("velaux"))
@@ -89,7 +89,7 @@ var _ = Describe("test FindWholeAddonPackagesFromRegistry", func() {
Expect(res[0].APISchema).ToNot(BeNil())
})
It("should return one valid result, matching one registry", func() {
res, err := FindWholeAddonPackagesFromRegistry(context.Background(), k8sClient, []string{"velaux"}, []string{"KubeVela"})
res, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{"velaux"}, []string{"KubeVela"})
Expect(err).To(Succeed())
Expect(res).To(HaveLen(1))
Expect(res[0].Name).To(Equal("velaux"))
@@ -100,7 +100,7 @@ var _ = Describe("test FindWholeAddonPackagesFromRegistry", func() {
Context("one non-existent addon name provided", func() {
It("should return error as ErrNotExist", func() {
res, err := FindWholeAddonPackagesFromRegistry(context.Background(), k8sClient, []string{"non-existent-addon"}, nil)
res, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{"non-existent-addon"}, nil)
Expect(errors.Is(err, ErrNotExist)).To(BeTrue())
Expect(res).To(BeNil())
})
@@ -108,7 +108,7 @@ var _ = Describe("test FindWholeAddonPackagesFromRegistry", func() {
Context("two existing addon names provided", func() {
It("should return two valid result", func() {
res, err := FindWholeAddonPackagesFromRegistry(context.Background(), k8sClient, []string{"velaux", "traefik"}, nil)
res, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{"velaux", "traefik"}, nil)
Expect(err).To(Succeed())
Expect(res).To(HaveLen(2))
Expect(res[0].Name).To(Equal("velaux"))
@@ -122,7 +122,7 @@ var _ = Describe("test FindWholeAddonPackagesFromRegistry", func() {
Context("one existing addon name and one non-existent addon name provided", func() {
It("should return only one valid result", func() {
res, err := FindWholeAddonPackagesFromRegistry(context.Background(), k8sClient, []string{"velaux", "non-existent-addon"}, nil)
res, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{"velaux", "non-existent-addon"}, nil)
Expect(err).To(Succeed())
Expect(res).To(HaveLen(1))
Expect(res[0].Name).To(Equal("velaux"))
@@ -151,25 +151,25 @@ var _ = Describe("test FindWholeAddonPackagesFromRegistry", func() {
Context("when empty addonNames and registryNames is supplied", func() {
It("should return error, empty addonNames are not allowed", func() {
_, err := FindWholeAddonPackagesFromRegistry(context.Background(), k8sClient, []string{}, []string{})
_, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{}, []string{})
Expect(err).To(HaveOccurred())
})
It("should return error, empty addonNames are not allowed", func() {
_, err := FindWholeAddonPackagesFromRegistry(context.Background(), k8sClient, nil, []string{"testreg"})
_, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, nil, []string{"testreg"})
Expect(err).To(HaveOccurred())
})
})
Context("one existing addon name provided", func() {
It("should return one valid result, matching all registries", func() {
res, err := FindWholeAddonPackagesFromRegistry(context.Background(), k8sClient, []string{"example"}, nil)
res, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{"example"}, nil)
Expect(err).To(Succeed())
Expect(res).To(HaveLen(1))
Expect(res[0].Name).To(Equal("example"))
Expect(res[0].InstallPackage).ToNot(BeNil())
})
It("should return one valid result, matching one registry", func() {
res, err := FindWholeAddonPackagesFromRegistry(context.Background(), k8sClient, []string{"example"}, []string{"testreg"})
res, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{"example"}, []string{"testreg"})
Expect(err).To(Succeed())
Expect(res).To(HaveLen(1))
Expect(res[0].Name).To(Equal("example"))
@@ -179,7 +179,7 @@ var _ = Describe("test FindWholeAddonPackagesFromRegistry", func() {
Context("one non-existent addon name provided", func() {
It("should return error as ErrNotExist", func() {
res, err := FindWholeAddonPackagesFromRegistry(context.Background(), k8sClient, []string{"non-existent-addon"}, nil)
res, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{"non-existent-addon"}, nil)
Expect(errors.Is(err, ErrNotExist)).To(BeTrue())
Expect(res).To(BeNil())
})
@@ -187,7 +187,7 @@ var _ = Describe("test FindWholeAddonPackagesFromRegistry", func() {
Context("one existing addon name and one non-existent addon name provided", func() {
It("should return only one valid result", func() {
res, err := FindWholeAddonPackagesFromRegistry(context.Background(), k8sClient, []string{"example", "non-existent-addon"}, nil)
res, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{"example", "non-existent-addon"}, nil)
Expect(err).To(Succeed())
Expect(res).To(HaveLen(1))
Expect(res[0].Name).To(Equal("example"))

View File

@@ -174,7 +174,7 @@ func (i versionedRegistry) loadAddon(ctx context.Context, name, version string)
addonPkg.Meta.SystemRequirements = LoadSystemRequirements(addonVersion.Annotations)
return addonPkg, nil
}
return nil, fmt.Errorf("cannot load addon '%s'(%s) package from registry '%s'", addonVersion.Name, addonVersion.Version, i.name)
return nil, ErrFetch
}
// loadAddonVersions Load all available versions of the addon

View File

@@ -1513,6 +1513,13 @@ func (c *applicationServiceImpl) DryRunAppOrRevision(ctx context.Context, appMod
}
case "REVISION":
app, _, err = c.getAppModelFromRevision(ctx, appModel.Name, dryRunReq.Version)
originalApp := &v1beta1.Application{}
if err := c.KubeClient.Get(ctx, types.NamespacedName{
Name: app.Name,
Namespace: app.Namespace,
}, originalApp); err == nil {
app.ResourceVersion = originalApp.ResourceVersion
}
if err != nil {
return nil, err
}

View File

@@ -741,7 +741,6 @@ func pipelineStep2WorkflowStep(step model.WorkflowStep) v1alpha1.WorkflowStep {
func pipelineSpec2WorkflowSpec(spec model.WorkflowSpec) *v1alpha1.WorkflowSpec {
res := &v1alpha1.WorkflowSpec{
Mode: spec.Mode,
Steps: make([]v1alpha1.WorkflowStep, 0),
}
for _, step := range spec.Steps {

View File

@@ -302,6 +302,11 @@ func (r *Reconciler) gcResourceTrackers(logCtx monitorContext.Context, handler *
}))
defer subCtx.Commit("finish gc resourceTrackers")
statusUpdater := r.patchStatus
if isUpdate {
statusUpdater = r.updateStatus
}
var options []resourcekeeper.GCOption
if !gcOutdated {
options = append(options, resourcekeeper.DisableMarkStageGCOption{}, resourcekeeper.DisableGCComponentRevisionOption{}, resourcekeeper.DisableLegacyGCOption{})
@@ -309,8 +314,10 @@ func (r *Reconciler) gcResourceTrackers(logCtx monitorContext.Context, handler *
finished, waiting, err := handler.resourceKeeper.GarbageCollect(logCtx, options...)
if err != nil {
logCtx.Error(err, "Failed to gc resourcetrackers")
r.Recorder.Event(handler.app, event.Warning(velatypes.ReasonFailedGC, err))
return r.endWithNegativeCondition(logCtx, handler.app, condition.ReconcileError(err), phase)
cond := condition.Deleting()
cond.Message = fmt.Sprintf("error encountered during garbage collection: %s", err.Error())
handler.app.Status.SetConditions(cond)
return r.result(statusUpdater(logCtx, handler.app, phase)).ret()
}
if !finished {
logCtx.Info("GarbageCollecting resourcetrackers unfinished")
@@ -319,13 +326,10 @@ func (r *Reconciler) gcResourceTrackers(logCtx monitorContext.Context, handler *
cond.Message = fmt.Sprintf("Waiting for %s to delete. (At least %d resources are deleting.)", waiting[0].DisplayName(), len(waiting))
}
handler.app.Status.SetConditions(cond)
return r.result(r.patchStatus(logCtx, handler.app, phase)).requeue(baseGCBackoffWaitTime).ret()
return r.result(statusUpdater(logCtx, handler.app, phase)).requeue(baseGCBackoffWaitTime).ret()
}
logCtx.Info("GarbageCollected resourcetrackers")
if isUpdate {
return r.result(r.updateStatus(logCtx, handler.app, phase)).ret()
}
return r.result(r.patchStatus(logCtx, handler.app, phase)).ret()
return r.result(statusUpdater(logCtx, handler.app, phase)).ret()
}
type reconcileResult struct {

View File

@@ -114,6 +114,7 @@ func HandleReplicas(ctx context.Context, rolloutComp string, c client.Client) as
klog.InfoS("assemble force set workload replicas to 0", "Kind", u.GetKind(), "name", u.GetName())
return nil
}
klog.Errorf("fail to get workload %s: %v", u.GetName(), err)
return err
}
// the workload already exist, we cannot reset the replicas with manifest
@@ -122,6 +123,7 @@ func HandleReplicas(ctx context.Context, rolloutComp string, c client.Client) as
wlpv := fieldpath.Pave(workload.UnstructuredContent())
replicas, err := wlpv.GetInteger(replicasFieldPath)
if err != nil {
klog.Errorf("fail to get `spec.replicas` field from workload %s: %v", u.GetName(), err)
return err
}
if err = pv.SetNumber(replicasFieldPath, float64(replicas)); err != nil {

View File

@@ -329,19 +329,6 @@ func RealtimePrintCommandOutput(cmd *exec.Cmd, logFile string) error {
return nil
}
// ClusterObject2Map convert ClusterObjectReference to a readable map
func ClusterObject2Map(refs []common.ClusterObjectReference) map[string]string {
clusterResourceRefTmpl := "Cluster: %s | Namespace: %s | Kind: %s | Name: %s"
objs := make(map[string]string, len(refs))
for _, r := range refs {
if r.Cluster == "" {
r.Cluster = "local"
}
objs[r.Cluster+"/"+r.Namespace+"/"+r.Name+"/"+r.Kind] = fmt.Sprintf(clusterResourceRefTmpl, r.Cluster, r.Namespace, r.Kind, r.Name)
}
return objs
}
// ResourceLocation indicates the resource location
type ResourceLocation struct {
Cluster string

View File

@@ -305,9 +305,11 @@ func isResourceInTargetCluster(opt FilterOption, resource common.ClusterObjectRe
if opt.Cluster == "" && opt.ClusterNamespace == "" {
return true
}
if (opt.Cluster == resource.Cluster || (opt.Cluster == "local" && resource.Cluster == "")) && opt.ClusterNamespace == resource.ObjectReference.Namespace {
if (opt.Cluster == resource.Cluster || (opt.Cluster == "local" && resource.Cluster == "")) &&
(opt.ClusterNamespace == resource.ObjectReference.Namespace || opt.ClusterNamespace == "") {
return true
}
return false
}

View File

@@ -35,6 +35,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
monitorContext "github.com/kubevela/pkg/monitor/context"
pkgmulticluster "github.com/kubevela/pkg/multicluster"
wfContext "github.com/kubevela/workflow/pkg/context"
"github.com/kubevela/workflow/pkg/cue/model/value"
"github.com/kubevela/workflow/pkg/types"
@@ -229,6 +230,7 @@ func (h *provider) CollectLogsInPod(ctx monitorContext.Context, wfCtx wfContext.
return errors.Wrapf(err, "invalid log options content")
}
cliCtx := multicluster.ContextWithClusterName(ctx, cluster)
h.cfg.Wrap(pkgmulticluster.NewTransportWrapper())
clientSet, err := kubernetes.NewForConfig(h.cfg)
if err != nil {
return errors.Wrapf(err, "failed to create kubernetes client")

View File

@@ -70,18 +70,16 @@ const (
var enabledAddonColor = color.New(color.Bold, color.FgGreen)
var forceDisable bool
var addonVersion string
var addonClusters string
var verboseStatus bool
var skipValidate bool
var overrideDefs bool
var dryRun bool
var (
forceDisable bool
addonVersion string
addonClusters string
verboseStatus bool
skipValidate bool
overrideDefs bool
dryRun bool
yes2all bool
)
// NewAddonCommand create `addon` command
func NewAddonCommand(c common.Args, order string, ioStreams cmdutil.IOStreams) *cobra.Command {
@@ -189,15 +187,24 @@ func NewAddonEnableCommand(c common.Args, ioStream cmdutil.IOStreams) *cobra.Com
return errors.Wrapf(err, "directory %s is invalid", addonOrDir)
}
name = filepath.Base(abs)
if !yes2all {
if err := checkUninstallFromClusters(ctx, k8sClient, name, addonArgs); err != nil {
return err
}
}
err = enableAddonByLocal(ctx, name, addonOrDir, k8sClient, dc, config, addonArgs)
if err != nil {
return err
}
} else {
if filepath.IsAbs(addonOrDir) || strings.HasPrefix(addonOrDir, ".") || strings.HasSuffix(addonOrDir, "/") {
return fmt.Errorf("addon directory %s not found in local", addonOrDir)
return fmt.Errorf("addon directory %s not found in local file system", addonOrDir)
}
if !yes2all {
if err := checkUninstallFromClusters(ctx, k8sClient, name, addonArgs); err != nil {
return err
}
}
err = enableAddon(ctx, k8sClient, dc, config, name, addonVersion, addonArgs)
if err != nil {
return err
@@ -217,6 +224,7 @@ func NewAddonEnableCommand(c common.Args, ioStream cmdutil.IOStreams) *cobra.Com
cmd.Flags().BoolVarP(&skipValidate, "skip-version-validating", "s", false, "skip validating system version requirement")
cmd.Flags().BoolVarP(&overrideDefs, "override-definitions", "", false, "override existing definitions if conflict with those contained in this addon")
cmd.Flags().BoolVarP(&dryRun, FlagDryRun, "", false, "render all yaml files out without real execute it")
cmd.Flags().BoolVarP(&yes2all, "yes", "y", false, "all checks will be skipped and the default answer is yes for all validation check.")
return cmd
}
@@ -567,7 +575,7 @@ func enableAddon(ctx context.Context, k8sClient client.Client, dc *discovery.Dis
continue
}
err = pkgaddon.EnableAddon(ctx, addonName, version, k8sClient, dc, apply.NewAPIApplicator(k8sClient), config, registry, args, nil, pkgaddon.FilterDependencyRegistries(i, registries), opts...)
if errors.Is(err, pkgaddon.ErrNotExist) {
if errors.Is(err, pkgaddon.ErrNotExist) || errors.Is(err, pkgaddon.ErrFetch) {
continue
}
if unMatchErr := new(pkgaddon.VersionUnMatchError); errors.As(err, unMatchErr) {
@@ -600,10 +608,10 @@ func enableAddon(ctx context.Context, k8sClient client.Client, dc *discovery.Dis
func addonOptions() []pkgaddon.InstallOption {
var opts []pkgaddon.InstallOption
if skipValidate {
if skipValidate || yes2all {
opts = append(opts, pkgaddon.SkipValidateVersion)
}
if overrideDefs {
if overrideDefs || yes2all {
opts = append(opts, pkgaddon.OverrideDefinitions)
}
if dryRun {
@@ -654,6 +662,16 @@ func statusAddon(name string, ioStreams cmdutil.IOStreams, cmd *cobra.Command, c
return nil
}
func addonNotExist(err error) bool {
if errors.Is(err, pkgaddon.ErrNotExist) || errors.Is(err, pkgaddon.ErrRegistryNotExist) {
return true
}
if strings.Contains(err.Error(), "not found") {
return true
}
return false
}
// generateAddonInfo will get addon status, description, version, dependencies (and whether they are installed),
// and parameters (and their current values).
// The first return value is the formatted string for printing.
@@ -664,12 +682,21 @@ func generateAddonInfo(c client.Client, name string) (string, pkgaddon.Status, e
var installed bool
var addonPackage *pkgaddon.WholeAddonPackage
// Check current addon status
status, err := pkgaddon.GetAddonStatus(context.Background(), c, name)
if err != nil {
return res, status, err
}
// Get addon install package
if verboseStatus {
if verboseStatus || status.AddonPhase == statusDisabled {
// We need the metadata to get descriptions about parameters
addonPackages, err := pkgaddon.FindWholeAddonPackagesFromRegistry(context.Background(), c, []string{name}, nil)
// Not found error can be ignored, because the user can define their own addon. Others can't.
if err != nil && !errors.Is(err, pkgaddon.ErrNotExist) && !errors.Is(err, pkgaddon.ErrRegistryNotExist) {
addonPackages, err := pkgaddon.FindAddonPackagesDetailFromRegistry(context.Background(), c, []string{name}, nil)
// If the state of addon is not disabled, we don't check the error, because it could be installed from local.
if status.AddonPhase == statusDisabled && err != nil {
if addonNotExist(err) {
return "", pkgaddon.Status{}, fmt.Errorf("addon '%s' not found in cluster or any registry", name)
}
return "", pkgaddon.Status{}, err
}
if len(addonPackages) != 0 {
@@ -677,12 +704,6 @@ func generateAddonInfo(c client.Client, name string) (string, pkgaddon.Status, e
}
}
// Check current addon status
status, err := pkgaddon.GetAddonStatus(context.Background(), c, name)
if err != nil {
return res, status, err
}
switch status.AddonPhase {
case statusEnabled:
installed = true
@@ -785,12 +806,23 @@ func generateParameterString(status pkgaddon.Status, addonPackage *pkgaddon.Whol
if addonPackage.APISchema == nil {
return ret
}
ret = printSchema(addonPackage.APISchema, status.Parameters, 0)
return ret
}
func convertInterface2StringList(l []interface{}) []string {
var strl []string
for _, s := range l {
str, ok := s.(string)
if !ok {
continue
}
strl = append(strl, str)
}
return strl
}
// printSchema prints the parameters in an addon recursively to a string
// Deeper the parameter is nested, more the indentations.
func printSchema(ref *openapi3.Schema, currentParams map[string]interface{}, indent int) string {
@@ -853,19 +885,25 @@ func printSchema(ref *openapi3.Schema, currentParams map[string]interface{}, ind
// Show current value
if currentValue != "" {
ret += addedIndent
ret += "\tcurrent: " + color.New(color.FgGreen).Sprintf("%s\n", currentValue)
}
// Show default value
if defaultValue != "" {
ret += addedIndent
ret += "\tdefault: " + fmt.Sprintf("%#v\n", defaultValue)
ret += "\tcurrent value: " + color.New(color.FgGreen).Sprintf("%s\n", currentValue)
}
// Show required or not
if required {
ret += addedIndent
ret += "\trequired: "
ret += color.GreenString("✔\n")
}
// Show Enum options
if len(propValue.Value.Enum) > 0 {
ret += addedIndent
ret += "\toptions: \"" + strings.Join(convertInterface2StringList(propValue.Value.Enum), "\", \"") + "\"\n"
}
// Show default value
if defaultValue != "" && currentValue == "" {
ret += addedIndent
ret += "\tdefault: " + fmt.Sprintf("%#v\n", defaultValue)
}
// Object type param, we will get inside the object.
// To show what's inside nested objects.
@@ -1171,3 +1209,51 @@ func splitSpecifyRegistry(name string) (string, string, error) {
return "", "", fmt.Errorf("invalid addon name, you should specify name only <addonName> or with registry as prefix <registryName>/<addonName>")
}
}
func checkUninstallFromClusters(ctx context.Context, k8sClient client.Client, addonName string, args map[string]interface{}) error {
status, err := pkgaddon.GetAddonStatus(ctx, k8sClient, addonName)
if err != nil {
return fmt.Errorf("failed to check addon status: %w", err)
}
if status.AddonPhase == statusDisabled {
return nil
}
if _, ok := args["clusters"]; !ok {
return nil
}
cList, ok := args["clusters"].([]interface{})
if !ok {
return fmt.Errorf("clusters parameter must be a list of string")
}
clusters := map[string]struct{}{}
for _, c := range cList {
clusterName := c.(string)
clusters[clusterName] = struct{}{}
}
var disableClusters, installedClusters []string
for c := range status.Clusters {
if _, ok := clusters[c]; !ok {
disableClusters = append(disableClusters, c)
}
installedClusters = append(installedClusters, c)
}
fmt.Println(color.New(color.FgRed).Sprintf("'%s' addon was currently installed on clusters %s, but this operation will uninstall from these clusters: %s \n", addonName, generateClustersInfo(installedClusters), generateClustersInfo(disableClusters)))
input := NewUserInput()
if !input.AskBool("Do you want to continue?", &UserInputOptions{AssumeYes: false}) {
return fmt.Errorf("operation abort")
}
return nil
}
func generateClustersInfo(clusters []string) string {
ret := "["
for i, cluster := range clusters {
ret += cluster
if i < len(clusters)-1 {
ret += ","
}
}
ret += "]"
return ret
}

View File

@@ -269,7 +269,7 @@ var _ = Describe("Addon status or info", func() {
BeforeEach(func() {
// Delete KubeVela registry
ds := pkgaddon.NewRegistryDataStore(k8sClient)
Expect(ds.DeleteRegistry(context.Background(), "KubeVela")).To(Succeed())
Expect(ds.DeleteRegistry(context.Background(), "KubeVela")).Should(SatisfyAny(Succeed(), util.NotFoundMatcher{}))
// Install fluxcd locally
Expect(k8sClient.Create(context.Background(), &fluxcd)).Should(SatisfyAny(BeNil(), util.AlreadyExistMatcher{}))
})
@@ -287,6 +287,7 @@ var _ = Describe("Addon status or info", func() {
if err != nil {
return err
}
fmt.Println(addonName, res, err)
// Should include enabled status, like:
// fluxcd: enabled (1.1.0)
if !strings.Contains(res,
@@ -349,6 +350,11 @@ var _ = Describe("Addon status or info", func() {
"KubeVela",
))
})
It("should report addon not exist in any registry name", func() {
addonName := "not-exist"
_, _, err := generateAddonInfo(k8sClient, addonName)
Expect(err.Error()).Should(BeEquivalentTo("addon 'not-exist' not found in cluster or any registry"))
})
})
})
})

View File

@@ -19,7 +19,6 @@ package cli
import (
"fmt"
"os"
"strings"
"testing"
. "github.com/onsi/ginkgo"
@@ -27,7 +26,7 @@ import (
"github.com/fatih/color"
"github.com/getkin/kin-openapi/openapi3"
"gotest.tools/assert"
"github.com/stretchr/testify/assert"
pkgaddon "github.com/oam-dev/kubevela/pkg/addon"
"github.com/oam-dev/kubevela/pkg/utils/common"
@@ -91,8 +90,8 @@ func TestParseMap(t *testing.T) {
for _, s := range testcase {
r, err := parseAddonArgsToMap(s.args)
if s.nilError {
assert.NilError(t, err)
assert.DeepEqual(t, s.res, r)
assert.NoError(t, err)
assert.Equal(t, s.res, r)
} else {
assert.Error(t, err, fmt.Sprintf("%v should be error case", s.args))
}
@@ -208,7 +207,7 @@ func TestTransCluster(t *testing.T) {
},
}
for _, s := range testcase {
assert.DeepEqual(t, transClusters(s.str), s.res)
assert.Equal(t, transClusters(s.str), s.res)
}
}
@@ -341,7 +340,7 @@ func TestPackageValidAddon(t *testing.T) {
cmd := NewAddonPackageCommand(commandArgs)
cmd.SetArgs([]string{"./test-data/addon/sample"})
err := cmd.Execute()
assert.NilError(t, err)
assert.NoError(t, err)
defer func() {
_ = os.RemoveAll("sample-1.0.1.tgz")
}()
@@ -380,13 +379,13 @@ func TestGenerateParameterString(t *testing.T) {
"dbURL": &openapi3.SchemaRef{
Value: &openapi3.Schema{
Description: "Specify the MongoDB URL. it only enabled where DB type is MongoDB.",
Default: nil,
Default: "abc.com",
},
},
"dbType": &openapi3.SchemaRef{
Value: &openapi3.Schema{
Description: "Specify the database type, current support KubeAPI(default) and MongoDB.",
Default: "kubeapi",
Enum: []interface{}{"kubeapi", "mongodb"},
},
},
},
@@ -397,18 +396,19 @@ func TestGenerateParameterString(t *testing.T) {
color.New(color.FgCyan).Sprintf("-> ") +
color.New(color.Bold).Sprint("dbType") + ": " +
"Specify the database type, current support KubeAPI(default) and MongoDB.\n" +
"\tcurrent: " + color.New(color.FgGreen).Sprint("\"kubeapi\"\n") +
"\tdefault: " + "\"kubeapi\"\n" +
"\trequired: " + color.GreenString("✔\n"),
"\tcurrent value: " + color.New(color.FgGreen).Sprint("\"kubeapi\"\n") +
"\trequired: " + color.GreenString("✔\n") +
"\toptions: \"kubeapi\", \"mongodb\"\n",
// dbURL
color.New(color.FgCyan).Sprintf("-> ") +
color.New(color.Bold).Sprint("dbURL") + ": " +
"Specify the MongoDB URL. it only enabled where DB type is MongoDB.",
"Specify the MongoDB URL. it only enabled where DB type is MongoDB.\n" +
"\tdefault: " + "\"abc.com\"\n",
// database
color.New(color.FgCyan).Sprintf("-> ") +
color.New(color.Bold).Sprint("database") + ": " +
"Specify the database name, for the kubeapi db type, it represents namespace.\n" +
"\tcurrent: " + color.New(color.FgGreen).Sprint("\"kubevela\""),
"\tcurrent value: " + color.New(color.FgGreen).Sprint("\"kubevela\""),
},
},
}
@@ -416,7 +416,7 @@ func TestGenerateParameterString(t *testing.T) {
for _, s := range testcase {
res := generateParameterString(s.status, s.addonPackage)
for _, o := range s.outputs {
assert.Check(t, strings.Contains(res, o))
assert.Contains(t, res, o)
}
}
@@ -434,12 +434,12 @@ func TestNewAddonCreateCommand(t *testing.T) {
cmd.SetArgs([]string{"test-addon", "--chart", "a", "--helm-repo", "https://some.com", "--chart-version", "c"})
err = cmd.Execute()
assert.NilError(t, err)
assert.NoError(t, err)
_ = os.RemoveAll("test-addon")
cmd.SetArgs([]string{"test-addon"})
err = cmd.Execute()
assert.NilError(t, err)
assert.NoError(t, err)
_ = os.RemoveAll("test-addon")
}

View File

@@ -99,6 +99,15 @@ func buildApplicationListTable(ctx context.Context, c client.Reader, namespace s
service[s.Name] = s
}
if len(a.Spec.Components) == 0 {
if AllNamespace {
table.AddRow(a.Namespace, a.Name, "", "", "", a.Status.Phase, "", "", a.CreationTimestamp)
} else {
table.AddRow(a.Name, "", "", "", a.Status.Phase, "", "", a.CreationTimestamp)
}
continue
}
for idx, cmp := range a.Spec.Components {
var appName = a.Name
if idx > 0 {

View File

@@ -95,7 +95,6 @@ func LoadPodDetail(cfg *rest.Config, pod *v1.Pod) Pod {
podInfo := Pod{
Name: pod.Name,
Namespace: pod.Namespace,
Cluster: pod.ClusterName,
Ready: readyContainerNum(pod),
Status: string(pod.Status.Phase),
Age: utils.TimeFormat(time.Since(pod.CreationTimestamp.Time)),

View File

@@ -256,13 +256,7 @@ func GetServiceEndpoints(ctx context.Context, appName string, namespace string,
"appName": appName,
"appNs": namespace,
}
if f.Component != "" {
params["name"] = f.Component
}
if f.Cluster != "" && f.ClusterNamespace != "" {
params["cluster"] = f.Cluster
params["clusterNs"] = f.ClusterNamespace
}
setFilterParams(f, params)
velaQL := MakeVelaQL("service-endpoints-view", params, "status")
queryView, err := velaql.ParseVelaQL(velaQL)
@@ -292,13 +286,7 @@ func GetApplicationPods(ctx context.Context, appName string, namespace string, v
"appName": appName,
"appNs": namespace,
}
if f.Component != "" {
params["name"] = f.Component
}
if f.Cluster != "" && f.ClusterNamespace != "" {
params["cluster"] = f.Cluster
params["clusterNs"] = f.ClusterNamespace
}
setFilterParams(f, params)
velaQL := MakeVelaQL("component-pod-view", params, "status")
queryView, err := velaql.ParseVelaQL(velaQL)
@@ -328,13 +316,7 @@ func GetApplicationServices(ctx context.Context, appName string, namespace strin
"appName": appName,
"appNs": namespace,
}
if f.Component != "" {
params["name"] = f.Component
}
if f.Cluster != "" && f.ClusterNamespace != "" {
params["cluster"] = f.Cluster
params["clusterNs"] = f.ClusterNamespace
}
setFilterParams(f, params)
velaQL := MakeVelaQL("component-service-view", params, "status")
queryView, err := velaql.ParseVelaQL(velaQL)
if err != nil {
@@ -357,6 +339,20 @@ func GetApplicationServices(ctx context.Context, appName string, namespace strin
return response.Services, nil
}
// setFilterParams will convert Filter fields to velaQL params
func setFilterParams(f Filter, params map[string]string) {
if f.Component != "" {
params["name"] = f.Component
}
if f.Cluster != "" {
params["cluster"] = f.Cluster
}
if f.ClusterNamespace != "" {
params["clusterNs"] = f.ClusterNamespace
}
}
// QueryValue get queryValue from velaQL
func QueryValue(ctx context.Context, velaC common.Args, queryView *velaql.QueryView) (*value.Value, error) {
dm, err := velaC.GetDiscoveryMapper()

View File

@@ -28,4 +28,30 @@ spec:
env:
key_for_nginx_first: value_first
key_for_nginx_second: value_second
```
```yaml
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: busybox
spec:
components:
- name: busybox
type: webservice
properties:
image: busybox
cmd: ["sleep", "86400"]
traits:
- type: sidecar
properties:
name: sidecar-nginx
image: nginx
- type: env
properties:
# you can use env to control one container, if containerName not specified, it will patch on the first index container
containerName: busybox
env:
key_for_busybox_first: value_first
key_for_busybox_second: value_second
```

View File

@@ -24,19 +24,15 @@ import (
"sort"
"strings"
"github.com/kubevela/workflow/pkg/cue/packages"
"github.com/pkg/errors"
"golang.org/x/text/cases"
"golang.org/x/text/language"
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
"k8s.io/klog/v2"
"github.com/pkg/errors"
"github.com/kubevela/workflow/pkg/cue/packages"
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/cue"
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
"github.com/oam-dev/kubevela/pkg/utils/common"
)

View File

@@ -156,8 +156,66 @@ func (ref *ParseReference) prepareConsoleParameter(tableName string, parameterLi
return ConsoleReference{TableName: tableName, TableObject: table}
}
// parseParameters parses every parameter
// TODO(wonderflowe2e/plugin/plugin_test.go:122): refactor the code to reduce the complexity
func cueValue2Ident(val cue.Value) *ast.Ident {
var ident *ast.Ident
if source, ok := val.Source().(*ast.Ident); ok {
ident = source
}
if source, ok := val.Source().(*ast.Field); ok {
if v, ok := source.Value.(*ast.Ident); ok {
ident = v
}
}
return ident
}
func getIndentName(val cue.Value) string {
ident := cueValue2Ident(val)
if ident != nil && len(ident.Name) != 0 {
return strings.TrimPrefix(ident.Name, "#")
}
return val.IncompleteKind().String()
}
func getConcreteOrValueType(val cue.Value) string {
op, elements := val.Expr()
if op != cue.OrOp {
return val.IncompleteKind().String()
}
var printTypes []string
for _, ev := range elements {
incompleteKind := ev.IncompleteKind().String()
if !ev.IsConcrete() {
return incompleteKind
}
ident := cueValue2Ident(ev)
if ident != nil && len(ident.Name) != 0 {
printTypes = append(printTypes, strings.TrimPrefix(ident.Name, "#"))
} else {
// only convert string in `or` operator for now
opName, err := ev.String()
if err != nil {
return incompleteKind
}
opName = `"` + opName + `"`
printTypes = append(printTypes, opName)
}
}
return strings.Join(printTypes, " or ")
}
func getSuffix(capName string, containSuffix bool) (string, string) {
var suffixTitle = " (" + capName + ")"
var suffixRef = "-" + strings.ToLower(capName)
if !containSuffix || capName == "" {
suffixTitle = ""
suffixRef = ""
}
return suffixTitle, suffixRef
}
// parseParameters parses every parameter to docs
// TODO(wonderflow): refactor the code to reduce the complexity
// nolint:staticcheck,gocyclo
func (ref *ParseReference) parseParameters(capName string, paraValue cue.Value, paramKey string, depth int, containSuffix bool) (string, []ConsoleReference, error) {
var doc string
@@ -167,19 +225,15 @@ func (ref *ParseReference) parseParameters(capName string, paraValue cue.Value,
if !paraValue.Exists() {
return "", console, nil
}
suffixTitle, suffixRef := getSuffix(capName, containSuffix)
var suffixTitle = " (" + capName + ")"
var suffixRef = "-" + strings.ToLower(capName)
if !containSuffix || capName == "" {
suffixTitle = ""
suffixRef = ""
}
switch paraValue.Kind() {
case cue.StructKind:
arguments, err := paraValue.Struct()
if err != nil {
return "", nil, fmt.Errorf("arguments not defined as struct %w", err)
return "", nil, fmt.Errorf("field %s not defined as struct %w", paramKey, err)
}
if arguments.Len() == 0 {
var param ReferenceParameter
param.Name = "\\-"
@@ -200,7 +254,7 @@ func (ref *ParseReference) parseParameters(capName string, paraValue cue.Value,
continue
}
val := fi.Value
name := fi.Name
name := fi.Selector
param.Name = name
if def, ok := val.Default(); ok && def.IsConcrete() {
param.Default = velacue.GetDefault(def)
@@ -212,19 +266,18 @@ func (ref *ParseReference) parseParameters(capName string, paraValue cue.Value,
case cue.StructKind:
if subField, err := val.Struct(); err == nil && subField.Len() == 0 { // err cannot be not nil,so ignore it
if mapValue, ok := val.Elem(); ok {
var ident *ast.Ident
if source, ok := mapValue.Source().(*ast.Ident); ok {
ident = source
}
if source, ok := mapValue.Source().(*ast.Field); ok {
if v, ok := source.Value.(*ast.Ident); ok {
ident = v
indentName := getIndentName(mapValue)
_, err := mapValue.Fields()
if err == nil {
subDoc, subConsole, err := ref.parseParameters(capName, mapValue, indentName, depth+1, containSuffix)
if err != nil {
return "", nil, err
}
}
if ident != nil && len(ident.Name) != 0 {
param.PrintableType = fmt.Sprintf("map[string]:%s", ident.Name)
param.PrintableType = fmt.Sprintf("map[string]%s(#%s%s)", indentName, strings.ToLower(indentName), suffixRef)
doc += subDoc
console = append(console, subConsole...)
} else {
param.PrintableType = fmt.Sprintf("map[string]:%s", mapValue.IncompleteKind().String())
param.PrintableType = "map[string]" + mapValue.IncompleteKind().String()
}
} else {
param.PrintableType = val.IncompleteKind().String()
@@ -234,7 +287,10 @@ func (ref *ParseReference) parseParameters(capName string, paraValue cue.Value,
if op == cue.OrOp {
var printTypes []string
for idx, ev := range elements {
opName := fmt.Sprintf("%s-option-%d", name, idx)
opName := getIndentName(ev)
if opName == "struct" {
opName = fmt.Sprintf("type-option-%d", idx+1)
}
subDoc, subConsole, err := ref.parseParameters(capName, ev, opName, depth+1, containSuffix)
if err != nil {
return "", nil, err
@@ -276,16 +332,35 @@ func (ref *ParseReference) parseParameters(capName string, paraValue cue.Value,
param.PrintableType = fmt.Sprintf("[]%s", elem.IncompleteKind().String())
}
default:
param.PrintableType = param.Type.String()
param.PrintableType = getConcreteOrValueType(val)
}
params = append(params, param)
}
default:
var param ReferenceParameter
// TODO better composition type handling, see command trait.
param.Name = "--"
param.Usage = "Composition type"
param.PrintableType = extractTypeFromError(paraValue)
op, elements := paraValue.Expr()
if op == cue.OrOp {
var printTypes []string
for idx, ev := range elements {
opName := getIndentName(ev)
if opName == "struct" {
opName = fmt.Sprintf("type-option-%d", idx+1)
}
subDoc, subConsole, err := ref.parseParameters(capName, ev, opName, depth+1, containSuffix)
if err != nil {
return "", nil, err
}
printTypes = append(printTypes, fmt.Sprintf("[%s](#%s%s)", opName, strings.ToLower(opName), suffixRef))
doc += subDoc
console = append(console, subConsole...)
}
param.PrintableType = strings.Join(printTypes, " or ")
} else {
// TODO more composition type to be handle here
param.Name = "--"
param.Usage = "Unsupported Composition Type"
param.PrintableType = extractTypeFromError(paraValue)
}
params = append(params, param)
}

View File

@@ -19,7 +19,6 @@ package docgen
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"reflect"
@@ -562,34 +561,272 @@ func TestParseLocalFile(t *testing.T) {
}
func TestExtractParameter(t *testing.T) {
ref := &ConsoleReference{}
cueTemplate := `
parameter: {
testcases := map[string]struct {
cueTemplate string
contains string
}{
"normal-case": {
cueTemplate: `parameter: {
str: string
itr: int
btr: bool
ct: cts: string
}`,
contains: `### normal-case
Name | Description | Type | Required | Default
---- | ----------- | ---- | -------- | -------
str | | string | true |
itr | | int | true |
btr | | bool | true |
ct | | [ct](#ct) | true |
#### ct
Name | Description | Type | Required | Default
---- | ----------- | ---- | -------- | -------
cts | | string | true |`,
},
"normal-map-string-string": {
cueTemplate: `parameter: {
envMappings: [string]: string
}`,
contains: `### normal-map-string-string
Name | Description | Type | Required | Default
---- | ----------- | ---- | -------- | -------
envMappings | | map[string]string | true |`,
},
"normal-map-case": {
cueTemplate: `parameter: {
// +usage=The mapping of environment variables to secret
envMappings: [string]: #KeySecret
}
#KeySecret: {
key?: string
secret: string
}
`
oldStdout := os.Stdout
defer func() {
os.Stdout = oldStdout
}()
}`,
contains: `### normal-map-case
r, w, _ := os.Pipe()
os.Stdout = w
cueValue, _ := common.GetCUEParameterValue(cueTemplate, nil)
defaultDepth := 0
defaultDisplay := "console"
ref.DisplayFormat = defaultDisplay
_, console, err := ref.parseParameters("", cueValue, Specification, defaultDepth, false)
assert.NoError(t, err)
assert.Equal(t, 1, len(console))
console[0].TableObject.Render()
err = w.Close()
assert.NoError(t, err)
out, _ := ioutil.ReadAll(r)
assert.Contains(t, string(out), "map[string]:#KeySecret")
Name | Description | Type | Required | Default
---- | ----------- | ---- | -------- | -------
envMappings | The mapping of environment variables to secret. | map[string]KeySecret(#keysecret) | true |
#### KeySecret
Name | Description | Type | Required | Default
---- | ----------- | ---- | -------- | -------
key | | string | false |
secret | | string | true |`,
},
"or-case-with-type": {
cueTemplate: ` parameter: {
orValue: #KeyConfig | #KeySecret
}
#KeySecret: {
key: "abc"
secret: string
}
#KeyConfig: {
key: "abc"
config: string
}`,
contains: `### or-case-with-type
Name | Description | Type | Required | Default
---- | ----------- | ---- | -------- | -------
orValue | | [KeyConfig](#keyconfig) or [KeySecret](#keysecret) | true |
#### KeyConfig
Name | Description | Type | Required | Default
---- | ----------- | ---- | -------- | -------
key | | string | true |
config | | string | true |
#### KeySecret
Name | Description | Type | Required | Default
---- | ----------- | ---- | -------- | -------
key | | string | true |
secret | | string | true | `,
},
"or-type-with-const-str": {
cueTemplate: `parameter: {
type: *"configMap" | "secret" | "emptyDir" | "ephemeral"
}`,
contains: `### or-type-with-const-str
Name | Description | Type | Required | Default
---- | ----------- | ---- | -------- | -------
type | | "configMap" or "secret" or "emptyDir" or "ephemeral" | false | configMap`,
},
"or-type-with-const-and-string": {
cueTemplate: `parameter: {
type: *"configMap" | "secret" | "emptyDir" | string
}`,
contains: `### or-type-with-const-and-string
Name | Description | Type | Required | Default
---- | ----------- | ---- | -------- | -------
type | | string | false | configMap`,
},
"var-or-with-struct-var": {
cueTemplate: `
parameter: {
orValue: KeyConfig | KeySecret
}
KeySecret: {
key: "abc"
secret: string
}
KeyConfig: {
key: "abc"
config: string
}`,
contains: `### var-or-with-struct-var
Name | Description | Type | Required | Default
---- | ----------- | ---- | -------- | -------
orValue | | [KeyConfig](#keyconfig) or [KeySecret](#keysecret) | true |
#### KeyConfig
Name | Description | Type | Required | Default
---- | ----------- | ---- | -------- | -------
key | | string | true |
config | | string | true |
#### KeySecret
Name | Description | Type | Required | Default
---- | ----------- | ---- | -------- | -------
key | | string | true |
secret | | string | true | `,
},
}
ref := &MarkdownReference{}
for key, ca := range testcases {
cueValue, _ := common.GetCUEParameterValue(ca.cueTemplate, nil)
out, _, err := ref.parseParameters("", cueValue, key, 0, false)
assert.NoError(t, err, key)
assert.Contains(t, out, ca.contains, key)
}
}
func TestExtractParameterFromFiles(t *testing.T) {
ref := &MarkdownReference{}
testcases := map[string]struct {
path string
contains string
}{
"env": {
path: "testdata/parameter/env.cue",
contains: `### env
Name | Description | Type | Required | Default
---- | ----------- | ---- | -------- | -------
| | [PatchParams](#patchparams) or [type-option-2](#type-option-2) | false |
#### PatchParams
Name | Description | Type | Required | Default
---- | ----------- | ---- | -------- | -------
containerName | Specify the name of the target container, if not set, use the component name. | string | false | empty
replace | Specify if replacing the whole environment settings for the container. | bool | false | false
env | Specify the environment variables to merge, if key already existing, override its value. | map[string]string | true |
unset | Specify which existing environment variables to unset. | []string | true |
#### type-option-2
Name | Description | Type | Required | Default
---- | ----------- | ---- | -------- | -------
containers | Specify the environment variables for multiple containers. | [[]containers](#containers) | true |
##### containers
Name | Description | Type | Required | Default
---- | ----------- | ---- | -------- | -------
containerName | Specify the name of the target container, if not set, use the component name. | string | false | empty
replace | Specify if replacing the whole environment settings for the container. | bool | false | false
env | Specify the environment variables to merge, if key already existing, override its value. | map[string]string | true |
unset | Specify which existing environment variables to unset. | []string | true |`,
},
"command": {
path: "testdata/parameter/command.cue",
contains: `### command
Name | Description | Type | Required | Default
---- | ----------- | ---- | -------- | -------
| | [PatchParams](#patchparams) or [type-option-2](#type-option-2) | false |
#### PatchParams
Name | Description | Type | Required | Default
---- | ----------- | ---- | -------- | -------
containerName | Specify the name of the target container, if not set, use the component name. | string | false | empty
command | Specify the command to use in the target container, if not set, it will not be changed. | null | true |
args | Specify the args to use in the target container, if set, it will override existing args. | null | true |
addArgs | Specify the args to add in the target container, existing args will be kept, cannot be used with args. | null | true |
delArgs | Specify the existing args to delete in the target container, cannot be used with args. | null | true |
#### type-option-2
Name | Description | Type | Required | Default
---- | ----------- | ---- | -------- | -------
containers | Specify the commands for multiple containers. | [[]containers](#containers) | true |
##### containers
Name | Description | Type | Required | Default
---- | ----------- | ---- | -------- | -------
containerName | Specify the name of the target container, if not set, use the component name. | string | false | empty
command | Specify the command to use in the target container, if not set, it will not be changed. | null | true |
args | Specify the args to use in the target container, if set, it will override existing args. | null | true |
addArgs | Specify the args to add in the target container, existing args will be kept, cannot be used with args. | null | true |
delArgs | Specify the existing args to delete in the target container, cannot be used with args. | null | true |`,
},
"condition": {
path: "testdata/parameter/condition.cue",
contains: `### condition
Name | Description | Type | Required | Default
---- | ----------- | ---- | -------- | -------
volumes | | [[]volumes](#volumes) | true |
#### volumes
Name | Description | Type | Required | Default
---- | ----------- | ---- | -------- | -------
name | | string | true |
defaultMode | only works when type equals configmap. | int | false | 420
type | | "configMap" or "secret" or "emptyDir" or "ephemeral" | false | configMap`,
},
}
for key, ca := range testcases {
content, err := os.ReadFile(ca.path)
assert.NoError(t, err, ca.path)
cueValue, _ := common.GetCUEParameterValue(string(content), nil)
out, _, err := ref.parseParameters("", cueValue, key, 0, false)
assert.NoError(t, err, key)
assert.Contains(t, out, ca.contains, key)
}
}

View File

@@ -0,0 +1,17 @@
#PatchParams: {
// +usage=Specify the name of the target container, if not set, use the component name
containerName: *"" | string
// +usage=Specify the command to use in the target container, if not set, it will not be changed
command: *null | [...string]
// +usage=Specify the args to use in the target container, if set, it will override existing args
args: *null | [...string]
// +usage=Specify the args to add in the target container, existing args will be kept, cannot be used with args
addArgs: *null | [...string]
// +usage=Specify the existing args to delete in the target container, cannot be used with args
delArgs: *null | [...string]
}
parameter: *#PatchParams | close({
// +usage=Specify the commands for multiple containers
containers: [...#PatchParams]
})

View File

@@ -0,0 +1,10 @@
parameter: {
volumes: [...{
name: string
type: *"configMap" | "secret" | "emptyDir" | "ephemeral"
if type == "configMap" {
//+usage=only works when type equals configmap
defaultMode: *420 | int
}},
]
}

View File

@@ -0,0 +1,14 @@
#PatchParams: {
// +usage=Specify the name of the target container, if not set, use the component name
containerName: *"" | string
// +usage=Specify if replacing the whole environment settings for the container
replace: *false | bool
// +usage=Specify the environment variables to merge, if key already existing, override its value
env: [string]: string
// +usage=Specify which existing environment variables to unset
unset: *[] | [...string]
}
parameter: *#PatchParams | close({
// +usage=Specify the environment variables for multiple containers
containers: [...#PatchParams]
})

View File

@@ -49,6 +49,8 @@ rules:
- apiGroups:
- "apps"
resources:
- statefulsets
- statefulsets/status
- deployments
- deployments/status
- controllerrevisions

View File

@@ -18,14 +18,16 @@ package e2e_multicluster_test
import (
"context"
"io/ioutil"
"io"
"net/http"
"os"
"os/exec"
"strings"
"time"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gbytes"
"github.com/onsi/gomega/gexec"
appsv1 "k8s.io/api/apps/v1"
v1 "k8s.io/api/core/v1"
@@ -36,6 +38,7 @@ import (
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/oam/util"
)
var _ = Describe("Test multicluster CLI commands", func() {
@@ -48,7 +51,7 @@ var _ = Describe("Test multicluster CLI commands", func() {
BeforeEach(func() {
hubCtx, workerCtx, namespace = initializeContextAndNamespace()
app = &v1beta1.Application{}
bs, err := ioutil.ReadFile("./testdata/app/example-vela-cli-tool-test-app.yaml")
bs, err := os.ReadFile("./testdata/app/example-vela-cli-tool-test-app.yaml")
Expect(err).Should(Succeed())
appYaml := strings.ReplaceAll(string(bs), "TEST_NAMESPACE", namespace)
Expect(yaml.Unmarshal([]byte(appYaml), app)).Should(Succeed())
@@ -87,7 +90,7 @@ var _ = Describe("Test multicluster CLI commands", func() {
go func() {
defer GinkgoRecover()
command := exec.Command("vela", "port-forward", app.Name, "-n", namespace)
session, err := gexec.Start(command, ioutil.Discard, ioutil.Discard)
session, err := gexec.Start(command, io.Discard, io.Discard)
Expect(err).Should(Succeed())
<-stopChannel
session.Terminate()
@@ -113,7 +116,7 @@ var _ = Describe("Test multicluster CLI commands", func() {
for _, format := range []string{"inline", "wide", "table", "list"} {
outputs, err := execCommand("status", app.Name, "-n", namespace, "--tree", "--detail", "--detail-format", format)
Expect(err).Should(Succeed())
Expect(string(outputs)).Should(SatisfyAll(
Expect(outputs).Should(SatisfyAll(
ContainSubstring("alias-worker-tree"),
ContainSubstring("Deployment/exec-podinfo"),
ContainSubstring("updated"),
@@ -121,6 +124,38 @@ var _ = Describe("Test multicluster CLI commands", func() {
))
}
})
It("Test vela logs", func() {
var (
err error
session *gexec.Session
waitingTime = 2 * time.Minute
)
podViewCMFile, err := os.ReadFile("./testdata/view/component-pod-view.yaml")
Expect(err).Should(BeNil())
podViewCM := &v1.ConfigMap{}
Expect(yaml.Unmarshal(podViewCMFile, podViewCM)).Should(BeNil())
Expect(k8sClient.Create(hubCtx, podViewCM)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
stopChannel := make(chan struct{}, 1)
defer func() {
stopChannel <- struct{}{}
}()
command := exec.Command("vela", "logs", app.Name, "-n", namespace, "--cluster", WorkerClusterName)
session, err = gexec.Start(command, nil, nil)
Expect(err).Should(Succeed())
go func() {
defer GinkgoRecover()
<-stopChannel
session.Terminate()
Eventually(session, 10*time.Second).Should(gexec.Exit())
}()
Expect(err).Should(Succeed())
Eventually(session, waitingTime).ShouldNot(BeNil())
Eventually(session, waitingTime).Should(gbytes.Say("exec-podinfo"))
Eventually(session, waitingTime).Should(gbytes.Say("httpd started"))
})
})
})

View File

@@ -714,5 +714,87 @@ var _ = Describe("Test multicluster scenario", func() {
Expect(cm.Data["cluster"]).Should(Equal("cluster-worker"))
Expect(k8sClient.Delete(hubCtx, def)).Should(Succeed())
})
It("Test application with failed gc and restart workflow", func() {
By("duplicate cluster")
secret := &corev1.Secret{}
const secretName = "disconnection-test"
Expect(k8sClient.Get(hubCtx, types.NamespacedName{Namespace: kubevelatypes.DefaultKubeVelaNS, Name: WorkerClusterName}, secret)).Should(Succeed())
secret.SetName(secretName)
secret.SetResourceVersion("")
Expect(k8sClient.Create(hubCtx, secret)).Should(Succeed())
defer func() {
_ = k8sClient.Delete(hubCtx, secret)
}()
By("create cluster normally")
bs, err := os.ReadFile("./testdata/app/app-disconnection-test.yaml")
Expect(err).Should(Succeed())
app := &v1beta1.Application{}
Expect(yaml.Unmarshal(bs, app)).Should(Succeed())
app.SetNamespace(namespace)
Expect(k8sClient.Create(hubCtx, app)).Should(Succeed())
key := client.ObjectKeyFromObject(app)
Eventually(func(g Gomega) {
g.Expect(k8sClient.Get(hubCtx, key, app)).Should(Succeed())
g.Expect(app.Status.Phase).Should(Equal(common.ApplicationRunning))
}).WithTimeout(10 * time.Second).WithPolling(2 * time.Second).Should(Succeed())
By("disconnect cluster")
Expect(k8sClient.Get(hubCtx, types.NamespacedName{Namespace: kubevelatypes.DefaultKubeVelaNS, Name: secretName}, secret)).Should(Succeed())
secret.Data["endpoint"] = []byte("https://1.2.3.4:9999")
Expect(k8sClient.Update(hubCtx, secret)).Should(Succeed())
By("update application")
Expect(k8sClient.Get(hubCtx, key, app)).Should(Succeed())
app.Spec.Policies = nil
Expect(k8sClient.Update(hubCtx, app)).Should(Succeed())
Eventually(func(g Gomega) {
g.Expect(k8sClient.Get(hubCtx, key, app)).Should(Succeed())
g.Expect(app.Status.ObservedGeneration).Should(Equal(app.Generation))
g.Expect(app.Status.Phase).Should(Equal(common.ApplicationRunning))
rts := &v1beta1.ResourceTrackerList{}
g.Expect(k8sClient.List(hubCtx, rts, client.MatchingLabels{oam.LabelAppName: key.Name, oam.LabelAppNamespace: key.Namespace})).Should(Succeed())
cnt := 0
for _, item := range rts.Items {
if item.Spec.Type == v1beta1.ResourceTrackerTypeVersioned {
cnt++
}
}
g.Expect(cnt).Should(Equal(2))
}).WithTimeout(10 * time.Second).WithPolling(2 * time.Second).Should(Succeed())
By("try update application again")
Expect(k8sClient.Get(hubCtx, key, app)).Should(Succeed())
if app.Annotations == nil {
app.Annotations = map[string]string{}
}
app.Annotations[oam.AnnotationPublishVersion] = "test"
Expect(k8sClient.Update(hubCtx, app)).Should(Succeed())
Eventually(func(g Gomega) {
g.Expect(k8sClient.Get(hubCtx, key, app)).Should(Succeed())
g.Expect(app.Status.LatestRevision).ShouldNot(BeNil())
g.Expect(app.Status.LatestRevision.Revision).Should(Equal(int64(3)))
g.Expect(app.Status.ObservedGeneration).Should(Equal(app.Generation))
g.Expect(app.Status.Phase).Should(Equal(common.ApplicationRunning))
}).WithTimeout(1 * time.Minute).WithPolling(2 * time.Second).Should(Succeed())
By("clear disconnection cluster secret")
Expect(k8sClient.Get(hubCtx, types.NamespacedName{Namespace: kubevelatypes.DefaultKubeVelaNS, Name: secretName}, secret)).Should(Succeed())
Expect(k8sClient.Delete(hubCtx, secret)).Should(Succeed())
By("wait gc application completed")
Eventually(func(g Gomega) {
rts := &v1beta1.ResourceTrackerList{}
g.Expect(k8sClient.List(hubCtx, rts, client.MatchingLabels{oam.LabelAppName: key.Name, oam.LabelAppNamespace: key.Namespace})).Should(Succeed())
cnt := 0
for _, item := range rts.Items {
if item.Spec.Type == v1beta1.ResourceTrackerTypeVersioned {
cnt++
}
}
g.Expect(cnt).Should(Equal(1))
}).WithTimeout(30 * time.Second).WithPolling(2 * time.Second).Should(Succeed())
})
})
})

View File

@@ -0,0 +1,17 @@
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: app-disconnection-test
spec:
components:
- type: k8s-objects
name: app-dis-cm
properties:
objects:
- apiVersion: v1
kind: ConfigMap
policies:
- type: topology
name: disconnection-test
properties:
clusters: ["disconnection-test"]

View File

@@ -0,0 +1,75 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: component-pod-view
namespace: vela-system
data:
template: |
import (
"vela/ql"
)
parameter: {
appName: string
appNs: string
name?: string
cluster?: string
clusterNs?: string
}
result: ql.#CollectPods & {
app: {
name: parameter.appName
namespace: parameter.appNs
filter: {
if parameter.cluster != _|_ {
cluster: parameter.cluster
}
if parameter.clusterNs != _|_ {
clusterNamespace: parameter.clusterNs
}
if parameter.name != _|_ {
components: [parameter.name]
}
}
}
}
if result.err == _|_ {
status: {
podList: [ for pod in result.list if pod.object != _|_ {
cluster: pod.cluster
workload: pod.workload
component: pod.component
metadata: {
name: pod.object.metadata.name
namespace: pod.object.metadata.namespace
creationTime: pod.object.metadata.creationTimestamp
labels: pod.object.metadata.labels
version: {
if pod.publishVersion != _|_ {
publishVersion: pod.publishVersion
}
if pod.deployVersion != _|_ {
deployVersion: pod.deployVersion
}
}
}
status: {
phase: pod.object.status.phase
// refer to https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-phase
if phase != "Pending" && phase != "Unknown" {
podIP: pod.object.status.podIP
hostIP: pod.object.status.hostIP
nodeName: pod.object.spec.nodeName
}
}
}]
}
}
if result.err != _|_ {
status: {
error: result.err
}
}