mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-09-19 06:26:44 +00:00
Save backup log file to object storage
Signed-off-by: Andy Goldstein <andy.goldstein@gmail.com>
This commit is contained in:
@@ -38,7 +38,7 @@ type BackupService interface {
|
||||
// UploadBackup uploads the specified Ark backup of a set of Kubernetes API objects, whose manifests are
|
||||
// stored in the specified file, into object storage in an Ark bucket, tagged with Ark metadata. Returns
|
||||
// an error if a problem is encountered accessing the file or performing the upload via the cloud API.
|
||||
UploadBackup(bucket, name string, metadata, backup io.ReadSeeker) error
|
||||
UploadBackup(bucket, name string, metadata, backup, log io.ReadSeeker) error
|
||||
|
||||
// DownloadBackup downloads an Ark backup with the specified object key from object storage via the cloud API.
|
||||
// It returns the snapshot metadata and data (separately), or an error if a problem is encountered
|
||||
@@ -62,8 +62,9 @@ type BackupGetter interface {
|
||||
}
|
||||
|
||||
const (
|
||||
metadataFileFormatString string = "%s/ark-backup.json"
|
||||
backupFileFormatString string = "%s/%s.tar.gz"
|
||||
metadataFileFormatString = "%s/ark-backup.json"
|
||||
backupFileFormatString = "%s/%s.tar.gz"
|
||||
logFileFormatString = "%s/%s.log.gz"
|
||||
)
|
||||
|
||||
type backupService struct {
|
||||
@@ -82,21 +83,30 @@ func NewBackupService(objectStorage ObjectStorageAdapter) BackupService {
|
||||
}
|
||||
}
|
||||
|
||||
func (br *backupService) UploadBackup(bucket, backupName string, metadata, backup io.ReadSeeker) error {
|
||||
func (br *backupService) UploadBackup(bucket, backupName string, metadata, backup, log io.ReadSeeker) error {
|
||||
// upload metadata file
|
||||
metadataKey := fmt.Sprintf(metadataFileFormatString, backupName)
|
||||
if err := br.objectStorage.PutObject(bucket, metadataKey, metadata); err != nil {
|
||||
// failure to upload metadata file is a hard-stop
|
||||
return err
|
||||
}
|
||||
|
||||
// upload tar file
|
||||
if err := br.objectStorage.PutObject(bucket, fmt.Sprintf(backupFileFormatString, backupName, backupName), backup); err != nil {
|
||||
backupKey := fmt.Sprintf(backupFileFormatString, backupName, backupName)
|
||||
if err := br.objectStorage.PutObject(bucket, backupKey, backup); err != nil {
|
||||
// try to delete the metadata file since the data upload failed
|
||||
deleteErr := br.objectStorage.DeleteObject(bucket, metadataKey)
|
||||
|
||||
return errors.NewAggregate([]error{err, deleteErr})
|
||||
}
|
||||
|
||||
// uploading log file is best-effort; if it fails, we log the error but call the overall upload a
|
||||
// success
|
||||
logKey := fmt.Sprintf(logFileFormatString, backupName, backupName)
|
||||
if err := br.objectStorage.PutObject(bucket, logKey, log); err != nil {
|
||||
glog.Errorf("error uploading %s/%s: %v", bucket, logKey, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ func TestUploadBackup(t *testing.T) {
|
||||
backupName string
|
||||
metadata io.ReadSeeker
|
||||
backup io.ReadSeeker
|
||||
log io.ReadSeeker
|
||||
objectStoreErrs map[string]map[string]interface{}
|
||||
expectedErr bool
|
||||
expectedRes map[string][]byte
|
||||
@@ -54,10 +55,12 @@ func TestUploadBackup(t *testing.T) {
|
||||
backupName: "test-backup",
|
||||
metadata: newStringReadSeeker("foo"),
|
||||
backup: newStringReadSeeker("bar"),
|
||||
log: newStringReadSeeker("baz"),
|
||||
expectedErr: false,
|
||||
expectedRes: map[string][]byte{
|
||||
"test-backup/ark-backup.json": []byte("foo"),
|
||||
"test-backup/test-backup.tar.gz": []byte("bar"),
|
||||
"test-backup/test-backup.log.gz": []byte("baz"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -68,12 +71,13 @@ func TestUploadBackup(t *testing.T) {
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
name: "error on metadata upload does not upload data",
|
||||
name: "error on metadata upload does not upload data or log",
|
||||
bucket: "test-bucket",
|
||||
bucketExists: true,
|
||||
backupName: "test-backup",
|
||||
metadata: newStringReadSeeker("foo"),
|
||||
backup: newStringReadSeeker("bar"),
|
||||
log: newStringReadSeeker("baz"),
|
||||
objectStoreErrs: map[string]map[string]interface{}{
|
||||
"putobject": map[string]interface{}{
|
||||
"test-bucket||test-backup/ark-backup.json": true,
|
||||
@@ -89,6 +93,7 @@ func TestUploadBackup(t *testing.T) {
|
||||
backupName: "test-backup",
|
||||
metadata: newStringReadSeeker("foo"),
|
||||
backup: newStringReadSeeker("bar"),
|
||||
log: newStringReadSeeker("baz"),
|
||||
objectStoreErrs: map[string]map[string]interface{}{
|
||||
"putobject": map[string]interface{}{
|
||||
"test-bucket||test-backup/test-backup.tar.gz": true,
|
||||
@@ -97,6 +102,25 @@ func TestUploadBackup(t *testing.T) {
|
||||
expectedErr: true,
|
||||
expectedRes: make(map[string][]byte),
|
||||
},
|
||||
{
|
||||
name: "error on log upload is ok",
|
||||
bucket: "test-bucket",
|
||||
bucketExists: true,
|
||||
backupName: "test-backup",
|
||||
metadata: newStringReadSeeker("foo"),
|
||||
backup: newStringReadSeeker("bar"),
|
||||
log: newStringReadSeeker("baz"),
|
||||
objectStoreErrs: map[string]map[string]interface{}{
|
||||
"putobject": map[string]interface{}{
|
||||
"test-bucket||test-backup/test-backup.log.gz": true,
|
||||
},
|
||||
},
|
||||
expectedErr: false,
|
||||
expectedRes: map[string][]byte{
|
||||
"test-backup/ark-backup.json": []byte("foo"),
|
||||
"test-backup/test-backup.tar.gz": []byte("bar"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
@@ -111,7 +135,7 @@ func TestUploadBackup(t *testing.T) {
|
||||
|
||||
backupService := NewBackupService(objStore)
|
||||
|
||||
err := backupService.UploadBackup(test.bucket, test.backupName, test.metadata, test.backup)
|
||||
err := backupService.UploadBackup(test.bucket, test.backupName, test.metadata, test.backup, test.log)
|
||||
|
||||
assert.Equal(t, test.expectedErr, err != nil, "got error %v", err)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user