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