Skip to content

Commit ceac988

Browse files
committed
Fix AWS CSI scale-up from zero
1 parent 5e07dd5 commit ceac988

8 files changed

Lines changed: 181 additions & 10 deletions

File tree

cluster-autoscaler/cloudprovider/aws/aws_cloud_provider.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,9 @@ func (ng *AwsNodeGroup) TemplateNodeInfo() (*framework.NodeInfo, error) {
426426
}
427427

428428
nodeInfo := framework.NewNodeInfo(node, nil, framework.NewPodInfo(cloudprovider.BuildKubeProxy(ng.asg.Name), nil))
429+
if csiNode := ng.awsManager.buildCSINodeFromTemplate(template, node.Name); csiNode != nil {
430+
nodeInfo.SetCSINode(csiNode)
431+
}
429432
return nodeInfo, nil
430433
}
431434

cluster-autoscaler/cloudprovider/aws/aws_cloud_provider_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -872,3 +872,50 @@ func TestDeleteNodesWithPlaceholderAndStaleCache(t *testing.T) {
872872
a.AssertNumberOfCalls(t, "TerminateInstanceInAutoScalingGroup", 2)
873873

874874
}
875+
876+
func TestAwsNodeGroupTemplateNodeInfoSetsCSINode(t *testing.T) {
877+
const instanceTypeName = "m5.large"
878+
879+
manager := &AwsManager{
880+
instanceTypes: map[string]*InstanceType{
881+
instanceTypeName: {
882+
InstanceType: instanceTypeName,
883+
VCPU: 2,
884+
MemoryMb: 8192,
885+
Architecture: "amd64",
886+
EBSVolumeLimit: 39,
887+
},
888+
},
889+
}
890+
891+
origGetInstanceTypeFunc := getInstanceTypeForAsg
892+
defer func() { getInstanceTypeForAsg = origGetInstanceTypeFunc }()
893+
getInstanceTypeForAsg = func(_ *asgCache, _ *asg) (string, error) {
894+
return instanceTypeName, nil
895+
}
896+
897+
ng := &AwsNodeGroup{
898+
awsManager: manager,
899+
asg: &asg{
900+
AwsRef: AwsRef{Name: "test-asg"},
901+
AvailabilityZones: []string{"us-east-1a"},
902+
minSize: 0,
903+
maxSize: 5,
904+
curSize: 0,
905+
},
906+
}
907+
908+
nodeInfo, err := ng.TemplateNodeInfo()
909+
assert.NoError(t, err)
910+
assert.NotNil(t, nodeInfo)
911+
assert.NotNil(t, nodeInfo.CSINode)
912+
assert.Equal(t, nodeInfo.Node().Name, nodeInfo.CSINode.Name)
913+
assert.Len(t, nodeInfo.CSINode.Spec.Drivers, 1)
914+
915+
driver := nodeInfo.CSINode.Spec.Drivers[0]
916+
assert.Equal(t, "ebs.csi.aws.com", driver.Name)
917+
assert.Equal(t, nodeInfo.Node().Name, driver.NodeID)
918+
assert.NotNil(t, driver.Allocatable)
919+
assert.NotNil(t, driver.Allocatable.Count)
920+
assert.Equal(t, int32(39), *driver.Allocatable.Count)
921+
}

