From fe313ed5ebad12caf9a2ed53f7ee1202bd9121f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Tue, 22 Oct 2024 23:34:53 -0400 Subject: [PATCH 1/2] incusd/storage_volumes_snapshots: Respect pattern on manual creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The snapshot pattern was correctly applied for automatic snapshots but completely ignored for manually created snapshots... Closes #1332 Signed-off-by: Stéphane Graber --- cmd/incusd/storage_volumes_snapshot.go | 75 ++++++++++++++++---------- 1 file changed, 48 insertions(+), 27 deletions(-) diff --git a/cmd/incusd/storage_volumes_snapshot.go b/cmd/incusd/storage_volumes_snapshot.go index a59f2469d34..0669287d8e8 100644 --- a/cmd/incusd/storage_volumes_snapshot.go +++ b/cmd/incusd/storage_volumes_snapshot.go @@ -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 { @@ -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 { @@ -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 { From c3d82bf37d696040959ad1772e3bdc34776dfc90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Tue, 22 Oct 2024 23:39:19 -0400 Subject: [PATCH 2/2] tests: Add test for custom storage volume snapshots pattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- test/suites/storage_snapshots.sh | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/suites/storage_snapshots.sh b/test/suites/storage_snapshots.sh index 10c3868c942..0205137db97 100644 --- a/test/suites/storage_snapshots.sh +++ b/test/suites/storage_snapshots.sh @@ -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"