The following pull request was submitted through Github.
It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/7930

This e-mail was sent by the LXC bot, direct replies will not reach the author
unless they happen to be subscribed to this list.

=== Description (from pull-request) ===
Ready for custom volume backups.
From fdea618103eb2c9b00e8dd358871bf52ef683963 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 24 Sep 2020 15:13:27 +0100
Subject: [PATCH 01/14] lxd/backup/instance/config: Renames InstanceConfig to
 Config

As a precursor to including custom volume support in this struct.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 ...backup_instance_config.go => backup_config.go} | 15 +++++++--------
 1 file changed, 7 insertions(+), 8 deletions(-)
 rename lxd/backup/{backup_instance_config.go => backup_config.go} (84%)

diff --git a/lxd/backup/backup_instance_config.go b/lxd/backup/backup_config.go
similarity index 84%
rename from lxd/backup/backup_instance_config.go
rename to lxd/backup/backup_config.go
index 5120087bb5..c0e6d3357d 100644
--- a/lxd/backup/backup_instance_config.go
+++ b/lxd/backup/backup_config.go
@@ -13,22 +13,22 @@ import (
        "github.com/lxc/lxd/shared/api"
 )
 
-// InstanceConfig represents the config of an instance that can be stored in a 
backup.yaml file.
-type InstanceConfig struct {
+// Config represents the config of a backup that can be stored in a 
backup.yaml file (or embedded in index.yaml).
+type Config struct {
        Container *api.Instance           `yaml:"container"`
        Snapshots []*api.InstanceSnapshot `yaml:"snapshots"`
        Pool      *api.StoragePool        `yaml:"pool"`
        Volume    *api.StorageVolume      `yaml:"volume"`
 }
 
-// ParseInstanceConfigYamlFile decodes the YAML file at path specified into an 
InstanceConfig.
-func ParseInstanceConfigYamlFile(path string) (*InstanceConfig, error) {
+// ParseConfigYamlFile decodes the YAML file at path specified into a Config.
+func ParseConfigYamlFile(path string) (*Config, error) {
        data, err := ioutil.ReadFile(path)
        if err != nil {
                return nil, err
        }
 
-       backup := InstanceConfig{}
+       backup := Config{}
        if err := yaml.Unmarshal(data, &backup); err != nil {
                return nil, err
        }
@@ -50,8 +50,7 @@ func updateRootDevicePool(devices 
map[string]map[string]string, poolName string)
        return false
 }
 
-// UpdateInstanceConfigStoragePool changes the pool information in the 
backup.yaml to the pool
-// specified in b.Pool.
+// UpdateInstanceConfigStoragePool changes the pool information in the 
backup.yaml to the pool specified in b.Pool.
 func UpdateInstanceConfigStoragePool(c *db.Cluster, b Info, mountPath string) 
error {
        // Load the storage pool.
        _, pool, err := c.GetStoragePool(b.Pool)
@@ -61,7 +60,7 @@ func UpdateInstanceConfigStoragePool(c *db.Cluster, b Info, 
mountPath string) er
 
        f := func(path string) error {
                // Read in the backup.yaml file.
-               backup, err := ParseInstanceConfigYamlFile(path)
+               backup, err := ParseConfigYamlFile(path)
                if err != nil {
                        return err
                }

From acb0551aad7b544be9055c0a1b2ccce4fbf08f59 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 24 Sep 2020 15:18:26 +0100
Subject: [PATCH 02/14] lxd/backup/config: Makes Config fields omitempty so
 custom volume's encoded yaml doesn't contain instance fields

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/backup/backup_config.go | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/lxd/backup/backup_config.go b/lxd/backup/backup_config.go
index c0e6d3357d..ddfd8078a0 100644
--- a/lxd/backup/backup_config.go
+++ b/lxd/backup/backup_config.go
@@ -15,10 +15,10 @@ import (
 
 // Config represents the config of a backup that can be stored in a 
backup.yaml file (or embedded in index.yaml).
 type Config struct {
-       Container *api.Instance           `yaml:"container"`
-       Snapshots []*api.InstanceSnapshot `yaml:"snapshots"`
-       Pool      *api.StoragePool        `yaml:"pool"`
-       Volume    *api.StorageVolume      `yaml:"volume"`
+       Container *api.Instance           `yaml:"container,omitempty"`
+       Snapshots []*api.InstanceSnapshot `yaml:"snapshots,omitempty"`
+       Pool      *api.StoragePool        `yaml:"pool,omitempty"`
+       Volume    *api.StorageVolume      `yaml:"volume,omitempty"`
 }
 
 // ParseConfigYamlFile decodes the YAML file at path specified into a Config.

From 52d6705ac9f6189a4a0e6c8f5cde9c84595bf14a Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 24 Sep 2020 15:19:13 +0100
Subject: [PATCH 03/14] lxd/storage/pool/interface: backup.Config usage

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/storage/pool_interface.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lxd/storage/pool_interface.go b/lxd/storage/pool_interface.go
index 82a424f07f..a4de84cc73 100644
--- a/lxd/storage/pool_interface.go
+++ b/lxd/storage/pool_interface.go
@@ -39,7 +39,7 @@ type Pool interface {
        DeleteInstance(inst instance.Instance, op *operations.Operation) error
        UpdateInstance(inst instance.Instance, newDesc string, newConfig 
map[string]string, op *operations.Operation) error
        UpdateInstanceBackupFile(inst instance.Instance, op 
*operations.Operation) error
-       CheckInstanceBackupFileSnapshots(backupConf *backup.InstanceConfig, 
projectName string, deleteMissing bool, op *operations.Operation) 
([]*api.InstanceSnapshot, error)
+       CheckInstanceBackupFileSnapshots(backupConf *backup.Config, projectName 
string, deleteMissing bool, op *operations.Operation) ([]*api.InstanceSnapshot, 
error)
 
        MigrateInstance(inst instance.Instance, conn io.ReadWriteCloser, args 
*migration.VolumeSourceArgs, op *operations.Operation) error
        RefreshInstance(inst instance.Instance, src instance.Instance, 
srcSnapshots []instance.Instance, op *operations.Operation) error

From fe5dd1852003af29bed60a9d54d01e72d15801ee Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 24 Sep 2020 15:19:38 +0100
Subject: [PATCH 04/14] lxd/api/internal: backup.ParseConfigYamlFile usage

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/api_internal.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lxd/api_internal.go b/lxd/api_internal.go
index 8df0ac82bc..320f09a955 100644
--- a/lxd/api_internal.go
+++ b/lxd/api_internal.go
@@ -495,7 +495,7 @@ func internalImport(d *Daemon, r *http.Request) 
response.Response {
 
        // Read in the backup.yaml file.
        backupYamlPath := filepath.Join(instanceMountPoint, "backup.yaml")
-       backupConf, err := backup.ParseInstanceConfigYamlFile(backupYamlPath)
+       backupConf, err := backup.ParseConfigYamlFile(backupYamlPath)
        if err != nil {
                return response.SmartError(err)
        }

From e26ca50d0f058a14be21312f7019f531e08bf47c Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 24 Sep 2020 15:20:06 +0100
Subject: [PATCH 05/14] lxd/storage/backend: backup.Config usage

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/storage/backend_lxd.go  | 4 ++--
 lxd/storage/backend_mock.go | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index c4f34c08d6..d5b523f437 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -3254,7 +3254,7 @@ func (b *lxdBackend) UpdateInstanceBackupFile(inst 
instance.Instance, op *operat
                return err
        }
 
-       data, err := yaml.Marshal(&backup.InstanceConfig{
+       data, err := yaml.Marshal(&backup.Config{
                Container: ci.(*api.Instance),
                Snapshots: sis,
                Pool:      &b.db,
@@ -3302,7 +3302,7 @@ func (b *lxdBackend) UpdateInstanceBackupFile(inst 
instance.Instance, op *operat
 // config are removed from the storage device, and any snapshots that exist in 
the backup config but do not exist
 // on the storage device are ignored. The remaining set of snapshots that 
exist on both the storage device and the
 // backup config are returned. They set can be used to re-create the snapshot 
database entries when importing.
-func (b *lxdBackend) CheckInstanceBackupFileSnapshots(backupConf 
*backup.InstanceConfig, projectName string, deleteMissing bool, op 
*operations.Operation) ([]*api.InstanceSnapshot, error) {
+func (b *lxdBackend) CheckInstanceBackupFileSnapshots(backupConf 
*backup.Config, projectName string, deleteMissing bool, op 
*operations.Operation) ([]*api.InstanceSnapshot, error) {
        logger := logging.AddContext(b.logger, log.Ctx{"project": projectName, 
"instance": backupConf.Container.Name, "deleteMissing": deleteMissing})
        logger.Debug("CheckInstanceBackupFileSnapshots started")
        defer logger.Debug("CheckInstanceBackupFileSnapshots finished")
diff --git a/lxd/storage/backend_mock.go b/lxd/storage/backend_mock.go
index 48d8be7ecd..becf5391bb 100644
--- a/lxd/storage/backend_mock.go
+++ b/lxd/storage/backend_mock.go
@@ -103,7 +103,7 @@ func (b *mockBackend) UpdateInstanceBackupFile(inst 
instance.Instance, op *opera
        return nil
 }
 
-func (b *mockBackend) CheckInstanceBackupFileSnapshots(backupConf 
*backup.InstanceConfig, projectName string, deleteMissing bool, op 
*operations.Operation) ([]*api.InstanceSnapshot, error) {
+func (b *mockBackend) CheckInstanceBackupFileSnapshots(backupConf 
*backup.Config, projectName string, deleteMissing bool, op 
*operations.Operation) ([]*api.InstanceSnapshot, error) {
        return nil, nil
 }
 

From 90a3ec2f66d06cacef316bca93ffe4d5e4fabc65 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 24 Sep 2020 15:21:53 +0100
Subject: [PATCH 06/14] lxd/backup: Moves Instance interface into own file

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/backup/backup.go          | 7 -------
 lxd/backup/backup_instance.go | 8 ++++++++
 2 files changed, 8 insertions(+), 7 deletions(-)
 create mode 100644 lxd/backup/backup_instance.go

diff --git a/lxd/backup/backup.go b/lxd/backup/backup.go
index c7226b32d3..6f6867bb05 100644
--- a/lxd/backup/backup.go
+++ b/lxd/backup/backup.go
@@ -20,13 +20,6 @@ import (
 // WorkingDirPrefix is used when temporary working directories are needed.
 const WorkingDirPrefix = "lxd_backup"
 
-// Instance represents the backup relevant subset of a LXD instance.
-// This is used rather than instance.Instance to avoid import loops.
-type Instance interface {
-       Name() string
-       Project() string
-}
-
 // Info represents exported backup information.
 type Info struct {
        Project          string           `json:"-" yaml:"-"` // Project is set 
during import based on current project.
diff --git a/lxd/backup/backup_instance.go b/lxd/backup/backup_instance.go
new file mode 100644
index 0000000000..d2d63c7074
--- /dev/null
+++ b/lxd/backup/backup_instance.go
@@ -0,0 +1,8 @@
+package backup
+
+// Instance represents the backup relevant subset of a LXD instance.
+// This is used rather than instance.Instance to avoid import loops.
+type Instance interface {
+       Name() string
+       Project() string
+}

From 9d6ee4b8d012a04391fad2ac8509cb50d4b82a00 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 24 Sep 2020 15:24:49 +0100
Subject: [PATCH 07/14] lxd/backup: Moves Info struct and GetInfo function into
 own file

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/backup/backup.go      | 103 -----------------------------------
 lxd/backup/backup_info.go | 110 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 110 insertions(+), 103 deletions(-)
 create mode 100644 lxd/backup/backup_info.go

diff --git a/lxd/backup/backup.go b/lxd/backup/backup.go
index 6f6867bb05..e586906feb 100644
--- a/lxd/backup/backup.go
+++ b/lxd/backup/backup.go
@@ -1,16 +1,10 @@
 package backup
 
 import (
-       "context"
-       "fmt"
-       "io"
        "os"
        "strings"
        "time"
 
-       "github.com/pkg/errors"
-       "gopkg.in/yaml.v2"
-
        "github.com/lxc/lxd/lxd/project"
        "github.com/lxc/lxd/lxd/state"
        "github.com/lxc/lxd/shared"
@@ -20,103 +14,6 @@ import (
 // WorkingDirPrefix is used when temporary working directories are needed.
 const WorkingDirPrefix = "lxd_backup"
 
-// Info represents exported backup information.
-type Info struct {
-       Project          string           `json:"-" yaml:"-"` // Project is set 
during import based on current project.
-       Name             string           `json:"name" yaml:"name"`
-       Backend          string           `json:"backend" yaml:"backend"`
-       Pool             string           `json:"pool" yaml:"pool"`
-       Snapshots        []string         `json:"snapshots,omitempty" 
yaml:"snapshots,omitempty"`
-       OptimizedStorage *bool            `json:"optimized,omitempty" 
yaml:"optimized,omitempty"`               // Optional field to handle older 
optimized backups that don't have this field.
-       OptimizedHeader  *bool            `json:"optimized_header,omitempty" 
yaml:"optimized_header,omitempty"` // Optional field to handle older optimized 
backups that don't have this field.
-       Type             api.InstanceType `json:"type" yaml:"type"`
-}
-
-// GetInfo extracts backup information from a given ReadSeeker.
-func GetInfo(r io.ReadSeeker) (*Info, error) {
-       result := Info{}
-       hasIndexFile := false
-
-       // Define some bools used to create points for OptimizedStorage field.
-       optimizedStorageFalse := false
-       optimizedHeaderFalse := false
-
-       // Extract
-       r.Seek(0, 0)
-       _, _, unpacker, err := shared.DetectCompressionFile(r)
-       if err != nil {
-               return nil, err
-       }
-
-       if unpacker == nil {
-               return nil, fmt.Errorf("Unsupported backup compression")
-       }
-
-       tr, cancelFunc, err := shared.CompressedTarReader(context.Background(), 
r, unpacker)
-       if err != nil {
-               return nil, err
-       }
-       defer cancelFunc()
-
-       for {
-               hdr, err := tr.Next()
-               if err == io.EOF {
-                       break // End of archive
-               }
-               if err != nil {
-                       return nil, errors.Wrapf(err, "Error reading backup 
file info")
-               }
-
-               if hdr.Name == "backup/index.yaml" {
-                       err = yaml.NewDecoder(tr).Decode(&result)
-                       if err != nil {
-                               return nil, err
-                       }
-
-                       hasIndexFile = true
-
-                       // Default to container if index doesn't specify 
instance type.
-                       if result.Type == api.InstanceTypeAny {
-                               result.Type = api.InstanceTypeContainer
-                       }
-
-                       // Default to no optimized header if not specified.
-                       if result.OptimizedHeader == nil {
-                               result.OptimizedHeader = &optimizedHeaderFalse
-                       }
-
-                       if result.OptimizedStorage != nil {
-                               // No need to continue looking for optimized 
storage hint using the presence of the
-                               // container.bin file below, as the index.yaml 
file tells us directly.
-                               cancelFunc()
-                               break
-                       } else {
-                               // Default to non-optimized if not specified 
and continue reading to see if
-                               // optimized container.bin file present.
-                               result.OptimizedStorage = &optimizedStorageFalse
-                       }
-               }
-
-               // If the tarball contains a binary dump of the container, then 
this is an optimized backup.
-               if hdr.Name == "backup/container.bin" {
-                       optimizedStorageTrue := true
-                       result.OptimizedStorage = &optimizedStorageTrue
-
-                       // Stop read loop if index.yaml already parsed.
-                       if hasIndexFile {
-                               cancelFunc()
-                               break
-                       }
-               }
-       }
-
-       if !hasIndexFile {
-               return nil, fmt.Errorf("Backup is missing index.yaml")
-       }
-
-       return &result, nil
-}
-
 // Backup represents a container backup
 type Backup struct {
        state    *state.State
diff --git a/lxd/backup/backup_info.go b/lxd/backup/backup_info.go
new file mode 100644
index 0000000000..1c8e47083e
--- /dev/null
+++ b/lxd/backup/backup_info.go
@@ -0,0 +1,110 @@
+package backup
+
+import (
+       "context"
+       "fmt"
+       "io"
+
+       "github.com/pkg/errors"
+       "gopkg.in/yaml.v2"
+
+       "github.com/lxc/lxd/shared"
+       "github.com/lxc/lxd/shared/api"
+)
+
+// Info represents exported backup information.
+type Info struct {
+       Project          string           `json:"-" yaml:"-"` // Project is set 
during import based on current project.
+       Name             string           `json:"name" yaml:"name"`
+       Backend          string           `json:"backend" yaml:"backend"`
+       Pool             string           `json:"pool" yaml:"pool"`
+       Snapshots        []string         `json:"snapshots,omitempty" 
yaml:"snapshots,omitempty"`
+       OptimizedStorage *bool            `json:"optimized,omitempty" 
yaml:"optimized,omitempty"`               // Optional field to handle older 
optimized backups that don't have this field.
+       OptimizedHeader  *bool            `json:"optimized_header,omitempty" 
yaml:"optimized_header,omitempty"` // Optional field to handle older optimized 
backups that don't have this field.
+       Type             api.InstanceType `json:"type" yaml:"type"`
+}
+
+// GetInfo extracts backup information from a given ReadSeeker.
+func GetInfo(r io.ReadSeeker) (*Info, error) {
+       result := Info{}
+       hasIndexFile := false
+
+       // Define some bools used to create points for OptimizedStorage field.
+       optimizedStorageFalse := false
+       optimizedHeaderFalse := false
+
+       // Extract
+       r.Seek(0, 0)
+       _, _, unpacker, err := shared.DetectCompressionFile(r)
+       if err != nil {
+               return nil, err
+       }
+
+       if unpacker == nil {
+               return nil, fmt.Errorf("Unsupported backup compression")
+       }
+
+       tr, cancelFunc, err := shared.CompressedTarReader(context.Background(), 
r, unpacker)
+       if err != nil {
+               return nil, err
+       }
+       defer cancelFunc()
+
+       for {
+               hdr, err := tr.Next()
+               if err == io.EOF {
+                       break // End of archive
+               }
+               if err != nil {
+                       return nil, errors.Wrapf(err, "Error reading backup 
file info")
+               }
+
+               if hdr.Name == "backup/index.yaml" {
+                       err = yaml.NewDecoder(tr).Decode(&result)
+                       if err != nil {
+                               return nil, err
+                       }
+
+                       hasIndexFile = true
+
+                       // Default to container if index doesn't specify 
instance type.
+                       if result.Type == api.InstanceTypeAny {
+                               result.Type = api.InstanceTypeContainer
+                       }
+
+                       // Default to no optimized header if not specified.
+                       if result.OptimizedHeader == nil {
+                               result.OptimizedHeader = &optimizedHeaderFalse
+                       }
+
+                       if result.OptimizedStorage != nil {
+                               // No need to continue looking for optimized 
storage hint using the presence of the
+                               // container.bin file below, as the index.yaml 
file tells us directly.
+                               cancelFunc()
+                               break
+                       } else {
+                               // Default to non-optimized if not specified 
and continue reading to see if
+                               // optimized container.bin file present.
+                               result.OptimizedStorage = &optimizedStorageFalse
+                       }
+               }
+
+               // If the tarball contains a binary dump of the container, then 
this is an optimized backup.
+               if hdr.Name == "backup/container.bin" {
+                       optimizedStorageTrue := true
+                       result.OptimizedStorage = &optimizedStorageTrue
+
+                       // Stop read loop if index.yaml already parsed.
+                       if hasIndexFile {
+                               cancelFunc()
+                               break
+                       }
+               }
+       }
+
+       if !hasIndexFile {
+               return nil, fmt.Errorf("Backup is missing index.yaml")
+       }
+
+       return &result, nil
+}

From f87c451c0718223ed57511c3e909fe786bbee05e Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 24 Sep 2020 15:27:31 +0100
Subject: [PATCH 08/14] lxd/backup: Renames backup to backup_common

To accomodate more specific implementation types.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/backup/{backup.go => backup_common.go} | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename lxd/backup/{backup.go => backup_common.go} (100%)

diff --git a/lxd/backup/backup.go b/lxd/backup/backup_common.go
similarity index 100%
rename from lxd/backup/backup.go
rename to lxd/backup/backup_common.go

From 00d0039cbb715cb2125187a8476cc41048c0e4dc Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 24 Sep 2020 15:44:30 +0100
Subject: [PATCH 09/14] lxd/backup/backup/common: Renames Backup to
 BackupCommon

Removes any-non common backup functionality.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/backup/backup_common.go | 132 +++---------------------------------
 1 file changed, 9 insertions(+), 123 deletions(-)

diff --git a/lxd/backup/backup_common.go b/lxd/backup/backup_common.go
index e586906feb..6b298bcdd2 100644
--- a/lxd/backup/backup_common.go
+++ b/lxd/backup/backup_common.go
@@ -1,156 +1,42 @@
 package backup
 
 import (
-       "os"
-       "strings"
        "time"
 
-       "github.com/lxc/lxd/lxd/project"
        "github.com/lxc/lxd/lxd/state"
-       "github.com/lxc/lxd/shared"
-       "github.com/lxc/lxd/shared/api"
 )
 
 // WorkingDirPrefix is used when temporary working directories are needed.
 const WorkingDirPrefix = "lxd_backup"
 
-// Backup represents a container backup
-type Backup struct {
-       state    *state.State
-       instance Instance
-
-       // Properties
+// CommonBackup represents a common backup.
+type CommonBackup struct {
+       state                *state.State
        id                   int
        name                 string
        creationDate         time.Time
        expiryDate           time.Time
-       instanceOnly         bool
        optimizedStorage     bool
        compressionAlgorithm string
 }
 
-// New instantiates a new Backup struct.
-func New(state *state.State, inst Instance, ID int, name string, creationDate, 
expiryDate time.Time, instanceOnly, optimizedStorage bool) *Backup {
-       return &Backup{
-               state:            state,
-               instance:         inst,
-               id:               ID,
-               name:             name,
-               creationDate:     creationDate,
-               expiryDate:       expiryDate,
-               instanceOnly:     instanceOnly,
-               optimizedStorage: optimizedStorage,
-       }
+// Name returns the name of the backup.
+func (b *CommonBackup) Name() string {
+       return b.name
 }
 
 // CompressionAlgorithm returns the compression used for the tarball.
-func (b *Backup) CompressionAlgorithm() string {
+func (b *CommonBackup) CompressionAlgorithm() string {
        return b.compressionAlgorithm
 }
 
 // SetCompressionAlgorithm sets the tarball compression.
-func (b *Backup) SetCompressionAlgorithm(compression string) {
+func (b *CommonBackup) SetCompressionAlgorithm(compression string) {
        b.compressionAlgorithm = compression
 }
 
-// InstanceOnly returns whether only the instance itself is to be backed up.
-func (b *Backup) InstanceOnly() bool {
-       return b.instanceOnly
-}
-
-// Name returns the name of the backup.
-func (b *Backup) Name() string {
-       return b.name
-}
-
 // OptimizedStorage returns whether the backup is to be performed using
 // optimization supported by the storage driver.
-func (b *Backup) OptimizedStorage() bool {
+func (b *CommonBackup) OptimizedStorage() bool {
        return b.optimizedStorage
 }
-
-// Rename renames an instance backup.
-func (b *Backup) Rename(newName string) error {
-       oldBackupPath := shared.VarPath("backups", "instances", 
project.Instance(b.instance.Project(), b.name))
-       newBackupPath := shared.VarPath("backups", "instances", 
project.Instance(b.instance.Project(), newName))
-
-       // Create the new backup path.
-       backupsPath := shared.VarPath("backups", "instances", 
project.Instance(b.instance.Project(), b.instance.Name()))
-       if !shared.PathExists(backupsPath) {
-               err := os.MkdirAll(backupsPath, 0700)
-               if err != nil {
-                       return err
-               }
-       }
-
-       // Rename the backup directory.
-       err := os.Rename(oldBackupPath, newBackupPath)
-       if err != nil {
-               return err
-       }
-
-       // Check if we can remove the instance directory.
-       empty, _ := shared.PathIsEmpty(backupsPath)
-       if empty {
-               err := os.Remove(backupsPath)
-               if err != nil {
-                       return err
-               }
-       }
-
-       // Rename the database record.
-       err = b.state.Cluster.RenameInstanceBackup(b.name, newName)
-       if err != nil {
-               return err
-       }
-
-       return nil
-}
-
-// Delete removes an instance backup
-func (b *Backup) Delete() error {
-       return DoBackupDelete(b.state, b.instance.Project(), b.name, 
b.instance.Name())
-}
-
-// Render returns an InstanceBackup struct of the backup.
-func (b *Backup) Render() *api.InstanceBackup {
-       return &api.InstanceBackup{
-               Name:             strings.SplitN(b.name, "/", 2)[1],
-               CreatedAt:        b.creationDate,
-               ExpiresAt:        b.expiryDate,
-               InstanceOnly:     b.instanceOnly,
-               ContainerOnly:    b.instanceOnly,
-               OptimizedStorage: b.optimizedStorage,
-       }
-}
-
-// DoBackupDelete deletes a backup.
-func DoBackupDelete(s *state.State, projectName, backupName, instanceName 
string) error {
-       backupPath := shared.VarPath("backups", "instances", 
project.Instance(projectName, backupName))
-
-       // Delete the on-disk data.
-       if shared.PathExists(backupPath) {
-               err := os.RemoveAll(backupPath)
-               if err != nil {
-                       return err
-               }
-       }
-
-       // Check if we can remove the instance directory.
-       backupsPath := shared.VarPath("backups", "instances", 
project.Instance(projectName, instanceName))
-       empty, _ := shared.PathIsEmpty(backupsPath)
-       if empty {
-               err := os.Remove(backupsPath)
-               if err != nil {
-                       return err
-               }
-       }
-
-       // Remove the database record.
-       err := s.Cluster.DeleteInstanceBackup(backupName)
-       if err != nil {
-               return err
-       }
-
-       return nil
-}

From 36a3f9d76401c11c0f5dfca0dfc943487c3965f2 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 24 Sep 2020 15:45:20 +0100
Subject: [PATCH 10/14] lxd/backup/backup/instance: Adds InstanceBackup using
 CommonBackup as basis

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/backup/backup_instance.go | 121 ++++++++++++++++++++++++++++++++++
 1 file changed, 121 insertions(+)

diff --git a/lxd/backup/backup_instance.go b/lxd/backup/backup_instance.go
index d2d63c7074..9d6554bd60 100644
--- a/lxd/backup/backup_instance.go
+++ b/lxd/backup/backup_instance.go
@@ -1,8 +1,129 @@
 package backup
 
+import (
+       "os"
+       "strings"
+       "time"
+
+       "github.com/lxc/lxd/lxd/project"
+       "github.com/lxc/lxd/lxd/state"
+       "github.com/lxc/lxd/shared"
+       "github.com/lxc/lxd/shared/api"
+)
+
 // Instance represents the backup relevant subset of a LXD instance.
 // This is used rather than instance.Instance to avoid import loops.
 type Instance interface {
        Name() string
        Project() string
 }
+
+// InstanceBackup represents an instance backup.
+type InstanceBackup struct {
+       CommonBackup
+
+       instance     Instance
+       instanceOnly bool
+}
+
+// NewInstanceBackup instantiates a new InstanceBackup struct.
+func NewInstanceBackup(state *state.State, inst Instance, ID int, name string, 
creationDate time.Time, expiryDate time.Time, instanceOnly bool, 
optimizedStorage bool) *InstanceBackup {
+       return &InstanceBackup{
+               CommonBackup: CommonBackup{
+                       state:            state,
+                       id:               ID,
+                       name:             name,
+                       creationDate:     creationDate,
+                       expiryDate:       expiryDate,
+                       optimizedStorage: optimizedStorage,
+               },
+               instance:     inst,
+               instanceOnly: instanceOnly,
+       }
+}
+
+// InstanceOnly returns whether only the instance itself is to be backed up.
+func (b *InstanceBackup) InstanceOnly() bool {
+       return b.instanceOnly
+}
+
+// Rename renames an instance backup.
+func (b *InstanceBackup) Rename(newName string) error {
+       oldBackupPath := shared.VarPath("backups", "instances", 
project.Instance(b.instance.Project(), b.name))
+       newBackupPath := shared.VarPath("backups", "instances", 
project.Instance(b.instance.Project(), newName))
+
+       // Create the new backup path.
+       backupsPath := shared.VarPath("backups", "instances", 
project.Instance(b.instance.Project(), b.instance.Name()))
+       if !shared.PathExists(backupsPath) {
+               err := os.MkdirAll(backupsPath, 0700)
+               if err != nil {
+                       return err
+               }
+       }
+
+       // Rename the backup directory.
+       err := os.Rename(oldBackupPath, newBackupPath)
+       if err != nil {
+               return err
+       }
+
+       // Check if we can remove the instance directory.
+       empty, _ := shared.PathIsEmpty(backupsPath)
+       if empty {
+               err := os.Remove(backupsPath)
+               if err != nil {
+                       return err
+               }
+       }
+
+       // Rename the database record.
+       err = b.state.Cluster.RenameInstanceBackup(b.name, newName)
+       if err != nil {
+               return err
+       }
+
+       return nil
+}
+
+// Delete removes an instance backup.
+func (b *InstanceBackup) Delete() error {
+       backupPath := shared.VarPath("backups", "instances", 
project.Instance(b.instance.Project(), b.name))
+
+       // Delete the on-disk data.
+       if shared.PathExists(backupPath) {
+               err := os.RemoveAll(backupPath)
+               if err != nil {
+                       return err
+               }
+       }
+
+       // Check if we can remove the instance directory.
+       backupsPath := shared.VarPath("backups", "instances", 
project.Instance(b.instance.Project(), b.instance.Name()))
+       empty, _ := shared.PathIsEmpty(backupsPath)
+       if empty {
+               err := os.Remove(backupsPath)
+               if err != nil {
+                       return err
+               }
+       }
+
+       // Remove the database record.
+       err := b.state.Cluster.DeleteInstanceBackup(b.name)
+       if err != nil {
+               return err
+       }
+
+       return nil
+}
+
+// Render returns an InstanceBackup struct of the backup.
+func (b *InstanceBackup) Render() *api.InstanceBackup {
+       return &api.InstanceBackup{
+               Name:             strings.SplitN(b.name, "/", 2)[1],
+               CreatedAt:        b.creationDate,
+               ExpiresAt:        b.expiryDate,
+               InstanceOnly:     b.instanceOnly,
+               ContainerOnly:    b.instanceOnly,
+               OptimizedStorage: b.optimizedStorage,
+       }
+}

From 68e2ecb85f4523449cf25911f6e4a3a47b4bb161 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 24 Sep 2020 15:46:24 +0100
Subject: [PATCH 11/14] lxd/backup: Changes pruneExpiredContainerBackups to use
 InstanceBackup.Delete() function

Avoids having to expose a separate standalone function.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/backup.go | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/lxd/backup.go b/lxd/backup.go
index 3014912073..c7c352e7c2 100644
--- a/lxd/backup.go
+++ b/lxd/backup.go
@@ -279,12 +279,13 @@ func pruneExpiredContainerBackups(ctx context.Context, d 
*Daemon) error {
        for _, b := range backups {
                inst, err := instance.LoadByID(d.State(), b.InstanceID)
                if err != nil {
-                       return errors.Wrapf(err, "Error deleting instance 
backup %s", b.Name)
+                       return errors.Wrapf(err, "Error loading instance for 
deleting backup %q", b.Name)
                }
 
-               err = backup.DoBackupDelete(d.State(), inst.Project(), b.Name, 
inst.Name())
+               instBackup := backup.NewInstanceBackup(d.State(), inst, b.ID, 
b.Name, b.CreationDate, b.ExpiryDate, b.InstanceOnly, b.OptimizedStorage)
+               err = instBackup.Delete()
                if err != nil {
-                       return errors.Wrapf(err, "Error deleting instance 
backup %s", b.Name)
+                       return errors.Wrapf(err, "Error deleting instance 
backup %q", b.Name)
                }
        }
 

From 17336382dca08e0f80782edb1fe3f2139b42ac98 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 24 Sep 2020 15:48:23 +0100
Subject: [PATCH 12/14] lxd/instance/instance/utils: backup.InstanceBackup
 usage

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/instance/instance_utils.go | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lxd/instance/instance_utils.go b/lxd/instance/instance_utils.go
index 7af24f7ee8..589ab7b567 100644
--- a/lxd/instance/instance_utils.go
+++ b/lxd/instance/instance_utils.go
@@ -661,7 +661,7 @@ func DeviceNextInterfaceHWAddr() (string, error) {
 }
 
 // BackupLoadByName load an instance backup from the database.
-func BackupLoadByName(s *state.State, project, name string) (*backup.Backup, 
error) {
+func BackupLoadByName(s *state.State, project, name string) 
(*backup.InstanceBackup, error) {
        // Get the backup database record
        args, err := s.Cluster.GetInstanceBackup(project, name)
        if err != nil {
@@ -674,7 +674,7 @@ func BackupLoadByName(s *state.State, project, name string) 
(*backup.Backup, err
                return nil, errors.Wrap(err, "Load instance from database")
        }
 
-       return backup.New(s, instance, args.ID, name, args.CreationDate, 
args.ExpiryDate, args.InstanceOnly, args.OptimizedStorage), nil
+       return backup.NewInstanceBackup(s, instance, args.ID, name, 
args.CreationDate, args.ExpiryDate, args.InstanceOnly, args.OptimizedStorage), 
nil
 }
 
 // ResolveImage takes an instance source and returns a hash suitable for 
instance creation or download.

From 84e4129c9e9e043db4906afcb837fa58f1751c9e Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 24 Sep 2020 15:48:37 +0100
Subject: [PATCH 13/14] lxd/instance/instance/interface: backup.InstanceBackup
 usage

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/instance/instance_interface.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lxd/instance/instance_interface.go 
b/lxd/instance/instance_interface.go
index bfe9054a70..e49458bd86 100644
--- a/lxd/instance/instance_interface.go
+++ b/lxd/instance/instance_interface.go
@@ -59,7 +59,7 @@ type Instance interface {
        // Snapshots & migration & backups.
        Restore(source Instance, stateful bool) error
        Snapshots() ([]Instance, error)
-       Backups() ([]backup.Backup, error)
+       Backups() ([]backup.InstanceBackup, error)
        UpdateBackupFile() error
 
        // Config handling.

From 76ba76ba0a976994b6e58a2dbc7b66028cb2e964 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 24 Sep 2020 15:49:04 +0100
Subject: [PATCH 14/14] lxd/instance/drivers: backup.InstanceBackup usage

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/instance/drivers/driver_lxc.go  | 4 ++--
 lxd/instance/drivers/driver_qemu.go | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/lxd/instance/drivers/driver_lxc.go 
b/lxd/instance/drivers/driver_lxc.go
index 70fe25c4e7..49c68c9469 100644
--- a/lxd/instance/drivers/driver_lxc.go
+++ b/lxd/instance/drivers/driver_lxc.go
@@ -3274,7 +3274,7 @@ func (c *lxc) Snapshots() ([]instance.Instance, error) {
 }
 
 // Backups returns the backups of the instance.
-func (c *lxc) Backups() ([]backup.Backup, error) {
+func (c *lxc) Backups() ([]backup.InstanceBackup, error) {
        // Get all the backups
        backupNames, err := c.state.Cluster.GetInstanceBackups(c.project, 
c.name)
        if err != nil {
@@ -3282,7 +3282,7 @@ func (c *lxc) Backups() ([]backup.Backup, error) {
        }
 
        // Build the backup list
-       backups := []backup.Backup{}
+       backups := []backup.InstanceBackup{}
        for _, backupName := range backupNames {
                backup, err := instance.BackupLoadByName(c.state, c.project, 
backupName)
                if err != nil {
diff --git a/lxd/instance/drivers/driver_qemu.go 
b/lxd/instance/drivers/driver_qemu.go
index c0ad24b982..8572365fb2 100644
--- a/lxd/instance/drivers/driver_qemu.go
+++ b/lxd/instance/drivers/driver_qemu.go
@@ -2505,8 +2505,8 @@ func (vm *qemu) Snapshots() ([]instance.Instance, error) {
 }
 
 // Backups returns a list of backups.
-func (vm *qemu) Backups() ([]backup.Backup, error) {
-       return []backup.Backup{}, nil
+func (vm *qemu) Backups() ([]backup.InstanceBackup, error) {
+       return []backup.InstanceBackup{}, nil
 }
 
 // Rename the instance.
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to