Skip to content

Commit 5a301d7

Browse files
committed
Adding SnapshotLock Capabilities
1 parent 87f31ef commit 5a301d7

File tree

8 files changed

+116
-5
lines changed

8 files changed

+116
-5
lines changed

pkg/cloud/cloud.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,12 +306,18 @@ type ListSnapshotsResponse struct {
306306
NextToken string
307307
}
308308

309-
// SnapshotOptions represents parameters to create an EBS volume.
309+
// SnapshotOptions represents parameters to create an EBS snapshot.
310310
type SnapshotOptions struct {
311311
Tags map[string]string
312312
OutpostArn string
313313
}
314314

315+
// SnapshotLockOptions represents the snapshot lock specific parameters for locking en EBS snapshot.
316+
type SnapshotLockOptions struct {
317+
SnapshotLockEnabled bool
318+
LockSnapshotInput ec2.LockSnapshotInput
319+
}
320+
315321
// ec2ListSnapshotsResponse is a helper struct returned from the AWS API calling function to the main ListSnapshots function.
316322
type ec2ListSnapshotsResponse struct {
317323
Snapshots []types.Snapshot
@@ -1867,6 +1873,15 @@ func (c *cloud) CreateSnapshot(ctx context.Context, volumeID string, snapshotOpt
18671873
}, nil
18681874
}
18691875

