mirror of https://github.com/kubevela/kubevela.git
Feat: Support kruise rollout (#4243)
* Feat: support kruise rollout Signed-off-by: Somefive <yd219913@alibaba-inc.com> resolve roll back fix add tests Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com> small fix * fix rollback Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com> topology filter by owner reference Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com> fix ci Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com> add comments Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com> fix imports Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com> fix lint * rollback related tests Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com> rename the operator Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com> fix bugs Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com> fix test Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com> fix test * clean args before start Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com> fix test * remove replace go mod Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com> * fix operation tests Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com> Co-authored-by: Somefive <yd219913@alibaba-inc.com>
This commit is contained in:
parent
dc660fc97d
commit
c4e1f39d28
20
go.mod
20
go.mod
|
|
@ -105,6 +105,18 @@ require (
|
|||
sigs.k8s.io/yaml v1.3.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/docker/distribution v2.8.1+incompatible // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.0 // indirect
|
||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
|
||||
github.com/openkruise/rollouts v0.1.1-0.20220622054609-149e5a48da5e
|
||||
github.com/xanzy/ssh-agent v0.3.0 // indirect
|
||||
golang.org/x/net v0.0.0-20220516155154-20f960328961 // indirect
|
||||
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect
|
||||
google.golang.org/protobuf v1.28.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go/compute v1.6.1 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||
|
|
@ -148,7 +160,6 @@ require (
|
|||
github.com/creack/pty v1.1.11 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.2.3 // indirect
|
||||
github.com/docker/cli v20.10.16+incompatible // indirect
|
||||
github.com/docker/distribution v2.8.1+incompatible // indirect
|
||||
github.com/docker/docker v20.10.16+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.6.4 // indirect
|
||||
github.com/docker/go-connections v0.4.0 // indirect
|
||||
|
|
@ -190,8 +201,6 @@ require (
|
|||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.0 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/huandu/xstrings v1.3.2 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
|
|
@ -201,7 +210,6 @@ require (
|
|||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
|
||||
github.com/klauspost/compress v1.15.4 // indirect
|
||||
github.com/kr/pretty v0.3.0 // indirect
|
||||
github.com/kr/pty v1.1.8 // indirect
|
||||
|
|
@ -254,7 +262,6 @@ require (
|
|||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
github.com/tjfoc/gmsm v1.3.2 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.0 // indirect
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||
github.com/xdg-go/scram v1.0.2 // indirect
|
||||
github.com/xdg-go/stringprep v1.0.2 // indirect
|
||||
|
|
@ -281,16 +288,13 @@ require (
|
|||
go.uber.org/atomic v1.7.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
|
||||
golang.org/x/net v0.0.0-20220516155154-20f960328961 // indirect
|
||||
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 // indirect
|
||||
golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3 // indirect
|
||||
google.golang.org/grpc v1.45.0 // indirect
|
||||
google.golang.org/protobuf v1.28.0 // indirect
|
||||
gopkg.in/gorp.v1 v1.7.2 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/ini.v1 v1.63.2 // indirect
|
||||
|
|
|
|||
33
go.sum
33
go.sum
|
|
@ -1608,6 +1608,8 @@ github.com/onsi/ginkgo v1.16.1/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvw
|
|||
github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E=
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
|
||||
github.com/onsi/ginkgo/v2 v2.1.4 h1:GNapqRSid3zijZ9H77KrgVG4/8KqiyRsxcSxe+7ApXY=
|
||||
|
|
@ -1627,6 +1629,7 @@ github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDs
|
|||
github.com/onsi/gomega v1.11.0/go.mod h1:azGKhqFUon9Vuj0YmTfLSmx0FUwqXYSTl5re8lQLTUg=
|
||||
github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY=
|
||||
github.com/onsi/gomega v1.14.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0=
|
||||
github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0=
|
||||
github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||
github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
|
||||
|
|
@ -1662,8 +1665,11 @@ github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqi
|
|||
github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo=
|
||||
github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8=
|
||||
github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
|
||||
github.com/openkruise/kruise-api v1.0.0/go.mod h1:kxV/UA/vrf/hz3z+kL21c0NOawC6K1ZjaKcJFgiOwsE=
|
||||
github.com/openkruise/kruise-api v1.1.0 h1:ZRhV0FnxUp4XHc60YPkUqj2LJD4GRFB92qhtdgU6Zhc=
|
||||
github.com/openkruise/kruise-api v1.1.0/go.mod h1:kxV/UA/vrf/hz3z+kL21c0NOawC6K1ZjaKcJFgiOwsE=
|
||||
github.com/openkruise/rollouts v0.1.1-0.20220622054609-149e5a48da5e h1:jUMEDsA0OOpp0262pK8MV8M2glac+jIjx+q5Aydn6G0=
|
||||
github.com/openkruise/rollouts v0.1.1-0.20220622054609-149e5a48da5e/go.mod h1:SORsT96ssCqMJYSVA90v6Z52utlV2jxPlyGh4czRfHA=
|
||||
github.com/openshift/api v0.0.0-20210915110300-3cd8091317c4/go.mod h1:RsQCVJu4qhUawxxDP7pGlwU3IA4F01wYm3qKEu29Su8=
|
||||
github.com/openshift/api v0.0.0-20211209135129-c58d9f695577/go.mod h1:DoslCwtqUpr3d/gsbq4ZlkaMEdYqKxuypsDjorcHhME=
|
||||
github.com/openshift/build-machinery-go v0.0.0-20210115170933-e575b44a7a94/go.mod h1:b1BuldmJlbA/xYtdZvKi+7j5YGB44qJUJDZ9zwiNCfE=
|
||||
|
|
@ -2199,6 +2205,7 @@ go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
|||
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
||||
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
|
||||
go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
|
||||
go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
|
||||
go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI=
|
||||
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
|
||||
golang.org/x/arch v0.0.0-20180920145803-b19384d3c130/go.mod h1:cYlCBUl1MsqxdiKgmc4uh7TxZfWSFLOGSRR090WDxt8=
|
||||
|
|
@ -2375,6 +2382,7 @@ golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qx
|
|||
golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211215060638-4ddde0e984e9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
|
|
@ -2547,6 +2555,7 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
|
@ -3073,7 +3082,10 @@ k8s.io/api v0.21.1/go.mod h1:FstGROTmsSHBarKc8bylzXih8BLNYTiS3TZcsoEDg2s=
|
|||
k8s.io/api v0.21.2/go.mod h1:Lv6UGJZ1rlMI1qusN8ruAp9PUBFyBwpEHAdG24vIsiU=
|
||||
k8s.io/api v0.21.3/go.mod h1:hUgeYHUbBp23Ue4qdX9tR8/ANi/g3ehylAqDn9NWVOg=
|
||||
k8s.io/api v0.22.1/go.mod h1:bh13rkTp3F1XEaLGykbyRD2QaTTzPm0e/BMd8ptFONY=
|
||||
k8s.io/api v0.22.2/go.mod h1:y3ydYpLJAaDI+BbSe2xmGcqxiWHmWjkEeIbiwHvnPR8=
|
||||
k8s.io/api v0.22.4/go.mod h1:Rgs+9gIGYC5laXQSZZ9JqT5NevNgoGiOdVWi1BAB3qk=
|
||||
k8s.io/api v0.22.4/go.mod h1:Rgs+9gIGYC5laXQSZZ9JqT5NevNgoGiOdVWi1BAB3qk=
|
||||
k8s.io/api v0.22.6/go.mod h1:q1F7IfaNrbi/83ebLy3YFQYLjPSNyunZ/IXQxMmbwCg=
|
||||
k8s.io/api v0.23.0/go.mod h1:8wmDdLBHBNxtOIytwLstXt5E9PddnZb0GaMcqsvDBpg=
|
||||
k8s.io/api v0.23.1/go.mod h1:WfXnOnwSqNtG62Y1CdjoMxh7r7u9QXGCkA1u0na2jgo=
|
||||
k8s.io/api v0.23.5/go.mod h1:Na4XuKng8PXJ2JsploYYrivXrINeTaycCGcYgF91Xm8=
|
||||
|
|
@ -3089,7 +3101,9 @@ k8s.io/apiextensions-apiserver v0.18.6/go.mod h1:lv89S7fUysXjLZO7ke783xOwVTm6lKi
|
|||
k8s.io/apiextensions-apiserver v0.21.2/go.mod h1:+Axoz5/l3AYpGLlhJDfcVQzCerVYq3K3CvDMvw6X1RA=
|
||||
k8s.io/apiextensions-apiserver v0.21.3/go.mod h1:kl6dap3Gd45+21Jnh6utCx8Z2xxLm8LGDkprcd+KbsE=
|
||||
k8s.io/apiextensions-apiserver v0.22.1/go.mod h1:HeGmorjtRmRLE+Q8dJu6AYRoZccvCMsghwS8XTUYb2c=
|
||||
k8s.io/apiextensions-apiserver v0.22.2/go.mod h1:2E0Ve/isxNl7tWLSUDgi6+cmwHi5fQRdwGVCxbC+KFA=
|
||||
k8s.io/apiextensions-apiserver v0.22.4/go.mod h1:kH9lxD8dbJ+k0ZizGET55lFgdGjO8t45fgZnCVdZEpw=
|
||||
k8s.io/apiextensions-apiserver v0.22.6/go.mod h1:wNsLwy8mfIkGThiv4Qq/Hy4qRazViKXqmH5pfYiRKyY=
|
||||
k8s.io/apiextensions-apiserver v0.23.0/go.mod h1:xIFAEEDlAZgpVBl/1VSjGDmLoXAWRG40+GsWhKhAxY4=
|
||||
k8s.io/apiextensions-apiserver v0.23.5/go.mod h1:ntcPWNXS8ZPKN+zTXuzYMeg731CP0heCTl6gYBxLcuQ=
|
||||
k8s.io/apiextensions-apiserver v0.23.6 h1:v58cQ6Z0/GK1IXYr+oW0fnYl52o9LTY0WgoWvI8uv5Q=
|
||||
|
|
@ -3119,7 +3133,9 @@ k8s.io/apimachinery v0.21.1/go.mod h1:jbreFvJo3ov9rj7eWT7+sYiRx+qZuCYXwWT1bcDswP
|
|||
k8s.io/apimachinery v0.21.2/go.mod h1:CdTY8fU/BlvAbJ2z/8kBwimGki5Zp8/fbVuLY8gJumM=
|
||||
k8s.io/apimachinery v0.21.3/go.mod h1:H/IM+5vH9kZRNJ4l3x/fXP/5bOPJaVP/guptnZPeCFI=
|
||||
k8s.io/apimachinery v0.22.1/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0=
|
||||
k8s.io/apimachinery v0.22.2/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0=
|
||||
k8s.io/apimachinery v0.22.4/go.mod h1:yU6oA6Gnax9RrxGzVvPFFJ+mpnW6PBSqp0sx0I0HHW0=
|
||||
k8s.io/apimachinery v0.22.6/go.mod h1:ZvVLP5iLhwVFg2Yx9Gh5W0um0DUauExbRhe+2Z8I1EU=
|
||||
k8s.io/apimachinery v0.23.0/go.mod h1:fFCTTBKvKcwTPFzjlcxp91uPFZr+JA0FubU4fLzzFYc=
|
||||
k8s.io/apimachinery v0.23.1/go.mod h1:SADt2Kl8/sttJ62RRsi9MIV4o8f5S3coArm0Iu3fBno=
|
||||
k8s.io/apimachinery v0.23.5/go.mod h1:BEuFMMBaIbcOqVIJqNZJXGFTP4W6AycEpb5+m/97hrM=
|
||||
|
|
@ -3139,7 +3155,10 @@ k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q=
|
|||
k8s.io/apiserver v0.21.2/go.mod h1:lN4yBoGyiNT7SC1dmNk0ue6a5Wi6O3SWOIw91TsucQw=
|
||||
k8s.io/apiserver v0.21.3/go.mod h1:eDPWlZG6/cCCMj/JBcEpDoK+I+6i3r9GsChYBHSbAzU=
|
||||
k8s.io/apiserver v0.22.1/go.mod h1:2mcM6dzSt+XndzVQJX21Gx0/Klo7Aen7i0Ai6tIa400=
|
||||
k8s.io/apiserver v0.22.2/go.mod h1:vrpMmbyjWrgdyOvZTSpsusQq5iigKNWv9o9KlDAbBHI=
|
||||
k8s.io/apiserver v0.22.4/go.mod h1:38WmcUZiiy41A7Aty8/VorWRa8vDGqoUzDf2XYlku0E=
|
||||
k8s.io/apiserver v0.22.4/go.mod h1:38WmcUZiiy41A7Aty8/VorWRa8vDGqoUzDf2XYlku0E=
|
||||
k8s.io/apiserver v0.22.6/go.mod h1:OlL1rGa2kKWGj2JEXnwBcul/BwC9Twe95gm4ohtiIIs=
|
||||
k8s.io/apiserver v0.23.0/go.mod h1:Cec35u/9zAepDPPFyT+UMrgqOCjgJ5qtfVJDxjZYmt4=
|
||||
k8s.io/apiserver v0.23.1/go.mod h1:Bqt0gWbeM2NefS8CjWswwd2VNAKN6lUKR85Ft4gippY=
|
||||
k8s.io/apiserver v0.23.5/go.mod h1:7wvMtGJ42VRxzgVI7jkbKvMbuCbVbgsWFT7RyXiRNTw=
|
||||
|
|
@ -3173,7 +3192,9 @@ k8s.io/client-go v0.21.1/go.mod h1:/kEw4RgW+3xnBGzvp9IWxKSNA+lXn3A7AuH3gdOAzLs=
|
|||
k8s.io/client-go v0.21.2/go.mod h1:HdJ9iknWpbl3vMGtib6T2PyI/VYxiZfq936WNVHBRrA=
|
||||
k8s.io/client-go v0.21.3/go.mod h1:+VPhCgTsaFmGILxR/7E1N0S+ryO010QBeNCv5JwRGYU=
|
||||
k8s.io/client-go v0.22.1/go.mod h1:BquC5A4UOo4qVDUtoc04/+Nxp1MeHcVc1HJm1KmG8kk=
|
||||
k8s.io/client-go v0.22.2/go.mod h1:sAlhrkVDf50ZHx6z4K0S40wISNTarf1r800F+RlCF6U=
|
||||
k8s.io/client-go v0.22.4/go.mod h1:Yzw4e5e7h1LNHA4uqnMVrpEpUs1hJOiuBsJKIlRCHDA=
|
||||
k8s.io/client-go v0.22.6/go.mod h1:TffU4AV2idZGeP+g3kdFZP+oHVHWPL1JYFySOALriw0=
|
||||
k8s.io/client-go v0.23.0/go.mod h1:hrDnpnK1mSr65lHHcUuIZIXDgEbzc7/683c6hyG4jTA=
|
||||
k8s.io/client-go v0.23.1/go.mod h1:6QSI8fEuqD4zgFK0xbdwfB/PthBsIxCJMa3s17WlcO0=
|
||||
k8s.io/client-go v0.23.5/go.mod h1:flkeinTO1CirYgzMPRWxUCnV0G4Fbu2vLhYCObnt/r4=
|
||||
|
|
@ -3189,10 +3210,13 @@ k8s.io/code-generator v0.18.2/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRV
|
|||
k8s.io/code-generator v0.18.6/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c=
|
||||
k8s.io/code-generator v0.20.0/go.mod h1:UsqdF+VX4PU2g46NC2JRs4gc+IfrctnwHb76RNbWHJg=
|
||||
k8s.io/code-generator v0.20.10/go.mod h1:i6FmG+QxaLxvJsezvZp0q/gAEzzOz3U53KFibghWToU=
|
||||
k8s.io/code-generator v0.20.10/go.mod h1:i6FmG+QxaLxvJsezvZp0q/gAEzzOz3U53KFibghWToU=
|
||||
k8s.io/code-generator v0.21.2/go.mod h1:8mXJDCB7HcRo1xiEQstcguZkbxZaqeUOrO9SsicWs3U=
|
||||
k8s.io/code-generator v0.21.3/go.mod h1:K3y0Bv9Cz2cOW2vXUrNZlFbflhuPvuadW6JdnN6gGKo=
|
||||
k8s.io/code-generator v0.22.1/go.mod h1:eV77Y09IopzeXOJzndrDyCI88UBok2h6WxAlBwpxa+o=
|
||||
k8s.io/code-generator v0.22.2/go.mod h1:eV77Y09IopzeXOJzndrDyCI88UBok2h6WxAlBwpxa+o=
|
||||
k8s.io/code-generator v0.22.4/go.mod h1:qjYl54pQ/emhkT0UxbufbREYJMWsHNNV/jSVwhYZQGw=
|
||||
k8s.io/code-generator v0.22.6/go.mod h1:iOZwYADSgFPNGWfqHFfg1V0TNJnl1t0WyZluQp4baqU=
|
||||
k8s.io/code-generator v0.23.0/go.mod h1:vQvOhDXhuzqiVfM/YHp+dmg10WDZCchJVObc9MvowsE=
|
||||
k8s.io/code-generator v0.23.1/go.mod h1:V7yn6VNTCWW8GqodYCESVo95fuiEg713S8B7WacWZDA=
|
||||
k8s.io/code-generator v0.23.5/go.mod h1:S0Q1JVA+kSzTI1oUvbKAxZY/DYbA/ZUb4Uknog12ETk=
|
||||
|
|
@ -3213,7 +3237,9 @@ k8s.io/component-base v0.20.10/go.mod h1:ZKOEin1xu68aJzxgzl5DZSp5J1IrjAOPlPN90/t
|
|||
k8s.io/component-base v0.21.2/go.mod h1:9lvmIThzdlrJj5Hp8Z/TOgIkdfsNARQ1pT+3PByuiuc=
|
||||
k8s.io/component-base v0.21.3/go.mod h1:kkuhtfEHeZM6LkX0saqSK8PbdO7A0HigUngmhhrwfGQ=
|
||||
k8s.io/component-base v0.22.1/go.mod h1:0D+Bl8rrnsPN9v0dyYvkqFfBeAd4u7n77ze+p8CMiPo=
|
||||
k8s.io/component-base v0.22.2/go.mod h1:5Br2QhI9OTe79p+TzPe9JKNQYvEKbq9rTJDWllunGug=
|
||||
k8s.io/component-base v0.22.4/go.mod h1:MrSaQy4a3tFVViff8TZL6JHYSewNCLshZCwHYM58v5A=
|
||||
k8s.io/component-base v0.22.6/go.mod h1:ngHLefY4J5fq2fApNdbWyj4yh0lvw36do4aAjNN8rc8=
|
||||
k8s.io/component-base v0.23.0/go.mod h1:DHH5uiFvLC1edCpvcTDV++NKULdYYU6pR9Tt3HIKMKI=
|
||||
k8s.io/component-base v0.23.1/go.mod h1:6llmap8QtJIXGDd4uIWJhAq0Op8AtQo6bDW2RrNMTeo=
|
||||
k8s.io/component-base v0.23.5/go.mod h1:c5Nq44KZyt1aLl0IpHX82fhsn84Sb0jjzwjpcA42bY0=
|
||||
|
|
@ -3262,6 +3288,7 @@ k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAG
|
|||
k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE=
|
||||
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
|
||||
k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
|
||||
k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
|
||||
k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 h1:E3J9oCLlaobFUqsjG9DfKbP2BmgwBL2p7pn0A3dG9W4=
|
||||
k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk=
|
||||
k8s.io/kubectl v0.0.0-20191219154910-1528d4eea6dd/go.mod h1:9ehGcuUGjXVZh0qbYSB0vvofQw2JQe6c6cO0k4wu/Oo=
|
||||
|
|
@ -3287,6 +3314,7 @@ k8s.io/utils v0.0.0-20210707171843-4b05e18ac7d9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/
|
|||
k8s.io/utils v0.0.0-20210722164352-7f3ee0f31471/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
k8s.io/utils v0.0.0-20211116205334-6203023598ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
k8s.io/utils v0.0.0-20211208161948-7d6a63dca704/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
|
|
@ -3330,6 +3358,7 @@ sigs.k8s.io/controller-runtime v0.6.0/go.mod h1:CpYf5pdNY/B352A1TFLAS2JVSlnGQ5O2
|
|||
sigs.k8s.io/controller-runtime v0.6.2/go.mod h1:vhcq/rlnENJ09SIRp3EveTaZ0yqH526hjf9iJdbUJ/E=
|
||||
sigs.k8s.io/controller-runtime v0.9.2/go.mod h1:TxzMCHyEUpaeuOiZx/bIdc2T81vfs/aKdvJt9wuu0zk=
|
||||
sigs.k8s.io/controller-runtime v0.9.5/go.mod h1:q6PpkM5vqQubEKUKOM6qr06oXGzOBcCby1DA9FbyZeA=
|
||||
sigs.k8s.io/controller-runtime v0.10.3/go.mod h1:CQp8eyUQZ/Q7PJvnIrB6/hgfTC1kBkGylwsLgOQi1WY=
|
||||
sigs.k8s.io/controller-runtime v0.11.0/go.mod h1:KKwLiTooNGu+JmLZGn9Sl3Gjmfj66eMbCQznLP5zcqA=
|
||||
sigs.k8s.io/controller-runtime v0.11.1/go.mod h1:KKwLiTooNGu+JmLZGn9Sl3Gjmfj66eMbCQznLP5zcqA=
|
||||
sigs.k8s.io/controller-runtime v0.11.2 h1:H5GTxQl0Mc9UjRJhORusqfJCIjBO8UtUxGggCwL1rLA=
|
||||
|
|
@ -3370,7 +3399,11 @@ sigs.k8s.io/structured-merge-diff/v4 v4.1.0/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK
|
|||
sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.0/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
|
||||
sigs.k8s.io/testing_frameworks v0.1.2/go.mod h1:ToQrwSC3s8Xf/lADdZp3Mktcql9CG0UAmdJG9th5i0w=
|
||||
sigs.k8s.io/testing_frameworks v0.1.2/go.mod h1:ToQrwSC3s8Xf/lADdZp3Mktcql9CG0UAmdJG9th5i0w=
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||
|
|
|
|||
|
|
@ -280,6 +280,9 @@ func (h *AppHandler) collectHealthStatus(ctx context.Context, wl *appfile.Worklo
|
|||
if err != nil {
|
||||
return nil, false, errors.WithMessagef(err, "app=%s, comp=%s, trait=%s, evaluate status message error", appName, wl.Name, tr.Name)
|
||||
}
|
||||
if status.Message == "" && traitStatus.Message != "" {
|
||||
status.Message = traitStatus.Message
|
||||
}
|
||||
traitStatusList = append(traitStatusList, traitStatus)
|
||||
namespace = appRev.GetNamespace()
|
||||
wl.Ctx.SetCtx(context.WithValue(wl.Ctx.GetCtx(), multicluster.ClusterContextKey, status.Cluster))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,200 @@
|
|||
/*
|
||||
Copyright 2021 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package rollout
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
k8stypes "k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/util/retry"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
kruisev1alpha1 "github.com/openkruise/rollouts/api/v1alpha1"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/pkg/multicluster"
|
||||
"github.com/oam-dev/kubevela/pkg/resourcetracker"
|
||||
velaerrors "github.com/oam-dev/kubevela/pkg/utils/errors"
|
||||
)
|
||||
|
||||
// ClusterRollout rollout in specified cluster
|
||||
type ClusterRollout struct {
|
||||
*kruisev1alpha1.Rollout
|
||||
Cluster string
|
||||
}
|
||||
|
||||
func getAssociatedRollouts(ctx context.Context, cli client.Client, app *v1beta1.Application, withHistoryRTs bool) ([]*ClusterRollout, error) {
|
||||
rootRT, currentRT, historyRTs, _, err := resourcetracker.ListApplicationResourceTrackers(ctx, cli, app)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to list resource trackers")
|
||||
}
|
||||
if !withHistoryRTs {
|
||||
historyRTs = []*v1beta1.ResourceTracker{}
|
||||
}
|
||||
var rollouts []*ClusterRollout
|
||||
for _, rt := range append(historyRTs, rootRT, currentRT) {
|
||||
if rt == nil {
|
||||
continue
|
||||
}
|
||||
for _, mr := range rt.Spec.ManagedResources {
|
||||
if mr.APIVersion == kruisev1alpha1.SchemeGroupVersion.String() && mr.Kind == "Rollout" {
|
||||
rollout := &kruisev1alpha1.Rollout{}
|
||||
if err = cli.Get(multicluster.ContextWithClusterName(ctx, mr.Cluster), k8stypes.NamespacedName{Namespace: mr.Namespace, Name: mr.Name}, rollout); err != nil {
|
||||
if multicluster.IsNotFoundOrClusterNotExists(err) || velaerrors.IsCRDNotExists(err) {
|
||||
continue
|
||||
}
|
||||
return nil, errors.Wrapf(err, "failed to get kruise rollout %s/%s in cluster %s", mr.Namespace, mr.Name, mr.Cluster)
|
||||
}
|
||||
rollouts = append(rollouts, &ClusterRollout{Rollout: rollout, Cluster: mr.Cluster})
|
||||
}
|
||||
}
|
||||
}
|
||||
return rollouts, nil
|
||||
}
|
||||
|
||||
// SuspendRollout find all rollouts associated with the application (including history RTs) and resume them
|
||||
func SuspendRollout(ctx context.Context, cli client.Client, app *v1beta1.Application, writer io.Writer) error {
|
||||
rollouts, err := getAssociatedRollouts(ctx, cli, app, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range rollouts {
|
||||
rollout := rollouts[i]
|
||||
if rollout.Status.Phase == kruisev1alpha1.RolloutPhaseProgressing && !rollout.Spec.Strategy.Paused {
|
||||
_ctx := multicluster.ContextWithClusterName(ctx, rollout.Cluster)
|
||||
rolloutKey := client.ObjectKeyFromObject(rollout.Rollout)
|
||||
if err = retry.RetryOnConflict(retry.DefaultBackoff, func() error {
|
||||
if err = cli.Get(_ctx, rolloutKey, rollout.Rollout); err != nil {
|
||||
return err
|
||||
}
|
||||
if rollout.Status.Phase == kruisev1alpha1.RolloutPhaseProgressing && !rollout.Spec.Strategy.Paused {
|
||||
rollout.Spec.Strategy.Paused = true
|
||||
if err = cli.Update(_ctx, rollout.Rollout); err != nil {
|
||||
return err
|
||||
}
|
||||
if writer != nil {
|
||||
_, _ = writer.Write([]byte(fmt.Sprintf("Rollout %s/%s in cluster %s suspended.\n", rollout.Namespace, rollout.Name, rollout.Cluster)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return errors.Wrapf(err, "failed to suspend rollout %s/%s in cluster %s", rollout.Namespace, rollout.Name, rollout.Cluster)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ResumeRollout find all rollouts associated with the application (in the current RT) and resume them
|
||||
func ResumeRollout(ctx context.Context, cli client.Client, app *v1beta1.Application, writer io.Writer) (bool, error) {
|
||||
rollouts, err := getAssociatedRollouts(ctx, cli, app, false)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
modified := false
|
||||
for i := range rollouts {
|
||||
rollout := rollouts[i]
|
||||
if rollout.Spec.Strategy.Paused || (rollout.Status.CanaryStatus != nil && rollout.Status.CanaryStatus.CurrentStepState == kruisev1alpha1.CanaryStepStatePaused) {
|
||||
_ctx := multicluster.ContextWithClusterName(ctx, rollout.Cluster)
|
||||
rolloutKey := client.ObjectKeyFromObject(rollout.Rollout)
|
||||
resumed := false
|
||||
if err = retry.RetryOnConflict(retry.DefaultBackoff, func() error {
|
||||
if err = cli.Get(_ctx, rolloutKey, rollout.Rollout); err != nil {
|
||||
return err
|
||||
}
|
||||
if rollout.Spec.Strategy.Paused {
|
||||
rollout.Spec.Strategy.Paused = false
|
||||
if err = cli.Update(_ctx, rollout.Rollout); err != nil {
|
||||
return err
|
||||
}
|
||||
resumed = true
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return false, errors.Wrapf(err, "failed to resume rollout %s/%s in cluster %s", rollout.Namespace, rollout.Name, rollout.Cluster)
|
||||
}
|
||||
if err = retry.RetryOnConflict(retry.DefaultBackoff, func() error {
|
||||
if err = cli.Get(_ctx, rolloutKey, rollout.Rollout); err != nil {
|
||||
return err
|
||||
}
|
||||
if rollout.Status.CanaryStatus != nil && rollout.Status.CanaryStatus.CurrentStepState == kruisev1alpha1.CanaryStepStatePaused {
|
||||
rollout.Status.CanaryStatus.CurrentStepState = kruisev1alpha1.CanaryStepStateReady
|
||||
if err = cli.Status().Update(_ctx, rollout.Rollout); err != nil {
|
||||
return err
|
||||
}
|
||||
resumed = true
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return false, errors.Wrapf(err, "failed to resume rollout %s/%s in cluster %s", rollout.Namespace, rollout.Name, rollout.Cluster)
|
||||
}
|
||||
if resumed {
|
||||
modified = true
|
||||
if writer != nil {
|
||||
_, _ = writer.Write([]byte(fmt.Sprintf("Rollout %s/%s in cluster %s resumed.\n", rollout.Namespace, rollout.Name, rollout.Cluster)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return modified, nil
|
||||
}
|
||||
|
||||
// RollbackRollout find all rollouts associated with the application (in the current RT) and disable the pause field.
|
||||
func RollbackRollout(ctx context.Context, cli client.Client, app *v1beta1.Application, writer io.Writer) (bool, error) {
|
||||
rollouts, err := getAssociatedRollouts(ctx, cli, app, false)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
modified := false
|
||||
for i := range rollouts {
|
||||
rollout := rollouts[i]
|
||||
if rollout.Spec.Strategy.Paused || (rollout.Status.CanaryStatus != nil && rollout.Status.CanaryStatus.CurrentStepState == kruisev1alpha1.CanaryStepStatePaused) {
|
||||
_ctx := multicluster.ContextWithClusterName(ctx, rollout.Cluster)
|
||||
rolloutKey := client.ObjectKeyFromObject(rollout.Rollout)
|
||||
resumed := false
|
||||
if err = retry.RetryOnConflict(retry.DefaultBackoff, func() error {
|
||||
if err = cli.Get(_ctx, rolloutKey, rollout.Rollout); err != nil {
|
||||
return err
|
||||
}
|
||||
if rollout.Spec.Strategy.Paused {
|
||||
rollout.Spec.Strategy.Paused = false
|
||||
if err = cli.Update(_ctx, rollout.Rollout); err != nil {
|
||||
return err
|
||||
}
|
||||
resumed = true
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return false, errors.Wrapf(err, "failed to rollback rollout %s/%s in cluster %s", rollout.Namespace, rollout.Name, rollout.Cluster)
|
||||
}
|
||||
if resumed {
|
||||
modified = true
|
||||
if writer != nil {
|
||||
_, _ = writer.Write([]byte(fmt.Sprintf("Rollout %s/%s in cluster %s rollback.\n", rollout.Namespace, rollout.Name, rollout.Cluster)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return modified, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
Copyright 2021 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package rollout
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
kruisev1alpha1 "github.com/openkruise/rollouts/api/v1alpha1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
)
|
||||
|
||||
var _ = Describe("Kruise rollout test", func() {
|
||||
ctx := context.Background()
|
||||
BeforeEach(func() {
|
||||
Expect(k8sClient.Create(ctx, rollout.DeepCopy())).Should(SatisfyAny(BeNil(), util.AlreadyExistMatcher{}))
|
||||
Expect(k8sClient.Create(ctx, rt.DeepCopy())).Should(SatisfyAny(BeNil(), util.AlreadyExistMatcher{}))
|
||||
Expect(k8sClient.Create(ctx, app.DeepCopy())).Should(SatisfyAny(BeNil(), util.AlreadyExistMatcher{}))
|
||||
})
|
||||
|
||||
It("test get associated rollout func", func() {
|
||||
rollouts, err := getAssociatedRollouts(ctx, k8sClient, &app, false)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(len(rollouts)).Should(BeEquivalentTo(1))
|
||||
})
|
||||
|
||||
It("Suspend rollout", func() {
|
||||
r := kruisev1alpha1.Rollout{}
|
||||
Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: "default", Name: "my-rollout"}, &r)).Should(BeNil())
|
||||
r.Status.Phase = kruisev1alpha1.RolloutPhaseProgressing
|
||||
Expect(k8sClient.Status().Update(ctx, &r)).Should(BeNil())
|
||||
Expect(SuspendRollout(ctx, k8sClient, &app, nil))
|
||||
Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: "default", Name: "my-rollout"}, &r))
|
||||
Expect(r.Spec.Strategy.Paused).Should(BeEquivalentTo(true))
|
||||
})
|
||||
|
||||
It("Resume rollout", func() {
|
||||
r := kruisev1alpha1.Rollout{}
|
||||
Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: "default", Name: "my-rollout"}, &r)).Should(BeNil())
|
||||
Expect(r.Spec.Strategy.Paused).Should(BeEquivalentTo(true))
|
||||
Expect(ResumeRollout(ctx, k8sClient, &app, nil))
|
||||
Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: "default", Name: "my-rollout"}, &r))
|
||||
Expect(r.Spec.Strategy.Paused).Should(BeEquivalentTo(false))
|
||||
})
|
||||
|
||||
It("Rollback rollout", func() {
|
||||
r := kruisev1alpha1.Rollout{}
|
||||
Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: "default", Name: "my-rollout"}, &r)).Should(BeNil())
|
||||
r.Spec.Strategy.Paused = true
|
||||
Expect(k8sClient.Update(ctx, &r)).Should(BeNil())
|
||||
Expect(RollbackRollout(ctx, k8sClient, &app, nil))
|
||||
Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: "default", Name: "my-rollout"}, &r))
|
||||
Expect(r.Spec.Strategy.Paused).Should(BeEquivalentTo(false))
|
||||
})
|
||||
})
|
||||
|
||||
var app = v1beta1.Application{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "core.oam.dev/v1beta1",
|
||||
Kind: "Application",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "rollout-app",
|
||||
Namespace: "default",
|
||||
Generation: 1,
|
||||
},
|
||||
Spec: v1beta1.ApplicationSpec{
|
||||
Components: []common.ApplicationComponent{},
|
||||
},
|
||||
}
|
||||
|
||||
var rt = v1beta1.ResourceTracker{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "core.oam.dev/v1beta1",
|
||||
Kind: "ResourceTracker",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "rollout-app",
|
||||
Labels: map[string]string{
|
||||
"app.oam.dev/appRevision": "rollout-app-v1",
|
||||
"app.oam.dev/name": "rollout-app",
|
||||
"app.oam.dev/namespace": "default",
|
||||
},
|
||||
},
|
||||
Spec: v1beta1.ResourceTrackerSpec{
|
||||
ApplicationGeneration: 1,
|
||||
Type: v1beta1.ResourceTrackerTypeVersioned,
|
||||
ManagedResources: []v1beta1.ManagedResource{
|
||||
{
|
||||
ClusterObjectReference: common.ClusterObjectReference{
|
||||
ObjectReference: v1.ObjectReference{
|
||||
APIVersion: "rollouts.kruise.io/v1alpha1",
|
||||
Kind: "Rollout",
|
||||
Name: "my-rollout",
|
||||
Namespace: "default",
|
||||
},
|
||||
},
|
||||
OAMObjectReference: common.OAMObjectReference{
|
||||
Component: "my-rollout",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var rollout = kruisev1alpha1.Rollout{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "rollouts.kruise.io/v1alpha1",
|
||||
Kind: "Rollout",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "my-rollout",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: kruisev1alpha1.RolloutSpec{
|
||||
ObjectRef: kruisev1alpha1.ObjectRef{
|
||||
WorkloadRef: &kruisev1alpha1.WorkloadRef{
|
||||
APIVersion: "appsv1",
|
||||
Kind: "Deployment",
|
||||
Name: "canary-demo",
|
||||
},
|
||||
},
|
||||
Strategy: kruisev1alpha1.RolloutStrategy{
|
||||
Canary: &kruisev1alpha1.CanaryStrategy{
|
||||
Steps: []kruisev1alpha1.CanaryStep{
|
||||
{
|
||||
Weight: 30,
|
||||
},
|
||||
},
|
||||
},
|
||||
Paused: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
Copyright 2021 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package rollout
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/client-go/discovery"
|
||||
ocmclusterv1 "open-cluster-management.io/api/cluster/v1"
|
||||
ocmclusterv1alpha1 "open-cluster-management.io/api/cluster/v1alpha1"
|
||||
ocmworkv1 "open-cluster-management.io/api/work/v1"
|
||||
|
||||
v12 "k8s.io/api/core/v1"
|
||||
crdv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/envtest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/envtest/printer"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/log"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log/zap"
|
||||
|
||||
kruisev1alpha1 "github.com/openkruise/rollouts/api/v1alpha1"
|
||||
|
||||
coreoam "github.com/oam-dev/kubevela/apis/core.oam.dev"
|
||||
"github.com/oam-dev/kubevela/pkg/cue/packages"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
|
||||
// +kubebuilder:scaffold:imports
|
||||
)
|
||||
|
||||
var cfg *rest.Config
|
||||
var scheme *runtime.Scheme
|
||||
var k8sClient client.Client
|
||||
var testEnv *envtest.Environment
|
||||
var dm discoverymapper.DiscoveryMapper
|
||||
var pd *packages.PackageDiscover
|
||||
var testns string
|
||||
var dc *discovery.DiscoveryClient
|
||||
|
||||
func TestAddon(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecsWithDefaultAndCustomReporters(t,
|
||||
"Kruise rollout Suite test",
|
||||
[]Reporter{printer.NewlineReporter{}})
|
||||
}
|
||||
|
||||
var _ = BeforeSuite(func(done Done) {
|
||||
logf.SetLogger(zap.New(zap.UseDevMode(true), zap.WriteTo(GinkgoWriter)))
|
||||
By("bootstrapping test environment")
|
||||
useExistCluster := false
|
||||
testEnv = &envtest.Environment{
|
||||
ControlPlaneStartTimeout: time.Minute,
|
||||
ControlPlaneStopTimeout: time.Minute,
|
||||
CRDDirectoryPaths: []string{filepath.Join("..", "..", "charts", "vela-core", "crds"), filepath.Join("", "testdata")},
|
||||
UseExistingCluster: &useExistCluster,
|
||||
}
|
||||
|
||||
var err error
|
||||
cfg, err = testEnv.Start()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(cfg).ToNot(BeNil())
|
||||
scheme = runtime.NewScheme()
|
||||
Expect(coreoam.AddToScheme(scheme)).NotTo(HaveOccurred())
|
||||
Expect(clientgoscheme.AddToScheme(scheme)).NotTo(HaveOccurred())
|
||||
Expect(crdv1.AddToScheme(scheme)).NotTo(HaveOccurred())
|
||||
_ = ocmclusterv1alpha1.Install(scheme)
|
||||
_ = ocmclusterv1.Install(scheme)
|
||||
_ = ocmworkv1.Install(scheme)
|
||||
_ = kruisev1alpha1.AddToScheme(scheme)
|
||||
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(k8sClient).ToNot(BeNil())
|
||||
|
||||
dc, err = discovery.NewDiscoveryClientForConfig(cfg)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(dc).ShouldNot(BeNil())
|
||||
|
||||
dm, err = discoverymapper.New(cfg)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(dm).ToNot(BeNil())
|
||||
pd, err = packages.NewPackageDiscover(cfg)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(pd).ToNot(BeNil())
|
||||
testns = "vela-system"
|
||||
Expect(k8sClient.Create(context.Background(),
|
||||
&v12.Namespace{TypeMeta: metav1.TypeMeta{APIVersion: "v1", Kind: "Namespace"}, ObjectMeta: metav1.ObjectMeta{
|
||||
Name: testns,
|
||||
}}))
|
||||
|
||||
close(done)
|
||||
}, 120)
|
||||
|
||||
var _ = AfterSuite(func() {
|
||||
By("tearing down the test environment")
|
||||
err := testEnv.Stop()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
|
@ -0,0 +1,290 @@
|
|||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.7.0
|
||||
creationTimestamp: null
|
||||
name: rollouts.rollouts.kruise.io
|
||||
spec:
|
||||
group: rollouts.kruise.io
|
||||
names:
|
||||
kind: Rollout
|
||||
listKind: RolloutList
|
||||
plural: rollouts
|
||||
singular: rollout
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- additionalPrinterColumns:
|
||||
- description: The rollout status phase
|
||||
jsonPath: .status.phase
|
||||
name: STATUS
|
||||
type: string
|
||||
- description: The rollout canary status step
|
||||
jsonPath: .status.canaryStatus.currentStepIndex
|
||||
name: CANARY_STEP
|
||||
type: integer
|
||||
- description: The rollout canary status step state
|
||||
jsonPath: .status.canaryStatus.currentStepState
|
||||
name: CANARY_STATE
|
||||
type: string
|
||||
- description: The rollout canary status message
|
||||
jsonPath: .status.message
|
||||
name: MESSAGE
|
||||
type: string
|
||||
- jsonPath: .metadata.creationTimestamp
|
||||
name: AGE
|
||||
type: date
|
||||
name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: Rollout is the Schema for the rollouts API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: RolloutSpec defines the desired state of Rollout
|
||||
properties:
|
||||
objectRef:
|
||||
description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
|
||||
Important: Run "make" to regenerate code after modifying this file
|
||||
ObjectRef indicates workload'
|
||||
properties:
|
||||
workloadRef:
|
||||
description: WorkloadRef contains enough information to let you
|
||||
identify a workload for Rollout Batch release of the bypass
|
||||
properties:
|
||||
apiVersion:
|
||||
description: API Version of the referent
|
||||
type: string
|
||||
kind:
|
||||
description: Kind of the referent
|
||||
type: string
|
||||
name:
|
||||
description: Name of the referent
|
||||
type: string
|
||||
required:
|
||||
- apiVersion
|
||||
- kind
|
||||
- name
|
||||
type: object
|
||||
type: object
|
||||
strategy:
|
||||
description: rollout strategy
|
||||
properties:
|
||||
canary:
|
||||
description: CanaryStrategy defines parameters for a Replica Based
|
||||
Canary
|
||||
properties:
|
||||
steps:
|
||||
description: Steps define the order of phases to execute release
|
||||
in batches(20%, 40%, 60%, 80%, 100%)
|
||||
items:
|
||||
description: CanaryStep defines a step of a canary workload.
|
||||
properties:
|
||||
pause:
|
||||
description: Pause defines a pause stage for a rollout,
|
||||
manual or auto
|
||||
properties:
|
||||
duration:
|
||||
description: Duration the amount of time to wait
|
||||
before moving to the next step.
|
||||
format: int32
|
||||
type: integer
|
||||
type: object
|
||||
replicas:
|
||||
anyOf:
|
||||
- type: integer
|
||||
- type: string
|
||||
description: 'Replicas is the number of expected canary
|
||||
pods in this batch it can be an absolute number (ex:
|
||||
5) or a percentage of total pods.'
|
||||
x-kubernetes-int-or-string: true
|
||||
weight:
|
||||
description: SetWeight sets what percentage of the canary
|
||||
pods should receive
|
||||
format: int32
|
||||
type: integer
|
||||
type: object
|
||||
type: array
|
||||
trafficRoutings:
|
||||
description: TrafficRoutings hosts all the supported service
|
||||
meshes supported to enable more fine-grained traffic routing
|
||||
todo current only support one
|
||||
items:
|
||||
description: TrafficRouting hosts all the different configuration
|
||||
for supported service meshes to enable more fine-grained
|
||||
traffic routing
|
||||
properties:
|
||||
gracePeriodSeconds:
|
||||
description: Optional duration in seconds the traffic
|
||||
provider(e.g. nginx ingress controller) consumes the
|
||||
service, ingress configuration changes gracefully.
|
||||
format: int32
|
||||
type: integer
|
||||
ingress:
|
||||
description: Ingress holds Ingress specific configuration
|
||||
to route traffic, e.g. Nginx, Alb.
|
||||
properties:
|
||||
name:
|
||||
description: Name refers to the name of an `Ingress`
|
||||
resource in the same namespace as the `Rollout`
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
service:
|
||||
description: Service holds the name of a service which
|
||||
selects pods with stable version and don't select
|
||||
any pods with canary version.
|
||||
type: string
|
||||
type:
|
||||
description: nginx, alb, istio etc.
|
||||
type: string
|
||||
required:
|
||||
- service
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
type: object
|
||||
paused:
|
||||
description: Paused indicates that the Rollout is paused. Default
|
||||
value is false
|
||||
type: boolean
|
||||
type: object
|
||||
required:
|
||||
- objectRef
|
||||
- strategy
|
||||
type: object
|
||||
status:
|
||||
description: RolloutStatus defines the observed state of Rollout
|
||||
properties:
|
||||
canaryStatus:
|
||||
description: Canary describes the state of the canary rollout
|
||||
properties:
|
||||
canaryReadyReplicas:
|
||||
description: CanaryReadyReplicas the numbers of ready canary revision
|
||||
pods
|
||||
format: int32
|
||||
type: integer
|
||||
canaryReplicas:
|
||||
description: CanaryReplicas the numbers of canary revision pods
|
||||
format: int32
|
||||
type: integer
|
||||
canaryRevision:
|
||||
description: CanaryRevision is calculated by rollout based on
|
||||
podTemplateHash, and the internal logic flow uses It may be
|
||||
different from rs podTemplateHash in different k8s versions,
|
||||
so it cannot be used as service selector label
|
||||
type: string
|
||||
canaryService:
|
||||
description: CanaryService holds the name of a service which selects
|
||||
pods with canary version and don't select any pods with stable
|
||||
version.
|
||||
type: string
|
||||
currentStepIndex:
|
||||
description: CurrentStepIndex defines the current step of the
|
||||
rollout is on. If the current step index is null, the controller
|
||||
will execute the rollout.
|
||||
format: int32
|
||||
type: integer
|
||||
currentStepState:
|
||||
type: string
|
||||
lastReadyTime:
|
||||
description: The last time this step pods is ready.
|
||||
format: date-time
|
||||
type: string
|
||||
message:
|
||||
type: string
|
||||
observedWorkloadGeneration:
|
||||
description: observedWorkloadGeneration is the most recent generation
|
||||
observed for this Rollout ref workload generation.
|
||||
format: int64
|
||||
type: integer
|
||||
podTemplateHash:
|
||||
description: pod template hash is used as service selector label
|
||||
type: string
|
||||
rolloutHash:
|
||||
description: RolloutHash from rollout.spec object
|
||||
type: string
|
||||
required:
|
||||
- canaryReadyReplicas
|
||||
- canaryReplicas
|
||||
- canaryService
|
||||
- currentStepState
|
||||
- podTemplateHash
|
||||
type: object
|
||||
conditions:
|
||||
description: Conditions a list of conditions a rollout can have.
|
||||
items:
|
||||
description: RolloutCondition describes the state of a rollout at
|
||||
a certain point.
|
||||
properties:
|
||||
lastTransitionTime:
|
||||
description: Last time the condition transitioned from one status
|
||||
to another.
|
||||
format: date-time
|
||||
type: string
|
||||
lastUpdateTime:
|
||||
description: The last time this condition was updated.
|
||||
format: date-time
|
||||
type: string
|
||||
message:
|
||||
description: A human readable message indicating details about
|
||||
the transition.
|
||||
type: string
|
||||
reason:
|
||||
description: The reason for the condition's last transition.
|
||||
type: string
|
||||
status:
|
||||
description: Phase of the condition, one of True, False, Unknown.
|
||||
type: string
|
||||
type:
|
||||
description: Type of rollout condition.
|
||||
type: string
|
||||
required:
|
||||
- message
|
||||
- reason
|
||||
- status
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
message:
|
||||
description: Message provides details on why the rollout is in its
|
||||
current phase
|
||||
type: string
|
||||
observedGeneration:
|
||||
description: observedGeneration is the most recent generation observed
|
||||
for this Rollout.
|
||||
format: int64
|
||||
type: integer
|
||||
phase:
|
||||
description: BlueGreenStatus *BlueGreenStatus `json:"blueGreenStatus,omitempty"`
|
||||
Phase is the rollout phase.
|
||||
type: string
|
||||
stableRevision:
|
||||
description: CanaryRevision the hash of the canary pod template CanaryRevision
|
||||
string `json:"canaryRevision,omitempty"` StableRevision indicates
|
||||
the revision pods that has successfully rolled out
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
plural: ""
|
||||
conditions: []
|
||||
storedVersions: []
|
||||
|
|
@ -37,6 +37,7 @@ import (
|
|||
"github.com/hashicorp/hcl/v2/hclparse"
|
||||
"github.com/oam-dev/terraform-config-inspect/tfconfig"
|
||||
kruise "github.com/openkruise/kruise-api/apps/v1alpha1"
|
||||
kruisev1alpha1 "github.com/openkruise/rollouts/api/v1alpha1"
|
||||
errors2 "github.com/pkg/errors"
|
||||
certmanager "github.com/wonderflow/cert-manager-api/pkg/apis/certmanager/v1"
|
||||
istioclientv1beta1 "istio.io/client-go/pkg/apis/networking/v1beta1"
|
||||
|
|
@ -102,6 +103,7 @@ func init() {
|
|||
_ = ocmworkv1.Install(Scheme)
|
||||
_ = clustergatewayapi.AddToScheme(Scheme)
|
||||
_ = metricsV1beta1api.AddToScheme(Scheme)
|
||||
_ = kruisev1alpha1.AddToScheme(Scheme)
|
||||
_ = prismclusterv1alpha1.AddToScheme(Scheme)
|
||||
// +kubebuilder:scaffold:scheme
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,7 +89,8 @@ func init() {
|
|||
{APIVersion: "rbac.authorization.k8s.io/v1", Kind: "Role"}: nil,
|
||||
{APIVersion: "rbac.authorization.k8s.io/v1", Kind: "RoleBinding"}: nil,
|
||||
},
|
||||
DefaultGenListOptionFunc: helmRelease2AnyListOption,
|
||||
DefaultGenListOptionFunc: helmRelease2AnyListOption,
|
||||
DisableFilterByOwnerReference: true,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -117,6 +118,8 @@ type ChildrenResourcesRule struct {
|
|||
CareResource map[ResourceType]genListOptionFunc
|
||||
// if specified genListOptionFunc is nil will use use default genListOptionFunc to generate listOption.
|
||||
DefaultGenListOptionFunc genListOptionFunc
|
||||
// DisableFilterByOwnerReference means don't use parent resource's UID filter the result.
|
||||
DisableFilterByOwnerReference bool
|
||||
}
|
||||
|
||||
type genListOptionFunc func(unstructured.Unstructured) (client.ListOptions, error)
|
||||
|
|
@ -618,7 +621,7 @@ func fetchObjectWithResourceTreeNode(ctx context.Context, cluster string, k8sCli
|
|||
}
|
||||
|
||||
func listItemByRule(clusterCTX context.Context, k8sClient client.Client, resource ResourceType,
|
||||
parentObject unstructured.Unstructured, specifiedFunc genListOptionFunc, defaultFunc genListOptionFunc) ([]unstructured.Unstructured, error) {
|
||||
parentObject unstructured.Unstructured, specifiedFunc genListOptionFunc, defaultFunc genListOptionFunc, disableFilterByOwner bool) ([]unstructured.Unstructured, error) {
|
||||
|
||||
itemList := unstructured.UnstructuredList{}
|
||||
itemList.SetAPIVersion(resource.APIVersion)
|
||||
|
|
@ -657,6 +660,20 @@ func listItemByRule(clusterCTX context.Context, k8sClient client.Client, resourc
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !disableFilterByOwner {
|
||||
var res []unstructured.Unstructured
|
||||
for _, item := range itemList.Items {
|
||||
if len(item.GetOwnerReferences()) == 0 {
|
||||
res = append(res, item)
|
||||
}
|
||||
for _, reference := range item.GetOwnerReferences() {
|
||||
if reference.UID == parentObject.GetUID() {
|
||||
res = append(res, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
return itemList.Items, nil
|
||||
}
|
||||
|
||||
|
|
@ -676,7 +693,7 @@ func iteratorChildResources(ctx context.Context, cluster string, k8sClient clien
|
|||
var resList []*types.ResourceTreeNode
|
||||
for resource, specifiedFunc := range rules.CareResource {
|
||||
clusterCTX := multicluster.ContextWithClusterName(ctx, cluster)
|
||||
items, err := listItemByRule(clusterCTX, k8sClient, resource, *parentObject, specifiedFunc, rules.DefaultGenListOptionFunc)
|
||||
items, err := listItemByRule(clusterCTX, k8sClient, resource, *parentObject, specifiedFunc, rules.DefaultGenListOptionFunc, rules.DisableFilterByOwnerReference)
|
||||
if err != nil {
|
||||
if meta.IsNoMatchError(err) || runtime.IsNotRegisteredError(err) {
|
||||
log.Logger.Errorf("error to list subresources: %s err: %v", resource.Kind, err)
|
||||
|
|
|
|||
|
|
@ -1266,14 +1266,14 @@ var _ = Describe("unit-test to e2e test", func() {
|
|||
u, err := runtime.DefaultUnstructuredConverter.ToUnstructured(deploy1.DeepCopy())
|
||||
Expect(err).Should(BeNil())
|
||||
items, err := listItemByRule(ctx, k8sClient, ResourceType{APIVersion: "apps/v1", Kind: "ReplicaSet"}, unstructured.Unstructured{Object: u},
|
||||
deploy2RsLabelListOption, nil)
|
||||
deploy2RsLabelListOption, nil, true)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(len(items)).Should(BeEquivalentTo(2))
|
||||
|
||||
u2, err := runtime.DefaultUnstructuredConverter.ToUnstructured(deploy2.DeepCopy())
|
||||
Expect(err).Should(BeNil())
|
||||
items2, err := listItemByRule(ctx, k8sClient, ResourceType{APIVersion: "apps/v1", Kind: "ReplicaSet"}, unstructured.Unstructured{Object: u2},
|
||||
nil, deploy2RsLabelListOption)
|
||||
nil, deploy2RsLabelListOption, true)
|
||||
Expect(len(items2)).Should(BeEquivalentTo(1))
|
||||
|
||||
// test use ownerReference UId to filter
|
||||
|
|
@ -1285,7 +1285,7 @@ var _ = Describe("unit-test to e2e test", func() {
|
|||
Expect(k8sClient.Get(ctx, types2.NamespacedName{Namespace: u3.GetNamespace(), Name: u3.GetName()}, &u3))
|
||||
Expect(err).Should(BeNil())
|
||||
items3, err := listItemByRule(ctx, k8sClient, ResourceType{APIVersion: "v1", Kind: "Pod"}, u3,
|
||||
nil, nil)
|
||||
nil, nil, true)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(len(items3)).Should(BeEquivalentTo(1))
|
||||
})
|
||||
|
|
|
|||
|
|
@ -0,0 +1,289 @@
|
|||
/*
|
||||
Copyright 2021 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package operation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"k8s.io/client-go/util/retry"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/domain/service"
|
||||
"github.com/oam-dev/kubevela/pkg/controller/core.oam.dev/v1alpha2/application"
|
||||
"github.com/oam-dev/kubevela/pkg/controller/utils"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/resourcetracker"
|
||||
"github.com/oam-dev/kubevela/pkg/rollout"
|
||||
errors3 "github.com/oam-dev/kubevela/pkg/utils/errors"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// WorkflowOperator is opratior handler for workflow's resume/rollback/restart
|
||||
type WorkflowOperator interface {
|
||||
Suspend(ctx context.Context, app *v1beta1.Application) error
|
||||
Resume(ctx context.Context, app *v1beta1.Application) error
|
||||
Rollback(ctx context.Context, app *v1beta1.Application) error
|
||||
Restart(ctx context.Context, app *v1beta1.Application) error
|
||||
Terminate(ctx context.Context, app *v1beta1.Application) error
|
||||
}
|
||||
|
||||
// NewWorkflowOperator get an workflow operator with k8sClient and ioWriter(optional, useful for cli)
|
||||
func NewWorkflowOperator(cli client.Client, w io.Writer) WorkflowOperator {
|
||||
return wfOperator{cli: cli, outputWriter: w}
|
||||
}
|
||||
|
||||
type wfOperator struct {
|
||||
cli client.Client
|
||||
outputWriter io.Writer
|
||||
}
|
||||
|
||||
// Suspend a running workflow
|
||||
func (wo wfOperator) Suspend(ctx context.Context, app *v1beta1.Application) error {
|
||||
if app.Status.Workflow == nil {
|
||||
return fmt.Errorf("the workflow in application is not running")
|
||||
}
|
||||
var err error
|
||||
if err = rollout.SuspendRollout(context.Background(), wo.cli, app, wo.outputWriter); err != nil {
|
||||
return err
|
||||
}
|
||||
appKey := client.ObjectKeyFromObject(app)
|
||||
if err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
|
||||
if err := wo.cli.Get(ctx, appKey, app); err != nil {
|
||||
return err
|
||||
}
|
||||
// set the workflow suspend to true
|
||||
app.Status.Workflow.Suspend = true
|
||||
return wo.cli.Status().Patch(ctx, app, client.Merge)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return wo.writeOutputF("Successfully suspend workflow: %s\n", app.Name)
|
||||
}
|
||||
|
||||
// Resume a suspending workflow
|
||||
func (wo wfOperator) Resume(ctx context.Context, app *v1beta1.Application) error {
|
||||
if app.Status.Workflow == nil {
|
||||
return fmt.Errorf("the workflow in application is not running")
|
||||
}
|
||||
if app.Status.Workflow.Terminated {
|
||||
return fmt.Errorf("can not resume a terminated workflow")
|
||||
}
|
||||
|
||||
var rolloutResumed bool
|
||||
var err error
|
||||
|
||||
if rolloutResumed, err = rollout.ResumeRollout(context.Background(), wo.cli, app, wo.outputWriter); err != nil {
|
||||
return err
|
||||
}
|
||||
if !rolloutResumed && !app.Status.Workflow.Suspend {
|
||||
return wo.writeOutput("the workflow is not suspending")
|
||||
}
|
||||
|
||||
if app.Status.Workflow.Suspend {
|
||||
if err = service.ResumeWorkflow(ctx, wo.cli, app); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Rollback a running in middle state workflow.
|
||||
//nolint
|
||||
func (wo wfOperator) Rollback(ctx context.Context, app *v1beta1.Application) error {
|
||||
if oam.GetPublishVersion(app) == "" {
|
||||
return fmt.Errorf("app without public version cannot rollback")
|
||||
}
|
||||
|
||||
appRevs, err := application.GetSortedAppRevisions(ctx, wo.cli, app.Name, app.Namespace)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to list revisions for application %s/%s", app.Namespace, app.Name)
|
||||
}
|
||||
|
||||
// find succeeded revision to rollback
|
||||
var rev *v1beta1.ApplicationRevision
|
||||
var outdatedRev []*v1beta1.ApplicationRevision
|
||||
for i := range appRevs {
|
||||
candidate := appRevs[len(appRevs)-i-1]
|
||||
_rev := candidate.DeepCopy()
|
||||
if !candidate.Status.Succeeded || oam.GetPublishVersion(_rev) == "" {
|
||||
outdatedRev = append(outdatedRev, _rev)
|
||||
continue
|
||||
}
|
||||
rev = _rev
|
||||
break
|
||||
}
|
||||
if rev == nil {
|
||||
return errors.Errorf("failed to find previous succeeded revision for application %s/%s", app.Namespace, app.Name)
|
||||
}
|
||||
publishVersion := oam.GetPublishVersion(rev)
|
||||
revisionNumber, err := utils.ExtractRevision(rev.Name)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to extract revision number from revision %s", rev.Name)
|
||||
}
|
||||
_, currentRT, historyRTs, _, err := resourcetracker.ListApplicationResourceTrackers(ctx, wo.cli, app)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to list resource trackers for application %s/%s", app.Namespace, app.Name)
|
||||
}
|
||||
var matchRT *v1beta1.ResourceTracker
|
||||
for _, rt := range append(historyRTs, currentRT) {
|
||||
if rt == nil {
|
||||
continue
|
||||
}
|
||||
labels := rt.GetLabels()
|
||||
if labels != nil && labels[oam.LabelAppRevision] == rev.Name {
|
||||
matchRT = rt.DeepCopy()
|
||||
}
|
||||
}
|
||||
if matchRT == nil {
|
||||
return errors.Errorf("cannot find resource tracker for previous revision %s, unable to rollback", rev.Name)
|
||||
}
|
||||
if matchRT.DeletionTimestamp != nil {
|
||||
return errors.Errorf("previous revision %s is being recycled, unable to rollback", rev.Name)
|
||||
}
|
||||
err = wo.writeOutput("Find succeeded application revision %s (PublishVersion: %s) to rollback.\n")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
appKey := client.ObjectKeyFromObject(app)
|
||||
// rollback application spec and freeze
|
||||
controllerRequirement, err := utils.FreezeApplication(ctx, wo.cli, app, func() {
|
||||
app.Spec = rev.Spec.Application.Spec
|
||||
oam.SetPublishVersion(app, publishVersion)
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to rollback application spec to revision %s (PublishVersion: %s)", rev.Name, publishVersion)
|
||||
}
|
||||
err = wo.writeOutput("Application spec rollback successfully.\n")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// rollback application status
|
||||
if err = retry.RetryOnConflict(retry.DefaultBackoff, func() error {
|
||||
if err = wo.cli.Get(ctx, appKey, app); err != nil {
|
||||
return err
|
||||
}
|
||||
app.Status.Workflow = rev.Status.Workflow
|
||||
app.Status.Services = []common.ApplicationComponentStatus{}
|
||||
app.Status.AppliedResources = []common.ClusterObjectReference{}
|
||||
for _, rsc := range matchRT.Spec.ManagedResources {
|
||||
app.Status.AppliedResources = append(app.Status.AppliedResources, rsc.ClusterObjectReference)
|
||||
}
|
||||
app.Status.LatestRevision = &common.Revision{
|
||||
Name: rev.Name,
|
||||
Revision: int64(revisionNumber),
|
||||
RevisionHash: rev.GetLabels()[oam.LabelAppRevisionHash],
|
||||
}
|
||||
return wo.cli.Status().Update(ctx, app)
|
||||
}); err != nil {
|
||||
return errors.Wrapf(err, "failed to rollback application status to revision %s (PublishVersion: %s)", rev.Name, publishVersion)
|
||||
}
|
||||
|
||||
err = wo.writeOutput("Application status rollback successfully.\n")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// update resource tracker generation
|
||||
matchRTKey := client.ObjectKeyFromObject(matchRT)
|
||||
if err = retry.RetryOnConflict(retry.DefaultBackoff, func() error {
|
||||
if err = wo.cli.Get(ctx, matchRTKey, matchRT); err != nil {
|
||||
return err
|
||||
}
|
||||
matchRT.Spec.ApplicationGeneration = app.Generation
|
||||
return wo.cli.Update(ctx, matchRT)
|
||||
}); err != nil {
|
||||
return errors.Wrapf(err, "failed to update application generation in resource tracker")
|
||||
}
|
||||
|
||||
// unfreeze application
|
||||
if err = utils.UnfreezeApplication(ctx, wo.cli, app, nil, controllerRequirement); err != nil {
|
||||
return errors.Wrapf(err, "failed to resume application to restart")
|
||||
}
|
||||
|
||||
rollback, err := rollout.RollbackRollout(ctx, wo.cli, app, wo.outputWriter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if rollback {
|
||||
err = wo.writeOutput("Successfully rollback rollout")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// clean up outdated revisions
|
||||
var errs errors3.ErrorList
|
||||
for _, _rev := range outdatedRev {
|
||||
if err = wo.cli.Delete(ctx, _rev); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
if errs.HasError() {
|
||||
return errors.Wrapf(errs, "failed to clean up outdated revisions")
|
||||
}
|
||||
|
||||
err = wo.writeOutput("Application outdated revision cleaned up.\n")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Restart a terminated or finished workflow.
|
||||
func (wo wfOperator) Restart(ctx context.Context, app *v1beta1.Application) error {
|
||||
if app.Status.Workflow == nil {
|
||||
return fmt.Errorf("the workflow in application is not running")
|
||||
}
|
||||
// reset the workflow status to restart the workflow
|
||||
app.Status.Workflow = nil
|
||||
|
||||
if err := wo.cli.Status().Update(context.TODO(), app); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return wo.writeOutputF("Successfully restart workflow: %s\n", app.Name)
|
||||
}
|
||||
|
||||
func (wo wfOperator) Terminate(ctx context.Context, app *v1beta1.Application) error {
|
||||
if err := service.TerminateWorkflow(context.TODO(), wo.cli, app); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (wo wfOperator) writeOutput(str string) error {
|
||||
if wo.outputWriter == nil {
|
||||
return nil
|
||||
}
|
||||
_, err := wo.outputWriter.Write([]byte(str))
|
||||
return err
|
||||
}
|
||||
|
||||
func (wo wfOperator) writeOutputF(format string, a ...interface{}) error {
|
||||
if wo.outputWriter == nil {
|
||||
return nil
|
||||
}
|
||||
_, err := wo.outputWriter.Write([]byte(fmt.Sprintf(format, a...)))
|
||||
return err
|
||||
}
|
||||
|
|
@ -0,0 +1,215 @@
|
|||
/*
|
||||
Copyright 2021 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package operation
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
kruisev1alpha1 "github.com/openkruise/rollouts/api/v1alpha1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
)
|
||||
|
||||
var _ = Describe("Kruise rollout test", func() {
|
||||
ctx := context.Background()
|
||||
BeforeEach(func() {
|
||||
Expect(k8sClient.Create(ctx, myRollout.DeepCopy())).Should(SatisfyAny(BeNil(), util.AlreadyExistMatcher{}))
|
||||
Expect(k8sClient.Create(ctx, rt.DeepCopy())).Should(SatisfyAny(BeNil(), util.AlreadyExistMatcher{}))
|
||||
Expect(k8sClient.Create(ctx, app.DeepCopy())).Should(SatisfyAny(BeNil(), util.AlreadyExistMatcher{}))
|
||||
})
|
||||
|
||||
It("Suspend workflow", func() {
|
||||
checkApp := v1beta1.Application{}
|
||||
Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: "default", Name: "opt-app"}, &checkApp)).Should(BeNil())
|
||||
checkApp.Status.Workflow = &common.WorkflowStatus{Suspend: false, StartTime: metav1.Now()}
|
||||
Expect(k8sClient.Status().Update(ctx, &checkApp)).Should(BeNil())
|
||||
operator := NewWorkflowOperator(k8sClient, nil)
|
||||
checkApp = v1beta1.Application{}
|
||||
Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: "default", Name: "opt-app"}, &checkApp)).Should(BeNil())
|
||||
Expect(operator.Suspend(ctx, checkApp.DeepCopy())).Should(BeNil())
|
||||
checkApp = v1beta1.Application{}
|
||||
Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: "default", Name: "opt-app"}, &checkApp)).Should(BeNil())
|
||||
Expect(checkApp.Status.Workflow.Suspend).Should(BeEquivalentTo(true))
|
||||
})
|
||||
|
||||
It("Resume workflow", func() {
|
||||
checkApp := v1beta1.Application{}
|
||||
Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: "default", Name: "opt-app"}, &checkApp)).Should(BeNil())
|
||||
operator := NewWorkflowOperator(k8sClient, nil)
|
||||
Expect(operator.Resume(ctx, &checkApp)).Should(BeNil())
|
||||
checkApp = v1beta1.Application{}
|
||||
Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: "default", Name: "opt-app"}, &checkApp)).Should(BeNil())
|
||||
Expect(checkApp.Status.Workflow.Suspend).Should(BeEquivalentTo(false))
|
||||
})
|
||||
|
||||
It("Terminate workflow", func() {
|
||||
checkApp := v1beta1.Application{}
|
||||
Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: "default", Name: "opt-app"}, &checkApp)).Should(BeNil())
|
||||
operator := NewWorkflowOperator(k8sClient, nil)
|
||||
Expect(operator.Terminate(ctx, &checkApp)).Should(BeNil())
|
||||
checkApp = v1beta1.Application{}
|
||||
Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: "default", Name: "opt-app"}, &checkApp)).Should(BeNil())
|
||||
Expect(checkApp.Status.Workflow.Terminated).Should(BeEquivalentTo(true))
|
||||
})
|
||||
|
||||
It("Restart workflow", func() {
|
||||
checkApp := v1beta1.Application{}
|
||||
Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: "default", Name: "opt-app"}, &checkApp)).Should(BeNil())
|
||||
operator := NewWorkflowOperator(k8sClient, nil)
|
||||
Expect(operator.Restart(ctx, &checkApp)).Should(BeNil())
|
||||
checkApp = v1beta1.Application{}
|
||||
Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: "default", Name: "opt-app"}, &checkApp)).Should(BeNil())
|
||||
Expect(checkApp.Status.Workflow).Should(BeNil())
|
||||
})
|
||||
|
||||
It("Rollback workflow", func() {
|
||||
Expect(k8sClient.Create(ctx, &appRev)).Should(BeNil())
|
||||
checkAppRev := v1beta1.ApplicationRevision{}
|
||||
Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: "default", Name: "opt-app-v1"}, &checkAppRev)).Should(BeNil())
|
||||
checkAppRev.Status.Succeeded = true
|
||||
Expect(k8sClient.Status().Update(ctx, checkAppRev.DeepCopy())).Should(BeNil())
|
||||
|
||||
checkApp := v1beta1.Application{}
|
||||
Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: "default", Name: "opt-app"}, &checkApp)).Should(BeNil())
|
||||
checkApp.Annotations = map[string]string{
|
||||
oam.AnnotationPublishVersion: "v2",
|
||||
}
|
||||
operator := NewWorkflowOperator(k8sClient, nil)
|
||||
Expect(operator.Rollback(ctx, checkApp.DeepCopy())).Should(BeNil())
|
||||
|
||||
checkApp = v1beta1.Application{}
|
||||
Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: "default", Name: "opt-app"}, &checkApp)).Should(BeNil())
|
||||
// must rollback to v1
|
||||
Expect(oam.GetPublishVersion(&checkApp)).Should(BeEquivalentTo("v1"))
|
||||
Expect(checkApp.Status.LatestRevision.Name).Should(BeEquivalentTo("opt-app-v1"))
|
||||
Expect(checkApp.Status.LatestRevision.Revision).Should(BeEquivalentTo(1))
|
||||
})
|
||||
})
|
||||
|
||||
var app = v1beta1.Application{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "core.oam.dev/v1beta1",
|
||||
Kind: "Application",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "opt-app",
|
||||
Namespace: "default",
|
||||
Generation: 1,
|
||||
Labels: map[string]string{
|
||||
oam.AnnotationPublishVersion: "v2",
|
||||
},
|
||||
},
|
||||
Spec: v1beta1.ApplicationSpec{
|
||||
Components: []common.ApplicationComponent{},
|
||||
},
|
||||
}
|
||||
|
||||
var rt = v1beta1.ResourceTracker{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "core.oam.dev/v1beta1",
|
||||
Kind: "ResourceTracker",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "rollout-app",
|
||||
Labels: map[string]string{
|
||||
"app.oam.dev/appRevision": "opt-app-v1",
|
||||
"app.oam.dev/name": "opt-app",
|
||||
"app.oam.dev/namespace": "default",
|
||||
},
|
||||
},
|
||||
Spec: v1beta1.ResourceTrackerSpec{
|
||||
ApplicationGeneration: 1,
|
||||
Type: v1beta1.ResourceTrackerTypeVersioned,
|
||||
ManagedResources: []v1beta1.ManagedResource{
|
||||
{
|
||||
ClusterObjectReference: common.ClusterObjectReference{
|
||||
ObjectReference: v1.ObjectReference{
|
||||
APIVersion: "rollouts.kruise.io/v1alpha1",
|
||||
Kind: "Rollout",
|
||||
Name: "my-rollout",
|
||||
Namespace: "default",
|
||||
},
|
||||
},
|
||||
OAMObjectReference: common.OAMObjectReference{
|
||||
Component: "my-rollout",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var appRev = v1beta1.ApplicationRevision{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "core.oam.dev/v1beta1",
|
||||
Kind: "ApplicationRevision",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "opt-app-v1",
|
||||
Namespace: "default",
|
||||
Labels: map[string]string{
|
||||
"app.oam.dev/name": "opt-app",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
oam.AnnotationPublishVersion: "v1",
|
||||
},
|
||||
},
|
||||
Spec: v1beta1.ApplicationRevisionSpec{
|
||||
Application: v1beta1.Application{
|
||||
Spec: v1beta1.ApplicationSpec{
|
||||
Components: []common.ApplicationComponent{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var myRollout = kruisev1alpha1.Rollout{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "rollouts.kruise.io/v1alpha1",
|
||||
Kind: "Rollout",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "my-rollout",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: kruisev1alpha1.RolloutSpec{
|
||||
ObjectRef: kruisev1alpha1.ObjectRef{
|
||||
WorkloadRef: &kruisev1alpha1.WorkloadRef{
|
||||
APIVersion: "appsv1",
|
||||
Kind: "Deployment",
|
||||
Name: "canary-demo",
|
||||
},
|
||||
},
|
||||
Strategy: kruisev1alpha1.RolloutStrategy{
|
||||
Canary: &kruisev1alpha1.CanaryStrategy{
|
||||
Steps: []kruisev1alpha1.CanaryStep{
|
||||
{
|
||||
Weight: 30,
|
||||
},
|
||||
},
|
||||
},
|
||||
Paused: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
Copyright 2021 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package operation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/client-go/discovery"
|
||||
ocmclusterv1 "open-cluster-management.io/api/cluster/v1"
|
||||
ocmclusterv1alpha1 "open-cluster-management.io/api/cluster/v1alpha1"
|
||||
ocmworkv1 "open-cluster-management.io/api/work/v1"
|
||||
|
||||
v12 "k8s.io/api/core/v1"
|
||||
crdv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/envtest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/envtest/printer"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/log"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log/zap"
|
||||
|
||||
kruisev1alpha1 "github.com/openkruise/rollouts/api/v1alpha1"
|
||||
|
||||
coreoam "github.com/oam-dev/kubevela/apis/core.oam.dev"
|
||||
"github.com/oam-dev/kubevela/pkg/cue/packages"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
|
||||
// +kubebuilder:scaffold:imports
|
||||
)
|
||||
|
||||
var cfg *rest.Config
|
||||
var scheme *runtime.Scheme
|
||||
var k8sClient client.Client
|
||||
var testEnv *envtest.Environment
|
||||
var dm discoverymapper.DiscoveryMapper
|
||||
var pd *packages.PackageDiscover
|
||||
var testns string
|
||||
var dc *discovery.DiscoveryClient
|
||||
|
||||
func TestAddon(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecsWithDefaultAndCustomReporters(t,
|
||||
"Kruise rollout Suite test",
|
||||
[]Reporter{printer.NewlineReporter{}})
|
||||
}
|
||||
|
||||
var _ = BeforeSuite(func(done Done) {
|
||||
logf.SetLogger(zap.New(zap.UseDevMode(true), zap.WriteTo(GinkgoWriter)))
|
||||
By("bootstrapping test environment")
|
||||
useExistCluster := false
|
||||
testEnv = &envtest.Environment{
|
||||
ControlPlaneStartTimeout: time.Minute,
|
||||
ControlPlaneStopTimeout: time.Minute,
|
||||
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "charts", "vela-core", "crds"), filepath.Join("", "testdata")},
|
||||
UseExistingCluster: &useExistCluster,
|
||||
}
|
||||
|
||||
var err error
|
||||
cfg, err = testEnv.Start()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(cfg).ToNot(BeNil())
|
||||
scheme = runtime.NewScheme()
|
||||
Expect(coreoam.AddToScheme(scheme)).NotTo(HaveOccurred())
|
||||
Expect(clientgoscheme.AddToScheme(scheme)).NotTo(HaveOccurred())
|
||||
Expect(crdv1.AddToScheme(scheme)).NotTo(HaveOccurred())
|
||||
_ = ocmclusterv1alpha1.Install(scheme)
|
||||
_ = ocmclusterv1.Install(scheme)
|
||||
_ = ocmworkv1.Install(scheme)
|
||||
_ = kruisev1alpha1.AddToScheme(scheme)
|
||||
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(k8sClient).ToNot(BeNil())
|
||||
|
||||
dc, err = discovery.NewDiscoveryClientForConfig(cfg)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(dc).ShouldNot(BeNil())
|
||||
|
||||
dm, err = discoverymapper.New(cfg)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(dm).ToNot(BeNil())
|
||||
pd, err = packages.NewPackageDiscover(cfg)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(pd).ToNot(BeNil())
|
||||
testns = "vela-system"
|
||||
Expect(k8sClient.Create(context.Background(),
|
||||
&v12.Namespace{TypeMeta: metav1.TypeMeta{APIVersion: "v1", Kind: "Namespace"}, ObjectMeta: metav1.ObjectMeta{
|
||||
Name: testns,
|
||||
}}))
|
||||
|
||||
close(done)
|
||||
}, 120)
|
||||
|
||||
var _ = AfterSuite(func() {
|
||||
By("tearing down the test environment")
|
||||
err := testEnv.Stop()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
|
@ -0,0 +1,290 @@
|
|||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.7.0
|
||||
creationTimestamp: null
|
||||
name: rollouts.rollouts.kruise.io
|
||||
spec:
|
||||
group: rollouts.kruise.io
|
||||
names:
|
||||
kind: Rollout
|
||||
listKind: RolloutList
|
||||
plural: rollouts
|
||||
singular: rollout
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- additionalPrinterColumns:
|
||||
- description: The rollout status phase
|
||||
jsonPath: .status.phase
|
||||
name: STATUS
|
||||
type: string
|
||||
- description: The rollout canary status step
|
||||
jsonPath: .status.canaryStatus.currentStepIndex
|
||||
name: CANARY_STEP
|
||||
type: integer
|
||||
- description: The rollout canary status step state
|
||||
jsonPath: .status.canaryStatus.currentStepState
|
||||
name: CANARY_STATE
|
||||
type: string
|
||||
- description: The rollout canary status message
|
||||
jsonPath: .status.message
|
||||
name: MESSAGE
|
||||
type: string
|
||||
- jsonPath: .metadata.creationTimestamp
|
||||
name: AGE
|
||||
type: date
|
||||
name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: Rollout is the Schema for the rollouts API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: RolloutSpec defines the desired state of Rollout
|
||||
properties:
|
||||
objectRef:
|
||||
description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
|
||||
Important: Run "make" to regenerate code after modifying this file
|
||||
ObjectRef indicates workload'
|
||||
properties:
|
||||
workloadRef:
|
||||
description: WorkloadRef contains enough information to let you
|
||||
identify a workload for Rollout Batch release of the bypass
|
||||
properties:
|
||||
apiVersion:
|
||||
description: API Version of the referent
|
||||
type: string
|
||||
kind:
|
||||
description: Kind of the referent
|
||||
type: string
|
||||
name:
|
||||
description: Name of the referent
|
||||
type: string
|
||||
required:
|
||||
- apiVersion
|
||||
- kind
|
||||
- name
|
||||
type: object
|
||||
type: object
|
||||
strategy:
|
||||
description: rollout strategy
|
||||
properties:
|
||||
canary:
|
||||
description: CanaryStrategy defines parameters for a Replica Based
|
||||
Canary
|
||||
properties:
|
||||
steps:
|
||||
description: Steps define the order of phases to execute release
|
||||
in batches(20%, 40%, 60%, 80%, 100%)
|
||||
items:
|
||||
description: CanaryStep defines a step of a canary workload.
|
||||
properties:
|
||||
pause:
|
||||
description: Pause defines a pause stage for a rollout,
|
||||
manual or auto
|
||||
properties:
|
||||
duration:
|
||||
description: Duration the amount of time to wait
|
||||
before moving to the next step.
|
||||
format: int32
|
||||
type: integer
|
||||
type: object
|
||||
replicas:
|
||||
anyOf:
|
||||
- type: integer
|
||||
- type: string
|
||||
description: 'Replicas is the number of expected canary
|
||||
pods in this batch it can be an absolute number (ex:
|
||||
5) or a percentage of total pods.'
|
||||
x-kubernetes-int-or-string: true
|
||||
weight:
|
||||
description: SetWeight sets what percentage of the canary
|
||||
pods should receive
|
||||
format: int32
|
||||
type: integer
|
||||
type: object
|
||||
type: array
|
||||
trafficRoutings:
|
||||
description: TrafficRoutings hosts all the supported service
|
||||
meshes supported to enable more fine-grained traffic routing
|
||||
todo current only support one
|
||||
items:
|
||||
description: TrafficRouting hosts all the different configuration
|
||||
for supported service meshes to enable more fine-grained
|
||||
traffic routing
|
||||
properties:
|
||||
gracePeriodSeconds:
|
||||
description: Optional duration in seconds the traffic
|
||||
provider(e.g. nginx ingress controller) consumes the
|
||||
service, ingress configuration changes gracefully.
|
||||
format: int32
|
||||
type: integer
|
||||
ingress:
|
||||
description: Ingress holds Ingress specific configuration
|
||||
to route traffic, e.g. Nginx, Alb.
|
||||
properties:
|
||||
name:
|
||||
description: Name refers to the name of an `Ingress`
|
||||
resource in the same namespace as the `Rollout`
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
service:
|
||||
description: Service holds the name of a service which
|
||||
selects pods with stable version and don't select
|
||||
any pods with canary version.
|
||||
type: string
|
||||
type:
|
||||
description: nginx, alb, istio etc.
|
||||
type: string
|
||||
required:
|
||||
- service
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
type: object
|
||||
paused:
|
||||
description: Paused indicates that the Rollout is paused. Default
|
||||
value is false
|
||||
type: boolean
|
||||
type: object
|
||||
required:
|
||||
- objectRef
|
||||
- strategy
|
||||
type: object
|
||||
status:
|
||||
description: RolloutStatus defines the observed state of Rollout
|
||||
properties:
|
||||
canaryStatus:
|
||||
description: Canary describes the state of the canary rollout
|
||||
properties:
|
||||
canaryReadyReplicas:
|
||||
description: CanaryReadyReplicas the numbers of ready canary revision
|
||||
pods
|
||||
format: int32
|
||||
type: integer
|
||||
canaryReplicas:
|
||||
description: CanaryReplicas the numbers of canary revision pods
|
||||
format: int32
|
||||
type: integer
|
||||
canaryRevision:
|
||||
description: CanaryRevision is calculated by rollout based on
|
||||
podTemplateHash, and the internal logic flow uses It may be
|
||||
different from rs podTemplateHash in different k8s versions,
|
||||
so it cannot be used as service selector label
|
||||
type: string
|
||||
canaryService:
|
||||
description: CanaryService holds the name of a service which selects
|
||||
pods with canary version and don't select any pods with stable
|
||||
version.
|
||||
type: string
|
||||
currentStepIndex:
|
||||
description: CurrentStepIndex defines the current step of the
|
||||
rollout is on. If the current step index is null, the controller
|
||||
will execute the rollout.
|
||||
format: int32
|
||||
type: integer
|
||||
currentStepState:
|
||||
type: string
|
||||
lastReadyTime:
|
||||
description: The last time this step pods is ready.
|
||||
format: date-time
|
||||
type: string
|
||||
message:
|
||||
type: string
|
||||
observedWorkloadGeneration:
|
||||
description: observedWorkloadGeneration is the most recent generation
|
||||
observed for this Rollout ref workload generation.
|
||||
format: int64
|
||||
type: integer
|
||||
podTemplateHash:
|
||||
description: pod template hash is used as service selector label
|
||||
type: string
|
||||
rolloutHash:
|
||||
description: RolloutHash from rollout.spec object
|
||||
type: string
|
||||
required:
|
||||
- canaryReadyReplicas
|
||||
- canaryReplicas
|
||||
- canaryService
|
||||
- currentStepState
|
||||
- podTemplateHash
|
||||
type: object
|
||||
conditions:
|
||||
description: Conditions a list of conditions a rollout can have.
|
||||
items:
|
||||
description: RolloutCondition describes the state of a rollout at
|
||||
a certain point.
|
||||
properties:
|
||||
lastTransitionTime:
|
||||
description: Last time the condition transitioned from one status
|
||||
to another.
|
||||
format: date-time
|
||||
type: string
|
||||
lastUpdateTime:
|
||||
description: The last time this condition was updated.
|
||||
format: date-time
|
||||
type: string
|
||||
message:
|
||||
description: A human readable message indicating details about
|
||||
the transition.
|
||||
type: string
|
||||
reason:
|
||||
description: The reason for the condition's last transition.
|
||||
type: string
|
||||
status:
|
||||
description: Phase of the condition, one of True, False, Unknown.
|
||||
type: string
|
||||
type:
|
||||
description: Type of rollout condition.
|
||||
type: string
|
||||
required:
|
||||
- message
|
||||
- reason
|
||||
- status
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
message:
|
||||
description: Message provides details on why the rollout is in its
|
||||
current phase
|
||||
type: string
|
||||
observedGeneration:
|
||||
description: observedGeneration is the most recent generation observed
|
||||
for this Rollout.
|
||||
format: int64
|
||||
type: integer
|
||||
phase:
|
||||
description: BlueGreenStatus *BlueGreenStatus `json:"blueGreenStatus,omitempty"`
|
||||
Phase is the rollout phase.
|
||||
type: string
|
||||
stableRevision:
|
||||
description: CanaryRevision the hash of the canary pod template CanaryRevision
|
||||
string `json:"canaryRevision,omitempty"` StableRevision indicates
|
||||
the revision pods that has successfully rolled out
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
plural: ""
|
||||
conditions: []
|
||||
storedVersions: []
|
||||
Binary file not shown.
|
|
@ -0,0 +1,8 @@
|
|||
apiVersion: v2
|
||||
appVersion: 1.0.1
|
||||
description: This is a test sample addon
|
||||
home: https://terraform.io/
|
||||
icon: https://www.terraform.io/assets/images/logo-text-8c3ba8a6.svg
|
||||
name: sample
|
||||
type: library
|
||||
version: 1.0.1
|
||||
|
|
@ -20,23 +20,16 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
k8stypes "k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/util/retry"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
oamcommon "github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/domain/service"
|
||||
"github.com/oam-dev/kubevela/pkg/controller/core.oam.dev/v1alpha2/application"
|
||||
"github.com/oam-dev/kubevela/pkg/controller/utils"
|
||||
"github.com/oam-dev/kubevela/pkg/multicluster"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/resourcetracker"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
velaerrors "github.com/oam-dev/kubevela/pkg/utils/errors"
|
||||
cmdutil "github.com/oam-dev/kubevela/pkg/utils/util"
|
||||
"github.com/oam-dev/kubevela/pkg/workflow/operation"
|
||||
"github.com/oam-dev/kubevela/references/appfile"
|
||||
)
|
||||
|
||||
|
|
@ -79,18 +72,19 @@ func NewWorkflowSuspendCommand(c common.Args, ioStream cmdutil.IOStreams) *cobra
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if app.Status.Workflow == nil {
|
||||
return fmt.Errorf("the workflow in application is not running")
|
||||
}
|
||||
client, err := c.GetClient()
|
||||
|
||||
config, err := c.GetConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = suspendWorkflow(client, app)
|
||||
config.Wrap(multicluster.NewSecretModeMultiClusterRoundTripper)
|
||||
cli, err := c.GetClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
wo := operation.NewWorkflowOperator(cli, cmd.OutOrStdout())
|
||||
return wo.Suspend(context.Background(), app)
|
||||
},
|
||||
}
|
||||
addNamespaceAndEnvArg(cmd)
|
||||
|
|
@ -116,29 +110,19 @@ func NewWorkflowResumeCommand(c common.Args, ioStream cmdutil.IOStreams) *cobra.
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if app.Status.Workflow == nil {
|
||||
return fmt.Errorf("the workflow in application is not running")
|
||||
|
||||
config, err := c.GetConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if app.Status.Workflow.Terminated {
|
||||
return fmt.Errorf("can not resume a terminated workflow")
|
||||
}
|
||||
if !app.Status.Workflow.Suspend {
|
||||
_, err := ioStream.Out.Write([]byte("the workflow is not suspending\n"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
client, err := c.GetClient()
|
||||
config.Wrap(multicluster.NewSecretModeMultiClusterRoundTripper)
|
||||
cli, err := c.GetClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = resumeWorkflow(client, app)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
wo := operation.NewWorkflowOperator(cli, cmd.OutOrStdout())
|
||||
return wo.Resume(context.Background(), app)
|
||||
},
|
||||
}
|
||||
addNamespaceAndEnvArg(cmd)
|
||||
|
|
@ -167,15 +151,12 @@ func NewWorkflowTerminateCommand(c common.Args, ioStream cmdutil.IOStreams) *cob
|
|||
if app.Status.Workflow == nil {
|
||||
return fmt.Errorf("the workflow in application is not running")
|
||||
}
|
||||
client, err := c.GetClient()
|
||||
cli, err := c.GetClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = terminateWorkflow(client, app)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
wo := operation.NewWorkflowOperator(cli, cmd.OutOrStdout())
|
||||
return wo.Terminate(context.Background(), app)
|
||||
},
|
||||
}
|
||||
addNamespaceAndEnvArg(cmd)
|
||||
|
|
@ -201,19 +182,18 @@ func NewWorkflowRestartCommand(c common.Args, ioStream cmdutil.IOStreams) *cobra
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if app.Status.Workflow == nil {
|
||||
return fmt.Errorf("the workflow in application is not running")
|
||||
config, err := c.GetConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client, err := c.GetClient()
|
||||
config.Wrap(multicluster.NewSecretModeMultiClusterRoundTripper)
|
||||
cli, err := c.GetClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = restartWorkflow(client, app)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
wo := operation.NewWorkflowOperator(cli, cmd.OutOrStdout())
|
||||
return wo.Restart(context.Background(), app)
|
||||
},
|
||||
}
|
||||
addNamespaceAndEnvArg(cmd)
|
||||
|
|
@ -239,205 +219,40 @@ func NewWorkflowRollbackCommand(c common.Args, ioStream cmdutil.IOStreams) *cobr
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config, err := c.GetConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config.Wrap(multicluster.NewSecretModeMultiClusterRoundTripper)
|
||||
cli, err := c.GetClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if app.Status.Workflow != nil && !app.Status.Workflow.Terminated && !app.Status.Workflow.Suspend && !app.Status.Workflow.Finished {
|
||||
return fmt.Errorf("can not rollback a running workflow")
|
||||
}
|
||||
client, err := c.GetClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if oam.GetPublishVersion(app) == "" {
|
||||
if app.Status.LatestRevision == nil || app.Status.LatestRevision.Name == "" {
|
||||
return fmt.Errorf("the latest revision is not set: %s", app.Name)
|
||||
}
|
||||
// get the last revision
|
||||
revision := &v1beta1.ApplicationRevision{}
|
||||
if err := cli.Get(context.TODO(), k8stypes.NamespacedName{Name: app.Status.LatestRevision.Name, Namespace: app.Namespace}, revision); err != nil {
|
||||
return fmt.Errorf("failed to get the latest revision: %w", err)
|
||||
}
|
||||
|
||||
err = rollbackWorkflow(cmd, client, app)
|
||||
if err != nil {
|
||||
return err
|
||||
app.Spec = revision.Spec.Application.Spec
|
||||
if err := cli.Status().Update(context.TODO(), app); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Successfully rollback workflow to the latest revision: %s\n", app.Name)
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
wo := operation.NewWorkflowOperator(cli, cmd.OutOrStdout())
|
||||
return wo.Rollback(context.Background(), app)
|
||||
},
|
||||
}
|
||||
addNamespaceAndEnvArg(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func suspendWorkflow(kubecli client.Client, app *v1beta1.Application) error {
|
||||
appKey := client.ObjectKeyFromObject(app)
|
||||
ctx := context.Background()
|
||||
if err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
|
||||
if err := kubecli.Get(ctx, appKey, app); err != nil {
|
||||
return err
|
||||
}
|
||||
// set the workflow suspend to true
|
||||
app.Status.Workflow.Suspend = true
|
||||
return kubecli.Status().Patch(ctx, app, client.Merge)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Successfully suspend workflow: %s\n", app.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
func resumeWorkflow(kubecli client.Client, app *v1beta1.Application) error {
|
||||
if err := service.ResumeWorkflow(context.TODO(), kubecli, app); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Successfully resume workflow: %s\n", app.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
func terminateWorkflow(kubecli client.Client, app *v1beta1.Application) error {
|
||||
if err := service.TerminateWorkflow(context.TODO(), kubecli, app); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Successfully terminate workflow: %s\n", app.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
func restartWorkflow(kubecli client.Client, app *v1beta1.Application) error {
|
||||
// reset the workflow status to restart the workflow
|
||||
app.Status.Workflow = nil
|
||||
|
||||
if err := kubecli.Status().Update(context.TODO(), app); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Successfully restart workflow: %s\n", app.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
func rollbackWorkflow(cmd *cobra.Command, kubecli client.Client, app *v1beta1.Application) error {
|
||||
if oam.GetPublishVersion(app) != "" {
|
||||
return rollbackApplicationWithPublishVersion(cmd, kubecli, app)
|
||||
}
|
||||
if app.Status.LatestRevision == nil || app.Status.LatestRevision.Name == "" {
|
||||
return fmt.Errorf("the latest revision is not set: %s", app.Name)
|
||||
}
|
||||
// get the last revision
|
||||
revision := &v1beta1.ApplicationRevision{}
|
||||
if err := kubecli.Get(context.TODO(), k8stypes.NamespacedName{Name: app.Status.LatestRevision.Name, Namespace: app.Namespace}, revision); err != nil {
|
||||
return fmt.Errorf("failed to get the latest revision: %w", err)
|
||||
}
|
||||
|
||||
app.Spec = revision.Spec.Application.Spec
|
||||
if err := kubecli.Status().Update(context.TODO(), app); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Successfully rollback workflow to the latest revision: %s\n", app.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
func rollbackApplicationWithPublishVersion(cmd *cobra.Command, cli client.Client, app *v1beta1.Application) error {
|
||||
ctx := context.Background()
|
||||
appRevs, err := application.GetSortedAppRevisions(ctx, cli, app.Name, app.Namespace)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to list revisions for application %s/%s", app.Namespace, app.Name)
|
||||
}
|
||||
|
||||
// find succeeded revision to rollback
|
||||
var rev *v1beta1.ApplicationRevision
|
||||
var outdatedRev []*v1beta1.ApplicationRevision
|
||||
for i := range appRevs {
|
||||
candidate := appRevs[len(appRevs)-i-1]
|
||||
_rev := candidate.DeepCopy()
|
||||
if !candidate.Status.Succeeded || oam.GetPublishVersion(_rev) == "" {
|
||||
outdatedRev = append(outdatedRev, _rev)
|
||||
continue
|
||||
}
|
||||
rev = _rev
|
||||
break
|
||||
}
|
||||
if rev == nil {
|
||||
return errors.Errorf("failed to find previous succeeded revision for application %s/%s", app.Namespace, app.Name)
|
||||
}
|
||||
publishVersion := oam.GetPublishVersion(rev)
|
||||
revisionNumber, err := utils.ExtractRevision(rev.Name)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to extract revision number from revision %s", rev.Name)
|
||||
}
|
||||
_, currentRT, historyRTs, _, err := resourcetracker.ListApplicationResourceTrackers(ctx, cli, app)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to list resource trackers for application %s/%s", app.Namespace, app.Name)
|
||||
}
|
||||
var matchRT *v1beta1.ResourceTracker
|
||||
for _, rt := range append(historyRTs, currentRT) {
|
||||
if rt == nil {
|
||||
continue
|
||||
}
|
||||
labels := rt.GetLabels()
|
||||
if labels != nil && labels[oam.LabelAppRevision] == rev.Name {
|
||||
matchRT = rt.DeepCopy()
|
||||
}
|
||||
}
|
||||
if matchRT == nil {
|
||||
return errors.Errorf("cannot find resource tracker for previous revision %s, unable to rollback", rev.Name)
|
||||
}
|
||||
if matchRT.DeletionTimestamp != nil {
|
||||
return errors.Errorf("previous revision %s is being recycled, unable to rollback", rev.Name)
|
||||
}
|
||||
cmd.Printf("Find succeeded application revision %s (PublishVersion: %s) to rollback.\n", rev.Name, publishVersion)
|
||||
|
||||
appKey := client.ObjectKeyFromObject(app)
|
||||
// rollback application spec and freeze
|
||||
controllerRequirement, err := utils.FreezeApplication(ctx, cli, app, func() {
|
||||
app.Spec = rev.Spec.Application.Spec
|
||||
oam.SetPublishVersion(app, publishVersion)
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to rollback application spec to revision %s (PublishVersion: %s)", rev.Name, publishVersion)
|
||||
}
|
||||
cmd.Printf("Application spec rollback successfully.\n")
|
||||
|
||||
// rollback application status
|
||||
if err = retry.RetryOnConflict(retry.DefaultBackoff, func() error {
|
||||
if err = cli.Get(ctx, appKey, app); err != nil {
|
||||
return err
|
||||
}
|
||||
app.Status.Workflow = rev.Status.Workflow
|
||||
app.Status.Services = []oamcommon.ApplicationComponentStatus{}
|
||||
app.Status.AppliedResources = []oamcommon.ClusterObjectReference{}
|
||||
for _, rsc := range matchRT.Spec.ManagedResources {
|
||||
app.Status.AppliedResources = append(app.Status.AppliedResources, rsc.ClusterObjectReference)
|
||||
}
|
||||
app.Status.LatestRevision = &oamcommon.Revision{
|
||||
Name: rev.Name,
|
||||
Revision: int64(revisionNumber),
|
||||
RevisionHash: rev.GetLabels()[oam.LabelAppRevisionHash],
|
||||
}
|
||||
return cli.Status().Update(ctx, app)
|
||||
}); err != nil {
|
||||
return errors.Wrapf(err, "failed to rollback application status to revision %s (PublishVersion: %s)", rev.Name, publishVersion)
|
||||
}
|
||||
cmd.Printf("Application status rollback successfully.\n")
|
||||
|
||||
// update resource tracker generation
|
||||
matchRTKey := client.ObjectKeyFromObject(matchRT)
|
||||
if err = retry.RetryOnConflict(retry.DefaultBackoff, func() error {
|
||||
if err = cli.Get(ctx, matchRTKey, matchRT); err != nil {
|
||||
return err
|
||||
}
|
||||
matchRT.Spec.ApplicationGeneration = app.Generation
|
||||
return cli.Update(ctx, matchRT)
|
||||
}); err != nil {
|
||||
return errors.Wrapf(err, "failed to update application generation in resource tracker")
|
||||
}
|
||||
|
||||
// unfreeze application
|
||||
if err = utils.UnfreezeApplication(ctx, cli, app, nil, controllerRequirement); err != nil {
|
||||
return errors.Wrapf(err, "failed to resume application to restart")
|
||||
}
|
||||
cmd.Printf("Application rollback completed.\n")
|
||||
|
||||
// clean up outdated revisions
|
||||
var errs velaerrors.ErrorList
|
||||
for _, _rev := range outdatedRev {
|
||||
if err = cli.Delete(ctx, _rev); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
if errs.HasError() {
|
||||
return errors.Wrapf(errs, "failed to clean up outdated revisions")
|
||||
}
|
||||
cmd.Printf("Application outdated revision cleaned up.\n")
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -225,6 +225,8 @@ func TestWorkflowResume(t *testing.T) {
|
|||
r := require.New(t)
|
||||
cmd := NewWorkflowResumeCommand(c, ioStream)
|
||||
initCommand(cmd)
|
||||
// clean up the arguments before start
|
||||
cmd.SetArgs([]string{})
|
||||
client, err := c.GetClient()
|
||||
r.NoError(err)
|
||||
if tc.app != nil {
|
||||
|
|
@ -352,6 +354,8 @@ func TestWorkflowTerminate(t *testing.T) {
|
|||
r := require.New(t)
|
||||
cmd := NewWorkflowTerminateCommand(c, ioStream)
|
||||
initCommand(cmd)
|
||||
// clean up the arguments before start
|
||||
cmd.SetArgs([]string{})
|
||||
client, err := c.GetClient()
|
||||
r.NoError(err)
|
||||
if tc.app != nil {
|
||||
|
|
@ -444,6 +448,8 @@ func TestWorkflowRestart(t *testing.T) {
|
|||
r := require.New(t)
|
||||
cmd := NewWorkflowRestartCommand(c, ioStream)
|
||||
initCommand(cmd)
|
||||
// clean up the arguments before start
|
||||
cmd.SetArgs([]string{})
|
||||
client, err := c.GetClient()
|
||||
r.NoError(err)
|
||||
if tc.app != nil {
|
||||
|
|
@ -567,6 +573,8 @@ func TestWorkflowRollback(t *testing.T) {
|
|||
r := require.New(t)
|
||||
cmd := NewWorkflowRollbackCommand(c, ioStream)
|
||||
initCommand(cmd)
|
||||
// clean up the arguments before start
|
||||
cmd.SetArgs([]string{})
|
||||
client, err := c.GetClient()
|
||||
r.NoError(err)
|
||||
if tc.app != nil {
|
||||
|
|
|
|||
Loading…
Reference in New Issue