Skip to content

Commit

Permalink
Serialize PodVolumeBackups and send to storage
Browse files Browse the repository at this point in the history
Signed-off-by: Carlisia <carlisiac@vmware.com>
  • Loading branch information
Carlisia committed Jun 18, 2019
1 parent c1c7bc0 commit 28cfac7
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 50 deletions.
1 change: 1 addition & 0 deletions pkg/backup/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
// materialized (e.g. backup/snapshot locations, includes/excludes, etc.)
type Request struct {
*velerov1api.Backup
PodVolumeBackup *velerov1api.PodVolumeBackup

StorageLocation *velerov1api.BackupStorageLocation
SnapshotLocations []*velerov1api.VolumeSnapshotLocation
Expand Down
1 change: 1 addition & 0 deletions pkg/cmd/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string

backupController := controller.NewBackupController(
s.sharedInformerFactory.Velero().V1().Backups(),
s.sharedInformerFactory.Velero().V1().PodVolumeBackups(),
s.veleroClient.VeleroV1(),
backupper,
s.logger,
Expand Down
45 changes: 44 additions & 1 deletion pkg/controller/backup_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"io"
"io/ioutil"
"os"
"strings"
"time"

jsonpatch "github.com/evanphx/json-patch"
Expand Down Expand Up @@ -58,6 +59,7 @@ type backupController struct {

backupper pkgbackup.Backupper
lister listers.BackupLister
listerPodVolumes listers.PodVolumeBackupLister
client velerov1client.BackupsGetter
clock clock.Clock
backupLogLevel logrus.Level
Expand All @@ -74,6 +76,7 @@ type backupController struct {

func NewBackupController(
backupInformer informers.BackupInformer,
podVolumeBackupInformer informers.PodVolumeBackupInformer,
client velerov1client.BackupsGetter,
backupper pkgbackup.Backupper,
logger logrus.FieldLogger,
Expand All @@ -91,6 +94,7 @@ func NewBackupController(
genericController: newGenericController("backup", logger),
backupper: backupper,
lister: backupInformer.Lister(),
listerPodVolumes: podVolumeBackupInformer.Lister(),
client: client,
clock: &clock.RealClock{},
backupLogLevel: backupLogLevel,
Expand Down Expand Up @@ -527,6 +531,34 @@ func (c *backupController) runBackup(backup *pkgbackup.Request) error {
backup.Status.Phase = velerov1api.BackupPhaseCompleted
}

// Find and add the PodVolumeBackup after the backup is processed
podVolumeBackups, err := c.listerPodVolumes.PodVolumeBackups(backup.Namespace).List(labels.Everything())
if err != nil {
return errors.Wrap(err, "error getting pod volume backups")
}

var podVolumeBackupName string
for _, podVolumeBackup := range podVolumeBackups {
res := strings.Split(podVolumeBackup.Name, "-")
if backup.Name == res[0] {
podVolumeBackupName = podVolumeBackup.Name
break
}
}

if podVolumeBackupName != "" {
c.logger.Info("getting pod volume backup ", podVolumeBackupName)
podVolumeBackup, err := c.listerPodVolumes.PodVolumeBackups(backup.Namespace).Get(podVolumeBackupName)
if apierrors.IsNotFound(err) {
c.logger.Warnf("pod volume backup %s expected but not found", podVolumeBackupName)
return nil
}
if err != nil {
return errors.Wrap(err, "error getting pod volume backup")
}
backup.PodVolumeBackup = podVolumeBackup
}

if errs := persistBackup(backup, backupFile, logFile, backupStore, c.logger); len(errs) > 0 {
fatalErrs = append(fatalErrs, errs...)
}
Expand Down Expand Up @@ -576,14 +608,25 @@ func persistBackup(backup *pkgbackup.Request, backupContents, backupLog *os.File
errs = append(errs, errors.Wrap(err, "error closing gzip writer"))
}

podVolumeBackup := new(bytes.Buffer)
gzw = gzip.NewWriter(podVolumeBackup)
defer gzw.Close()

if err := json.NewEncoder(gzw).Encode(backup.PodVolumeBackup); err != nil {
errs = append(errs, errors.Wrap(err, "error encoding pod volume backup"))
}
if err := gzw.Close(); err != nil {
errs = append(errs, errors.Wrap(err, "error closing gzip writer"))
}

if len(errs) > 0 {
// Don't upload the JSON files or backup tarball if encoding to json fails.
backupJSON = nil
backupContents = nil
volumeSnapshots = nil
}

if err := backupStore.PutBackup(backup.Name, backupJSON, backupContents, backupLog, volumeSnapshots); err != nil {
if err := backupStore.PutBackup(backup.Name, backupJSON, backupContents, backupLog, podVolumeBackup, volumeSnapshots); err != nil {
errs = append(errs, err)
}

Expand Down
3 changes: 2 additions & 1 deletion pkg/controller/backup_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,7 @@ func TestProcessBackupCompletions(t *testing.T) {
genericController: newGenericController("backup-test", logger),
client: clientset.VeleroV1(),
lister: sharedInformers.Velero().V1().Backups().Lister(),
listerPodVolumes: sharedInformers.Velero().V1().PodVolumeBackups().Lister(),
backupLocationLister: sharedInformers.Velero().V1().BackupStorageLocations().Lister(),
snapshotLocationLister: sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(),
defaultBackupLocation: defaultBackupLocation.Name,
Expand All @@ -535,7 +536,7 @@ func TestProcessBackupCompletions(t *testing.T) {
return strings.Contains(buf.String(), `"completionTimestamp": "2006-01-02T22:04:05Z"`)
}
backupStore.On("BackupExists", test.backupLocation.Spec.StorageType.ObjectStorage.Bucket, test.backup.Name).Return(test.backupExists, test.existenceCheckError)
backupStore.On("PutBackup", test.backup.Name, mock.MatchedBy(completionTimestampIsPresent), mock.Anything, mock.Anything, mock.Anything).Return(nil)
backupStore.On("PutBackup", test.backup.Name, mock.MatchedBy(completionTimestampIsPresent), mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)

// add the test's backup to the informer/lister store
require.NotNil(t, test.backup)
Expand Down
8 changes: 4 additions & 4 deletions pkg/persistence/mocks/backup_store.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 14 additions & 2 deletions pkg/persistence/object_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ type BackupStore interface {

ListBackups() ([]string, error)

PutBackup(name string, metadata, contents, log, volumeSnapshots io.Reader) error
PutBackup(name string, metadata, contents, log, podVolumeBackup, volumeSnapshots io.Reader) error
GetBackupMetadata(name string) (*velerov1api.Backup, error)
GetBackupVolumeSnapshots(name string) ([]*volume.Snapshot, error)
GetBackupContents(name string) (io.ReadCloser, error)
Expand Down Expand Up @@ -166,7 +166,7 @@ func (s *objectBackupStore) ListBackups() ([]string, error) {
return output, nil
}

func (s *objectBackupStore) PutBackup(name string, metadata, contents, log, volumeSnapshots io.Reader) error {
func (s *objectBackupStore) PutBackup(name string, metadata, contents, log, podVolumeBackup, volumeSnapshots io.Reader) error {
if err := seekAndPutObject(s.objectStore, s.bucket, s.layout.getBackupLogKey(name), log); err != nil {
// Uploading the log file is best-effort; if it fails, we log the error but it doesn't impact the
// backup's status.
Expand All @@ -190,6 +190,18 @@ func (s *objectBackupStore) PutBackup(name string, metadata, contents, log, volu
return kerrors.NewAggregate([]error{err, deleteErr})
}

if err := seekAndPutObject(s.objectStore, s.bucket, s.layout.getBackupPodVolumesKey(name), podVolumeBackup); err != nil {
errs := []error{err}

deleteErr := s.objectStore.DeleteObject(s.bucket, s.layout.getBackupContentsKey(name))
errs = append(errs, deleteErr)

deleteErr = s.objectStore.DeleteObject(s.bucket, s.layout.getBackupMetadataKey(name))
errs = append(errs, deleteErr)

return kerrors.NewAggregate(errs)
}

if err := seekAndPutObject(s.objectStore, s.bucket, s.layout.getBackupVolumeSnapshotsKey(name), volumeSnapshots); err != nil {
errs := []error{err}

Expand Down
4 changes: 4 additions & 0 deletions pkg/persistence/object_store_layout.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ func (l *ObjectStoreLayout) getBackupLogKey(backup string) string {
return path.Join(l.subdirs["backups"], backup, fmt.Sprintf("%s-logs.gz", backup))
}

func (l *ObjectStoreLayout) getBackupPodVolumesKey(backup string) string {
return path.Join(l.subdirs["backups"], backup, fmt.Sprintf("%s-podvolumebackups.json.gz", backup))
}

func (l *ObjectStoreLayout) getBackupVolumeSnapshotsKey(backup string) string {
return path.Join(l.subdirs["backups"], backup, fmt.Sprintf("%s-volumesnapshots.json.gz", backup))
}
Expand Down
93 changes: 51 additions & 42 deletions pkg/persistence/object_store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,54 +208,60 @@ func TestListBackups(t *testing.T) {

func TestPutBackup(t *testing.T) {
tests := []struct {
name string
prefix string
metadata io.Reader
contents io.Reader
log io.Reader
snapshots io.Reader
expectedErr string
expectedKeys []string
name string
prefix string
metadata io.Reader
contents io.Reader
log io.Reader
podVolumeBackup io.Reader
snapshots io.Reader
expectedErr string
expectedKeys []string
}{
{
name: "normal case",
metadata: newStringReadSeeker("metadata"),
contents: newStringReadSeeker("contents"),
log: newStringReadSeeker("log"),
snapshots: newStringReadSeeker("snapshots"),
expectedErr: "",
name: "normal case",
metadata: newStringReadSeeker("metadata"),
contents: newStringReadSeeker("contents"),
log: newStringReadSeeker("log"),
podVolumeBackup: newStringReadSeeker("podVolumeBackup"),
snapshots: newStringReadSeeker("snapshots"),
expectedErr: "",
expectedKeys: []string{
"backups/backup-1/velero-backup.json",
"backups/backup-1/backup-1.tar.gz",
"backups/backup-1/backup-1-logs.gz",
"backups/backup-1/backup-1-podvolumebackups.json.gz",
"backups/backup-1/backup-1-volumesnapshots.json.gz",
"metadata/revision",
},
},
{
name: "normal case with backup store prefix",
prefix: "prefix-1/",
metadata: newStringReadSeeker("metadata"),
contents: newStringReadSeeker("contents"),
log: newStringReadSeeker("log"),
snapshots: newStringReadSeeker("snapshots"),
expectedErr: "",
name: "normal case with backup store prefix",
prefix: "prefix-1/",
metadata: newStringReadSeeker("metadata"),
contents: newStringReadSeeker("contents"),
log: newStringReadSeeker("log"),
podVolumeBackup: newStringReadSeeker("podVolumeBackup"),
snapshots: newStringReadSeeker("snapshots"),
expectedErr: "",
expectedKeys: []string{
"prefix-1/backups/backup-1/velero-backup.json",
"prefix-1/backups/backup-1/backup-1.tar.gz",
"prefix-1/backups/backup-1/backup-1-logs.gz",
"prefix-1/backups/backup-1/backup-1-podvolumebackups.json.gz",
"prefix-1/backups/backup-1/backup-1-volumesnapshots.json.gz",
"prefix-1/metadata/revision",
},
},
{
name: "error on metadata upload does not upload data",
metadata: new(errorReader),
contents: newStringReadSeeker("contents"),
log: newStringReadSeeker("log"),
snapshots: newStringReadSeeker("snapshots"),
expectedErr: "error readers return errors",
expectedKeys: []string{"backups/backup-1/backup-1-logs.gz"},
name: "error on metadata upload does not upload data",
metadata: new(errorReader),
contents: newStringReadSeeker("contents"),
log: newStringReadSeeker("log"),
podVolumeBackup: newStringReadSeeker("podVolumeBackup"),
snapshots: newStringReadSeeker("snapshots"),
expectedErr: "error readers return errors",
expectedKeys: []string{"backups/backup-1/backup-1-logs.gz"},
},
{
name: "error on data upload deletes metadata",
Expand All @@ -267,35 +273,38 @@ func TestPutBackup(t *testing.T) {
expectedKeys: []string{"backups/backup-1/backup-1-logs.gz"},
},
{
name: "error on log upload is ok",
metadata: newStringReadSeeker("foo"),
contents: newStringReadSeeker("bar"),
log: new(errorReader),
snapshots: newStringReadSeeker("snapshots"),
expectedErr: "",
name: "error on log upload is ok",
metadata: newStringReadSeeker("foo"),
contents: newStringReadSeeker("bar"),
log: new(errorReader),
podVolumeBackup: newStringReadSeeker("podVolumeBackup"),
snapshots: newStringReadSeeker("snapshots"),
expectedErr: "",
expectedKeys: []string{
"backups/backup-1/velero-backup.json",
"backups/backup-1/backup-1.tar.gz",
"backups/backup-1/backup-1-podvolumebackups.json.gz",
"backups/backup-1/backup-1-volumesnapshots.json.gz",
"metadata/revision",
},
},
{
name: "don't upload data when metadata is nil",
metadata: nil,
contents: newStringReadSeeker("contents"),
log: newStringReadSeeker("log"),
snapshots: newStringReadSeeker("snapshots"),
expectedErr: "",
expectedKeys: []string{"backups/backup-1/backup-1-logs.gz"},
name: "don't upload data when metadata is nil",
metadata: nil,
contents: newStringReadSeeker("contents"),
log: newStringReadSeeker("log"),
podVolumeBackup: newStringReadSeeker("podVolumeBackup"),
snapshots: newStringReadSeeker("snapshots"),
expectedErr: "",
expectedKeys: []string{"backups/backup-1/backup-1-logs.gz"},
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
harness := newObjectBackupStoreTestHarness("foo", tc.prefix)

err := harness.PutBackup("backup-1", tc.metadata, tc.contents, tc.log, tc.snapshots)
err := harness.PutBackup("backup-1", tc.metadata, tc.contents, tc.log, tc.podVolumeBackup, tc.snapshots)

velerotest.AssertErrorMatches(t, tc.expectedErr, err)
assert.Len(t, harness.objectStore.Data[harness.bucket], len(tc.expectedKeys))
Expand Down

0 comments on commit 28cfac7

Please sign in to comment.