cluster-autoscaler/cloudprovider/aws/aws_manager.go

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,18 @@ import (
2727
"strings"
2828
"time"
2929

30-
apiv1 "k8s.io/api/core/v1"
31-
"k8s.io/apimachinery/pkg/api/resource"
32-
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
33-
"k8s.io/klog/v2"
34-
3530
"github.com/aws/aws-sdk-go-v2/aws"
3631
"github.com/aws/aws-sdk-go-v2/service/autoscaling"
3732
autoscalingtypes "github.com/aws/aws-sdk-go-v2/service/autoscaling/types"
3833
"github.com/aws/aws-sdk-go-v2/service/ec2"
3934
ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
4035
"github.com/aws/aws-sdk-go-v2/service/eks"
36+
apiv1 "k8s.io/api/core/v1"
37+
storagev1 "k8s.io/api/storage/v1"
38+
"k8s.io/apimachinery/pkg/api/resource"
39+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
40+
"k8s.io/klog/v2"
41+
"k8s.io/utils/ptr"
4142
"sigs.k8s.io/cluster-autoscaler/pkg/cloudprovider"
4243
"sigs.k8s.io/cluster-autoscaler/pkg/config"
4344
"sigs.k8s.io/cluster-autoscaler/pkg/utils/gpu"
@@ -341,6 +342,33 @@ func (m *AwsManager) buildNodeFromTemplate(asg *asg, template *asgTemplate) (*ap
341342
return &node, nil
342343
}
343344

345+
func (m *AwsManager) buildCSINodeFromTemplate(template *asgTemplate, nodeName string) *storagev1.CSINode {
346+
if template == nil || template.InstanceType == nil {
347+
return nil
348+
}
349+
350+
driver := storagev1.CSINodeDriver{
351+
Name: "ebs.csi.aws.com",
352+
NodeID: nodeName,
353+
}
354+
// Prefer a CSINode without Allocatable over nil when the attachment limit is
355+
// unknown. Returning nil blocks CSI-aware scale-from-zero simulation.
356+
if template.InstanceType.EBSVolumeLimit > 0 {
357+
driver.Allocatable = &storagev1.VolumeNodeResources{
358+
Count: ptr.To(int32(template.InstanceType.EBSVolumeLimit)),
359+
}
360+
}
361+
362+
return &storagev1.CSINode{
363+
ObjectMeta: metav1.ObjectMeta{
364+
Name: nodeName,
365+
},
366+
Spec: storagev1.CSINodeSpec{
367+
Drivers: []storagev1.CSINodeDriver{driver},
368+
},
369+
}
370+
}
371+
344372
func joinNodeLabelsChoosingUserValuesOverAPIValues(extractedLabels map[string]string, mngLabels map[string]string) map[string]string {
345373
result := make(map[string]string)
346374

cluster-autoscaler/cloudprovider/aws/aws_manager_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -951,3 +951,66 @@ func TestParseASGAutoDiscoverySpecs(t *testing.T) {
951951
})
952952
}
953953
}
954+
955+
func TestBuildCSINodeFromTemplate(t *testing.T) {
956+
manager := &AwsManager{}
957+
958+
template := &asgTemplate{
959+
InstanceType: &InstanceType{
960+
InstanceType: "m5.large",
961+
EBSVolumeLimit: 39,
962+
},
963+
}
964+
965+
got := manager.buildCSINodeFromTemplate(template, "template-node")
966+
967+
require.NotNil(t, got)
968+
require.Equal(t, "template-node", got.Name)
969+
require.Len(t, got.Spec.Drivers, 1)
970+
971+
driver := got.Spec.Drivers[0]
972+
require.Equal(t, "ebs.csi.aws.com", driver.Name)
973+
require.Equal(t, "template-node", driver.NodeID)
974+
require.NotNil(t, driver.Allocatable)
975+
require.Equal(t, int32(39), *driver.Allocatable.Count)
976+
}
977+
978+
func TestBuildCSINodeFromTemplate_NoVolumeLimit(t *testing.T) {
979+
manager := &AwsManager{}
980+
981+
template := &asgTemplate{
982+
InstanceType: &InstanceType{
983+
InstanceType: "m5.large",
984+
EBSVolumeLimit: 0,
985+
},
986+
}
987+
988+
got := manager.buildCSINodeFromTemplate(template, "template-node")
989+
990+
require.NotNil(t, got)
991+
require.Equal(t, "template-node", got.Name)
992+
require.Len(t, got.Spec.Drivers, 1)
993+
994+
driver := got.Spec.Drivers[0]
995+
require.Equal(t, "ebs.csi.aws.com", driver.Name)
996+
require.Equal(t, "template-node", driver.NodeID)
997+
require.Nil(t, driver.Allocatable)
998+
}
999+
1000+
func TestBuildCSINodeFromTemplate_NilTemplate(t *testing.T) {
1001+
manager := &AwsManager{}
1002+
1003+
got := manager.buildCSINodeFromTemplate(nil, "template-node")
1004+
1005+
require.Nil(t, got)
1006+
}
1007+
1008+
func TestBuildCSINodeFromTemplate_NilInstanceType(t *testing.T) {
1009+
manager := &AwsManager{}
1010+
1011+
template := &asgTemplate{}
1012+
1013+
got := manager.buildCSINodeFromTemplate(template, "template-node")
1014+
1015+
require.Nil(t, got)
1016+
}