1876+
func (c *cloud) LockSnapshot(ctx context.Context, lockSnapshotInput ec2.LockSnapshotInput) (*ec2.LockSnapshotOutput, error) {
1877+
klog.InfoS("Attempting to lock Snapshot", "request parameters: ", lockSnapshotInput)
1878+
response, err := c.ec2.LockSnapshot(ctx, &lockSnapshotInput)
1879+
if err != nil {
1880+
return nil, err
1881+
}
1882+
return response, nil
1883+
}
1884+
18701885
func (c *cloud) DeleteSnapshot(ctx context.Context, snapshotID string) (success bool, err error) {
18711886
request := &ec2.DeleteSnapshotInput{}
18721887
request.SnapshotId = aws.String(snapshotID)

pkg/cloud/interface.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,5 @@ type Cloud interface {
4242
AvailabilityZones(ctx context.Context) (map[string]struct{}, error)
4343
DryRun(ctx context.Context) error
4444
GetInstancesPatching(ctx context.Context, nodeIDs []string) ([]*types.Instance, error)
45+
LockSnapshot(ctx context.Context, lockOptions ec2.LockSnapshotInput) (*ec2.LockSnapshotOutput, error)
4546
}

pkg/cloud/mock_cloud.go

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/cloud/mock_ec2.go

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/driver/constants.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,21 @@ const (
126126
const (
127127
// FastSnapshotRestoreAvailabilityZones represents key for fast snapshot restore availability zones.
128128
FastSnapshotRestoreAvailabilityZones = "fastsnapshotrestoreavailabilityzones"
129+
130+
// SnapshotLockEnabled represents a key for indicating whether snapshot lock is enabled or disabled.
131+
SnapshotLockEnabled = "snapshotlockenabled"
132+
133+
// SnapshotLockMode represents a key for indicating whether snapshots are locked in Governance or Compliance mode.
134+
SnapshotLockMode = "snapshotlockmode"
135+
136+
// SnapshotLockDuration is a key for the duration for which to lock the snapshots, specified in days.
137+
SnapshotLockDuration = "snapshotlockduration"
138+
139+
// SnapshotLockExpirationDate is a key for specifying the expiration date for the snapshot lock, specified in the format "YYYY-MM-DDThh:mm:ss.sssZ".
140+
SnapshotLockExpirationDate = "snapshotlockexpirationdate"
141+
142+
// SnapshotLockCoolOffPeriod is a key specifying the cooling-off period for compliance mode, specified in hours.
143+
SnapshotLockCoolOffPeriod = "snapshotlockcooloffperiod"
129144
)
130145

131146
// constants for volume tags and their values.

pkg/driver/controller.go

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,11 @@ import (
2323
"maps"
2424
"strconv"
2525
"strings"
26+
"time"
2627

28+
"github.com/aws/aws-sdk-go-v2/aws"
2729
"github.com/aws/aws-sdk-go-v2/aws/arn"
30+
"github.com/aws/aws-sdk-go-v2/service/ec2/types"
2831
"github.com/awslabs/volume-modifier-for-k8s/pkg/rpc"
2932
csi "github.com/container-storage-interface/spec/lib/go/csi"
3033
"github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/cloud"
@@ -856,9 +859,11 @@ func (d *ControllerService) CreateSnapshot(ctx context.Context, req *csi.CreateS
856859
cloud.AwsEbsDriverTagKey: isManagedByDriver,
857860
}
858861

862+
// Get Parameters Here
859863
var vscTags []string
860864
var fsrAvailabilityZones []string
861865
vsProps := new(template.VolumeSnapshotProps)
866+
vsLock := new(cloud.SnapshotLockOptions)
862867
for key, value := range req.GetParameters() {
863868
switch strings.ToLower(key) {
864869
case VolumeSnapshotNameKey:
@@ -876,6 +881,28 @@ func (d *ControllerService) CreateSnapshot(ctx context.Context, req *csi.CreateS
876881
} else {
877882
return nil, status.Errorf(codes.InvalidArgument, "Invalid parameter value %s is not a valid arn", value)
878883
}
884+
case SnapshotLockEnabled:
885+
vsLock.SnapshotLockEnabled = isTrue(value)
886+
case SnapshotLockMode:
887+
vsLock.LockSnapshotInput.LockMode = types.LockMode(value)
888+
case SnapshotLockDuration:
889+
lockDuration, err := strconv.ParseInt(value, 10, 32)
890+
if err != nil {
891+
return nil, status.Errorf(codes.InvalidArgument, "Could not parse SnapshotLockDuration: %q", value)
892+
}
893+
vsLock.LockSnapshotInput.LockDuration = aws.Int32(int32(lockDuration))
894+
case SnapshotLockExpirationDate:
895+
expirationDate, err := time.Parse(time.RFC3339, value)
896+
if err != nil {
897+
return nil, status.Errorf(codes.InvalidArgument, "Could not parse SnapshotLockExpirationDate: %q", value)
898+
}
899+
vsLock.LockSnapshotInput.ExpirationDate = &expirationDate
900+
case SnapshotLockCoolOffPeriod:
901+
lockCoolOffPeriod, err := strconv.ParseInt(value, 10, 32)
902+
if err != nil {
903+
return nil, status.Errorf(codes.InvalidArgument, "Could not parse SnapshotLockCoolOffPeriod: %q", value)
904+
}
905+
vsLock.LockSnapshotInput.CoolOffPeriod = aws.Int32(int32(lockCoolOffPeriod))
879906
default:
880907
if strings.HasPrefix(key, TagKeyPrefix) {
881908
vscTags = append(vscTags, value)
@@ -936,12 +963,18 @@ func (d *ControllerService) CreateSnapshot(ctx context.Context, req *csi.CreateS
936963
if len(fsrAvailabilityZones) > 0 {
937964
_, err := d.cloud.EnableFastSnapshotRestores(ctx, fsrAvailabilityZones, snapshot.SnapshotID)
938965
if err != nil {
939-
if _, deleteErr := d.cloud.DeleteSnapshot(ctx, snapshot.SnapshotID); deleteErr != nil {
940-
return nil, status.Errorf(codes.Internal, "Could not delete snapshot ID %q: %v", snapshotName, deleteErr)
941-
}
942-
return nil, status.Errorf(codes.Internal, "Failed to create Fast Snapshot Restores for snapshot ID %q: %v", snapshotName, err)
966+
return nil, d.cleanupSnapshotOnError(ctx, snapshot.SnapshotID, snapshotName, err, "Failed to create Fast Snapshot Restores")
943967
}
944968
}
969+
970+
if vsLock.SnapshotLockEnabled {
971+
vsLock.LockSnapshotInput.SnapshotId = &snapshot.SnapshotID
972+
_, err := d.cloud.LockSnapshot(ctx, vsLock.LockSnapshotInput)
973+
if err != nil {
974+
return nil, d.cleanupSnapshotOnError(ctx, snapshot.SnapshotID, snapshotName, err, "Failed to lock snapshot")
975+
}
976+
}
977+
945978
return newCreateSnapshotResponse(snapshot), nil
946979
}
947980

@@ -1299,3 +1332,10 @@ func validateFormattingOption(volumeCapabilities []*csi.VolumeCapability, paramN
12991332
func isTrue(value string) bool {
13001333
return value == trueStr
13011334
}
1335+
1336+
func (d *ControllerService) cleanupSnapshotOnError(ctx context.Context, snapshotID, snapshotName string, originalErr error, errorMsg string) error {
1337+
if _, deleteErr := d.cloud.DeleteSnapshot(ctx, snapshotID); deleteErr != nil {
1338+
return status.Errorf(codes.Internal, "Could not delete snapshot ID %q: %v", snapshotName, deleteErr)
1339+
}
1340+
return status.Errorf(codes.Internal, "%s for snapshot ID %q: %v", errorMsg, snapshotName, originalErr)
1341+
}

pkg/util/ec2_interface.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,5 @@ type EC2API interface {
4242
CreateTags(ctx context.Context, params *ec2.CreateTagsInput, optFns ...func(*ec2.Options)) (*ec2.CreateTagsOutput, error)
4343
DeleteTags(ctx context.Context, params *ec2.DeleteTagsInput, optFns ...func(*ec2.Options)) (*ec2.DeleteTagsOutput, error)
4444
EnableFastSnapshotRestores(ctx context.Context, params *ec2.EnableFastSnapshotRestoresInput, optFns ...func(*ec2.Options)) (*ec2.EnableFastSnapshotRestoresOutput, error)
45+
LockSnapshot(ctx context.Context, params *ec2.LockSnapshotInput, optFns ...func(*ec2.Options)) (*ec2.LockSnapshotOutput, error)
4546
}

tests/sanity/fake_sanity_cloud.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,10 @@ func (d *fakeCloud) EnableFastSnapshotRestores(ctx context.Context, availability
210210
return &ec2.EnableFastSnapshotRestoresOutput{}, nil
211211
}
212212

213+
func (d *fakeCloud) LockSnapshot(ctx context.Context, lockOptions ec2.LockSnapshotInput) (*ec2.LockSnapshotOutput, error) {
214+
return &ec2.LockSnapshotOutput{}, nil
215+
}
216+
213217
func (d *fakeCloud) GetDiskByName(ctx context.Context, name string, capacityBytes int64) (*cloud.Disk, error) {
214218
return &cloud.Disk{}, nil
215219
}

0 commit comments

Comments
 (0)