Skip to content

Commit

Permalink
discovering efs with single AWS call
Browse files Browse the repository at this point in the history
  • Loading branch information
avanish23 committed Nov 1, 2024
1 parent 0c109b9 commit b445adf
Show file tree
Hide file tree
Showing 7 changed files with 588 additions and 123 deletions.
33 changes: 13 additions & 20 deletions pkg/cloud/cloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ var (
type FileSystem struct {
FileSystemId string
FileSystemArn string
Tags map[string]string
}

type AccessPoint struct {
Expand Down Expand Up @@ -97,7 +98,6 @@ type Efs interface {
DescribeAccessPoints(context.Context, *efs.DescribeAccessPointsInput, ...func(*efs.Options)) (*efs.DescribeAccessPointsOutput, error)
DescribeFileSystems(context.Context, *efs.DescribeFileSystemsInput, ...func(*efs.Options)) (*efs.DescribeFileSystemsOutput, error)
DescribeMountTargets(context.Context, *efs.DescribeMountTargetsInput, ...func(*efs.Options)) (*efs.DescribeMountTargetsOutput, error)
ListTagsForResource(context.Context, *efs.ListTagsForResourceInput, ...func(*efs.Options)) (*efs.ListTagsForResourceOutput, error)
}

type Cloud interface {
Expand All @@ -110,7 +110,6 @@ type Cloud interface {
DescribeFileSystemById(ctx context.Context, fileSystemId string) (fs *FileSystem, err error)
DescribeFileSystemByToken(ctx context.Context, creationToken string) (fs []*FileSystem, err error)
DescribeMountTargets(ctx context.Context, fileSystemId, az string) (fs *MountTarget, err error)
ListTagsForFileSystem(ctx context.Context, fileSystemArn string) (tags []types.Tag, err error)
}

type cloud struct {
Expand Down Expand Up @@ -325,8 +324,14 @@ func (c *cloud) ListAccessPoints(ctx context.Context, fileSystemId string) (acce

return
}

func (c *cloud) DescribeFileSystemByToken(ctx context.Context, creationToken string) (fs []*FileSystem, err error) {
describeFsInput := &efs.DescribeFileSystemsInput{FileSystemId: &creationToken}
var describeFsInput *efs.DescribeFileSystemsInput
if creationToken == "" {
describeFsInput = &efs.DescribeFileSystemsInput{}
} else {
describeFsInput = &efs.DescribeFileSystemsInput{CreationToken: &creationToken}
}
klog.V(5).Infof("Calling DescribeFileSystems with input: %+v", *describeFsInput)
res, err := c.efs.DescribeFileSystems(ctx, describeFsInput)
if err != nil {
Expand All @@ -341,9 +346,14 @@ func (c *cloud) DescribeFileSystemByToken(ctx context.Context, creationToken str

var efsList = make([]*FileSystem, 0)
for _, fileSystem := range res.FileSystems {
var tagsList = make(map[string]string, 0)
for _, tag := range fileSystem.Tags {
tagsList[*tag.Key] = *tag.Value
}
efsList = append(efsList, &FileSystem{
FileSystemId: *fileSystem.FileSystemId,
FileSystemArn: *fileSystem.FileSystemArn,
Tags: tagsList,
})
}
return efsList, nil
Expand Down Expand Up @@ -418,23 +428,6 @@ func (c *cloud) DescribeMountTargets(ctx context.Context, fileSystemId, azName s
}, nil
}

func (c *cloud) ListTagsForFileSystem(ctx context.Context, fileSystemArn string) ([]types.Tag, error) {
listTagsInput := &efs.ListTagsForResourceInput{ResourceId: aws.String(fileSystemArn)}
klog.V(5).Infof("Calling ListTagsForFileSystem with input: %+v", *listTagsInput)
res, err := c.efs.ListTagsForResource(ctx, listTagsInput)
if err != nil {
if isAccessDenied(err) {
return nil, ErrAccessDenied
}
if isFileSystemNotFound(err) {
return nil, ErrNotFound
}
return nil, fmt.Errorf("List Tags for FileSystem failed: %v", err)
}
tagsList := res.Tags
return tagsList, nil
}

func isFileSystemNotFound(err error) bool {
var FileSystemNotFoundErr *types.FileSystemNotFound
if errors.As(err, &FileSystemNotFoundErr) {
Expand Down
208 changes: 208 additions & 0 deletions pkg/cloud/cloud_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1050,6 +1050,214 @@ func TestDescribeMountTargets(t *testing.T) {
}
}

func TestDescribeFileSystemByToken(t *testing.T) {
var (
fsId = []string{"fs-abcd1234", "fs-efgh5678"}
fsArn = []string{"arn:aws:elasticfilesystem:us-west-2:1111333322228888:file-system/fs-0123456789abcdef8", "arn:aws:elasticfilesystem:us-west-2:1111333322228888:file-system/fs-987654321abcdef0"}
creationToken = "efs-for-discovery"
az = "us-east-1a"
)

testCases := []struct {
name string
testFunc func(t *testing.T)
}{
{
name: "Success: Normal flow",
testFunc: func(t *testing.T) {
mockctl := gomock.NewController(t)
mockEfs := mocks.NewMockEfs(mockctl)
c := &cloud{efs: mockEfs}

fs := &efs.DescribeFileSystemsOutput{
FileSystems: []types.FileSystemDescription{
{
FileSystemId: aws.String(fsId[0]),
FileSystemArn: aws.String(fsArn[0]),
Encrypted: aws.Bool(true),
CreationToken: aws.String("efs-for-discovery"),
AvailabilityZoneName: aws.String(az),
Tags: []types.Tag{
{
Key: aws.String("env"),
Value: aws.String("prod"),
},
{
Key: aws.String("owner"),
Value: aws.String("[email protected]"),
},
},
},
{
FileSystemId: aws.String(fsId[1]),
FileSystemArn: aws.String(fsArn[1]),
Encrypted: aws.Bool(true),
CreationToken: aws.String("efs-not-for-discovery"),
AvailabilityZoneName: aws.String(az),
Tags: []types.Tag{
{
Key: aws.String("env"),
Value: aws.String("prod"),
},
{
Key: aws.String("owner"),
Value: aws.String("[email protected]"),
},
},
},
},
}

ctx := context.Background()
mockEfs.EXPECT().DescribeFileSystems(gomock.Eq(ctx), gomock.Any()).DoAndReturn(func(ctx context.Context, input *efs.DescribeFileSystemsInput, opts ...func(*efs.Options)) (*efs.DescribeFileSystemsOutput, error) {
res := &efs.DescribeFileSystemsOutput{}
for _, fileSystem := range fs.FileSystems {
if input.CreationToken != nil && *fileSystem.CreationToken == *input.CreationToken {
res.FileSystems = append(res.FileSystems, fileSystem)
} else if input.CreationToken == nil {
res.FileSystems = append(res.FileSystems, fileSystem)
}
}
return res, nil
})

efsList, err := c.DescribeFileSystemByToken(ctx, creationToken)
if err != nil {
t.Fatalf("DescribeFileSystem failed")
}
if len(efsList) != 1 {
t.Fatalf("Expected 1 fileSystems got %d", len(efsList))
}
mockctl.Finish()
},
},
{
name: "Success: Normal flow without creation token",
testFunc: func(t *testing.T) {
mockctl := gomock.NewController(t)
mockEfs := mocks.NewMockEfs(mockctl)
c := &cloud{efs: mockEfs}

fs := &efs.DescribeFileSystemsOutput{
FileSystems: []types.FileSystemDescription{
{
FileSystemId: aws.String(fsId[0]),
FileSystemArn: aws.String(fsArn[0]),
Encrypted: aws.Bool(true),
CreationToken: aws.String("efs-for-discovery"),
AvailabilityZoneName: aws.String(az),
Tags: []types.Tag{
{
Key: aws.String("env"),
Value: aws.String("prod"),
},
{
Key: aws.String("owner"),
Value: aws.String("[email protected]"),
},
},
},
{
FileSystemId: aws.String(fsId[1]),
FileSystemArn: aws.String(fsArn[1]),
Encrypted: aws.Bool(true),
CreationToken: aws.String("efs-not-for-discovery"),
AvailabilityZoneName: aws.String(az),
Tags: []types.Tag{
{
Key: aws.String("env"),
Value: aws.String("prod"),
},
{
Key: aws.String("owner"),
Value: aws.String("[email protected]"),
},
},
},
},
}

ctx := context.Background()
mockEfs.EXPECT().DescribeFileSystems(gomock.Eq(ctx), gomock.Any()).DoAndReturn(func(ctx context.Context, input *efs.DescribeFileSystemsInput, opts ...func(*efs.Options)) (*efs.DescribeFileSystemsOutput, error) {
res := &efs.DescribeFileSystemsOutput{}
for _, fileSystem := range fs.FileSystems {
if input.CreationToken != nil && *fileSystem.CreationToken == *input.CreationToken {
res.FileSystems = append(res.FileSystems, fileSystem)
} else if input.CreationToken == nil {
res.FileSystems = append(res.FileSystems, fileSystem)
}
}
return res, nil
})

efsList, err := c.DescribeFileSystemByToken(ctx, "")
if err != nil {
t.Fatalf("DescribeFileSystem failed")
}
if len(efsList) != len(fs.FileSystems) {
t.Fatalf("Expected 1 fileSystems got %d", len(efsList))
}
for i, fileSystem := range fs.FileSystems {
for _, v := range fileSystem.Tags {
if val, exists := efsList[i].Tags[*v.Key]; !exists || val != *v.Value {
t.Fatalf("Tags list is corrupted, expected %s for %s but got %s", *v.Value, *v.Key, val)
}
}
}
mockctl.Finish()
},
},
{
name: "Fail: Access Denied",
testFunc: func(t *testing.T) {
mockctl := gomock.NewController(t)
mockEfs := mocks.NewMockEfs(mockctl)
c := &cloud{efs: mockEfs}

ctx := context.Background()
mockEfs.EXPECT().DescribeFileSystems(gomock.Eq(ctx), gomock.Any()).Return(nil, &smithy.GenericAPIError{
Code: AccessDeniedException,
Message: "Access Denied",
})

_, err := c.DescribeFileSystemByToken(ctx, "efs-discovery")
if err == nil {
t.Fatalf("DescribeFileSystemByToken did not fail")
}
if err != ErrAccessDenied {
t.Fatalf("Failed. Expected: %v, Actual:%v", ErrAccessDenied, err)
}
mockctl.Finish()
},
},
{
name: "Fail: File System not found",
testFunc: func(t *testing.T) {
mockctl := gomock.NewController(t)
mockEfs := mocks.NewMockEfs(mockctl)
c := &cloud{efs: mockEfs}

ctx := context.Background()
mockEfs.EXPECT().DescribeFileSystems(gomock.Eq(ctx), gomock.Any()).Return(nil, &types.FileSystemNotFound{
Message: aws.String("File System not found"),
})

_, err := c.DescribeFileSystemByToken(ctx, "efs-discovery")
if err == nil {
t.Fatalf("DescribeFileSystemByToken did not fail")
}
if err != ErrNotFound {
t.Fatalf("Failed. Expected: %v, Actual:%v", ErrNotFound, err)
}
mockctl.Finish()
},
},
}
for _, tc := range testCases {
t.Run(tc.name, tc.testFunc)
}
}

func testResult(t *testing.T, funcName string, ret interface{}, err error, expectError errtyp) {
if expectError.message == "" {
if err != nil {
Expand Down
23 changes: 6 additions & 17 deletions pkg/cloud/fakes.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ package cloud
import (
"context"
"fmt"
"github.com/aws/aws-sdk-go-v2/service/efs/types"
"github.com/aws/aws-sdk-go/aws"
"math/rand"
"time"
)
Expand All @@ -14,7 +12,7 @@ type FakeCloudProvider struct {
fileSystems map[string]*FileSystem
accessPoints map[string]*AccessPoint
mountTargets map[string]*MountTarget
tags map[string][]types.Tag
tags map[string]map[string]string
}

func NewFakeCloudProvider() *FakeCloudProvider {
Expand All @@ -23,7 +21,7 @@ func NewFakeCloudProvider() *FakeCloudProvider {
fileSystems: make(map[string]*FileSystem),
accessPoints: make(map[string]*AccessPoint),
mountTargets: make(map[string]*MountTarget),
tags: make(map[string][]types.Tag),
tags: make(map[string]map[string]string),
}
}

Expand Down Expand Up @@ -101,16 +99,15 @@ func (c *FakeCloudProvider) DescribeFileSystemByToken(ctx context.Context, creat
return efsList, nil
}

tags := []types.Tag{
{
Key: aws.String("Environment"),
Value: aws.String("Production"),
},
tags := map[string]string{
"env": "prod",
"owner": "[email protected]",
}

fs := &FileSystem{
FileSystemId: creationToken,
FileSystemArn: "arn:aws:elasticfilesystem:us-west-2:xxxx:file-system/fs-xxxx",
Tags: tags,
}
c.fileSystems[creationToken] = fs

Expand All @@ -121,7 +118,6 @@ func (c *FakeCloudProvider) DescribeFileSystemByToken(ctx context.Context, creat
IPAddress: "127.0.0.1",
}

c.tags[fs.FileSystemArn] = tags
c.mountTargets[creationToken] = mt
efsList = append(efsList, c.fileSystems[creationToken])
return efsList, nil
Expand Down Expand Up @@ -149,10 +145,3 @@ func (c *FakeCloudProvider) ListAccessPoints(ctx context.Context, fileSystemId s
}
return accessPoints, nil
}

func (c *FakeCloudProvider) ListTagsForFileSystem(ctx context.Context, fileSystemArn string) ([]types.Tag, error) {
if tags, ok := c.tags[fileSystemArn]; ok {
return tags, nil
}
return []types.Tag{}, ErrNotFound
}
20 changes: 0 additions & 20 deletions pkg/cloud/mocks/mock_efs.go

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

Loading

0 comments on commit b445adf

Please sign in to comment.