@@ -30,9 +30,12 @@ import (
30
30
"github.com/stretchr/testify/assert"
31
31
32
32
"k8s.io/apimachinery/pkg/types"
33
+ "k8s.io/apimachinery/pkg/util/wait"
33
34
cloudprovider "k8s.io/cloud-provider"
34
35
"k8s.io/legacy-cloud-providers/azure/clients/diskclient/mockdiskclient"
35
36
"k8s.io/legacy-cloud-providers/azure/clients/vmclient/mockvmclient"
37
+ "k8s.io/legacy-cloud-providers/azure/clients/vmssclient/mockvmssclient"
38
+ "k8s.io/legacy-cloud-providers/azure/clients/vmssvmclient/mockvmssvmclient"
36
39
"k8s.io/legacy-cloud-providers/azure/retry"
37
40
"k8s.io/utils/pointer"
38
41
)
@@ -41,11 +44,18 @@ func TestCommonAttachDisk(t *testing.T) {
41
44
ctrl := gomock .NewController (t )
42
45
defer ctrl .Finish ()
43
46
47
+ maxShare := int32 (1 )
48
+ goodInstanceID := fmt .Sprintf ("/subscriptions/subscription/resourceGroups/rg/providers/Microsoft.Compute/virtualMachines/%s" , "vm1" )
49
+ diskEncryptionSetID := fmt .Sprintf ("/subscriptions/subscription/resourceGroups/rg/providers/Microsoft.Compute/diskEncryptionSets/%s" , "diskEncryptionSet-name" )
50
+ testTags := make (map [string ]* string )
51
+ testTags [WriteAcceleratorEnabled ] = to .StringPtr ("true" )
44
52
testCases := []struct {
45
53
desc string
46
54
vmList map [string ]string
47
55
nodeName types.NodeName
48
56
isDataDisksFull bool
57
+ isBadDiskURI bool
58
+ isDiskUsed bool
49
59
existedDisk compute.Disk
50
60
expectedLun int32
51
61
expectedErr bool
@@ -70,10 +80,35 @@ func TestCommonAttachDisk(t *testing.T) {
70
80
desc : "correct LUN and no error shall be returned if everything is good" ,
71
81
vmList : map [string ]string {"vm1" : "PowerState/Running" },
72
82
nodeName : "vm1" ,
73
- existedDisk : compute.Disk {Name : to .StringPtr ("disk-name" )},
83
+ existedDisk : compute.Disk {Name : to .StringPtr ("disk-name" ), DiskProperties : & compute. DiskProperties { Encryption : & compute. Encryption { DiskEncryptionSetID : & diskEncryptionSetID , Type : compute . EncryptionAtRestWithCustomerKey }}, Tags : testTags },
74
84
expectedLun : 1 ,
75
85
expectedErr : false ,
76
86
},
87
+ {
88
+ desc : "an error shall be returned if there's invalid disk uri" ,
89
+ vmList : map [string ]string {"vm1" : "PowerState/Running" },
90
+ nodeName : "vm1" ,
91
+ isBadDiskURI : true ,
92
+ existedDisk : compute.Disk {Name : to .StringPtr ("disk-name" )},
93
+ expectedLun : - 1 ,
94
+ expectedErr : true ,
95
+ },
96
+ {
97
+ desc : "an error shall be returned if attach an already attached disk with good ManagedBy instance id" ,
98
+ vmList : map [string ]string {"vm1" : "PowerState/Running" },
99
+ nodeName : "vm1" ,
100
+ existedDisk : compute.Disk {Name : to .StringPtr ("disk-name" ), ManagedBy : to .StringPtr (goodInstanceID ), DiskProperties : & compute.DiskProperties {MaxShares : & maxShare }},
101
+ expectedLun : - 1 ,
102
+ expectedErr : true ,
103
+ },
104
+ {
105
+ desc : "an error shall be returned if attach an already attached disk with bad ManagedBy instance id" ,
106
+ vmList : map [string ]string {"vm1" : "PowerState/Running" },
107
+ nodeName : "vm1" ,
108
+ existedDisk : compute.Disk {Name : to .StringPtr ("disk-name" ), ManagedBy : to .StringPtr ("test" ), DiskProperties : & compute.DiskProperties {MaxShares : & maxShare }},
109
+ expectedLun : - 1 ,
110
+ expectedErr : true ,
111
+ },
77
112
{
78
113
desc : "an error shall be returned if there's no matching disk" ,
79
114
vmList : map [string ]string {"vm1" : "PowerState/Running" },
@@ -96,7 +131,15 @@ func TestCommonAttachDisk(t *testing.T) {
96
131
}
97
132
diskURI := fmt .Sprintf ("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/disks/%s" ,
98
133
testCloud .SubscriptionID , testCloud .ResourceGroup , * test .existedDisk .Name )
134
+ if test .isBadDiskURI {
135
+ diskURI = fmt .Sprintf ("/baduri/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/disks/%s" ,
136
+ testCloud .SubscriptionID , testCloud .ResourceGroup , * test .existedDisk .Name )
137
+ }
99
138
expectedVMs := setTestVirtualMachines (testCloud , test .vmList , test .isDataDisksFull )
139
+ if test .isDiskUsed {
140
+ vm0 := setTestVirtualMachines (testCloud , map [string ]string {"vm0" : "PowerState/Running" }, test .isDataDisksFull )[0 ]
141
+ expectedVMs = append (expectedVMs , vm0 )
142
+ }
100
143
mockVMsClient := testCloud .VirtualMachinesClient .(* mockvmclient.MockInterface )
101
144
for _ , vm := range expectedVMs {
102
145
mockVMsClient .EXPECT ().Get (gomock .Any (), testCloud .ResourceGroup , * vm .Name , gomock .Any ()).Return (vm , nil ).AnyTimes ()
@@ -116,22 +159,128 @@ func TestCommonAttachDisk(t *testing.T) {
116
159
}
117
160
}
118
161
162
+ func TestCommonAttachDiskWithVMSS (t * testing.T ) {
163
+ ctrl := gomock .NewController (t )
164
+ defer ctrl .Finish ()
165
+
166
+ testCases := []struct {
167
+ desc string
168
+ vmList map [string ]string
169
+ vmssList []string
170
+ nodeName types.NodeName
171
+ isVMSS bool
172
+ isManagedBy bool
173
+ isManagedDisk bool
174
+ isDataDisksFull bool
175
+ existedDisk compute.Disk
176
+ expectedLun int32
177
+ expectedErr bool
178
+ }{
179
+ {
180
+ desc : "an error shall be returned if convert vmSet to scaleSet failed" ,
181
+ vmList : map [string ]string {"vm1" : "PowerState/Running" },
182
+ nodeName : "vm1" ,
183
+ isVMSS : false ,
184
+ isManagedBy : false ,
185
+ isManagedDisk : false ,
186
+ existedDisk : compute.Disk {Name : to .StringPtr ("disk-name" )},
187
+ expectedLun : - 1 ,
188
+ expectedErr : true ,
189
+ },
190
+ {
191
+ desc : "an error shall be returned if convert vmSet to scaleSet success but node is not managed by availability set" ,
192
+ vmssList : []string {"vmss-vm-000001" },
193
+ nodeName : "vmss1" ,
194
+ isVMSS : true ,
195
+ isManagedBy : false ,
196
+ isManagedDisk : false ,
197
+ existedDisk : compute.Disk {Name : to .StringPtr ("disk-name" )},
198
+ expectedLun : - 1 ,
199
+ expectedErr : true ,
200
+ },
201
+ }
202
+
203
+ for i , test := range testCases {
204
+ testCloud := GetTestCloud (ctrl )
205
+ testCloud .VMType = vmTypeVMSS
206
+ if test .isVMSS {
207
+ if test .isManagedBy {
208
+ testCloud .DisableAvailabilitySetNodes = false
209
+ testVMSSName := "vmss"
210
+ expectedVMSS := compute.VirtualMachineScaleSet {Name : to .StringPtr (testVMSSName )}
211
+ mockVMSSClient := testCloud .VirtualMachineScaleSetsClient .(* mockvmssclient.MockInterface )
212
+ mockVMSSClient .EXPECT ().List (gomock .Any (), testCloud .ResourceGroup ).Return ([]compute.VirtualMachineScaleSet {expectedVMSS }, nil ).AnyTimes ()
213
+
214
+ expectedVMSSVMs , _ , _ := buildTestVirtualMachineEnv (testCloud , testVMSSName , "" , 0 , test .vmssList , "" )
215
+ mockVMSSVMClient := testCloud .VirtualMachineScaleSetVMsClient .(* mockvmssvmclient.MockInterface )
216
+ mockVMSSVMClient .EXPECT ().List (gomock .Any (), testCloud .ResourceGroup , testVMSSName , gomock .Any ()).Return (expectedVMSSVMs , nil ).AnyTimes ()
217
+
218
+ mockVMsClient := testCloud .VirtualMachinesClient .(* mockvmclient.MockInterface )
219
+ mockVMsClient .EXPECT ().List (gomock .Any (), gomock .Any ()).Return ([]compute.VirtualMachine {}, nil ).AnyTimes ()
220
+ } else {
221
+ testCloud .DisableAvailabilitySetNodes = true
222
+ }
223
+ ss , err := newScaleSet (testCloud )
224
+ assert .Nil (t , err )
225
+ testCloud .vmSet = ss
226
+ }
227
+
228
+ common := & controllerCommon {
229
+ location : testCloud .Location ,
230
+ storageEndpointSuffix : testCloud .Environment .StorageEndpointSuffix ,
231
+ resourceGroup : testCloud .ResourceGroup ,
232
+ subscriptionID : testCloud .SubscriptionID ,
233
+ cloud : testCloud ,
234
+ vmLockMap : newLockMap (),
235
+ }
236
+ diskURI := fmt .Sprintf ("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/disks/%s" ,
237
+ testCloud .SubscriptionID , testCloud .ResourceGroup , * test .existedDisk .Name )
238
+ if ! test .isVMSS {
239
+ expectedVMs := setTestVirtualMachines (testCloud , test .vmList , test .isDataDisksFull )
240
+ mockVMsClient := testCloud .VirtualMachinesClient .(* mockvmclient.MockInterface )
241
+ for _ , vm := range expectedVMs {
242
+ mockVMsClient .EXPECT ().Get (gomock .Any (), testCloud .ResourceGroup , * vm .Name , gomock .Any ()).Return (vm , nil ).AnyTimes ()
243
+ }
244
+ if len (expectedVMs ) == 0 {
245
+ mockVMsClient .EXPECT ().Get (gomock .Any (), testCloud .ResourceGroup , gomock .Any (), gomock .Any ()).Return (compute.VirtualMachine {}, & retry.Error {HTTPStatusCode : http .StatusNotFound , RawError : cloudprovider .InstanceNotFound }).AnyTimes ()
246
+ }
247
+ mockVMsClient .EXPECT ().Update (gomock .Any (), testCloud .ResourceGroup , gomock .Any (), gomock .Any (), gomock .Any ()).Return (nil ).AnyTimes ()
248
+
249
+ mockDisksClient := testCloud .DisksClient .(* mockdiskclient.MockInterface )
250
+ mockDisksClient .EXPECT ().Get (gomock .Any (), testCloud .ResourceGroup , "disk-name" ).Return (test .existedDisk , nil ).AnyTimes ()
251
+ mockDisksClient .EXPECT ().Get (gomock .Any (), testCloud .ResourceGroup , gomock .Not ("disk-name" )).Return (compute.Disk {}, & retry.Error {HTTPStatusCode : http .StatusNotFound , RawError : cloudprovider .InstanceNotFound }).AnyTimes ()
252
+ }
253
+
254
+ lun , err := common .AttachDisk (test .isManagedDisk , "test" , diskURI , test .nodeName , compute .CachingTypesReadOnly )
255
+ assert .Equal (t , test .expectedLun , lun , "TestCase[%d]: %s" , i , test .desc )
256
+ assert .Equal (t , test .expectedErr , err != nil , "TestCase[%d]: %s, return error: %v" , i , test .desc , err )
257
+ }
258
+ }
259
+
119
260
func TestCommonDetachDisk (t * testing.T ) {
120
261
ctrl := gomock .NewController (t )
121
262
defer ctrl .Finish ()
122
263
123
264
testCases := []struct {
124
- desc string
125
- vmList map [string ]string
126
- nodeName types.NodeName
127
- diskName string
128
- expectedErr bool
265
+ desc string
266
+ vmList map [string ]string
267
+ nodeName types.NodeName
268
+ diskName string
269
+ isErrorRetriable bool
270
+ expectedErr bool
129
271
}{
130
272
{
131
273
desc : "error should not be returned if there's no such instance corresponding to given nodeName" ,
132
274
nodeName : "vm1" ,
133
275
expectedErr : false ,
134
276
},
277
+ {
278
+ desc : "an error should be returned if vmset detach failed with isErrorRetriable error" ,
279
+ vmList : map [string ]string {"vm1" : "PowerState/Running" },
280
+ nodeName : "vm1" ,
281
+ isErrorRetriable : true ,
282
+ expectedErr : true ,
283
+ },
135
284
{
136
285
desc : "no error shall be returned if there's no matching disk according to given diskName" ,
137
286
vmList : map [string ]string {"vm1" : "PowerState/Running" },
@@ -168,7 +317,13 @@ func TestCommonDetachDisk(t *testing.T) {
168
317
if len (expectedVMs ) == 0 {
169
318
mockVMsClient .EXPECT ().Get (gomock .Any (), testCloud .ResourceGroup , gomock .Any (), gomock .Any ()).Return (compute.VirtualMachine {}, & retry.Error {HTTPStatusCode : http .StatusNotFound , RawError : cloudprovider .InstanceNotFound }).AnyTimes ()
170
319
}
171
- mockVMsClient .EXPECT ().Update (gomock .Any (), testCloud .ResourceGroup , gomock .Any (), gomock .Any (), gomock .Any ()).Return (nil ).AnyTimes ()
320
+ if test .isErrorRetriable {
321
+ testCloud .CloudProviderBackoff = true
322
+ testCloud .ResourceRequestBackoff = wait.Backoff {Steps : 1 }
323
+ mockVMsClient .EXPECT ().Update (gomock .Any (), testCloud .ResourceGroup , gomock .Any (), gomock .Any (), gomock .Any ()).Return (& retry.Error {HTTPStatusCode : http .StatusBadRequest , Retriable : true , RawError : fmt .Errorf ("Retriable: true" )}).AnyTimes ()
324
+ } else {
325
+ mockVMsClient .EXPECT ().Update (gomock .Any (), testCloud .ResourceGroup , gomock .Any (), gomock .Any (), gomock .Any ()).Return (nil ).AnyTimes ()
326
+ }
172
327
173
328
err := common .DetachDisk (test .diskName , diskURI , test .nodeName )
174
329
assert .Equal (t , test .expectedErr , err != nil , "TestCase[%d]: %s, err: %v" , i , test .desc , err )
@@ -594,3 +749,42 @@ func TestFilterNonExistingDisks(t *testing.T) {
594
749
filteredDisks = filterDetachingDisks (disks )
595
750
assert .Equal (t , 0 , len (filteredDisks ))
596
751
}
752
+
753
+ func TestFilterNonExistingDisksWithSpecialHTTPStatusCode (t * testing.T ) {
754
+ ctrl := gomock .NewController (t )
755
+ defer ctrl .Finish ()
756
+
757
+ ctx , cancel := getContextWithCancel ()
758
+ defer cancel ()
759
+
760
+ testCloud := GetTestCloud (ctrl )
761
+ common := & controllerCommon {
762
+ location : testCloud .Location ,
763
+ storageEndpointSuffix : testCloud .Environment .StorageEndpointSuffix ,
764
+ resourceGroup : testCloud .ResourceGroup ,
765
+ subscriptionID : testCloud .SubscriptionID ,
766
+ cloud : testCloud ,
767
+ vmLockMap : newLockMap (),
768
+ }
769
+ // create a new disk before running test
770
+ diskURIPrefix := fmt .Sprintf ("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/disks/" ,
771
+ testCloud .SubscriptionID , testCloud .ResourceGroup )
772
+ newDiskName := "specialdisk"
773
+ newDiskURI := diskURIPrefix + newDiskName
774
+
775
+ mockDisksClient := testCloud .DisksClient .(* mockdiskclient.MockInterface )
776
+ mockDisksClient .EXPECT ().Get (gomock .Any (), testCloud .ResourceGroup , gomock .Eq (newDiskName )).Return (compute.Disk {}, & retry.Error {HTTPStatusCode : http .StatusBadRequest , RawError : cloudprovider .InstanceNotFound }).AnyTimes ()
777
+
778
+ disks := []compute.DataDisk {
779
+ {
780
+ Name : & newDiskName ,
781
+ ManagedDisk : & compute.ManagedDiskParameters {
782
+ ID : & newDiskURI ,
783
+ },
784
+ },
785
+ }
786
+
787
+ filteredDisks := common .filterNonExistingDisks (ctx , disks )
788
+ assert .Equal (t , 1 , len (filteredDisks ))
789
+ assert .Equal (t , newDiskName , * filteredDisks [0 ].Name )
790
+ }
0 commit comments