cluster-autoscaler/cloudprovider/aws/aws_util.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ func transformInstanceType(rawInstanceType *ec2types.InstanceTypeInfo) *Instance
7575
if rawInstanceType.ProcessorInfo != nil && len(rawInstanceType.ProcessorInfo.SupportedArchitectures) > 0 {
7676
instanceType.Architecture = interpretEc2SupportedArchitecure(string(rawInstanceType.ProcessorInfo.SupportedArchitectures[0]))
7777
}
78+
if rawInstanceType.EbsInfo != nil && rawInstanceType.EbsInfo.MaximumEbsAttachments != nil {
79+
instanceType.EBSVolumeLimit = int64(*rawInstanceType.EbsInfo.MaximumEbsAttachments)
80+
}
7881
return instanceType
7982
}
8083

cluster-autoscaler/cloudprovider/aws/aws_util_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ func TestInstanceTypeTransform(t *testing.T) {
4141
MemoryInfo: &ec2types.MemoryInfo{
4242
SizeInMiB: aws.Int64(7680),
4343
},
44+
EbsInfo: &ec2types.EbsInfo{
45+
MaximumEbsAttachments: aws.Int32(39),
46+
},
4447
}
4548

4649
instanceType := transformInstanceType(&rawInstanceType)
@@ -50,6 +53,27 @@ func TestInstanceTypeTransform(t *testing.T) {
5053
assert.Equal(t, int64(7680), instanceType.MemoryMb)
5154
assert.Equal(t, int64(0), instanceType.GPU)
5255
assert.Equal(t, "amd64", instanceType.Architecture)
56+
assert.Equal(t, int64(39), instanceType.EBSVolumeLimit)
57+
}
58+
59+
func TestInstanceTypeTransform_NilEbsInfo(t *testing.T) {
60+
rawInstanceType := ec2types.InstanceTypeInfo{
61+
InstanceType: ec2types.InstanceType("c4.xlarge"),
62+
ProcessorInfo: &ec2types.ProcessorInfo{
63+
SupportedArchitectures: []ec2types.ArchitectureType{ec2types.ArchitectureTypeX8664},
64+
},
65+
VCpuInfo: &ec2types.VCpuInfo{
66+
DefaultVCpus: aws.Int32(4),
67+
},
68+
MemoryInfo: &ec2types.MemoryInfo{
69+
SizeInMiB: aws.Int64(7680),
70+
},
71+
}
72+
73+
instanceType := transformInstanceType(&rawInstanceType)
74+
75+
assert.Equal(t, "c4.xlarge", instanceType.InstanceType)
76+
assert.Equal(t, int64(0), instanceType.EBSVolumeLimit)
5377
}
5478

5579
func TestInterpretEc2SupportedArchitecure(t *testing.T) {

cluster-autoscaler/cloudprovider/aws/ec2_instance_types.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,12 @@ package aws
2020

2121
// InstanceType is spec of EC2 instance
2222
type InstanceType struct {
23-
InstanceType string
24-
VCPU int64
25-
MemoryMb int64
26-
GPU int64
27-
Architecture string
23+
InstanceType string
24+
VCPU int64
25+
MemoryMb int64
26+
GPU int64
27+
Architecture string
28+
EBSVolumeLimit int64
2829
}
2930

3031
// StaticListLastUpdateTime is a string declaring the last time the static list was updated.

cluster-autoscaler/cloudprovider/aws/ec2_instance_types/gen.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ type InstanceType struct {
5959
MemoryMb int64
6060
GPU int64
6161
Architecture string
62+
EBSVolumeLimit int64
6263
}
6364
6465
// StaticListLastUpdateTime is a string declaring the last time the static list was updated.
@@ -73,6 +74,7 @@ var InstanceTypes = map[string]*InstanceType{
7374
MemoryMb: {{ .MemoryMb }},
7475
GPU: {{ .GPU }},
7576
Architecture: "{{ .Architecture }}",
77+
EBSVolumeLimit: {{ .EBSVolumeLimit }},
7678
},
7779
{{- end }}
7880
}

0 commit comments

Comments
 (0)