Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix handling of custom volume snapshot patterns #1333

Merged
merged 2 commits into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 48 additions & 27 deletions cmd/incusd/storage_volumes_snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,19 +167,6 @@ func storagePoolVolumeSnapshotsTypePost(d *Daemon, r *http.Request) response.Res
return response.BadRequest(err)
}

// Get a snapshot name.
if req.Name == "" {
var i int

_ = s.DB.Cluster.Transaction(r.Context(), func(ctx context.Context, tx *db.ClusterTx) error {
i = tx.GetNextStorageVolumeSnapshotIndex(ctx, poolName, volumeName, volumeType, "snap%d")

return nil
})

req.Name = fmt.Sprintf("snap%d", i)
}

// Check that this isn't a restricted volume
used, err := storagePools.VolumeUsedByDaemon(s, poolName, volumeName)
if err != nil {
Expand All @@ -196,22 +183,9 @@ func storagePoolVolumeSnapshotsTypePost(d *Daemon, r *http.Request) response.Res
return response.SmartError(err)
}

// Validate the snapshot name using same rule as pool name.
err = pool.ValidateName(req.Name)
if err != nil {
return response.BadRequest(err)
}

// Get the parent volume.
var parentDBVolume *db.StorageVolume
err = s.DB.Cluster.Transaction(r.Context(), func(ctx context.Context, tx *db.ClusterTx) error {
// Ensure that the snapshot doesn't already exist.
snapDBVolume, err := tx.GetStoragePoolVolume(ctx, pool.ID(), projectName, volumeType, fmt.Sprintf("%s/%s", volumeName, req.Name), true)
if err != nil && !response.IsNotFoundError(err) {
return err
} else if snapDBVolume != nil {
return api.StatusErrorf(http.StatusConflict, "Snapshot %q already in use", req.Name)
}

// Get the parent volume so we can get the config.
parentDBVolume, err = tx.GetStoragePoolVolume(ctx, pool.ID(), projectName, volumeType, volumeName, true)
if err != nil {
Expand All @@ -224,6 +198,53 @@ func storagePoolVolumeSnapshotsTypePost(d *Daemon, r *http.Request) response.Res
return response.SmartError(err)
}

// Get the snapshot pattern.
pattern := parentDBVolume.Config["snapshots.pattern"]
if pattern == "" {
pattern = "snap%d"
}

pattern, err = internalUtil.RenderTemplate(pattern, pongo2.Context{
"creation_date": time.Now(),
})
if err != nil {
return response.InternalError(err)
}

// Get a snapshot name.
if req.Name == "" {
var i int

_ = s.DB.Cluster.Transaction(r.Context(), func(ctx context.Context, tx *db.ClusterTx) error {
i = tx.GetNextStorageVolumeSnapshotIndex(ctx, poolName, volumeName, volumeType, pattern)

return nil
})

req.Name = fmt.Sprintf(pattern, i)
} else {
// Make sure the snapshot doesn't already exist.
err = s.DB.Cluster.Transaction(r.Context(), func(ctx context.Context, tx *db.ClusterTx) error {
snapDBVolume, err := tx.GetStoragePoolVolume(ctx, pool.ID(), projectName, volumeType, fmt.Sprintf("%s/%s", volumeName, req.Name), true)
if err != nil && !response.IsNotFoundError(err) {
return err
} else if snapDBVolume != nil {
return api.StatusErrorf(http.StatusConflict, "Snapshot %q already in use", req.Name)
}

return nil
})
if err != nil {
return response.SmartError(err)
}
}

// Validate the snapshot name using same rule as pool name.
err = pool.ValidateName(req.Name)
if err != nil {
return response.BadRequest(err)
}

// Fill in the expiry.
var expiry time.Time
if req.ExpiresAt != nil {
Expand Down
9 changes: 9 additions & 0 deletions test/suites/storage_snapshots.sh
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,15 @@ test_storage_volume_snapshots() {
incus storage volume delete "${storage_pool}" "vol1"
incus storage volume delete "${storage_pool}" "vol1-snap0"

# Check snapshot pattern
incus storage volume create "${storage_pool}" "vol1"
incus storage volume snapshot create "${storage_pool}" "vol1"
incus storage volume snapshot show "${storage_pool}" "vol1/snap0"
incus storage volume set "${storage_pool}" "vol1" snapshots.pattern="test%d"
incus storage volume snapshot create "${storage_pool}" "vol1"
incus storage volume snapshot show "${storage_pool}" "vol1/test0"
incus storage volume delete "${storage_pool}" "vol1"

# Check snapshot restore of type block volumes.
incus storage volume create "${storage_pool}" "vol1" --type block
incus storage volume snapshot create "${storage_pool}" "vol1" "snap0"
Expand Down
Loading