Skip to content

Commit 752ad68

Browse files
committed
add unit test for attach disk
1 parent 6ac86a9 commit 752ad68

File tree

2 files changed

+172
-0
lines changed

2 files changed

+172
-0
lines changed

pkg/disk/batcher/passthrough.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package batcher
2+
3+
import (
4+
"context"
5+
6+
"github.com/kubernetes-sigs/alibaba-cloud-csi-driver/pkg/disk/desc"
7+
)
8+
9+
type Passthrough[T any] struct {
10+
client desc.Client[T]
11+
}
12+
13+
func NewPassthrough[T any](client desc.Client[T]) Passthrough[T] {
14+
return Passthrough[T]{
15+
client: client,
16+
}
17+
}
18+
19+
func (w Passthrough[T]) Describe(ctx context.Context, id string) (*T, error) {
20+
resp, err := w.client.Describe([]string{id})
21+
if err != nil {
22+
return nil, err
23+
}
24+
return &resp.Resources[0], nil
25+
}

pkg/disk/cloud_test.go

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ import (
1313
"github.com/kubernetes-sigs/alibaba-cloud-csi-driver/pkg/cloud"
1414
"github.com/kubernetes-sigs/alibaba-cloud-csi-driver/pkg/disk/batcher"
1515
"github.com/kubernetes-sigs/alibaba-cloud-csi-driver/pkg/disk/desc"
16+
"github.com/kubernetes-sigs/alibaba-cloud-csi-driver/pkg/disk/waitstatus"
1617
"github.com/stretchr/testify/assert"
18+
"github.com/stretchr/testify/require"
1719
"k8s.io/apimachinery/pkg/util/sets"
1820
"k8s.io/klog/v2/ktesting"
1921
"k8s.io/utils/clock"
@@ -598,3 +600,148 @@ func Test_getDiskDescribeRequest(t *testing.T) {
598600
})
599601
}
600602
}
603+
604+
func testAttachDetach(t *testing.T) (context.Context, *cloud.MockECSInterface, *DiskAttachDetach) {
605+
ctrl := gomock.NewController(t)
606+
ecs := cloud.NewMockECSInterface(ctrl)
607+
_, ctx := ktesting.NewTestContext(t)
608+
609+
client := desc.Disk(ecs)
610+
b := batcher.NewPassthrough(client)
611+
return ctx, ecs, &DiskAttachDetach{
612+
slots: NewSlots(0, 0),
613+
ecs: ecs,
614+
waiter: waitstatus.NewSimple(client, clock.RealClock{}),
615+
batcher: b,
616+
attachThrottler: defaultThrottler(),
617+
detachThrottler: defaultThrottler(),
618+
dev: DefaultDeviceManager,
619+
}
620+
}
621+
622+
func disk(status string, node string) ecs.Disk {
623+
disk := ecs.Disk{
624+
Status: status,
625+
Category: "cloud_regional_disk_auto", // only one support forceAttach
626+
MultiAttach: "Disabled",
627+
DiskId: "d-testdiskid",
628+
InstanceId: node,
629+
SerialNumber: "fake-serial-number",
630+
}
631+
if node != "" {
632+
disk.Attachments.Attachment = []ecs.Attachment{
633+
{InstanceId: node},
634+
}
635+
}
636+
return disk
637+
}
638+
639+
func diskResp(disk ecs.Disk) *ecs.DescribeDisksResponse {
640+
return &ecs.DescribeDisksResponse{
641+
Disks: ecs.DisksInDescribeDisks{
642+
Disk: []ecs.Disk{disk},
643+
},
644+
}
645+
}
646+
647+
func TestAttachDisk(t *testing.T) {
648+
GlobalConfigVar.DetachBeforeAttach = true // This is the default
649+
cases := []struct {
650+
name string
651+
before, after ecs.Disk
652+
detaching bool
653+
detach bool
654+
detached ecs.Disk
655+
noAttach bool
656+
forceAttach bool
657+
expectErr bool
658+
}{
659+
{
660+
name: "already attached",
661+
before: disk("In_use", "i-testinstanceid"),
662+
noAttach: true,
663+
},
664+
{
665+
name: "attached to other",
666+
before: disk("In_use", "i-anotherinstance"),
667+
detaching: true,
668+
forceAttach: true,
669+
after: disk("In_use", "i-testinstanceid"),
670+
},
671+
{
672+
name: "attached to other (no force attach)",
673+
before: disk("In_use", "i-anotherinstance"),
674+
detach: true,
675+
detached: disk("Available", ""),
676+
after: disk("In_use", "i-testinstanceid"),
677+
},
678+
{
679+
name: "normal",
680+
before: disk("Available", ""),
681+
after: disk("In_use", "i-testinstanceid"),
682+
},
683+
{
684+
name: "attaching",
685+
before: disk("Attaching", ""),
686+
noAttach: true,
687+
expectErr: true,
688+
},
689+
{
690+
name: "detaching from self",
691+
before: disk("Detaching", "i-testinstanceid"),
692+
after: disk("In_use", "i-testinstanceid"), // FIXME
693+
},
694+
{
695+
name: "detaching from other",
696+
before: disk("Detaching", "i-anotherinstance"),
697+
detaching: true,
698+
forceAttach: true,
699+
after: disk("In_use", "i-testinstanceid"),
700+
},
701+
{
702+
// This is likely to fail in real env. But we try it anyway, in case the detach just finished after we checked
703+
name: "detaching from other (no force attach)",
704+
before: disk("Detaching", "i-anotherinstance"),
705+
forceAttach: false,
706+
after: disk("In_use", "i-testinstanceid"),
707+
},
708+
}
709+
for _, tc := range cases {
710+
t.Run(tc.name, func(t *testing.T) {
711+
ctx, c, ad := testAttachDetach(t)
712+
713+
if tc.detaching {
714+
ad.detaching.Store("d-testdiskid", "i-anotherinstance")
715+
}
716+
717+
c.EXPECT().DescribeDisks(gomock.Any()).Return(diskResp(tc.before), nil)
718+
if tc.detach {
719+
detachCall := c.EXPECT().DetachDisk(gomock.Any()).Return(&ecs.DetachDiskResponse{}, nil)
720+
c.EXPECT().DescribeDisks(gomock.Any()).Return(diskResp(tc.detached), nil).After(detachCall)
721+
}
722+
force := false
723+
if !tc.noAttach {
724+
attachCall := c.EXPECT().AttachDisk(gomock.Any()).Return(&ecs.AttachDiskResponse{}, nil).
725+
Do(func(request *ecs.AttachDiskRequest) {
726+
if request.Force.HasValue() {
727+
var err error
728+
force, err = request.Force.GetValue()
729+
if err != nil {
730+
t.Fatalf("unexpected error: %v", err)
731+
}
732+
}
733+
})
734+
c.EXPECT().DescribeDisks(gomock.Any()).Return(diskResp(tc.after), nil).After(attachCall)
735+
}
736+
serial, err := ad.attachDisk(ctx, "d-testdiskid", "i-testinstanceid", false)
737+
738+
assert.Equal(t, tc.forceAttach, force)
739+
if tc.expectErr {
740+
require.Error(t, err)
741+
} else {
742+
require.NoError(t, err)
743+
assert.Equal(t, "fake-serial-number", serial)
744+
}
745+
})
746+
}
747+
}

0 commit comments

Comments
 (0)