Skip to content

Commit

Permalink
Merge pull request kubernetes-sigs#213 from gnufied/backport-attachab…
Browse files Browse the repository at this point in the history
…le-count

OCPBUGS-4185: Fix nodeService.getVolumesLimit() adding more instance
  • Loading branch information
openshift-merge-robot authored Nov 29, 2022
2 parents 550e22c + ac5d958 commit ba03552
Show file tree
Hide file tree
Showing 9 changed files with 517 additions and 25 deletions.
32 changes: 25 additions & 7 deletions pkg/cloud/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,25 @@ import (

// Metadata is info about the ec2 instance on which the driver is running
type Metadata struct {
InstanceID string
InstanceType string
Region string
AvailabilityZone string
OutpostArn arn.ARN
InstanceID string
InstanceType string
Region string
AvailabilityZone string
NumAttachedENIs int
NumBlockDeviceMappings int
OutpostArn arn.ARN
}

// OutpostArnEndpoint is the ec2 instance metadata endpoint to query to get the outpost arn
const OutpostArnEndpoint string = "outpost-arn"
const (
// OutpostArnEndpoint is the ec2 instance metadata endpoint to query to get the outpost arn
outpostArnEndpoint string = "outpost-arn"

// enisEndpoint is the ec2 instance metadata endpoint to query the number of attached ENIs
enisEndpoint string = "network/interfaces/macs"

// blockDevicesEndpoint is the ec2 instance metadata endpoint to query the number of attached block devices
blockDevicesEndpoint string = "block-device-mapping"
)

var _ MetadataService = &Metadata{}

Expand All @@ -58,6 +68,14 @@ func (m *Metadata) GetAvailabilityZone() string {
return m.AvailabilityZone
}

func (m *Metadata) GetNumAttachedENIs() int {
return m.NumAttachedENIs
}

func (m *Metadata) GetNumBlockDeviceMappings() int {
return m.NumBlockDeviceMappings
}

// GetOutpostArn returns outpost arn if instance is running on an outpost. empty otherwise.
func (m *Metadata) GetOutpostArn() arn.ARN {
return m.OutpostArn
Expand Down
27 changes: 22 additions & 5 deletions pkg/cloud/metadata_ec2.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,31 @@ func EC2MetadataInstanceInfo(svc EC2Metadata) (*Metadata, error) {
return nil, fmt.Errorf("could not get valid EC2 availability zone")
}

enis, err := svc.GetMetadata(enisEndpoint)
// the ENIs should not be empty; if (somehow) it is empty, return an error
if enis == "" || err != nil {
return nil, fmt.Errorf("could not get number of attached ENIs")
}

attachedENIs := strings.Count(enis, "\n") + 1

mappings, err := svc.GetMetadata(blockDevicesEndpoint)
if err != nil {
return nil, fmt.Errorf("could not get number of block device mappings")
}
// The output contains 1 volume for the AMI. Any other block device contributes to the attachment limit
blockDevMappings := strings.Count(mappings, "\n")

instanceInfo := Metadata{
InstanceID: doc.InstanceID,
InstanceType: doc.InstanceType,
Region: doc.Region,
AvailabilityZone: doc.AvailabilityZone,
InstanceID: doc.InstanceID,
InstanceType: doc.InstanceType,
Region: doc.Region,
AvailabilityZone: doc.AvailabilityZone,
NumAttachedENIs: attachedENIs,
NumBlockDeviceMappings: blockDevMappings,
}

outpostArn, err := svc.GetMetadata(OutpostArnEndpoint)
outpostArn, err := svc.GetMetadata(outpostArnEndpoint)
// "outpust-arn" returns 404 for non-outpost instances. note that the request is made to a link-local address.
// it's guaranteed to be in the form `arn:<partition>:outposts:<region>:<account>:outpost/<outpost-id>`
// There's a case to be made here to ignore the error so a failure here wouldn't affect non-outpost calls.
Expand Down
2 changes: 2 additions & 0 deletions pkg/cloud/metadata_interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ type MetadataService interface {
GetInstanceType() string
GetRegion() string
GetAvailabilityZone() string
GetNumAttachedENIs() int
GetNumBlockDeviceMappings() int
GetOutpostArn() arn.ARN
}

Expand Down
10 changes: 6 additions & 4 deletions pkg/cloud/metadata_k8s.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,12 @@ func KubernetesAPIInstanceInfo(clientset kubernetes.Interface) (*Metadata, error
}

instanceInfo := Metadata{
InstanceID: instanceID,
InstanceType: instanceType,
Region: region,
AvailabilityZone: availabilityZone,
InstanceID: instanceID,
InstanceType: instanceType,
Region: region,
AvailabilityZone: availabilityZone,
NumAttachedENIs: 1, // All nodes have at least 1 attached ENI, so we'll use that
NumBlockDeviceMappings: 0,
}

return &instanceInfo, nil
Expand Down
56 changes: 54 additions & 2 deletions pkg/cloud/metadata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ func TestNewMetadataService(t *testing.T) {
invalidInstanceIdentityDocument bool
getMetadataValue string
getMetadataError error
imdsBlockDeviceOutput string
imdsENIOutput string
expectedENIs int
expectedBlockDevices int
expectedOutpostArn arn.ARN
expectedErr error
node v1.Node
Expand All @@ -69,6 +73,8 @@ func TestNewMetadataService(t *testing.T) {
Region: stdRegion,
AvailabilityZone: stdAvailabilityZone,
},
imdsENIOutput: "00:00:00:00:00:00",
expectedENIs: 1,
},
{
name: "success: outpost-arn is available",
Expand All @@ -81,6 +87,8 @@ func TestNewMetadataService(t *testing.T) {
},
getMetadataValue: validRawOutpostArn,
expectedOutpostArn: validOutpostArn,
imdsENIOutput: "00:00:00:00:00:00",
expectedENIs: 1,
},
{
name: "success: outpost-arn is invalid",
Expand All @@ -92,6 +100,8 @@ func TestNewMetadataService(t *testing.T) {
AvailabilityZone: stdAvailabilityZone,
},
getMetadataValue: "foo",
imdsENIOutput: "00:00:00:00:00:00",
expectedENIs: 1,
},
{
name: "success: outpost-arn is not found",
Expand All @@ -103,6 +113,8 @@ func TestNewMetadataService(t *testing.T) {
AvailabilityZone: stdAvailabilityZone,
},
getMetadataError: fmt.Errorf("404"),
imdsENIOutput: "00:00:00:00:00:00",
expectedENIs: 1,
},
{
name: "success: metadata not available, used k8s api",
Expand All @@ -123,6 +135,7 @@ func TestNewMetadataService(t *testing.T) {
},
Status: v1.NodeStatus{},
},
expectedENIs: 1,
nodeNameEnvVar: nodeName,
},
{
Expand Down Expand Up @@ -270,9 +283,37 @@ func TestNewMetadataService(t *testing.T) {
Region: stdRegion,
AvailabilityZone: stdAvailabilityZone,
},
imdsENIOutput: "00:00:00:00:00:00",
expectedENIs: 1,
getMetadataError: fmt.Errorf("405"),
expectedErr: fmt.Errorf("something went wrong while getting EC2 outpost arn: 405"),
},
{
name: "success: GetMetadata() returns correct number of ENIs",
ec2metadataAvailable: true,
getInstanceIdentityDocumentValue: ec2metadata.EC2InstanceIdentityDocument{
InstanceID: stdInstanceID,
InstanceType: stdInstanceType,
Region: stdRegion,
AvailabilityZone: stdAvailabilityZone,
},
imdsENIOutput: "00:00:00:00:00:00\n00:00:00:00:00:01",
expectedENIs: 2,
},
{
name: "success: GetMetadata() returns correct number of block device mappings",
ec2metadataAvailable: true,
getInstanceIdentityDocumentValue: ec2metadata.EC2InstanceIdentityDocument{
InstanceID: stdInstanceID,
InstanceType: stdInstanceType,
Region: stdRegion,
AvailabilityZone: stdAvailabilityZone,
},
imdsENIOutput: "00:00:00:00:00:00",
expectedENIs: 1,
imdsBlockDeviceOutput: "ami\nroot\nebs1\nebs2",
expectedBlockDevices: 3,
},
}

