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  }