Skip to content

Commit de34e14

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

File tree

2 files changed

+173
-0
lines changed

2 files changed

+173
-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: 148 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,149 @@ 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+
// This not supported by ECS, but desired by us to speed up failover. Hopes they will support it someday.
696+
name: "detaching from other",
697+
before: disk("Detaching", "i-anotherinstance"),
698+
detaching: true,
699+
forceAttach: true,
700+
after: disk("In_use", "i-testinstanceid"),
701+
},
702+
{
703+
// This is likely to fail in real env. But we try it anyway, in case the detach just finished after we checked
704+
name: "detaching from other (no force attach)",
705+
before: disk("Detaching", "i-anotherinstance"),
706+
forceAttach: false,
707+
after: disk("In_use", "i-testinstanceid"),
708+
},
709+
}
710+
for _, tc := range cases {
711+
t.Run(tc.name, func(t *testing.T) {
712+
ctx, c, ad := testAttachDetach(t)
713+
714+
if tc.detaching {
715+
ad.detaching.Store("d-testdiskid", "i-anotherinstance")
716+
}
717+
718+
c.EXPECT().DescribeDisks(gomock.Any()).Return(diskResp(tc.before), nil)
719+
if tc.detach {
720+
detachCall := c.EXPECT().DetachDisk(gomock.Any()).Return(&ecs.DetachDiskResponse{}, nil)
721+
c.EXPECT().DescribeDisks(gomock.Any()).Return(diskResp(tc.detached), nil).After(detachCall)
722+
}
723+
force := false
724+
if !tc.noAttach {
725+
attachCall := c.EXPECT().AttachDisk(gomock.Any()).Return(&ecs.AttachDiskResponse{}, nil).
726+
Do(func(request *ecs.AttachDiskRequest) {
727+
if request.Force.HasValue() {
728+
var err error
729+
force, err = request.Force.GetValue()
730+
if err != nil {
731+
t.Fatalf("unexpected error: %v", err)
732+
}
733+
}
734+
})
735+
c.EXPECT().DescribeDisks(gomock.Any()).Return(diskResp(tc.after), nil).After(attachCall)
736+
}
737+
serial, err := ad.attachDisk(ctx, "d-testdiskid", "i-testinstanceid", false)
738+
739+
assert.Equal(t, tc.forceAttach, force)
740+
if tc.expectErr {
741+
require.Error(t, err)
742+
} else {
743+
require.NoError(t, err)
744+
assert.Equal(t, "fake-serial-number", serial)
745+
}
746+
})
747+
}
748+
}

0 commit comments

Comments
 (0)