2018-08-20 23:29:54 +00:00
/ *
2021-03-04 21:43:15 +00:00
Copyright the Velero contributors .
2018-08-20 23:29:54 +00:00
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
package persistence
import (
2018-10-17 21:10:42 +00:00
"compress/gzip"
2018-10-12 17:55:02 +00:00
"encoding/json"
2018-08-20 23:29:54 +00:00
"io"
"strings"
"time"
2022-03-31 14:47:22 +00:00
snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
2021-03-04 21:43:15 +00:00
2018-08-20 23:29:54 +00:00
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
2023-10-31 03:48:54 +00:00
"k8s.io/apimachinery/pkg/runtime/serializer"
2018-08-20 23:29:54 +00:00
kerrors "k8s.io/apimachinery/pkg/util/errors"
2021-03-04 21:43:15 +00:00
"github.com/vmware-tanzu/velero/internal/credentials"
2019-09-30 21:26:56 +00:00
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
2023-01-09 22:56:01 +00:00
"github.com/vmware-tanzu/velero/pkg/itemoperation"
2019-09-30 21:26:56 +00:00
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
2023-10-31 03:48:54 +00:00
"github.com/vmware-tanzu/velero/pkg/util"
2019-09-30 21:26:56 +00:00
"github.com/vmware-tanzu/velero/pkg/volume"
2018-08-20 23:29:54 +00:00
)
2019-07-24 19:51:20 +00:00
type BackupInfo struct {
2019-08-05 17:15:55 +00:00
Name string
Metadata ,
Contents ,
Log ,
2023-02-16 04:27:53 +00:00
BackupResults ,
2019-08-05 17:15:55 +00:00
PodVolumeBackups ,
VolumeSnapshots ,
2023-01-09 22:56:01 +00:00
BackupItemOperations ,
2020-03-12 21:01:14 +00:00
BackupResourceList ,
CSIVolumeSnapshots ,
2022-03-31 10:09:35 +00:00
CSIVolumeSnapshotContents ,
2023-11-07 12:05:34 +00:00
CSIVolumeSnapshotClasses ,
BackupVolumeInfo io . Reader
2019-07-24 19:51:20 +00:00
}
2018-08-20 23:29:54 +00:00
// BackupStore defines operations for creating, retrieving, and deleting
2019-01-25 03:33:07 +00:00
// Velero backup and restore data in/from a persistent backup store.
2018-08-20 23:29:54 +00:00
type BackupStore interface {
2018-09-18 15:56:45 +00:00
IsValid ( ) error
2018-08-27 15:44:48 +00:00
ListBackups ( ) ( [ ] string , error )
2018-08-20 23:29:54 +00:00
2020-03-12 21:01:14 +00:00
PutBackup ( info BackupInfo ) error
2023-01-24 17:59:53 +00:00
PutBackupMetadata ( backup string , backupMetadata io . Reader ) error
2023-01-09 22:56:01 +00:00
PutBackupItemOperations ( backup string , backupItemOperations io . Reader ) error
2023-01-24 17:59:53 +00:00
PutBackupContents ( backup string , backupContents io . Reader ) error
2019-01-25 03:33:07 +00:00
GetBackupMetadata ( name string ) ( * velerov1api . Backup , error )
2023-01-09 22:56:01 +00:00
GetBackupItemOperations ( name string ) ( [ ] * itemoperation . BackupOperation , error )
2018-10-12 17:55:02 +00:00
GetBackupVolumeSnapshots ( name string ) ( [ ] * volume . Snapshot , error )
2019-07-24 19:51:20 +00:00
GetPodVolumeBackups ( name string ) ( [ ] * velerov1api . PodVolumeBackup , error )
2018-08-20 23:29:54 +00:00
GetBackupContents ( name string ) ( io . ReadCloser , error )
2022-03-31 14:47:22 +00:00
GetCSIVolumeSnapshots ( name string ) ( [ ] * snapshotv1api . VolumeSnapshot , error )
GetCSIVolumeSnapshotContents ( name string ) ( [ ] * snapshotv1api . VolumeSnapshotContent , error )
2022-03-31 10:09:35 +00:00
GetCSIVolumeSnapshotClasses ( name string ) ( [ ] * snapshotv1api . VolumeSnapshotClass , error )
2023-11-22 10:35:31 +00:00
GetBackupVolumeInfos ( name string ) ( * volume . VolumeInfos , error )
2019-04-23 23:19:00 +00:00
// BackupExists checks if the backup metadata file exists in object storage.
BackupExists ( bucket , backupName string ) ( bool , error )
2018-08-20 23:29:54 +00:00
DeleteBackup ( name string ) error
PutRestoreLog ( backup , restore string , log io . Reader ) error
PutRestoreResults ( backup , restore string , results io . Reader ) error
2023-02-02 08:51:23 +00:00
PutRestoredResourceList ( restore string , results io . Reader ) error
2023-03-10 17:40:20 +00:00
PutRestoreItemOperations ( restore string , restoreItemOperations io . Reader ) error
2023-01-09 22:56:01 +00:00
GetRestoreItemOperations ( name string ) ( [ ] * itemoperation . RestoreOperation , error )
2018-09-18 15:56:45 +00:00
DeleteRestore ( name string ) error
2018-08-20 23:29:54 +00:00
2019-01-25 03:33:07 +00:00
GetDownloadURL ( target velerov1api . DownloadTarget ) ( string , error )
2018-08-20 23:29:54 +00:00
}
2018-09-18 15:56:45 +00:00
// DownloadURLTTL is how long a download URL is valid for.
const DownloadURLTTL = 10 * time . Minute
2018-08-20 23:29:54 +00:00
type objectBackupStore struct {
2019-03-14 20:35:06 +00:00
objectStore velero . ObjectStore
2018-08-20 23:29:54 +00:00
bucket string
2018-09-25 19:10:03 +00:00
layout * ObjectStoreLayout
2018-08-20 23:29:54 +00:00
logger logrus . FieldLogger
}
2019-03-14 20:35:06 +00:00
// ObjectStoreGetter is a type that can get a velero.ObjectStore
2018-08-20 23:29:54 +00:00
// from a provider name.
type ObjectStoreGetter interface {
2019-03-14 20:35:06 +00:00
GetObjectStore ( provider string ) ( velero . ObjectStore , error )
2018-08-20 23:29:54 +00:00
}
2021-02-08 18:04:08 +00:00
// ObjectBackupStoreGetter is a type that can get a velero.BackupStore for a
// given BackupStorageLocation and ObjectStore.
type ObjectBackupStoreGetter interface {
Get ( location * velerov1api . BackupStorageLocation , objectStoreGetter ObjectStoreGetter , logger logrus . FieldLogger ) ( BackupStore , error )
}
2021-03-04 21:43:15 +00:00
type objectBackupStoreGetter struct {
credentialStore credentials . FileStore
}
2021-02-08 18:04:08 +00:00
2021-03-04 21:43:15 +00:00
// NewObjectBackupStoreGetter returns a ObjectBackupStoreGetter that can get a velero.BackupStore.
func NewObjectBackupStoreGetter ( credentialStore credentials . FileStore ) ObjectBackupStoreGetter {
return & objectBackupStoreGetter { credentialStore : credentialStore }
2021-02-08 18:04:08 +00:00
}
func ( b * objectBackupStoreGetter ) Get ( location * velerov1api . BackupStorageLocation , objectStoreGetter ObjectStoreGetter , logger logrus . FieldLogger ) ( BackupStore , error ) {
2018-08-20 23:29:54 +00:00
if location . Spec . ObjectStorage == nil {
return nil , errors . New ( "backup storage location does not use object storage" )
}
if location . Spec . Provider == "" {
return nil , errors . New ( "object storage provider name must not be empty" )
}
2019-07-31 15:27:12 +00:00
// trim off any leading/trailing slashes
bucket := strings . Trim ( location . Spec . ObjectStorage . Bucket , "/" )
prefix := strings . Trim ( location . Spec . ObjectStorage . Prefix , "/" )
// if there are any slashes in the middle of 'bucket', the user
// probably put <bucket>/<prefix> in the bucket field, which we
// don't support.
if strings . Contains ( bucket , "/" ) {
return nil , errors . Errorf ( "backup storage location's bucket name %q must not contain a '/' (if using a prefix, put it in the 'Prefix' field instead)" , location . Spec . ObjectStorage . Bucket )
2018-08-20 23:29:54 +00:00
}
2022-07-14 20:25:25 +00:00
// Pass a new map into the object store rather than modifying the passed-in
// location. This prevents Velero controllers from accidentally modifying
// the in-cluster BSL with data which doesn't belong in Spec.Config
objectStoreConfig := make ( map [ string ] string )
if location . Spec . Config != nil {
for key , val := range location . Spec . Config {
objectStoreConfig [ key ] = val
}
}
2019-08-19 20:05:38 +00:00
// add the bucket name and prefix to the config map so that object stores
// can use them when initializing. The AWS object store uses the bucket
// name to determine the bucket's region when setting up its client.
2022-07-14 20:25:25 +00:00
objectStoreConfig [ "bucket" ] = bucket
objectStoreConfig [ "prefix" ] = prefix
2021-03-04 21:43:15 +00:00
// Only include a CACert if it's specified in order to maintain compatibility with plugins that don't expect it.
if location . Spec . ObjectStorage . CACert != nil {
2022-07-14 20:25:25 +00:00
objectStoreConfig [ "caCert" ] = string ( location . Spec . ObjectStorage . CACert )
2021-03-04 21:43:15 +00:00
}
// If the BSL specifies a credential, fetch its path on disk and pass to
// plugin via the config.
if location . Spec . Credential != nil {
credsFile , err := b . credentialStore . Path ( location . Spec . Credential )
if err != nil {
return nil , errors . Wrap ( err , "unable to get credentials" )
2020-03-31 17:59:37 +00:00
}
2021-03-04 21:43:15 +00:00
2022-07-14 20:25:25 +00:00
objectStoreConfig [ "credentialsFile" ] = credsFile
2019-07-31 15:27:12 +00:00
}
objectStore , err := objectStoreGetter . GetObjectStore ( location . Spec . Provider )
if err != nil {
return nil , err
2018-08-20 23:29:54 +00:00
}
2022-07-14 20:25:25 +00:00
if err := objectStore . Init ( objectStoreConfig ) ; err != nil {
2018-08-20 23:29:54 +00:00
return nil , err
}
log := logger . WithFields ( logrus . Fields ( map [ string ] interface { } {
2019-07-31 15:27:12 +00:00
"bucket" : bucket ,
"prefix" : prefix ,
2018-08-20 23:29:54 +00:00
} ) )
return & objectBackupStore {
objectStore : objectStore ,
2019-07-31 15:27:12 +00:00
bucket : bucket ,
layout : NewObjectStoreLayout ( prefix ) ,
2018-08-20 23:29:54 +00:00
logger : log ,
} , nil
}
2018-09-18 15:56:45 +00:00
func ( s * objectBackupStore ) IsValid ( ) error {
dirs , err := s . objectStore . ListCommonPrefixes ( s . bucket , s . layout . rootPrefix , "/" )
if err != nil {
return errors . WithStack ( err )
}
var invalid [ ] string
for _ , dir := range dirs {
subdir := strings . TrimSuffix ( strings . TrimPrefix ( dir , s . layout . rootPrefix ) , "/" )
2018-09-25 19:10:03 +00:00
if ! s . layout . isValidSubdir ( subdir ) {
2018-09-18 15:56:45 +00:00
invalid = append ( invalid , subdir )
}
}
if len ( invalid ) > 0 {
// don't include more than 3 invalid dirs in the error message
if len ( invalid ) > 3 {
return errors . Errorf ( "Backup store contains %d invalid top-level directories: %v" , len ( invalid ) , append ( invalid [ : 3 ] , "..." ) )
}
return errors . Errorf ( "Backup store contains invalid top-level directories: %v" , invalid )
}
return nil
}
2018-08-27 15:44:48 +00:00
func ( s * objectBackupStore ) ListBackups ( ) ( [ ] string , error ) {
2018-09-25 19:10:03 +00:00
prefixes , err := s . objectStore . ListCommonPrefixes ( s . bucket , s . layout . subdirs [ "backups" ] , "/" )
2018-08-20 23:29:54 +00:00
if err != nil {
return nil , err
}
if len ( prefixes ) == 0 {
2018-08-27 15:44:48 +00:00
return [ ] string { } , nil
2018-08-20 23:29:54 +00:00
}
2018-08-27 15:44:48 +00:00
output := make ( [ ] string , 0 , len ( prefixes ) )
2018-08-20 23:29:54 +00:00
for _ , prefix := range prefixes {
2019-03-14 20:35:06 +00:00
// values returned from a call to ObjectStore's
2018-09-18 15:56:45 +00:00
// ListCommonPrefixes method return the *full* prefix, inclusive
// of s.backupsPrefix, and include the delimiter ("/") as a suffix. Trim
2018-08-20 23:29:54 +00:00
// each of those off to get the backup name.
2018-09-25 19:10:03 +00:00
backupName := strings . TrimSuffix ( strings . TrimPrefix ( prefix , s . layout . subdirs [ "backups" ] ) , "/" )
2018-08-20 23:29:54 +00:00
2018-08-27 15:44:48 +00:00
output = append ( output , backupName )
2018-08-20 23:29:54 +00:00
}
return output , nil
}
2019-07-24 19:51:20 +00:00
func ( s * objectBackupStore ) PutBackup ( info BackupInfo ) error {
if err := seekAndPutObject ( s . objectStore , s . bucket , s . layout . getBackupLogKey ( info . Name ) , info . Log ) ; err != nil {
2018-08-20 23:29:54 +00:00
// Uploading the log file is best-effort; if it fails, we log the error but it doesn't impact the
// backup's status.
2019-07-24 19:51:20 +00:00
s . logger . WithError ( err ) . WithField ( "backup" , info . Name ) . Error ( "Error uploading log file" )
2018-08-20 23:29:54 +00:00
}
2019-07-24 19:51:20 +00:00
if err := seekAndPutObject ( s . objectStore , s . bucket , s . layout . getBackupMetadataKey ( info . Name ) , info . Metadata ) ; err != nil {
2018-08-20 23:29:54 +00:00
// failure to upload metadata file is a hard-stop
return err
}
2019-07-24 19:51:20 +00:00
if err := seekAndPutObject ( s . objectStore , s . bucket , s . layout . getBackupContentsKey ( info . Name ) , info . Contents ) ; err != nil {
deleteErr := s . objectStore . DeleteObject ( s . bucket , s . layout . getBackupMetadataKey ( info . Name ) )
2018-08-20 23:29:54 +00:00
return kerrors . NewAggregate ( [ ] error { err , deleteErr } )
}
2020-04-13 17:37:39 +00:00
// Since the logic for all of these files is the exact same except for the name and the contents,
// use a map literal to iterate through them and write them to the bucket.
var backupObjs = map [ string ] io . Reader {
2020-04-17 20:05:29 +00:00
s . layout . getPodVolumeBackupsKey ( info . Name ) : info . PodVolumeBackups ,
s . layout . getBackupVolumeSnapshotsKey ( info . Name ) : info . VolumeSnapshots ,
2023-01-09 22:56:01 +00:00
s . layout . getBackupItemOperationsKey ( info . Name ) : info . BackupItemOperations ,
2020-04-17 20:05:29 +00:00
s . layout . getBackupResourceListKey ( info . Name ) : info . BackupResourceList ,
s . layout . getCSIVolumeSnapshotKey ( info . Name ) : info . CSIVolumeSnapshots ,
s . layout . getCSIVolumeSnapshotContentsKey ( info . Name ) : info . CSIVolumeSnapshotContents ,
2022-03-31 10:09:35 +00:00
s . layout . getCSIVolumeSnapshotClassesKey ( info . Name ) : info . CSIVolumeSnapshotClasses ,
2023-02-16 04:27:53 +00:00
s . layout . getBackupResultsKey ( info . Name ) : info . BackupResults ,
2023-11-07 12:05:34 +00:00
s . layout . getBackupVolumeInfoKey ( info . Name ) : info . BackupVolumeInfo ,
2020-04-13 17:37:39 +00:00
}
for key , reader := range backupObjs {
if err := seekAndPutObject ( s . objectStore , s . bucket , key , reader ) ; err != nil {
2020-03-30 18:26:23 +00:00
errs := [ ] error { err }
2020-03-12 21:01:14 +00:00
2020-04-16 19:17:41 +00:00
// attempt to clean up the backup contents and metadata if we fail to upload and of the extra files.
deleteErr := s . objectStore . DeleteObject ( s . bucket , s . layout . getBackupContentsKey ( info . Name ) )
2020-03-30 18:26:23 +00:00
errs = append ( errs , deleteErr )
2020-03-12 21:01:14 +00:00
2020-03-30 18:26:23 +00:00
deleteErr = s . objectStore . DeleteObject ( s . bucket , s . layout . getBackupMetadataKey ( info . Name ) )
errs = append ( errs , deleteErr )
return kerrors . NewAggregate ( errs )
}
2020-03-12 21:01:14 +00:00
}
2018-08-20 23:29:54 +00:00
return nil
}
2019-01-25 03:33:07 +00:00
func ( s * objectBackupStore ) GetBackupMetadata ( name string ) ( * velerov1api . Backup , error ) {
2019-03-27 22:49:42 +00:00
metadataKey := s . layout . getBackupMetadataKey ( name )
2019-01-25 03:33:07 +00:00
res , err := s . objectStore . GetObject ( s . bucket , metadataKey )
2018-08-20 23:29:54 +00:00
if err != nil {
return nil , err
}
defer res . Close ( )
2023-03-16 01:25:58 +00:00
data , err := io . ReadAll ( res )
2018-08-20 23:29:54 +00:00
if err != nil {
return nil , errors . WithStack ( err )
}
2023-10-31 03:48:54 +00:00
codecFactory := serializer . NewCodecFactory ( util . VeleroScheme )
decoder := codecFactory . UniversalDecoder ( velerov1api . SchemeGroupVersion )
2018-08-20 23:29:54 +00:00
obj , _ , err := decoder . Decode ( data , nil , nil )
if err != nil {
return nil , errors . WithStack ( err )
}
2019-01-25 03:33:07 +00:00
backupObj , ok := obj . ( * velerov1api . Backup )
2018-08-20 23:29:54 +00:00
if ! ok {
2019-01-25 03:33:07 +00:00
return nil , errors . Errorf ( "unexpected type for %s/%s: %T" , s . bucket , metadataKey , obj )
2018-08-20 23:29:54 +00:00
}
return backupObj , nil
2018-10-12 17:55:02 +00:00
}
2023-01-24 17:59:53 +00:00
func ( s * objectBackupStore ) PutBackupMetadata ( backup string , backupMetadata io . Reader ) error {
return seekAndPutObject ( s . objectStore , s . bucket , s . layout . getBackupMetadataKey ( backup ) , backupMetadata )
}
2019-08-05 21:42:01 +00:00
func ( s * objectBackupStore ) GetBackupVolumeSnapshots ( name string ) ( [ ] * volume . Snapshot , error ) {
// if the volumesnapshots file doesn't exist, we don't want to return an error, since
// a legacy backup or a backup with no snapshots would not have this file, so check for
// its existence before attempting to get its contents.
res , err := tryGet ( s . objectStore , s . bucket , s . layout . getBackupVolumeSnapshotsKey ( name ) )
2018-10-17 21:10:42 +00:00
if err != nil {
2019-08-05 21:42:01 +00:00
return nil , err
2018-10-17 21:10:42 +00:00
}
2019-08-05 21:42:01 +00:00
if res == nil {
return nil , nil
}
defer res . Close ( )
2018-10-17 21:10:42 +00:00
2019-08-05 21:42:01 +00:00
var volumeSnapshots [ ] * volume . Snapshot
if err := decode ( res , & volumeSnapshots ) ; err != nil {
return nil , err
2018-10-17 21:10:42 +00:00
}
2019-08-05 21:42:01 +00:00
return volumeSnapshots , nil
2018-10-17 21:10:42 +00:00
}
2023-01-09 22:56:01 +00:00
func ( s * objectBackupStore ) GetBackupItemOperations ( name string ) ( [ ] * itemoperation . BackupOperation , error ) {
// if the itemoperations file doesn't exist, we don't want to return an error, since
// a legacy backup or a backup with no async operations would not have this file, so check for
2021-12-13 19:47:50 +00:00
// its existence before attempting to get its contents.
2023-01-09 22:56:01 +00:00
res , err := tryGet ( s . objectStore , s . bucket , s . layout . getBackupItemOperationsKey ( name ) )
2021-12-13 19:47:50 +00:00
if err != nil {
return nil , err
}
if res == nil {
return nil , nil
}
defer res . Close ( )
2023-01-09 22:56:01 +00:00
var backupItemOperations [ ] * itemoperation . BackupOperation
if err := decode ( res , & backupItemOperations ) ; err != nil {
2021-12-13 19:47:50 +00:00
return nil , err
}
2023-01-09 22:56:01 +00:00
return backupItemOperations , nil
}
func ( s * objectBackupStore ) GetRestoreItemOperations ( name string ) ( [ ] * itemoperation . RestoreOperation , error ) {
// if the itemoperations file doesn't exist, we don't want to return an error, since
// a legacy restore or a restore with no async operations would not have this file, so check for
// its existence before attempting to get its contents.
res , err := tryGet ( s . objectStore , s . bucket , s . layout . getRestoreItemOperationsKey ( name ) )
if err != nil {
return nil , err
}
if res == nil {
return nil , nil
}
defer res . Close ( )
var restoreItemOperations [ ] * itemoperation . RestoreOperation
if err := decode ( res , & restoreItemOperations ) ; err != nil {
return nil , err
}
return restoreItemOperations , nil
2021-12-13 19:47:50 +00:00
}
2019-08-05 21:42:01 +00:00
// tryGet returns the object with the given key if it exists, nil if it does not exist,
// or an error if it was unable to check existence or get the object.
func tryGet ( objectStore velero . ObjectStore , bucket , key string ) ( io . ReadCloser , error ) {
exists , err := objectStore . ObjectExists ( bucket , key )
2018-10-17 21:10:42 +00:00
if err != nil {
return nil , errors . WithStack ( err )
}
2019-08-05 21:42:01 +00:00
if ! exists {
2018-10-17 21:10:42 +00:00
return nil , nil
}
2019-08-05 21:42:01 +00:00
return objectStore . GetObject ( bucket , key )
}
2018-10-12 17:55:02 +00:00
2019-08-05 21:42:01 +00:00
// decode extracts a .json.gz file reader into the object pointed to
// by 'into'.
func decode ( jsongzReader io . Reader , into interface { } ) error {
gzr , err := gzip . NewReader ( jsongzReader )
2018-10-17 21:10:42 +00:00
if err != nil {
2019-08-05 21:42:01 +00:00
return errors . WithStack ( err )
2018-10-17 21:10:42 +00:00
}
defer gzr . Close ( )
2019-08-05 21:42:01 +00:00
if err := json . NewDecoder ( gzr ) . Decode ( into ) ; err != nil {
return errors . Wrap ( err , "error decoding object data" )
2018-10-12 17:55:02 +00:00
}
2018-08-20 23:29:54 +00:00
2019-08-05 21:42:01 +00:00
return nil
2018-08-20 23:29:54 +00:00
}
2022-03-31 10:09:35 +00:00
func ( s * objectBackupStore ) GetCSIVolumeSnapshotClasses ( name string ) ( [ ] * snapshotv1api . VolumeSnapshotClass , error ) {
res , err := tryGet ( s . objectStore , s . bucket , s . layout . getCSIVolumeSnapshotClassesKey ( name ) )
if err != nil {
return nil , err
}
if res == nil {
// this indicates that the no CSI volumesnapshots were prensent in the backup
return nil , nil
}
defer res . Close ( )
var csiVSClasses [ ] * snapshotv1api . VolumeSnapshotClass
if err := decode ( res , & csiVSClasses ) ; err != nil {
return nil , err
}
return csiVSClasses , nil
}
2022-03-31 14:47:22 +00:00
func ( s * objectBackupStore ) GetCSIVolumeSnapshots ( name string ) ( [ ] * snapshotv1api . VolumeSnapshot , error ) {
2020-05-06 14:00:05 +00:00
res , err := tryGet ( s . objectStore , s . bucket , s . layout . getCSIVolumeSnapshotKey ( name ) )
if err != nil {
return nil , err
}
if res == nil {
// this indicates that the no CSI volumesnapshots were prensent in the backup
return nil , nil
}
defer res . Close ( )
2022-03-31 14:47:22 +00:00
var csiSnaps [ ] * snapshotv1api . VolumeSnapshot
2020-05-06 14:00:05 +00:00
if err := decode ( res , & csiSnaps ) ; err != nil {
return nil , err
}
return csiSnaps , nil
}
2022-03-31 14:47:22 +00:00
func ( s * objectBackupStore ) GetCSIVolumeSnapshotContents ( name string ) ( [ ] * snapshotv1api . VolumeSnapshotContent , error ) {
2020-05-06 14:00:05 +00:00
res , err := tryGet ( s . objectStore , s . bucket , s . layout . getCSIVolumeSnapshotContentsKey ( name ) )
if err != nil {
return nil , err
}
if res == nil {
// this indicates that the no CSI volumesnapshotcontents were prensent in the backup
return nil , nil
}
defer res . Close ( )
2022-03-31 14:47:22 +00:00
var snapConts [ ] * snapshotv1api . VolumeSnapshotContent
2020-05-06 14:00:05 +00:00
if err := decode ( res , & snapConts ) ; err != nil {
return nil , err
}
return snapConts , nil
}
2019-07-24 19:51:20 +00:00
func ( s * objectBackupStore ) GetPodVolumeBackups ( name string ) ( [ ] * velerov1api . PodVolumeBackup , error ) {
// if the podvolumebackups file doesn't exist, we don't want to return an error, since
2019-08-05 21:42:01 +00:00
// a legacy backup or a backup with no pod volume backups would not have this file, so
// check for its existence before attempting to get its contents.
res , err := tryGet ( s . objectStore , s . bucket , s . layout . getPodVolumeBackupsKey ( name ) )
2019-07-24 19:51:20 +00:00
if err != nil {
2019-08-05 21:42:01 +00:00
return nil , err
2019-07-24 19:51:20 +00:00
}
2019-08-05 21:42:01 +00:00
if res == nil {
2019-07-24 19:51:20 +00:00
return nil , nil
}
defer res . Close ( )
var podVolumeBackups [ ] * velerov1api . PodVolumeBackup
2019-08-05 21:42:01 +00:00
if err := decode ( res , & podVolumeBackups ) ; err != nil {
return nil , err
2019-07-24 19:51:20 +00:00
}
return podVolumeBackups , nil
}
2023-11-07 12:05:34 +00:00
func ( s * objectBackupStore ) GetBackupVolumeInfos ( name string ) ( * volume . VolumeInfos , error ) {
var volumeInfos * volume . VolumeInfos
res , err := tryGet ( s . objectStore , s . bucket , s . layout . getBackupVolumeInfoKey ( name ) )
if err != nil {
return volumeInfos , err
}
if res == nil {
return volumeInfos , nil
}
defer res . Close ( )
if err := decode ( res , & volumeInfos ) ; err != nil {
return volumeInfos , err
}
return volumeInfos , nil
}
2018-08-20 23:29:54 +00:00
func ( s * objectBackupStore ) GetBackupContents ( name string ) ( io . ReadCloser , error ) {
2018-09-18 15:56:45 +00:00
return s . objectStore . GetObject ( s . bucket , s . layout . getBackupContentsKey ( name ) )
2018-08-20 23:29:54 +00:00
}
2019-04-23 23:19:00 +00:00
func ( s * objectBackupStore ) BackupExists ( bucket , backupName string ) ( bool , error ) {
return s . objectStore . ObjectExists ( bucket , s . layout . getBackupMetadataKey ( backupName ) )
}
2018-08-20 23:29:54 +00:00
func ( s * objectBackupStore ) DeleteBackup ( name string ) error {
2018-09-18 15:56:45 +00:00
objects , err := s . objectStore . ListObjects ( s . bucket , s . layout . getBackupDir ( name ) )
if err != nil {
return err
}
var errs [ ] error
for _ , key := range objects {
s . logger . WithFields ( logrus . Fields {
"key" : key ,
} ) . Debug ( "Trying to delete object" )
if err := s . objectStore . DeleteObject ( s . bucket , key ) ; err != nil {
errs = append ( errs , err )
}
}
return errors . WithStack ( kerrors . NewAggregate ( errs ) )
}
func ( s * objectBackupStore ) DeleteRestore ( name string ) error {
objects , err := s . objectStore . ListObjects ( s . bucket , s . layout . getRestoreDir ( name ) )
2018-08-20 23:29:54 +00:00
if err != nil {
return err
}
var errs [ ] error
for _ , key := range objects {
s . logger . WithFields ( logrus . Fields {
"key" : key ,
} ) . Debug ( "Trying to delete object" )
if err := s . objectStore . DeleteObject ( s . bucket , key ) ; err != nil {
errs = append ( errs , err )
}
}
return errors . WithStack ( kerrors . NewAggregate ( errs ) )
}
func ( s * objectBackupStore ) PutRestoreLog ( backup string , restore string , log io . Reader ) error {
2018-09-18 15:56:45 +00:00
return s . objectStore . PutObject ( s . bucket , s . layout . getRestoreLogKey ( restore ) , log )
2018-08-20 23:29:54 +00:00
}
func ( s * objectBackupStore ) PutRestoreResults ( backup string , restore string , results io . Reader ) error {
2018-09-18 15:56:45 +00:00
return s . objectStore . PutObject ( s . bucket , s . layout . getRestoreResultsKey ( restore ) , results )
2018-08-20 23:29:54 +00:00
}
2023-02-02 08:51:23 +00:00
func ( s * objectBackupStore ) PutRestoredResourceList ( restore string , list io . Reader ) error {
return s . objectStore . PutObject ( s . bucket , s . layout . getRestoreResourceListKey ( restore ) , list )
}
2023-03-10 17:40:20 +00:00
func ( s * objectBackupStore ) PutRestoreItemOperations ( restore string , restoreItemOperations io . Reader ) error {
2023-01-09 22:56:01 +00:00
return seekAndPutObject ( s . objectStore , s . bucket , s . layout . getRestoreItemOperationsKey ( restore ) , restoreItemOperations )
}
func ( s * objectBackupStore ) PutBackupItemOperations ( backup string , backupItemOperations io . Reader ) error {
return seekAndPutObject ( s . objectStore , s . bucket , s . layout . getBackupItemOperationsKey ( backup ) , backupItemOperations )
}
2023-01-24 17:59:53 +00:00
func ( s * objectBackupStore ) PutBackupContents ( backup string , backupContents io . Reader ) error {
return seekAndPutObject ( s . objectStore , s . bucket , s . layout . getBackupContentsKey ( backup ) , backupContents )
}
2019-01-25 03:33:07 +00:00
func ( s * objectBackupStore ) GetDownloadURL ( target velerov1api . DownloadTarget ) ( string , error ) {
2018-08-20 23:29:54 +00:00
switch target . Kind {
2019-01-25 03:33:07 +00:00
case velerov1api . DownloadTargetKindBackupContents :
2018-09-18 15:56:45 +00:00
return s . objectStore . CreateSignedURL ( s . bucket , s . layout . getBackupContentsKey ( target . Name ) , DownloadURLTTL )
2019-01-25 03:33:07 +00:00
case velerov1api . DownloadTargetKindBackupLog :
2018-09-18 15:56:45 +00:00
return s . objectStore . CreateSignedURL ( s . bucket , s . layout . getBackupLogKey ( target . Name ) , DownloadURLTTL )
2019-01-25 03:33:07 +00:00
case velerov1api . DownloadTargetKindBackupVolumeSnapshots :
2018-10-22 16:37:30 +00:00
return s . objectStore . CreateSignedURL ( s . bucket , s . layout . getBackupVolumeSnapshotsKey ( target . Name ) , DownloadURLTTL )
2023-01-09 22:56:01 +00:00
case velerov1api . DownloadTargetKindBackupItemOperations :
return s . objectStore . CreateSignedURL ( s . bucket , s . layout . getBackupItemOperationsKey ( target . Name ) , DownloadURLTTL )
case velerov1api . DownloadTargetKindRestoreItemOperations :
return s . objectStore . CreateSignedURL ( s . bucket , s . layout . getRestoreItemOperationsKey ( target . Name ) , DownloadURLTTL )
2019-08-05 18:23:20 +00:00
case velerov1api . DownloadTargetKindBackupResourceList :
return s . objectStore . CreateSignedURL ( s . bucket , s . layout . getBackupResourceListKey ( target . Name ) , DownloadURLTTL )
2019-01-25 03:33:07 +00:00
case velerov1api . DownloadTargetKindRestoreLog :
2018-09-18 15:56:45 +00:00
return s . objectStore . CreateSignedURL ( s . bucket , s . layout . getRestoreLogKey ( target . Name ) , DownloadURLTTL )
2019-01-25 03:33:07 +00:00
case velerov1api . DownloadTargetKindRestoreResults :
2018-09-18 15:56:45 +00:00
return s . objectStore . CreateSignedURL ( s . bucket , s . layout . getRestoreResultsKey ( target . Name ) , DownloadURLTTL )
2023-02-02 08:51:23 +00:00
case velerov1api . DownloadTargetKindRestoreResourceList :
return s . objectStore . CreateSignedURL ( s . bucket , s . layout . getRestoreResourceListKey ( target . Name ) , DownloadURLTTL )
2022-06-10 07:57:58 +00:00
case velerov1api . DownloadTargetKindCSIBackupVolumeSnapshots :
return s . objectStore . CreateSignedURL ( s . bucket , s . layout . getCSIVolumeSnapshotKey ( target . Name ) , DownloadURLTTL )
case velerov1api . DownloadTargetKindCSIBackupVolumeSnapshotContents :
return s . objectStore . CreateSignedURL ( s . bucket , s . layout . getCSIVolumeSnapshotContentsKey ( target . Name ) , DownloadURLTTL )
2023-03-15 06:20:47 +00:00
case velerov1api . DownloadTargetKindBackupResults :
return s . objectStore . CreateSignedURL ( s . bucket , s . layout . getBackupResultsKey ( target . Name ) , DownloadURLTTL )
2023-11-11 03:35:23 +00:00
case velerov1api . DownloadTargetKindBackupVolumeInfos :
return s . objectStore . CreateSignedURL ( s . bucket , s . layout . getBackupVolumeInfoKey ( target . Name ) , DownloadURLTTL )
2018-08-20 23:29:54 +00:00
default :
return "" , errors . Errorf ( "unsupported download target kind %q" , target . Kind )
}
}
func seekToBeginning ( r io . Reader ) error {
seeker , ok := r . ( io . Seeker )
if ! ok {
return nil
}
_ , err := seeker . Seek ( 0 , 0 )
return err
}
2019-03-14 20:35:06 +00:00
func seekAndPutObject ( objectStore velero . ObjectStore , bucket , key string , file io . Reader ) error {
2018-08-20 23:29:54 +00:00
if file == nil {
return nil
}
if err := seekToBeginning ( file ) ; err != nil {
return errors . WithStack ( err )
}
return objectStore . PutObject ( bucket , key , file )
}