github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/controllers/apps/configuration/parallel_upgrade_policy_test.go (about)

     1  /*
     2  Copyright (C) 2022-2023 ApeCloud Co., Ltd
     3  
     4  This file is part of KubeBlocks project
     5  
     6  This program is free software: you can redistribute it and/or modify
     7  it under the terms of the GNU Affero General Public License as published by
     8  the Free Software Foundation, either version 3 of the License, or
     9  (at your option) any later version.
    10  
    11  This program is distributed in the hope that it will be useful
    12  but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  GNU Affero General Public License for more details.
    15  
    16  You should have received a copy of the GNU Affero General Public License
    17  along with this program.  If not, see <http://www.gnu.org/licenses/>.
    18  */
    19  
    20  package configuration
    21  
    22  import (
    23  	. "github.com/onsi/ginkgo/v2"
    24  	. "github.com/onsi/gomega"
    25  
    26  	"github.com/golang/mock/gomock"
    27  
    28  	appsv1alpha1 "github.com/1aal/kubeblocks/apis/apps/v1alpha1"
    29  	cfgcore "github.com/1aal/kubeblocks/pkg/configuration/core"
    30  	cfgproto "github.com/1aal/kubeblocks/pkg/configuration/proto"
    31  	mock_proto "github.com/1aal/kubeblocks/pkg/configuration/proto/mocks"
    32  	testutil "github.com/1aal/kubeblocks/pkg/testutil/k8s"
    33  )
    34  
    35  var parallelPolicy = parallelUpgradePolicy{}
    36  
    37  var _ = Describe("Reconfigure ParallelPolicy", func() {
    38  
    39  	var (
    40  		k8sMockClient     *testutil.K8sClientMockHelper
    41  		reconfigureClient *mock_proto.MockReconfigureClient
    42  	)
    43  
    44  	BeforeEach(func() {
    45  		k8sMockClient = testutil.NewK8sMockClient()
    46  		reconfigureClient = mock_proto.NewMockReconfigureClient(k8sMockClient.Controller())
    47  	})
    48  
    49  	AfterEach(func() {
    50  		k8sMockClient.Finish()
    51  	})
    52  
    53  	Context("parallel reconfigure policy test", func() {
    54  		It("Should success without error", func() {
    55  			Expect(parallelPolicy.GetPolicyName()).Should(BeEquivalentTo("parallel"))
    56  
    57  			// mock client update caller
    58  			k8sMockClient.MockPatchMethod(testutil.WithSucceed(testutil.WithTimes(3)))
    59  
    60  			reconfigureClient.EXPECT().StopContainer(gomock.Any(), gomock.Any()).Return(
    61  				&cfgproto.StopContainerResponse{}, nil).
    62  				Times(3)
    63  
    64  			mockParam := newMockReconfigureParams("parallelPolicy", k8sMockClient.Client(),
    65  				withGRPCClient(func(addr string) (cfgproto.ReconfigureClient, error) {
    66  					return reconfigureClient, nil
    67  				}),
    68  				withMockStatefulSet(3, nil),
    69  				withClusterComponent(3),
    70  				withConfigSpec("for_test", map[string]string{
    71  					"a": "b",
    72  				}),
    73  				withCDComponent(appsv1alpha1.Consensus, []appsv1alpha1.ComponentConfigSpec{{
    74  					ComponentTemplateSpec: appsv1alpha1.ComponentTemplateSpec{
    75  						Name:       "for_test",
    76  						VolumeName: "test_volume",
    77  					},
    78  				}}))
    79  
    80  			k8sMockClient.MockListMethod(testutil.WithListReturned(
    81  				testutil.WithConstructListReturnedResult(fromPodObjectList(
    82  					newMockPodsWithStatefulSet(&mockParam.ComponentUnits[0], 3),
    83  				))))
    84  
    85  			status, err := parallelPolicy.Upgrade(mockParam)
    86  			Expect(err).Should(Succeed())
    87  			Expect(status.Status).Should(BeEquivalentTo(ESNone))
    88  		})
    89  	})
    90  
    91  	Context("parallel reconfigure policy test with List pods failed", func() {
    92  		It("Should failed", func() {
    93  			mockParam := newMockReconfigureParams("parallelPolicy", k8sMockClient.Client(),
    94  				withGRPCClient(func(addr string) (cfgproto.ReconfigureClient, error) {
    95  					return reconfigureClient, nil
    96  				}),
    97  				withMockStatefulSet(3, nil),
    98  				withClusterComponent(3),
    99  				withConfigSpec("for_test", map[string]string{
   100  					"a": "b",
   101  				}),
   102  				withCDComponent(appsv1alpha1.Consensus, []appsv1alpha1.ComponentConfigSpec{{
   103  					ComponentTemplateSpec: appsv1alpha1.ComponentTemplateSpec{
   104  						Name:       "for_test",
   105  						VolumeName: "test_volume",
   106  					},
   107  				}}))
   108  
   109  			// first failed
   110  			getPodsError := cfgcore.MakeError("for grpc failed.")
   111  			k8sMockClient.MockListMethod(testutil.WithFailed(getPodsError))
   112  
   113  			status, err := parallelPolicy.Upgrade(mockParam)
   114  			// first failed
   115  			Expect(err).Should(BeEquivalentTo(getPodsError))
   116  			Expect(status.Status).Should(BeEquivalentTo(ESFailedAndRetry))
   117  		})
   118  	})
   119  
   120  	Context("parallel reconfigure policy test with stop container failed", func() {
   121  		It("Should failed", func() {
   122  			stopError := cfgcore.MakeError("failed to stop!")
   123  			reconfigureClient.EXPECT().StopContainer(gomock.Any(), gomock.Any()).Return(
   124  				&cfgproto.StopContainerResponse{}, stopError).
   125  				Times(1)
   126  
   127  			reconfigureClient.EXPECT().StopContainer(gomock.Any(), gomock.Any()).Return(
   128  				&cfgproto.StopContainerResponse{
   129  					ErrMessage: "failed to stop container.",
   130  				}, nil).
   131  				Times(1)
   132  
   133  			mockParam := newMockReconfigureParams("parallelPolicy", k8sMockClient.Client(),
   134  				withGRPCClient(func(addr string) (cfgproto.ReconfigureClient, error) {
   135  					return reconfigureClient, nil
   136  				}),
   137  				withMockStatefulSet(3, nil),
   138  				withClusterComponent(3),
   139  				withConfigSpec("for_test", map[string]string{
   140  					"a": "b",
   141  				}),
   142  				withCDComponent(appsv1alpha1.Consensus, []appsv1alpha1.ComponentConfigSpec{{
   143  					ComponentTemplateSpec: appsv1alpha1.ComponentTemplateSpec{
   144  						Name:       "for_test",
   145  						VolumeName: "test_volume",
   146  					}}}))
   147  
   148  			k8sMockClient.MockListMethod(testutil.WithListReturned(
   149  				testutil.WithConstructListReturnedResult(
   150  					fromPodObjectList(newMockPodsWithStatefulSet(&mockParam.ComponentUnits[0], 3))), testutil.WithTimes(2),
   151  			))
   152  
   153  			status, err := parallelPolicy.Upgrade(mockParam)
   154  			// first failed
   155  			Expect(err).Should(BeEquivalentTo(stopError))
   156  			Expect(status.Status).Should(BeEquivalentTo(ESFailedAndRetry))
   157  
   158  			status, err = parallelPolicy.Upgrade(mockParam)
   159  			Expect(err).ShouldNot(Succeed())
   160  			Expect(err.Error()).Should(ContainSubstring("failed to stop container"))
   161  			Expect(status.Status).Should(BeEquivalentTo(ESFailedAndRetry))
   162  		})
   163  	})
   164  
   165  	Context("parallel reconfigure policy test with patch failed", func() {
   166  		It("Should failed", func() {
   167  			// mock client update caller
   168  			patchError := cfgcore.MakeError("update failed!")
   169  			k8sMockClient.MockPatchMethod(testutil.WithFailed(patchError, testutil.WithTimes(1)))
   170  
   171  			reconfigureClient.EXPECT().StopContainer(gomock.Any(), gomock.Any()).Return(
   172  				&cfgproto.StopContainerResponse{}, nil).
   173  				Times(1)
   174  
   175  			mockParam := newMockReconfigureParams("parallelPolicy", k8sMockClient.Client(),
   176  				withGRPCClient(func(addr string) (cfgproto.ReconfigureClient, error) {
   177  					return reconfigureClient, nil
   178  				}),
   179  				withMockStatefulSet(3, nil),
   180  				withClusterComponent(3),
   181  				withConfigSpec("for_test", map[string]string{
   182  					"a": "b",
   183  				}),
   184  				withCDComponent(appsv1alpha1.Consensus, []appsv1alpha1.ComponentConfigSpec{{
   185  					ComponentTemplateSpec: appsv1alpha1.ComponentTemplateSpec{
   186  						Name:       "for_test",
   187  						VolumeName: "test_volume",
   188  					}}}))
   189  
   190  			setPods := newMockPodsWithStatefulSet(&mockParam.ComponentUnits[0], 5)
   191  			k8sMockClient.MockListMethod(testutil.WithListReturned(
   192  				testutil.WithConstructListReturnedResult(fromPodObjectList(setPods)), testutil.WithAnyTimes(),
   193  			))
   194  
   195  			status, err := parallelPolicy.Upgrade(mockParam)
   196  			// first failed
   197  			Expect(err).Should(BeEquivalentTo(patchError))
   198  			Expect(status.Status).Should(BeEquivalentTo(ESFailedAndRetry))
   199  		})
   200  	})
   201  
   202  	Context("parallel reconfigure policy test for not supported component", func() {
   203  		It("Should failed", func() {
   204  			// not support type
   205  			mockParam := newMockReconfigureParams("parallelPolicy", nil,
   206  				withMockStatefulSet(2, nil),
   207  				withConfigSpec("for_test", map[string]string{
   208  					"key": "value",
   209  				}),
   210  				withClusterComponent(2),
   211  				withCDComponent(appsv1alpha1.Stateless, []appsv1alpha1.ComponentConfigSpec{{
   212  					ComponentTemplateSpec: appsv1alpha1.ComponentTemplateSpec{
   213  						Name:       "for_test",
   214  						VolumeName: "test_volume",
   215  					}}}))
   216  			status, err := parallelPolicy.Upgrade(mockParam)
   217  			Expect(err).ShouldNot(Succeed())
   218  			Expect(err.Error()).Should(ContainSubstring("not supported component workload type"))
   219  			Expect(status.Status).Should(BeEquivalentTo(ESNotSupport))
   220  		})
   221  	})
   222  })