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 }