sigs.k8s.io/cluster-api-provider-azure@v1.14.3/azure/services/scalesetvms/scalesetvms_test.go (about)

     1  /*
     2  Copyright 2021 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package scalesetvms
    18  
    19  import (
    20  	"context"
    21  	"io"
    22  	"net/http"
    23  	"strings"
    24  	"testing"
    25  
    26  	"github.com/Azure/azure-sdk-for-go/sdk/azcore"
    27  	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5"
    28  	. "github.com/onsi/gomega"
    29  	"go.uber.org/mock/gomock"
    30  	infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1"
    31  	"sigs.k8s.io/cluster-api-provider-azure/azure/converters"
    32  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/async/mock_async"
    33  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/scalesetvms/mock_scalesetvms"
    34  	gomockinternal "sigs.k8s.io/cluster-api-provider-azure/internal/test/matchers/gomock"
    35  )
    36  
    37  var (
    38  	uniformScaleSetVMSpec = &ScaleSetVMSpec{
    39  		Name:          "my-vmss",
    40  		InstanceID:    "0",
    41  		ResourceGroup: "my-rg",
    42  		ScaleSetName:  "my-vmss",
    43  		ProviderID:    "azure:///subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Compute/virtualMachineScaleSets/my-vmss/virtualMachines/0",
    44  		ResourceID:    "/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Compute/virtualMachineScaleSets/my-vmss/virtualMachines/0",
    45  		IsFlex:        false,
    46  	}
    47  	uniformScaleSetVM = armcompute.VirtualMachineScaleSetVM{
    48  		ID: &uniformScaleSetVMSpec.ResourceID,
    49  	}
    50  
    51  	flexScaleSetVMSpec = &ScaleSetVMSpec{
    52  		Name:          "my-vmss",
    53  		InstanceID:    "0",
    54  		ResourceGroup: "my-rg",
    55  		ScaleSetName:  "my-vmss",
    56  		ProviderID:    "azure:///subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Compute/virtualMachineScaleSets/my-vmss/virtualMachines/my-vmss",
    57  		ResourceID:    "/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Compute/virtualMachineScaleSets/my-vmss/virtualMachines/my-vmss",
    58  		IsFlex:        true,
    59  	}
    60  	flexGetter = &VMSSFlexGetter{
    61  		Name:          flexScaleSetVMSpec.Name,
    62  		ResourceGroup: flexScaleSetVMSpec.ResourceGroup,
    63  	}
    64  	flexScaleSetVM = armcompute.VirtualMachine{
    65  		Name: &uniformScaleSetVMSpec.Name,
    66  	}
    67  )
    68  
    69  func errInternal() *azcore.ResponseError {
    70  	return &azcore.ResponseError{
    71  		RawResponse: &http.Response{
    72  			Body:       io.NopCloser(strings.NewReader("#: Internal Server Error: StatusCode=500")),
    73  			StatusCode: http.StatusInternalServerError,
    74  		},
    75  	}
    76  }
    77  
    78  func TestReconcileVMSS(t *testing.T) {
    79  	testcases := []struct {
    80  		name          string
    81  		expect        func(g *WithT, s *mock_scalesetvms.MockScaleSetVMScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder, v *mock_async.MockReconcilerMockRecorder)
    82  		expectedError string
    83  	}{
    84  		{
    85  			name:          "get a uniform vmss vm",
    86  			expectedError: "",
    87  			expect: func(g *WithT, s *mock_scalesetvms.MockScaleSetVMScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder, v *mock_async.MockReconcilerMockRecorder) {
    88  				s.ScaleSetVMSpec().Return(uniformScaleSetVMSpec)
    89  				r.CreateOrUpdateResource(gomockinternal.AContext(), uniformScaleSetVMSpec, serviceName).Return(uniformScaleSetVM, nil)
    90  				s.SetVMSSVM(converters.SDKToVMSSVM(uniformScaleSetVM))
    91  			},
    92  		},
    93  		{
    94  			name:          "get a vmss flex vm",
    95  			expectedError: "",
    96  			expect: func(g *WithT, s *mock_scalesetvms.MockScaleSetVMScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder, v *mock_async.MockReconcilerMockRecorder) {
    97  				s.ScaleSetVMSpec().Return(flexScaleSetVMSpec)
    98  				v.CreateOrUpdateResource(gomockinternal.AContext(), flexGetter, serviceName).Return(flexScaleSetVM, nil)
    99  				s.SetVMSSVM(converters.SDKVMToVMSSVM(flexScaleSetVM, infrav1.FlexibleOrchestrationMode))
   100  			},
   101  		},
   102  		{
   103  			name:          "uniform vmss vm doesn't exist yet",
   104  			expectedError: "instance does not exist yet. Object will be requeued after 30s",
   105  			expect: func(g *WithT, s *mock_scalesetvms.MockScaleSetVMScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder, v *mock_async.MockReconcilerMockRecorder) {
   106  				s.ScaleSetVMSpec().Return(uniformScaleSetVMSpec)
   107  				r.CreateOrUpdateResource(gomockinternal.AContext(), uniformScaleSetVMSpec, serviceName).Return(nil, nil)
   108  			},
   109  		},
   110  		{
   111  			name:          "vmss flex vm doesn't exist yet",
   112  			expectedError: "instance does not exist yet. Object will be requeued after 30s",
   113  			expect: func(g *WithT, s *mock_scalesetvms.MockScaleSetVMScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder, v *mock_async.MockReconcilerMockRecorder) {
   114  				s.ScaleSetVMSpec().Return(flexScaleSetVMSpec)
   115  				v.CreateOrUpdateResource(gomockinternal.AContext(), flexGetter, serviceName).Return(nil, nil)
   116  			},
   117  		},
   118  		{
   119  			name:          "error getting uniform vmss vm",
   120  			expectedError: "#: Internal Server Error: StatusCode=500",
   121  			expect: func(g *WithT, s *mock_scalesetvms.MockScaleSetVMScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder, v *mock_async.MockReconcilerMockRecorder) {
   122  				s.ScaleSetVMSpec().Return(uniformScaleSetVMSpec)
   123  				r.CreateOrUpdateResource(gomockinternal.AContext(), uniformScaleSetVMSpec, serviceName).Return(nil, errInternal())
   124  			},
   125  		},
   126  		{
   127  			name:          "error getting vmss flex vm",
   128  			expectedError: "#: Internal Server Error: StatusCode=500",
   129  			expect: func(g *WithT, s *mock_scalesetvms.MockScaleSetVMScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder, v *mock_async.MockReconcilerMockRecorder) {
   130  				s.ScaleSetVMSpec().Return(flexScaleSetVMSpec)
   131  				v.CreateOrUpdateResource(gomockinternal.AContext(), flexGetter, serviceName).Return(nil, errInternal())
   132  			},
   133  		},
   134  	}
   135  
   136  	for _, tc := range testcases {
   137  		tc := tc
   138  		t.Run(tc.name, func(t *testing.T) {
   139  			g := NewWithT(t)
   140  			t.Parallel()
   141  			mockCtrl := gomock.NewController(t)
   142  			defer mockCtrl.Finish()
   143  
   144  			scopeMock := mock_scalesetvms.NewMockScaleSetVMScope(mockCtrl)
   145  			asyncMock := mock_async.NewMockReconciler(mockCtrl)
   146  			vmAsyncMock := mock_async.NewMockReconciler(mockCtrl)
   147  
   148  			tc.expect(g, scopeMock.EXPECT(), asyncMock.EXPECT(), vmAsyncMock.EXPECT())
   149  
   150  			s := &Service{
   151  				Scope:        scopeMock,
   152  				Reconciler:   asyncMock,
   153  				VMReconciler: vmAsyncMock,
   154  			}
   155  
   156  			err := s.Reconcile(context.TODO())
   157  			if tc.expectedError != "" {
   158  				g.Expect(err).To(HaveOccurred())
   159  				g.Expect(err.Error()).To(ContainSubstring(tc.expectedError), err.Error())
   160  			} else {
   161  				g.Expect(err).NotTo(HaveOccurred())
   162  			}
   163  		})
   164  	}
   165  }
   166  
   167  func TestDeleteVMSS(t *testing.T) {
   168  	testcases := []struct {
   169  		name          string
   170  		expect        func(g *WithT, s *mock_scalesetvms.MockScaleSetVMScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder, v *mock_async.MockReconcilerMockRecorder)
   171  		expectedError string
   172  	}{
   173  		{
   174  			name:          "delete a uniform vmss vm",
   175  			expectedError: "",
   176  			expect: func(g *WithT, s *mock_scalesetvms.MockScaleSetVMScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder, v *mock_async.MockReconcilerMockRecorder) {
   177  				s.ScaleSetVMSpec().Return(uniformScaleSetVMSpec)
   178  				r.DeleteResource(gomockinternal.AContext(), uniformScaleSetVMSpec, serviceName).Return(nil)
   179  				s.SetVMSSVMState(infrav1.Deleted)
   180  			},
   181  		},
   182  		{
   183  			name:          "delete a vmss flex vm",
   184  			expectedError: "",
   185  			expect: func(g *WithT, s *mock_scalesetvms.MockScaleSetVMScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder, v *mock_async.MockReconcilerMockRecorder) {
   186  				s.ScaleSetVMSpec().Return(flexScaleSetVMSpec)
   187  				v.DeleteResource(gomockinternal.AContext(), flexGetter, serviceName).Return(nil)
   188  				s.SetVMSSVMState(infrav1.Deleted)
   189  			},
   190  		},
   191  		{
   192  			name:          "error when deleting a uniform vmss vm",
   193  			expectedError: "#: Internal Server Error: StatusCode=500",
   194  			expect: func(g *WithT, s *mock_scalesetvms.MockScaleSetVMScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder, v *mock_async.MockReconcilerMockRecorder) {
   195  				s.ScaleSetVMSpec().Return(uniformScaleSetVMSpec)
   196  				r.DeleteResource(gomockinternal.AContext(), uniformScaleSetVMSpec, serviceName).Return(errInternal())
   197  				s.SetVMSSVMState(infrav1.Deleting)
   198  			},
   199  		},
   200  		{
   201  			name:          "error when deleting a vmss flex vm",
   202  			expectedError: "#: Internal Server Error: StatusCode=500",
   203  			expect: func(g *WithT, s *mock_scalesetvms.MockScaleSetVMScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder, v *mock_async.MockReconcilerMockRecorder) {
   204  				s.ScaleSetVMSpec().Return(flexScaleSetVMSpec)
   205  				v.DeleteResource(gomockinternal.AContext(), flexGetter, serviceName).Return(errInternal())
   206  				s.SetVMSSVMState(infrav1.Deleting)
   207  			},
   208  		},
   209  	}
   210  
   211  	for _, tc := range testcases {
   212  		tc := tc
   213  		t.Run(tc.name, func(t *testing.T) {
   214  			g := NewWithT(t)
   215  			t.Parallel()
   216  			mockCtrl := gomock.NewController(t)
   217  			defer mockCtrl.Finish()
   218  
   219  			scopeMock := mock_scalesetvms.NewMockScaleSetVMScope(mockCtrl)
   220  			asyncMock := mock_async.NewMockReconciler(mockCtrl)
   221  			vmAsyncMock := mock_async.NewMockReconciler(mockCtrl)
   222  
   223  			tc.expect(g, scopeMock.EXPECT(), asyncMock.EXPECT(), vmAsyncMock.EXPECT())
   224  
   225  			s := &Service{
   226  				Scope:        scopeMock,
   227  				Reconciler:   asyncMock,
   228  				VMReconciler: vmAsyncMock,
   229  			}
   230  
   231  			err := s.Delete(context.TODO())
   232  			if tc.expectedError != "" {
   233  				g.Expect(err).To(HaveOccurred())
   234  				g.Expect(err.Error()).To(ContainSubstring(tc.expectedError), err.Error())
   235  			} else {
   236  				g.Expect(err).NotTo(HaveOccurred())
   237  			}
   238  		})
   239  	}
   240  }