sigs.k8s.io/cluster-api-provider-azure@v1.14.3/azure/services/roleassignments/roleassignments_test.go (about) 1 /* 2 Copyright 2019 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 roleassignments 18 19 import ( 20 "context" 21 "fmt" 22 "io" 23 "net/http" 24 "strings" 25 "testing" 26 27 "github.com/Azure/azure-sdk-for-go/sdk/azcore" 28 "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5" 29 . "github.com/onsi/gomega" 30 "go.uber.org/mock/gomock" 31 "k8s.io/utils/ptr" 32 "sigs.k8s.io/cluster-api-provider-azure/azure" 33 "sigs.k8s.io/cluster-api-provider-azure/azure/services/async/mock_async" 34 "sigs.k8s.io/cluster-api-provider-azure/azure/services/roleassignments/mock_roleassignments" 35 "sigs.k8s.io/cluster-api-provider-azure/azure/services/scalesets" 36 "sigs.k8s.io/cluster-api-provider-azure/azure/services/scalesets/mock_scalesets" 37 "sigs.k8s.io/cluster-api-provider-azure/azure/services/virtualmachines" 38 gomockinternal "sigs.k8s.io/cluster-api-provider-azure/internal/test/matchers/gomock" 39 "sigs.k8s.io/cluster-api-provider-azure/util/reconciler" 40 ) 41 42 var ( 43 fakeVMSpec = virtualmachines.VMSpec{ 44 Name: "test-vm", 45 ResourceGroup: "my-rg", 46 } 47 fakePrincipalID = "fake-p-id" 48 fakeRoleAssignment1 = RoleAssignmentSpec{ 49 MachineName: "test-vm", 50 ResourceGroup: "my-rg", 51 ResourceType: azure.VirtualMachine, 52 PrincipalID: ptr.To("fake-principal-id"), 53 } 54 fakeRoleAssignment2 = RoleAssignmentSpec{ 55 MachineName: "test-vmss", 56 ResourceGroup: "my-rg", 57 ResourceType: azure.VirtualMachineScaleSet, 58 } 59 60 emptyRoleAssignmentSpec = RoleAssignmentSpec{} 61 fakeRoleAssignmentSpecs = []azure.ResourceSpecGetter{&fakeRoleAssignment1, &fakeRoleAssignment2, &emptyRoleAssignmentSpec} 62 fakeVMSSSpec = scalesets.ScaleSetSpec{ 63 Name: "test-vmss", 64 ResourceGroup: "my-rg", 65 } 66 ) 67 68 func internalError() *azcore.ResponseError { 69 return &azcore.ResponseError{ 70 RawResponse: &http.Response{ 71 Body: io.NopCloser(strings.NewReader("#: Internal Server Error: StatusCode=500")), 72 StatusCode: http.StatusInternalServerError, 73 }, 74 } 75 } 76 77 func TestReconcileRoleAssignmentsVM(t *testing.T) { 78 testcases := []struct { 79 name string 80 expect func(s *mock_roleassignments.MockRoleAssignmentScopeMockRecorder, m *mock_async.MockGetterMockRecorder, r *mock_async.MockReconcilerMockRecorder) 81 expectedError string 82 }{ 83 { 84 name: "create a role assignment", 85 expectedError: "", 86 expect: func(s *mock_roleassignments.MockRoleAssignmentScopeMockRecorder, 87 m *mock_async.MockGetterMockRecorder, 88 r *mock_async.MockReconcilerMockRecorder) { 89 s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout) 90 s.SubscriptionID().AnyTimes().Return("12345") 91 s.ResourceGroup().Return("my-rg") 92 s.Name().Return(fakeRoleAssignment1.MachineName) 93 s.HasSystemAssignedIdentity().Return(true) 94 s.RoleAssignmentResourceType().Return("VirtualMachine") 95 s.RoleAssignmentSpecs(&fakePrincipalID).Return(fakeRoleAssignmentSpecs[:1]) 96 m.Get(gomockinternal.AContext(), &fakeVMSpec).Return(armcompute.VirtualMachine{ 97 Identity: &armcompute.VirtualMachineIdentity{ 98 PrincipalID: &fakePrincipalID, 99 }, 100 }, nil) 101 r.CreateOrUpdateResource(gomockinternal.AContext(), &fakeRoleAssignment1, serviceName).Return(&fakeRoleAssignment1, nil) 102 }, 103 }, 104 { 105 name: "error getting VM", 106 expectedError: "failed to assign role to system assigned identity: failed to get principal ID for VM:.*#: Internal Server Error: StatusCode=500", 107 expect: func(s *mock_roleassignments.MockRoleAssignmentScopeMockRecorder, 108 m *mock_async.MockGetterMockRecorder, 109 r *mock_async.MockReconcilerMockRecorder) { 110 s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout) 111 s.SubscriptionID().AnyTimes().Return("12345") 112 s.ResourceGroup().Return("my-rg") 113 s.Name().Return(fakeRoleAssignment1.MachineName) 114 s.HasSystemAssignedIdentity().Return(true) 115 s.RoleAssignmentResourceType().Return("VirtualMachine") 116 m.Get(gomockinternal.AContext(), &fakeVMSpec).Return(armcompute.VirtualMachine{}, internalError()) 117 }, 118 }, 119 { 120 name: "return error when creating a role assignment", 121 expectedError: "cannot assign role to VirtualMachine system assigned identity:.*#: Internal Server Error: StatusCode=500", 122 expect: func(s *mock_roleassignments.MockRoleAssignmentScopeMockRecorder, 123 m *mock_async.MockGetterMockRecorder, 124 r *mock_async.MockReconcilerMockRecorder) { 125 s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout) 126 s.SubscriptionID().AnyTimes().Return("12345") 127 s.ResourceGroup().Return("my-rg") 128 s.Name().Return(fakeRoleAssignment1.MachineName) 129 s.RoleAssignmentResourceType().Return("VirtualMachine") 130 s.HasSystemAssignedIdentity().Return(true) 131 s.RoleAssignmentSpecs(&fakePrincipalID).Return(fakeRoleAssignmentSpecs[0:1]) 132 m.Get(gomockinternal.AContext(), &fakeVMSpec).Return(armcompute.VirtualMachine{ 133 Identity: &armcompute.VirtualMachineIdentity{ 134 PrincipalID: &fakePrincipalID, 135 }, 136 }, nil) 137 r.CreateOrUpdateResource(gomockinternal.AContext(), &fakeRoleAssignment1, serviceName).Return(&RoleAssignmentSpec{}, 138 internalError()) 139 }, 140 }, 141 } 142 143 for _, tc := range testcases { 144 tc := tc 145 t.Run(tc.name, func(t *testing.T) { 146 g := NewWithT(t) 147 t.Parallel() 148 mockCtrl := gomock.NewController(t) 149 defer mockCtrl.Finish() 150 scopeMock := mock_roleassignments.NewMockRoleAssignmentScope(mockCtrl) 151 vmGetterMock := mock_async.NewMockGetter(mockCtrl) 152 asyncMock := mock_async.NewMockReconciler(mockCtrl) 153 154 tc.expect(scopeMock.EXPECT(), vmGetterMock.EXPECT(), asyncMock.EXPECT()) 155 156 s := &Service{ 157 Scope: scopeMock, 158 virtualMachinesGetter: vmGetterMock, 159 Reconciler: asyncMock, 160 } 161 162 err := s.Reconcile(context.TODO()) 163 if tc.expectedError != "" { 164 g.Expect(err).To(HaveOccurred()) 165 g.Expect(strings.ReplaceAll(err.Error(), "\n", "")).To(MatchRegexp(tc.expectedError)) 166 } else { 167 g.Expect(err).NotTo(HaveOccurred()) 168 } 169 }) 170 } 171 } 172 173 func TestReconcileRoleAssignmentsVMSS(t *testing.T) { 174 testcases := []struct { 175 name string 176 expect func(s *mock_roleassignments.MockRoleAssignmentScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder, 177 mvmss *mock_scalesets.MockClientMockRecorder) 178 expectedError string 179 }{ 180 { 181 name: "create a role assignment", 182 expectedError: "", 183 expect: func(s *mock_roleassignments.MockRoleAssignmentScopeMockRecorder, 184 r *mock_async.MockReconcilerMockRecorder, 185 mvmss *mock_scalesets.MockClientMockRecorder) { 186 s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout) 187 s.HasSystemAssignedIdentity().Return(true) 188 s.RoleAssignmentSpecs(&fakePrincipalID).Return(fakeRoleAssignmentSpecs[1:2]) 189 s.RoleAssignmentResourceType().Return(azure.VirtualMachineScaleSet) 190 s.ResourceGroup().Return("my-rg") 191 s.Name().Return("test-vmss") 192 mvmss.Get(gomockinternal.AContext(), &fakeVMSSSpec).Return(armcompute.VirtualMachineScaleSet{ 193 Identity: &armcompute.VirtualMachineScaleSetIdentity{ 194 PrincipalID: &fakePrincipalID, 195 }, 196 }, nil) 197 r.CreateOrUpdateResource(gomockinternal.AContext(), &fakeRoleAssignment2, serviceName).Return(&fakeRoleAssignment2, nil) 198 }, 199 }, 200 { 201 name: "error getting VMSS", 202 expectedError: "failed to assign role to system assigned identity: failed to get principal ID for VMSS:.*#: Internal Server Error: StatusCode=500", 203 expect: func(s *mock_roleassignments.MockRoleAssignmentScopeMockRecorder, 204 r *mock_async.MockReconcilerMockRecorder, 205 mvmss *mock_scalesets.MockClientMockRecorder) { 206 s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout) 207 s.RoleAssignmentResourceType().Return(azure.VirtualMachineScaleSet) 208 s.ResourceGroup().Return("my-rg") 209 s.Name().Return("test-vmss") 210 s.HasSystemAssignedIdentity().Return(true) 211 mvmss.Get(gomockinternal.AContext(), &fakeVMSSSpec).Return(armcompute.VirtualMachineScaleSet{}, 212 internalError()) 213 }, 214 }, 215 { 216 name: "return error when creating a role assignment", 217 expectedError: fmt.Sprintf("cannot assign role to %s system assigned identity:.*#: Internal Server Error: StatusCode=500", azure.VirtualMachineScaleSet), 218 expect: func(s *mock_roleassignments.MockRoleAssignmentScopeMockRecorder, 219 r *mock_async.MockReconcilerMockRecorder, 220 mvmss *mock_scalesets.MockClientMockRecorder) { 221 s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout) 222 s.HasSystemAssignedIdentity().Return(true) 223 s.RoleAssignmentSpecs(&fakePrincipalID).Return(fakeRoleAssignmentSpecs[1:2]) 224 s.RoleAssignmentResourceType().Return(azure.VirtualMachineScaleSet) 225 s.ResourceGroup().Return("my-rg") 226 s.Name().Return("test-vmss") 227 mvmss.Get(gomockinternal.AContext(), &fakeVMSSSpec).Return(armcompute.VirtualMachineScaleSet{ 228 Identity: &armcompute.VirtualMachineScaleSetIdentity{ 229 PrincipalID: &fakePrincipalID, 230 }, 231 }, nil) 232 r.CreateOrUpdateResource(gomockinternal.AContext(), &fakeRoleAssignment2, serviceName).Return(&RoleAssignmentSpec{}, 233 internalError()) 234 }, 235 }, 236 } 237 238 for _, tc := range testcases { 239 tc := tc 240 t.Run(tc.name, func(t *testing.T) { 241 g := NewWithT(t) 242 t.Parallel() 243 mockCtrl := gomock.NewController(t) 244 defer mockCtrl.Finish() 245 scopeMock := mock_roleassignments.NewMockRoleAssignmentScope(mockCtrl) 246 asyncMock := mock_async.NewMockReconciler(mockCtrl) 247 vmMock := mock_scalesets.NewMockClient(mockCtrl) 248 249 tc.expect(scopeMock.EXPECT(), asyncMock.EXPECT(), vmMock.EXPECT()) 250 251 s := &Service{ 252 Scope: scopeMock, 253 Reconciler: asyncMock, 254 virtualMachineScaleSetGetter: vmMock, 255 } 256 257 err := s.Reconcile(context.TODO()) 258 if tc.expectedError != "" { 259 g.Expect(err).To(HaveOccurred()) 260 g.Expect(strings.ReplaceAll(err.Error(), "\n", "")).To(MatchRegexp(tc.expectedError)) 261 } else { 262 g.Expect(err).NotTo(HaveOccurred()) 263 } 264 }) 265 } 266 }