Skip to content

Commit aafc5df

Browse files
authored
Merge pull request #1620 from jrakas-dev/use-existing-ap
Return existing access point if one already exists during create workflow
2 parents a65c9dc + c3d4981 commit aafc5df

File tree

4 files changed

+404
-6
lines changed

4 files changed

+404
-6
lines changed

pkg/cloud/cloud.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,9 @@ func (c *cloud) CreateAccessPoint(ctx context.Context, clientToken string, acces
204204
if isAccessDenied(err) {
205205
return nil, ErrAccessDenied
206206
}
207+
if isAccessPointAlreadyExists(err) {
208+
return nil, ErrAlreadyExists
209+
}
207210
return nil, fmt.Errorf("Failed to create access point: %v", err)
208211
}
209212
klog.V(5).Infof("Create AP response : %+v", res)
@@ -289,6 +292,10 @@ func (c *cloud) FindAccessPointByClientToken(ctx context.Context, clientToken, f
289292
AccessPointId: *ap.AccessPointId,
290293
FileSystemId: *ap.FileSystemId,
291294
AccessPointRootDir: *ap.RootDirectory.Path,
295+
PosixUser: &PosixUser{
296+
Gid: *ap.PosixUser.Gid,
297+
Uid: *ap.PosixUser.Uid,
298+
},
292299
}, nil
293300
}
294301
}
@@ -435,6 +442,16 @@ func isAccessDenied(err error) bool {
435442
return false
436443
}
437444

445+
func isAccessPointAlreadyExists(err error) bool {
446+
var apiErr smithy.APIError
447+
if errors.As(err, &apiErr) {
448+
if apiErr.ErrorCode() == AccessPointAlreadyExists {
449+
return true
450+
}
451+
}
452+
return false
453+
}
454+
438455
func isDriverBootedInECS() bool {
439456
ecsContainerMetadataUri := os.Getenv(taskMetadataV4EnvName)
440457
return ecsContainerMetadataUri != ""

pkg/cloud/cloud_test.go

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,38 @@ func TestCreateAccessPoint(t *testing.T) {
158158
mockCtl.Finish()
159159
},
160160
},
161+
{
162+
name: "Error: Access Point already exists",
163+
testFunc: func(t *testing.T) {
164+
mockCtl := gomock.NewController(t)
165+
mockEfs := mocks.NewMockEfs(mockCtl)
166+
c := &cloud{efs: mockEfs}
167+
168+
req := &AccessPointOptions{
169+
FileSystemId: fsId,
170+
Uid: uid,
171+
Gid: gid,
172+
DirectoryPerms: directoryPerms,
173+
DirectoryPath: directoryPath,
174+
}
175+
176+
ctx := context.Background()
177+
mockEfs.EXPECT().CreateAccessPoint(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil,
178+
&smithy.GenericAPIError{
179+
Code: AccessPointAlreadyExists,
180+
Message: "Access point already exists",
181+
})
182+
183+
_, err := c.CreateAccessPoint(ctx, clientToken, req)
184+
if err == nil {
185+
t.Fatalf("CreateAccessPoint did not return error")
186+
}
187+
if err != ErrAlreadyExists {
188+
t.Fatalf("Failed. Expected: %v, Actual:%v", ErrAlreadyExists, err)
189+
}
190+
mockCtl.Finish()
191+
},
192+
},
161193
}
162194