for _, tc := range testCases {
Expand All @@ -297,12 +338,17 @@ func TestNewMetadataService(t *testing.T) {
// GetInstanceIdentityDocument returns an error or (somehow?) partial
// output
if tc.getInstanceIdentityDocumentError == nil && !tc.invalidInstanceIdentityDocument {
mockEC2Metadata.EXPECT().GetMetadata(enisEndpoint).Return(tc.imdsENIOutput, nil)
mockEC2Metadata.EXPECT().GetMetadata(blockDevicesEndpoint).Return(tc.imdsBlockDeviceOutput, nil)

if tc.getMetadataValue != "" || tc.getMetadataError != nil {
mockEC2Metadata.EXPECT().GetMetadata(OutpostArnEndpoint).Return(tc.getMetadataValue, tc.getMetadataError)
mockEC2Metadata.EXPECT().GetMetadata(outpostArnEndpoint).Return(tc.getMetadataValue, tc.getMetadataError)

} else {
mockEC2Metadata.EXPECT().GetMetadata(OutpostArnEndpoint).Return("", fmt.Errorf("404"))
mockEC2Metadata.EXPECT().GetMetadata(outpostArnEndpoint).Return("", fmt.Errorf("404"))
}
}

if clientsetInitialized == true {
t.Errorf("kubernetes client was unexpectedly initialized when metadata is available!")
if len(clientset.Actions()) > 0 {
Expand Down Expand Up @@ -339,6 +385,12 @@ func TestNewMetadataService(t *testing.T) {
if m.GetOutpostArn() != tc.expectedOutpostArn {
t.Errorf("GetOutpostArn() failed: got %v, expected %v", m.GetOutpostArn(), tc.expectedOutpostArn)
}
if m.GetNumAttachedENIs() != tc.expectedENIs {
t.Errorf("GetMetadata() failed for %s: got %v, expected %v", enisEndpoint, m.GetNumAttachedENIs(), tc.expectedENIs)
}
if m.GetNumBlockDeviceMappings() != tc.expectedBlockDevices {
t.Errorf("GetMetadata() failed for %s: got %v, expected %v", blockDevicesEndpoint, m.GetNumBlockDeviceMappings(), tc.expectedBlockDevices)
}
}
mockCtrl.Finish()
})
Expand Down
28 changes: 28 additions & 0 deletions pkg/cloud/mock_metadata.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit ba03552

Please sign in to comment.