1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
|
package backup
import (
"context"
"os"
"time"
"github.com/lxc/incus/v6/internal/server/db"
"github.com/lxc/incus/v6/internal/server/project"
"github.com/lxc/incus/v6/internal/server/state"
internalUtil "github.com/lxc/incus/v6/internal/util"
"github.com/lxc/incus/v6/shared/api"
"github.com/lxc/incus/v6/shared/revert"
"github.com/lxc/incus/v6/shared/util"
)
// BucketBackup represents a bucket backup.
type BucketBackup struct {
CommonBackup
projectName string
poolName string
bucketName string
}
// NewBucketBackup instantiates a new BucketBackup struct.
func NewBucketBackup(s *state.State, projectName, poolName, bucketName string, ID int, name string, creationDate, expiryDate time.Time) *BucketBackup {
return &BucketBackup{
CommonBackup: CommonBackup{
state: s,
id: ID,
name: name,
creationDate: creationDate,
expiryDate: expiryDate,
},
projectName: projectName,
poolName: poolName,
bucketName: bucketName,
}
}
// Delete removes a bucket backup.
func (b *BucketBackup) Delete() error {
backupPath := internalUtil.VarPath("backups", "buckets", b.poolName, project.StorageBucket(b.projectName, b.name))
// Delete the on-disk data.
if util.PathExists(backupPath) {
err := os.RemoveAll(backupPath)
if err != nil {
return err
}
}
// Check if we can remove the bucket directory.
backupsPath := internalUtil.VarPath("backups", "buckets", b.poolName, project.StorageBucket(b.projectName, b.bucketName))
empty, _ := internalUtil.PathIsEmpty(backupsPath)
if empty {
err := os.Remove(backupsPath)
if err != nil {
return err
}
}
// Remove the database record.
err := b.state.DB.Cluster.Transaction(context.TODO(), func(ctx context.Context, tx *db.ClusterTx) error {
return tx.DeleteStoragePoolBucketBackup(ctx, b.name)
})
if err != nil {
return err
}
return nil
}
// Rename renames a bucket backup.
func (b *BucketBackup) Rename(newName string) error {
oldBackupPath := internalUtil.VarPath("backups", "buckets", b.poolName, project.StorageBucket(b.projectName, b.name))
newBackupPath := internalUtil.VarPath("backups", "buckets", b.poolName, project.StorageBucket(b.projectName, newName))
// Extract the old and new parent backup paths from the old and new backup names rather than use
// bucket.Name() as this may be in flux if the bucket itself is being renamed, whereas the relevant
// bucket name is encoded into the backup names.
oldParentName, _, _ := api.GetParentAndSnapshotName(b.name)
oldParentBackupsPath := internalUtil.VarPath("backups", "buckets", b.poolName, project.StorageBucket(b.projectName, oldParentName))
newParentName, _, _ := api.GetParentAndSnapshotName(newName)
newParentBackupsPath := internalUtil.VarPath("backups", "buckets", b.poolName, project.StorageBucket(b.projectName, newParentName))
reverter := revert.New()
defer reverter.Fail()
// Create the new backup path if doesn't exist.
if !util.PathExists(newParentBackupsPath) {
err := os.MkdirAll(newParentBackupsPath, 0o700)
if err != nil {
return err
}
}
// Rename the backup directory.
err := os.Rename(oldBackupPath, newBackupPath)
if err != nil {
return err
}
reverter.Add(func() { _ = os.Rename(newBackupPath, oldBackupPath) })
// Check if we can remove the old parent directory.
empty, _ := internalUtil.PathIsEmpty(oldParentBackupsPath)
if empty {
err := os.Remove(oldParentBackupsPath)
if err != nil {
return err
}
}
// Rename the database record.
err = b.state.DB.Cluster.Transaction(context.TODO(), func(ctx context.Context, tx *db.ClusterTx) error {
return tx.RenameBucketBackup(ctx, b.name, newName)
})
if err != nil {
return err
}
reverter.Success()
return nil
}
// Render returns a BucketBackup struct of the backup.
func (b *BucketBackup) Render() *api.StorageBucketBackup {
return &api.StorageBucketBackup{
Name: b.name,
ExpiresAt: b.expiryDate,
CompressionAlgorithm: b.compressionAlgorithm,
}
}
|