163195
for _, tc := range testCases {
@@ -1085,6 +1117,16 @@ func Test_findAccessPointByPath(t *testing.T) {
10851117
FileSystemId: fsId,
10861118
}
10871119

1120+
expectedSingleAPWithPosixUser := &AccessPoint{
1121+
AccessPointId: "testApId",
1122+
AccessPointRootDir: dirPath,
1123+
PosixUser: &PosixUser{
1124+
Uid: 1000,
1125+
Gid: 1000,
1126+
},
1127+
FileSystemId: fsId,
1128+
}
1129+
10881130
type args struct {
10891131
clientToken string
10901132
accessPointOpts *AccessPointOptions
@@ -1105,10 +1147,16 @@ func Test_findAccessPointByPath(t *testing.T) {
11051147
mockEfs.EXPECT().DescribeAccessPoints(gomock.Any(), gomock.Any(), gomock.Any()).Return(&efs.DescribeAccessPointsOutput{
11061148
AccessPoints: []types.AccessPointDescription{
11071149
{FileSystemId: aws.String(fsId), ClientToken: diffClientToken, AccessPointId: aws.String("differentApId"), RootDirectory: &types.RootDirectory{Path: aws.String(expectedSingleAP.AccessPointRootDir)}},
1108-
{FileSystemId: aws.String(fsId), ClientToken: &clientToken, AccessPointId: aws.String(expectedSingleAP.AccessPointId), RootDirectory: &types.RootDirectory{Path: aws.String(expectedSingleAP.AccessPointRootDir)}},
1150+
{
1151+
FileSystemId: aws.String(fsId),
1152+
ClientToken: &clientToken,
1153+
AccessPointId: aws.String(expectedSingleAPWithPosixUser.AccessPointId),
1154+
RootDirectory: &types.RootDirectory{Path: aws.String(expectedSingleAPWithPosixUser.AccessPointRootDir)},
1155+
PosixUser: &types.PosixUser{Gid: aws.Int64(1000), Uid: aws.Int64(1000)},
1156+
},
11091157
},
11101158
}, nil)
1111-
}, wantAccessPoint: expectedSingleAP, wantErr: false},
1159+
}, wantAccessPoint: expectedSingleAPWithPosixUser, wantErr: false},
11121160
{name: "Fail_DescribeAccessPoints", args: args{clientToken, &AccessPointOptions{FileSystemId: fsId, DirectoryPath: dirPath}}, prepare: func(mockEfs *mocks.MockEfs) {
11131161
mockEfs.EXPECT().DescribeAccessPoints(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, errors.New("access_denied"))
11141162
}, wantAccessPoint: nil, wantErr: true},

pkg/driver/controller.go

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -351,11 +351,23 @@ func (d *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest)
351351
if err != nil {
352352
if err == cloud.ErrAccessDenied {
353353
return nil, status.Errorf(codes.Unauthenticated, "Access Denied. Please ensure you have the right AWS permissions: %v", err)
354+
} else if err == cloud.ErrAlreadyExists {
355+
klog.V(4).Infof("Access point already exists for client token %s. Retrieving existing access point details.", clientToken)
356+
existingAccessPoint, err := localCloud.FindAccessPointByClientToken(ctx, clientToken, accessPointsOptions.FileSystemId)
357+
if err != nil {
358+
return nil, fmt.Errorf("Error attempting to retrieve existing access point for client token %s: %v", clientToken, err)
359+
}
360+
if existingAccessPoint == nil {
361+
return nil, fmt.Errorf("No access point for client token %s was returned: %v", clientToken, err)
362+
}
363+
err = validateExistingAccessPoint(existingAccessPoint, basePath, gidMin, gidMax)
364+
if err != nil {
365+
return nil, status.Errorf(codes.AlreadyExists, "Invalid existing access point: %v", err)
366+
}
367+
accessPoint = existingAccessPoint
368+
} else {
369+
return nil, status.Errorf(codes.Internal, "Failed to create Access point in File System %v : %v", accessPointsOptions.FileSystemId, err)
354370
}
355-
if err == cloud.ErrAlreadyExists {
356-
return nil, status.Errorf(codes.AlreadyExists, "Access Point already exists")
357-
}
358-
return nil, status.Errorf(codes.Internal, "Failed to create Access point in File System %v : %v", accessPointsOptions.FileSystemId, err)
359371
}
360372

361373
// Lock on the new access point to prevent accidental deletion before creation is done
@@ -711,3 +723,26 @@ func get64LenHash(text string) string {
711723
h.Write([]byte(text))
712724
return fmt.Sprintf("%x", h.Sum(nil))
713725
}
726+
727+
func validateExistingAccessPoint(existingAccessPoint *cloud.AccessPoint, basePath string, gidMin int64, gidMax int64) error {
728+
729+
normalizedBasePath := strings.TrimPrefix(basePath, "/")
730+
normalizedAccessPointPath := strings.TrimPrefix(existingAccessPoint.AccessPointRootDir, "/")
731+
if !strings.HasPrefix(normalizedAccessPointPath, normalizedBasePath) {
732+
return fmt.Errorf("Access point found but has different base path than what's specified in storage class")
733+
}
734+
735+
if existingAccessPoint.PosixUser == nil {
736+
return fmt.Errorf("Access point found but PosixUser is nil")
737+
}
738+
739+
if existingAccessPoint.PosixUser.Gid < gidMin || existingAccessPoint.PosixUser.Gid > gidMax {
740+
return fmt.Errorf("Access point found but its GID is outside the specified range")
741+
}
742+
743+
if existingAccessPoint.PosixUser.Uid < gidMin || existingAccessPoint.PosixUser.Uid > gidMax {
744+
return fmt.Errorf("Access point found but its UID is outside the specified range")
745+
}
746+
747+
return nil
748+
}

0 commit comments

Comments
 (0)