2017-08-02 17:27:17 +00:00
/ *
2021-09-03 15:03:35 +00:00
Copyright The Velero Contributors .
2017-08-02 17:27:17 +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 controller
import (
"bytes"
2020-06-24 16:55:18 +00:00
"context"
2017-08-02 17:27:17 +00:00
"fmt"
"os"
"time"
2023-02-15 07:20:41 +00:00
snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
snapshotterClientSet "github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned"
snapshotv1listers "github.com/kubernetes-csi/external-snapshotter/client/v4/listers/volumesnapshot/v1"
2017-09-14 21:27:31 +00:00
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
2023-02-15 07:20:41 +00:00
corev1api "k8s.io/api/core/v1"
2018-10-24 00:38:08 +00:00
apierrors "k8s.io/apimachinery/pkg/api/errors"
2017-08-02 17:27:17 +00:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2018-10-26 15:32:46 +00:00
"k8s.io/apimachinery/pkg/labels"
2017-12-19 17:16:39 +00:00
kerrors "k8s.io/apimachinery/pkg/util/errors"
2022-03-31 10:09:35 +00:00
"k8s.io/apimachinery/pkg/util/sets"
2022-05-02 08:37:28 +00:00
"k8s.io/apimachinery/pkg/util/wait"
2023-03-10 00:59:40 +00:00
"k8s.io/utils/clock"
ctrl "sigs.k8s.io/controller-runtime"
2023-02-15 07:20:41 +00:00
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
2020-03-04 19:02:47 +00:00
2022-04-01 17:29:52 +00:00
"github.com/vmware-tanzu/velero/internal/credentials"
2023-03-21 06:39:25 +00:00
"github.com/vmware-tanzu/velero/internal/resourcepolicies"
2020-12-08 21:38:29 +00:00
"github.com/vmware-tanzu/velero/internal/storage"
2019-09-30 21:26:56 +00:00
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
pkgbackup "github.com/vmware-tanzu/velero/pkg/backup"
2020-03-19 16:18:05 +00:00
"github.com/vmware-tanzu/velero/pkg/discovery"
2020-03-03 20:57:15 +00:00
"github.com/vmware-tanzu/velero/pkg/features"
2019-09-30 21:26:56 +00:00
"github.com/vmware-tanzu/velero/pkg/label"
"github.com/vmware-tanzu/velero/pkg/metrics"
"github.com/vmware-tanzu/velero/pkg/persistence"
"github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt"
2021-12-10 17:53:47 +00:00
"github.com/vmware-tanzu/velero/pkg/plugin/framework"
2020-04-24 16:46:20 +00:00
"github.com/vmware-tanzu/velero/pkg/util/boolptr"
2019-09-30 21:26:56 +00:00
"github.com/vmware-tanzu/velero/pkg/util/collections"
"github.com/vmware-tanzu/velero/pkg/util/encode"
kubeutil "github.com/vmware-tanzu/velero/pkg/util/kube"
"github.com/vmware-tanzu/velero/pkg/util/logging"
2023-02-15 07:20:41 +00:00
"github.com/vmware-tanzu/velero/pkg/util/results"
2019-09-30 21:26:56 +00:00
"github.com/vmware-tanzu/velero/pkg/volume"
2017-08-02 17:27:17 +00:00
)
2023-03-10 00:59:40 +00:00
const (
backupResyncPeriod = time . Minute
)
type backupReconciler struct {
ctx context . Context
logger logrus . FieldLogger
2023-01-24 17:59:53 +00:00
discoveryHelper discovery . Helper
backupper pkgbackup . Backupper
kbClient kbclient . Client
2023-03-10 00:59:40 +00:00
clock clock . WithTickerAndDelayedExecution
2023-01-24 17:59:53 +00:00
backupLogLevel logrus . Level
newPluginManager func ( logrus . FieldLogger ) clientmgmt . Manager
backupTracker BackupTracker
defaultBackupLocation string
defaultVolumesToFsBackup bool
defaultBackupTTL time . Duration
defaultCSISnapshotTimeout time . Duration
2023-02-27 23:13:31 +00:00
resourceTimeout time . Duration
2023-01-24 17:59:53 +00:00
defaultItemOperationTimeout time . Duration
defaultSnapshotLocations map [ string ] string
metrics * metrics . ServerMetrics
backupStoreGetter persistence . ObjectBackupStoreGetter
formatFlag logging . Format
volumeSnapshotLister snapshotv1listers . VolumeSnapshotLister
volumeSnapshotClient snapshotterClientSet . Interface
credentialFileStore credentials . FileStore
2023-02-22 06:15:00 +00:00
maxConcurrentK8SConnections int
2017-08-02 17:27:17 +00:00
}
2023-03-10 00:59:40 +00:00
func NewBackupReconciler (
ctx context . Context ,
2020-03-19 16:18:05 +00:00
discoveryHelper discovery . Helper ,
2018-09-26 22:18:45 +00:00
backupper pkgbackup . Backupper ,
2017-12-11 22:10:52 +00:00
logger logrus . FieldLogger ,
2018-08-29 19:52:09 +00:00
backupLogLevel logrus . Level ,
2019-03-15 18:32:11 +00:00
newPluginManager func ( logrus . FieldLogger ) clientmgmt . Manager ,
2018-04-06 17:08:39 +00:00
backupTracker BackupTracker ,
2020-06-24 16:55:18 +00:00
kbClient kbclient . Client ,
2018-08-16 22:41:59 +00:00
defaultBackupLocation string ,
2022-09-23 01:13:36 +00:00
defaultVolumesToFsBackup bool ,
2019-04-04 21:59:13 +00:00
defaultBackupTTL time . Duration ,
2022-07-06 07:51:42 +00:00
defaultCSISnapshotTimeout time . Duration ,
2023-02-27 23:13:31 +00:00
resourceTimeout time . Duration ,
2023-01-24 17:59:53 +00:00
defaultItemOperationTimeout time . Duration ,
2018-10-26 15:32:46 +00:00
defaultSnapshotLocations map [ string ] string ,
2018-06-06 21:35:06 +00:00
metrics * metrics . ServerMetrics ,
2021-02-08 18:04:08 +00:00
backupStoreGetter persistence . ObjectBackupStoreGetter ,
2022-10-18 06:58:03 +00:00
formatFlag logging . Format ,
volumeSnapshotLister snapshotv1listers . VolumeSnapshotLister ,
2022-10-24 02:42:08 +00:00
volumeSnapshotClient snapshotterClientSet . Interface ,
2022-04-01 17:29:52 +00:00
credentialStore credentials . FileStore ,
2023-02-22 06:15:00 +00:00
maxConcurrentK8SConnections int ,
2023-03-10 00:59:40 +00:00
) * backupReconciler {
b := & backupReconciler {
ctx : ctx ,
2023-01-24 17:59:53 +00:00
discoveryHelper : discoveryHelper ,
backupper : backupper ,
2023-03-10 00:59:40 +00:00
clock : & clock . RealClock { } ,
logger : logger ,
2023-01-24 17:59:53 +00:00
backupLogLevel : backupLogLevel ,
newPluginManager : newPluginManager ,
backupTracker : backupTracker ,
kbClient : kbClient ,
defaultBackupLocation : defaultBackupLocation ,
defaultVolumesToFsBackup : defaultVolumesToFsBackup ,
defaultBackupTTL : defaultBackupTTL ,
defaultCSISnapshotTimeout : defaultCSISnapshotTimeout ,
2023-02-27 23:13:31 +00:00
resourceTimeout : resourceTimeout ,
2023-01-24 17:59:53 +00:00
defaultItemOperationTimeout : defaultItemOperationTimeout ,
defaultSnapshotLocations : defaultSnapshotLocations ,
metrics : metrics ,
backupStoreGetter : backupStoreGetter ,
formatFlag : formatFlag ,
volumeSnapshotLister : volumeSnapshotLister ,
volumeSnapshotClient : volumeSnapshotClient ,
credentialFileStore : credentialStore ,
2023-02-22 06:15:00 +00:00
maxConcurrentK8SConnections : maxConcurrentK8SConnections ,
2017-08-02 17:27:17 +00:00
}
2023-03-10 00:59:40 +00:00
b . updateTotalBackupMetric ( )
return b
}
2017-08-02 17:27:17 +00:00
2023-03-10 00:59:40 +00:00
func ( b * backupReconciler ) SetupWithManager ( mgr ctrl . Manager ) error {
return ctrl . NewControllerManagedBy ( mgr ) .
For ( & velerov1api . Backup { } ) .
Complete ( b )
}
2017-08-02 17:27:17 +00:00
2023-03-10 00:59:40 +00:00
func ( b * backupReconciler ) updateTotalBackupMetric ( ) {
go func ( ) {
// Wait for 5 seconds to let controller-runtime to setup k8s clients.
time . Sleep ( 5 * time . Second )
2017-08-02 17:27:17 +00:00
2023-03-10 00:59:40 +00:00
wait . Until (
func ( ) {
// recompute backup_total metric
backups := & velerov1api . BackupList { }
err := b . kbClient . List ( context . Background ( ) , backups , & kbclient . ListOptions { LabelSelector : labels . Everything ( ) } )
if err != nil {
b . logger . Error ( err , "Error computing backup_total metric" )
} else {
b . metrics . SetBackupTotal ( int64 ( len ( backups . Items ) ) )
2017-08-02 17:27:17 +00:00
}
2023-03-10 00:59:40 +00:00
// recompute backup_last_successful_timestamp metric for each
// schedule (including the empty schedule, i.e. ad-hoc backups)
for schedule , timestamp := range getLastSuccessBySchedule ( backups . Items ) {
b . metrics . SetBackupLastSuccessfulTimestamp ( schedule , timestamp )
2017-08-02 17:27:17 +00:00
}
} ,
2023-03-10 00:59:40 +00:00
backupResyncPeriod ,
b . ctx . Done ( ) ,
)
} ( )
2020-01-21 20:21:28 +00:00
}
// getLastSuccessBySchedule finds the most recent completed backup for each schedule
// and returns a map of schedule name -> completion time of the most recent completed
// backup. This map includes an entry for ad-hoc/non-scheduled backups, where the key
// is the empty string.
2023-03-10 00:59:40 +00:00
func getLastSuccessBySchedule ( backups [ ] velerov1api . Backup ) map [ string ] time . Time {
2020-01-14 21:11:21 +00:00
lastSuccessBySchedule := map [ string ] time . Time { }
for _ , backup := range backups {
if backup . Status . Phase != velerov1api . BackupPhaseCompleted {
continue
}
if backup . Status . CompletionTimestamp == nil {
continue
}
schedule := backup . Labels [ velerov1api . ScheduleNameLabel ]
timestamp := backup . Status . CompletionTimestamp . Time
if timestamp . After ( lastSuccessBySchedule [ schedule ] ) {
lastSuccessBySchedule [ schedule ] = timestamp
}
}
2020-01-21 20:21:28 +00:00
return lastSuccessBySchedule
2019-04-05 02:11:53 +00:00
}
2023-03-10 00:59:40 +00:00
func ( b * backupReconciler ) Reconcile ( ctx context . Context , req ctrl . Request ) ( ctrl . Result , error ) {
log := b . logger . WithFields ( logrus . Fields {
"controller" : Backup ,
"backuprequest" : req . String ( ) ,
} )
2017-08-02 17:27:17 +00:00
2018-08-29 19:52:09 +00:00
log . Debug ( "Getting backup" )
2023-03-10 00:59:40 +00:00
original := & velerov1api . Backup { }
err := b . kbClient . Get ( ctx , req . NamespacedName , original )
2017-08-02 17:27:17 +00:00
if err != nil {
2023-03-10 00:59:40 +00:00
if apierrors . IsNotFound ( err ) {
log . Debug ( "backup not found" )
return ctrl . Result { } , nil
}
log . WithError ( err ) . Error ( "error getting backup" )
return ctrl . Result { } , err
2017-08-02 17:27:17 +00:00
}
2017-12-19 17:16:39 +00:00
// Double-check we have the correct phase. In the unlikely event that multiple controller
// instances are running, it's possible for controller A to succeed in changing the phase to
// InProgress, while controller B's attempt to patch the phase fails. When controller B
// reprocesses the same backup, it will either show up as New (informer hasn't seen the update
// yet) or as InProgress. In the former case, the patch attempt will fail again, until the
// informer sees the update. In the latter case, after the informer has seen the update to
// InProgress, we still need this check so we can return nil to indicate we've finished processing
// this key (even though it was a no-op).
2018-09-26 22:18:45 +00:00
switch original . Status . Phase {
2019-01-25 03:33:07 +00:00
case "" , velerov1api . BackupPhaseNew :
2022-06-06 14:56:29 +00:00
// only process new backups
2017-08-02 17:27:17 +00:00
default :
2023-03-10 00:59:40 +00:00
b . logger . WithFields ( logrus . Fields {
"backup" : kubeutil . NamespaceAndName ( original ) ,
"phase" : original . Status . Phase ,
} ) . Debug ( "Backup is not handled" )
return ctrl . Result { } , nil
2017-08-02 17:27:17 +00:00
}
2018-09-26 22:18:45 +00:00
log . Debug ( "Preparing backup request" )
2023-03-10 00:59:40 +00:00
request := b . prepareBackupRequest ( original , log )
2018-09-26 22:18:45 +00:00
if len ( request . Status . ValidationErrors ) > 0 {
2019-01-25 03:33:07 +00:00
request . Status . Phase = velerov1api . BackupPhaseFailedValidation
2017-08-02 17:27:17 +00:00
} else {
2019-01-25 03:33:07 +00:00
request . Status . Phase = velerov1api . BackupPhaseInProgress
2023-03-10 00:59:40 +00:00
request . Status . StartTimestamp = & metav1 . Time { Time : b . clock . Now ( ) }
2017-08-02 17:27:17 +00:00
}
// update status
2023-03-10 00:59:40 +00:00
if err := kubeutil . PatchResource ( original , request . Backup , b . kbClient ) ; err != nil {
return ctrl . Result { } , errors . Wrapf ( err , "error updating Backup status to %s" , request . Status . Phase )
2017-08-02 17:27:17 +00:00
}
2017-12-11 22:10:52 +00:00
// store ref to just-updated item for creating patch
2023-03-10 00:59:40 +00:00
original = request . Backup . DeepCopy ( )
2017-08-02 17:27:17 +00:00
2023-06-09 03:19:26 +00:00
backupScheduleName := request . GetLabels ( ) [ velerov1api . ScheduleNameLabel ]
2019-01-25 03:33:07 +00:00
if request . Status . Phase == velerov1api . BackupPhaseFailedValidation {
2023-03-10 00:59:40 +00:00
log . Debug ( "failed to validate backup status" )
2023-06-09 03:19:26 +00:00
b . metrics . RegisterBackupValidationFailure ( backupScheduleName )
b . metrics . RegisterBackupLastStatus ( backupScheduleName , metrics . BackupLastStatusFailure )
2023-03-10 00:59:40 +00:00
return ctrl . Result { } , nil
2017-08-02 17:27:17 +00:00
}
2023-03-10 00:59:40 +00:00
b . backupTracker . Add ( request . Namespace , request . Name )
2023-03-27 13:47:23 +00:00
defer func ( ) {
switch request . Status . Phase {
case velerov1api . BackupPhaseCompleted , velerov1api . BackupPhasePartiallyFailed , velerov1api . BackupPhaseFailed , velerov1api . BackupPhaseFailedValidation :
b . backupTracker . Delete ( request . Namespace , request . Name )
}
} ( )
2018-04-06 17:08:39 +00:00
2018-08-29 19:52:09 +00:00
log . Debug ( "Running backup" )
2019-04-26 16:14:26 +00:00
2023-03-10 00:59:40 +00:00
b . metrics . RegisterBackupAttempt ( backupScheduleName )
2018-06-06 21:35:06 +00:00
2019-04-26 16:14:26 +00:00
// execution & upload of backup
2023-03-10 00:59:40 +00:00
if err := b . runBackup ( request ) ; err != nil {
2019-04-26 16:14:26 +00:00
// even though runBackup sets the backup's phase prior
// to uploading artifacts to object storage, we have to
// check for an error again here and update the phase if
// one is found, because there could've been an error
// while uploading artifacts to object storage, which would
// result in the backup being Failed.
2018-08-29 19:52:09 +00:00
log . WithError ( err ) . Error ( "backup failed" )
2019-01-25 03:33:07 +00:00
request . Status . Phase = velerov1api . BackupPhaseFailed
2022-04-14 12:05:55 +00:00
request . Status . FailureReason = err . Error ( )
2019-04-26 16:14:26 +00:00
}
switch request . Status . Phase {
case velerov1api . BackupPhaseCompleted :
2023-03-10 00:59:40 +00:00
b . metrics . RegisterBackupSuccess ( backupScheduleName )
b . metrics . RegisterBackupLastStatus ( backupScheduleName , metrics . BackupLastStatusSucc )
2019-04-26 16:14:26 +00:00
case velerov1api . BackupPhasePartiallyFailed :
2023-03-10 00:59:40 +00:00
b . metrics . RegisterBackupPartialFailure ( backupScheduleName )
b . metrics . RegisterBackupLastStatus ( backupScheduleName , metrics . BackupLastStatusFailure )
2019-04-26 16:14:26 +00:00
case velerov1api . BackupPhaseFailed :
2023-03-10 00:59:40 +00:00
b . metrics . RegisterBackupFailed ( backupScheduleName )
b . metrics . RegisterBackupLastStatus ( backupScheduleName , metrics . BackupLastStatusFailure )
2020-07-16 17:13:17 +00:00
case velerov1api . BackupPhaseFailedValidation :
2023-03-10 00:59:40 +00:00
b . metrics . RegisterBackupValidationFailure ( backupScheduleName )
b . metrics . RegisterBackupLastStatus ( backupScheduleName , metrics . BackupLastStatusFailure )
2017-08-02 17:27:17 +00:00
}
2023-03-10 00:59:40 +00:00
log . Info ( "Updating backup's final status" )
if err := kubeutil . PatchResource ( original , request . Backup , b . kbClient ) ; err != nil {
2018-08-29 19:52:09 +00:00
log . WithError ( err ) . Error ( "error updating backup's final status" )
2017-08-02 17:27:17 +00:00
}
2023-03-10 00:59:40 +00:00
return ctrl . Result { } , nil
2017-12-11 22:10:52 +00:00
}
2023-03-10 00:59:40 +00:00
func ( b * backupReconciler ) prepareBackupRequest ( backup * velerov1api . Backup , logger logrus . FieldLogger ) * pkgbackup . Request {
2018-09-26 22:18:45 +00:00
request := & pkgbackup . Request {
Backup : backup . DeepCopy ( ) , // don't modify items in the cache
2017-08-02 17:27:17 +00:00
}
2020-05-01 19:54:57 +00:00
// set backup major version - deprecated, use Status.FormatVersion
2018-12-05 22:56:53 +00:00
request . Status . Version = pkgbackup . BackupVersion
2018-09-26 22:18:45 +00:00
2020-05-01 19:54:57 +00:00
// set backup major, minor, and patch version
request . Status . FormatVersion = pkgbackup . BackupFormatVersion
2019-04-04 21:59:13 +00:00
if request . Spec . TTL . Duration == 0 {
// set default backup TTL
2023-03-10 00:59:40 +00:00
request . Spec . TTL . Duration = b . defaultBackupTTL
2017-08-02 17:27:17 +00:00
}
2022-07-06 07:51:42 +00:00
if request . Spec . CSISnapshotTimeout . Duration == 0 {
// set default CSI VolumeSnapshot timeout
2023-03-10 00:59:40 +00:00
request . Spec . CSISnapshotTimeout . Duration = b . defaultCSISnapshotTimeout
2022-07-06 07:51:42 +00:00
}
2023-01-24 17:59:53 +00:00
if request . Spec . ItemOperationTimeout . Duration == 0 {
// set default item operation timeout
2023-03-10 00:59:40 +00:00
request . Spec . ItemOperationTimeout . Duration = b . defaultItemOperationTimeout
2023-01-24 17:59:53 +00:00
}
2019-04-04 21:59:13 +00:00
// calculate expiration
2023-03-10 00:59:40 +00:00
request . Status . Expiration = & metav1 . Time { Time : b . clock . Now ( ) . Add ( request . Spec . TTL . Duration ) }
2019-04-04 21:59:13 +00:00
2022-09-23 01:13:36 +00:00
// TODO: post v1.10. Remove this code block after DefaultVolumesToRestic is removed from CRD
// For now, for CRs created by old versions, we need to respect the DefaultVolumesToRestic value if it is set true
if boolptr . IsSetToTrue ( request . Spec . DefaultVolumesToRestic ) {
logger . Warn ( "DefaultVolumesToRestic field will be deprecated, use DefaultVolumesToFsBackup instead. Automatically remap it to DefaultVolumesToFsBackup" )
request . Spec . DefaultVolumesToFsBackup = request . Spec . DefaultVolumesToRestic
}
if request . Spec . DefaultVolumesToFsBackup == nil {
2023-03-10 00:59:40 +00:00
request . Spec . DefaultVolumesToFsBackup = & b . defaultVolumesToFsBackup
2020-12-09 17:38:31 +00:00
}
// find which storage location to use
var serverSpecified bool
2018-09-26 22:18:45 +00:00
if request . Spec . StorageLocation == "" {
2020-12-09 17:38:31 +00:00
// when the user doesn't specify a location, use the server default unless there is an existing BSL marked as default
2023-03-10 00:59:40 +00:00
// TODO(2.0) b.defaultBackupLocation will be deprecated
request . Spec . StorageLocation = b . defaultBackupLocation
2020-12-08 21:38:29 +00:00
2023-03-10 00:59:40 +00:00
locationList , err := storage . ListBackupStorageLocations ( context . Background ( ) , b . kbClient , request . Namespace )
2020-12-08 21:38:29 +00:00
if err == nil {
for _ , location := range locationList . Items {
if location . Spec . Default {
request . Spec . StorageLocation = location . Name
break
}
}
}
2020-12-09 17:38:31 +00:00
serverSpecified = true
2018-08-16 22:41:59 +00:00
}
2020-12-09 17:38:31 +00:00
// get the storage location, and store the BackupStorageLocation API obj on the request
2020-06-24 16:55:18 +00:00
storageLocation := & velerov1api . BackupStorageLocation { }
2023-03-10 00:59:40 +00:00
if err := b . kbClient . Get ( context . Background ( ) , kbclient . ObjectKey {
2020-06-24 16:55:18 +00:00
Namespace : request . Namespace ,
Name : request . Spec . StorageLocation ,
} , storageLocation ) ; err != nil {
2018-10-24 00:38:08 +00:00
if apierrors . IsNotFound ( err ) {
2020-12-09 17:38:31 +00:00
if serverSpecified {
// TODO(2.0) remove this. For now, without mentioning "server default" it could be confusing trying to grasp where the default came from.
request . Status . ValidationErrors = append ( request . Status . ValidationErrors , fmt . Sprintf ( "an existing backup storage location wasn't specified at backup creation time and the server default '%s' doesn't exist. Please address this issue (see `velero backup-location -h` for options) and create a new backup. Error: %v" , request . Spec . StorageLocation , err ) )
} else {
request . Status . ValidationErrors = append ( request . Status . ValidationErrors , fmt . Sprintf ( "an existing backup storage location wasn't specified at backup creation time and the default '%s' wasn't found. Please address this issue (see `velero backup-location -h` for options) and create a new backup. Error: %v" , request . Spec . StorageLocation , err ) )
}
2018-10-24 00:38:08 +00:00
} else {
request . Status . ValidationErrors = append ( request . Status . ValidationErrors , fmt . Sprintf ( "error getting backup storage location: %v" , err ) )
}
2018-09-26 22:18:45 +00:00
} else {
request . StorageLocation = storageLocation
2019-05-29 18:21:25 +00:00
if request . StorageLocation . Spec . AccessMode == velerov1api . BackupStorageLocationAccessModeReadOnly {
request . Status . ValidationErrors = append ( request . Status . ValidationErrors ,
fmt . Sprintf ( "backup can't be created because backup storage location %s is currently in read-only mode" , request . StorageLocation . Name ) )
}
2018-09-26 22:18:45 +00:00
}
2020-12-09 17:38:31 +00:00
// add the storage location as a label for easy filtering later.
if request . Labels == nil {
request . Labels = make ( map [ string ] string )
}
request . Labels [ velerov1api . StorageLocationLabel ] = label . GetValidName ( request . Spec . StorageLocation )
2018-09-26 22:18:45 +00:00
// validate and get the backup's VolumeSnapshotLocations, and store the
// VolumeSnapshotLocation API objs on the request
2023-03-10 00:59:40 +00:00
if locs , errs := b . validateAndGetSnapshotLocations ( request . Backup ) ; len ( errs ) > 0 {
2018-09-26 22:18:45 +00:00
request . Status . ValidationErrors = append ( request . Status . ValidationErrors , errs ... )
} else {
request . Spec . VolumeSnapshotLocations = nil
for _ , loc := range locs {
request . Spec . VolumeSnapshotLocations = append ( request . Spec . VolumeSnapshotLocations , loc . Name )
request . SnapshotLocations = append ( request . SnapshotLocations , loc )
}
}
2020-12-09 17:38:31 +00:00
// Getting all information of cluster version - useful for future skip-level migration
if request . Annotations == nil {
request . Annotations = make ( map [ string ] string )
}
2023-03-10 00:59:40 +00:00
request . Annotations [ velerov1api . SourceClusterK8sGitVersionAnnotation ] = b . discoveryHelper . ServerVersion ( ) . String ( )
request . Annotations [ velerov1api . SourceClusterK8sMajorVersionAnnotation ] = b . discoveryHelper . ServerVersion ( ) . Major
request . Annotations [ velerov1api . SourceClusterK8sMinorVersionAnnotation ] = b . discoveryHelper . ServerVersion ( ) . Minor
2023-05-25 12:55:45 +00:00
request . Annotations [ velerov1api . ResourceTimeoutAnnotation ] = b . resourceTimeout . String ( )
2020-12-09 17:38:31 +00:00
2022-08-03 12:00:49 +00:00
// Add namespaces with label velero.io/exclude-from-backup=true into request.Spec.ExcludedNamespaces
// Essentially, adding the label velero.io/exclude-from-backup=true to a namespace would be equivalent to setting spec.ExcludedNamespaces
2022-08-08 04:05:08 +00:00
namespaces := corev1api . NamespaceList { }
2023-03-10 00:59:40 +00:00
if err := b . kbClient . List ( context . Background ( ) , & namespaces , kbclient . MatchingLabels { "velero.io/exclude-from-backup" : "true" } ) ; err == nil {
2022-08-03 12:00:49 +00:00
for _ , ns := range namespaces . Items {
request . Spec . ExcludedNamespaces = append ( request . Spec . ExcludedNamespaces , ns . Name )
}
2022-08-08 04:05:08 +00:00
} else {
request . Status . ValidationErrors = append ( request . Status . ValidationErrors , fmt . Sprintf ( "error getting namespace list: %v" , err ) )
2022-08-03 12:00:49 +00:00
}
2023-02-02 09:31:35 +00:00
// validate whether Included/Excluded resources and IncludedClusterResource are mixed with
2023-04-06 02:59:28 +00:00
// Included/Excluded cluster-scoped/namespace-scoped resources.
2023-02-02 09:31:35 +00:00
if oldAndNewFilterParametersUsedTogether ( request . Spec ) {
validatedError := fmt . Sprintf ( "include-resources, exclude-resources and include-cluster-resources are old filter parameters.\n" +
2023-04-06 02:59:28 +00:00
"include-cluster-scoped-resources, exclude-cluster-scoped-resources, include-namespace-scoped-resources and exclude-namespace-scoped-resources are new filter parameters.\n" +
2023-02-02 09:31:35 +00:00
"They cannot be used together" )
request . Status . ValidationErrors = append ( request . Status . ValidationErrors , validatedError )
}
2020-12-09 17:38:31 +00:00
// validate the included/excluded resources
for _ , err := range collections . ValidateIncludesExcludes ( request . Spec . IncludedResources , request . Spec . ExcludedResources ) {
request . Status . ValidationErrors = append ( request . Status . ValidationErrors , fmt . Sprintf ( "Invalid included/excluded resource lists: %v" , err ) )
}
2023-02-02 09:31:35 +00:00
// validate the cluster-scoped included/excluded resources
2023-04-06 02:59:28 +00:00
for _ , err := range collections . ValidateScopedIncludesExcludes ( request . Spec . IncludedClusterScopedResources , request . Spec . ExcludedClusterScopedResources ) {
2023-02-02 09:31:35 +00:00
request . Status . ValidationErrors = append ( request . Status . ValidationErrors , fmt . Sprintf ( "Invalid cluster-scoped included/excluded resource lists: %s" , err ) )
}
2023-04-06 02:59:28 +00:00
// validate the namespace-scoped included/excluded resources
for _ , err := range collections . ValidateScopedIncludesExcludes ( request . Spec . IncludedNamespaceScopedResources , request . Spec . ExcludedNamespaceScopedResources ) {
request . Status . ValidationErrors = append ( request . Status . ValidationErrors , fmt . Sprintf ( "Invalid namespace-scoped included/excluded resource lists: %s" , err ) )
2023-02-02 09:31:35 +00:00
}
2020-12-09 17:38:31 +00:00
// validate the included/excluded namespaces
2021-09-03 15:03:35 +00:00
for _ , err := range collections . ValidateNamespaceIncludesExcludes ( request . Spec . IncludedNamespaces , request . Spec . ExcludedNamespaces ) {
2020-12-09 17:38:31 +00:00
request . Status . ValidationErrors = append ( request . Status . ValidationErrors , fmt . Sprintf ( "Invalid included/excluded namespace lists: %v" , err ) )
}
2022-02-15 14:52:47 +00:00
// validate that only one exists orLabelSelector or just labelSelector (singular)
if request . Spec . OrLabelSelectors != nil && request . Spec . LabelSelector != nil {
2023-04-25 12:06:42 +00:00
request . Status . ValidationErrors = append ( request . Status . ValidationErrors , "encountered labelSelector as well as orLabelSelectors in backup spec, only one can be specified" )
2022-02-15 14:52:47 +00:00
}
2023-03-21 08:33:15 +00:00
if request . Spec . ResourcePolicy != nil && request . Spec . ResourcePolicy . Kind == resourcepolicies . ConfigmapRefType {
2023-04-25 05:50:52 +00:00
policiesConfigmap := & corev1api . ConfigMap { }
2023-03-21 08:33:15 +00:00
err := b . kbClient . Get ( context . Background ( ) , kbclient . ObjectKey { Namespace : request . Namespace , Name : request . Spec . ResourcePolicy . Name } , policiesConfigmap )
2023-03-21 06:39:25 +00:00
if err != nil {
2023-03-21 08:33:15 +00:00
request . Status . ValidationErrors = append ( request . Status . ValidationErrors , fmt . Sprintf ( "failed to get resource policies %s/%s configmap with err %v" , request . Namespace , request . Spec . ResourcePolicy . Name , err ) )
2023-03-21 06:39:25 +00:00
}
res , err := resourcepolicies . GetResourcePoliciesFromConfig ( policiesConfigmap )
if err != nil {
2023-03-21 08:33:15 +00:00
request . Status . ValidationErrors = append ( request . Status . ValidationErrors , errors . Wrapf ( err , fmt . Sprintf ( "resource policies %s/%s" , request . Namespace , request . Spec . ResourcePolicy . Name ) ) . Error ( ) )
2023-03-21 06:39:25 +00:00
} else if err = res . Validate ( ) ; err != nil {
2023-03-21 08:33:15 +00:00
request . Status . ValidationErrors = append ( request . Status . ValidationErrors , errors . Wrapf ( err , fmt . Sprintf ( "resource policies %s/%s" , request . Namespace , request . Spec . ResourcePolicy . Name ) ) . Error ( ) )
2023-03-21 06:39:25 +00:00
}
request . ResPolicies = res
}
2018-09-26 22:18:45 +00:00
return request
2017-08-02 17:27:17 +00:00
}
2018-09-26 22:18:45 +00:00
// validateAndGetSnapshotLocations gets a collection of VolumeSnapshotLocation objects that
// this backup will use (returned as a map of provider name -> VSL), and ensures:
2023-02-15 07:20:41 +00:00
// - each location name in .spec.volumeSnapshotLocations exists as a location
// - exactly 1 location per provider
// - a given provider's default location name is added to .spec.volumeSnapshotLocations if one
// is not explicitly specified for the provider (if there's only one location for the provider,
// it will automatically be used)
//
2020-04-24 16:46:20 +00:00
// if backup has snapshotVolume disabled then it returns empty VSL
2023-03-10 00:59:40 +00:00
func ( b * backupReconciler ) validateAndGetSnapshotLocations ( backup * velerov1api . Backup ) ( map [ string ] * velerov1api . VolumeSnapshotLocation , [ ] string ) {
2018-09-26 22:18:45 +00:00
errors := [ ] string { }
2019-01-25 03:33:07 +00:00
providerLocations := make ( map [ string ] * velerov1api . VolumeSnapshotLocation )
2018-09-26 22:18:45 +00:00
2020-04-24 16:46:20 +00:00
// if snapshotVolume is set to false then we don't need to validate volumesnapshotlocation
if boolptr . IsSetToFalse ( backup . Spec . SnapshotVolumes ) {
return nil , nil
}
2018-09-26 22:18:45 +00:00
for _ , locationName := range backup . Spec . VolumeSnapshotLocations {
2018-09-25 14:51:28 +00:00
// validate each locationName exists as a VolumeSnapshotLocation
2023-03-10 00:59:40 +00:00
location := & velerov1api . VolumeSnapshotLocation { }
if err := b . kbClient . Get ( context . Background ( ) , kbclient . ObjectKey { Namespace : backup . Namespace , Name : locationName } , location ) ; err != nil {
2018-10-24 00:38:08 +00:00
if apierrors . IsNotFound ( err ) {
errors = append ( errors , fmt . Sprintf ( "a VolumeSnapshotLocation CRD for the location %s with the name specified in the backup spec needs to be created before this snapshot can be executed. Error: %v" , locationName , err ) )
} else {
errors = append ( errors , fmt . Sprintf ( "error getting volume snapshot location named %s: %v" , locationName , err ) )
}
2018-09-25 14:51:28 +00:00
continue
}
2018-09-26 22:18:45 +00:00
// ensure we end up with exactly 1 location *per provider*
if providerLocation , ok := providerLocations [ location . Spec . Provider ] ; ok {
2018-09-25 14:51:28 +00:00
// if > 1 location name per provider as in ["aws-us-east-1" | "aws-us-west-1"] (same provider, multiple names)
2018-09-26 22:18:45 +00:00
if providerLocation . Name != locationName {
errors = append ( errors , fmt . Sprintf ( "more than one VolumeSnapshotLocation name specified for provider %s: %s; unexpected name was %s" , location . Spec . Provider , locationName , providerLocation . Name ) )
2018-09-25 14:51:28 +00:00
continue
}
} else {
// keep track of all valid existing locations, per provider
2018-09-26 22:18:45 +00:00
providerLocations [ location . Spec . Provider ] = location
2018-09-25 14:51:28 +00:00
}
}
if len ( errors ) > 0 {
2018-09-26 22:18:45 +00:00
return nil , errors
2018-09-25 14:51:28 +00:00
}
2023-03-10 00:59:40 +00:00
allLocations := & velerov1api . VolumeSnapshotLocationList { }
err := b . kbClient . List ( context . Background ( ) , allLocations , & kbclient . ListOptions { Namespace : backup . Namespace , LabelSelector : labels . Everything ( ) } )
2018-10-26 15:32:46 +00:00
if err != nil {
errors = append ( errors , fmt . Sprintf ( "error listing volume snapshot locations: %v" , err ) )
return nil , errors
}
// build a map of provider->list of all locations for the provider
2019-01-25 03:33:07 +00:00
allProviderLocations := make ( map [ string ] [ ] * velerov1api . VolumeSnapshotLocation )
2023-03-10 00:59:40 +00:00
for i := range allLocations . Items {
loc := allLocations . Items [ i ]
allProviderLocations [ loc . Spec . Provider ] = append ( allProviderLocations [ loc . Spec . Provider ] , & loc )
2018-10-26 15:32:46 +00:00
}
// go through each provider and make sure we have/can get a VSL
// for it
for provider , locations := range allProviderLocations {
if _ , ok := providerLocations [ provider ] ; ok {
// backup's spec had a location named for this provider
continue
}
if len ( locations ) > 1 {
// more than one possible location for the provider: check
// the defaults
2023-03-10 00:59:40 +00:00
defaultLocation := b . defaultSnapshotLocations [ provider ]
2018-10-26 15:32:46 +00:00
if defaultLocation == "" {
errors = append ( errors , fmt . Sprintf ( "provider %s has more than one possible volume snapshot location, and none were specified explicitly or as a default" , provider ) )
continue
}
2023-03-10 00:59:40 +00:00
location := & velerov1api . VolumeSnapshotLocation { }
2023-04-27 12:39:53 +00:00
if err := b . kbClient . Get ( context . Background ( ) , kbclient . ObjectKey { Namespace : backup . Namespace , Name : defaultLocation } , location ) ; err != nil {
2018-10-26 15:32:46 +00:00
errors = append ( errors , fmt . Sprintf ( "error getting volume snapshot location named %s: %v" , defaultLocation , err ) )
continue
}
providerLocations [ provider ] = location
continue
2018-09-25 14:51:28 +00:00
}
2018-10-26 15:32:46 +00:00
// exactly one location for the provider: use it
providerLocations [ provider ] = locations [ 0 ]
}
if len ( errors ) > 0 {
return nil , errors
2018-09-25 14:51:28 +00:00
}
2022-04-01 17:29:52 +00:00
// add credential to config for each location
for _ , location := range providerLocations {
2023-03-15 05:50:54 +00:00
err = volume . UpdateVolumeSnapshotLocationWithCredentialConfig ( location , b . credentialFileStore )
2022-04-01 17:29:52 +00:00
if err != nil {
errors = append ( errors , fmt . Sprintf ( "error adding credentials to volume snapshot location named %s: %v" , location . Name , err ) )
continue
}
}
2018-09-26 22:18:45 +00:00
return providerLocations , nil
2018-09-25 14:51:28 +00:00
}
2019-04-26 16:14:26 +00:00
// runBackup runs and uploads a validated backup. Any error returned from this function
// causes the backup to be Failed; if no error is returned, the backup's status's Errors
// field is checked to see if the backup was a partial failure.
2023-03-16 01:25:58 +00:00
2023-03-10 00:59:40 +00:00
func ( b * backupReconciler ) runBackup ( backup * pkgbackup . Request ) error {
b . logger . WithField ( Backup , kubeutil . NamespaceAndName ( backup ) ) . Info ( "Setting up backup log" )
2017-08-11 15:05:56 +00:00
2018-05-13 13:28:09 +00:00
// Log the backup to both a backup log file and to stdout. This will help see what happened if the upload of the
// backup log failed for whatever reason.
2023-02-16 04:27:53 +00:00
logCounter := logging . NewLogHook ( )
2023-03-10 00:59:40 +00:00
backupLog , err := logging . NewTempFileLogger ( b . backupLogLevel , b . formatFlag , logCounter , logrus . Fields { Backup : kubeutil . NamespaceAndName ( backup ) } )
2023-03-06 09:29:52 +00:00
if err != nil {
return errors . Wrap ( err , "error creating dual mode logger for backup" )
}
2023-03-10 00:59:40 +00:00
defer backupLog . Dispose ( b . logger . WithField ( Backup , kubeutil . NamespaceAndName ( backup ) ) )
2019-04-26 16:14:26 +00:00
backupLog . Info ( "Setting up backup temp file" )
2023-03-16 01:25:58 +00:00
backupFile , err := os . CreateTemp ( "" , "" )
2017-12-19 17:16:39 +00:00
if err != nil {
return errors . Wrap ( err , "error creating temp file for backup" )
}
2019-04-26 16:14:26 +00:00
defer closeAndRemoveFile ( backupFile , backupLog )
2017-08-02 17:27:17 +00:00
2019-04-26 16:14:26 +00:00
backupLog . Info ( "Setting up plugin manager" )
2023-03-10 00:59:40 +00:00
pluginManager := b . newPluginManager ( backupLog )
2018-08-20 23:29:54 +00:00
defer pluginManager . CleanupClients ( )
2019-04-26 16:14:26 +00:00
backupLog . Info ( "Getting backup item actions" )
2022-09-01 18:36:35 +00:00
actions , err := pluginManager . GetBackupItemActionsV2 ( )
2018-05-13 13:28:09 +00:00
if err != nil {
return err
}
2020-05-27 23:03:52 +00:00
backupLog . Info ( "Setting up backup store to check for backup existence" )
2023-03-10 00:59:40 +00:00
backupStore , err := b . backupStoreGetter . Get ( backup . StorageLocation , pluginManager , backupLog )
2017-11-15 02:35:02 +00:00
if err != nil {
return err
}
2019-04-24 17:54:43 +00:00
exists , err := backupStore . BackupExists ( backup . StorageLocation . Spec . StorageType . ObjectStorage . Bucket , backup . Name )
if exists || err != nil {
2019-04-23 23:19:00 +00:00
backup . Status . Phase = velerov1api . BackupPhaseFailed
2023-03-10 00:59:40 +00:00
backup . Status . CompletionTimestamp = & metav1 . Time { Time : b . clock . Now ( ) }
2019-04-24 17:54:43 +00:00
if err != nil {
2019-04-24 21:18:24 +00:00
return errors . Wrapf ( err , "error checking if backup already exists in object storage" )
2019-04-24 17:54:43 +00:00
}
2019-04-24 21:18:24 +00:00
return errors . Errorf ( "backup already exists in object storage" )
2019-04-23 23:19:00 +00:00
}
2017-11-15 02:35:02 +00:00
2022-09-01 18:36:35 +00:00
backupItemActionsResolver := framework . NewBackupItemActionResolverV2 ( actions )
2021-12-10 17:53:47 +00:00
2019-04-26 16:14:26 +00:00
var fatalErrs [ ] error
2023-03-21 01:05:29 +00:00
if err := b . backupper . BackupWithResolvers ( backupLog , backup , backupFile , backupItemActionsResolver , pluginManager ) ; err != nil {
2019-04-26 16:14:26 +00:00
fatalErrs = append ( fatalErrs , err )
2018-10-12 17:55:02 +00:00
}
2020-03-12 21:01:14 +00:00
// Empty slices here so that they can be passed in to the persistBackup call later, regardless of whether or not CSI's enabled.
// This way, we only make the Lister call if the feature flag's on.
2022-09-23 07:44:39 +00:00
var volumeSnapshots [ ] snapshotv1api . VolumeSnapshot
var volumeSnapshotContents [ ] snapshotv1api . VolumeSnapshotContent
var volumeSnapshotClasses [ ] snapshotv1api . VolumeSnapshotClass
2023-06-08 09:05:17 +00:00
if boolptr . IsSetToTrue ( backup . Spec . SnapshotMoveData ) {
backupLog . Info ( "backup SnapshotMoveData is set to true, skip VolumeSnapshot resource persistence." )
} else if features . IsEnabled ( velerov1api . CSIFeatureFlag ) {
2020-05-07 18:56:13 +00:00
selector := label . NewSelectorForBackup ( backup . Name )
2022-09-23 07:44:39 +00:00
vscList := & snapshotv1api . VolumeSnapshotContentList { }
2022-04-20 15:51:21 +00:00
2023-05-25 12:55:45 +00:00
if b . volumeSnapshotLister != nil {
tmpVSs , err := b . volumeSnapshotLister . List ( label . NewSelectorForBackup ( backup . Name ) )
if err != nil {
backupLog . Error ( err )
}
for _ , vs := range tmpVSs {
volumeSnapshots = append ( volumeSnapshots , * vs )
}
2020-03-12 21:01:14 +00:00
}
2022-10-24 02:42:08 +00:00
2022-09-23 07:44:39 +00:00
backup . CSISnapshots = volumeSnapshots
2020-03-12 21:01:14 +00:00
2023-03-10 00:59:40 +00:00
err = b . kbClient . List ( context . Background ( ) , vscList , & kbclient . ListOptions { LabelSelector : selector } )
2022-09-23 07:44:39 +00:00
if err != nil {
backupLog . Error ( err )
2020-03-12 21:01:14 +00:00
}
2022-09-23 07:44:39 +00:00
if len ( vscList . Items ) >= 0 {
volumeSnapshotContents = vscList . Items
}
2022-03-31 10:09:35 +00:00
vsClassSet := sets . NewString ( )
2022-10-18 06:58:03 +00:00
for index := range volumeSnapshotContents {
2022-03-31 10:09:35 +00:00
// persist the volumesnapshotclasses referenced by vsc
2022-10-18 06:58:03 +00:00
if volumeSnapshotContents [ index ] . Spec . VolumeSnapshotClassName != nil && ! vsClassSet . Has ( * volumeSnapshotContents [ index ] . Spec . VolumeSnapshotClassName ) {
2022-09-23 07:44:39 +00:00
vsClass := & snapshotv1api . VolumeSnapshotClass { }
2023-03-10 00:59:40 +00:00
if err := b . kbClient . Get ( context . TODO ( ) , kbclient . ObjectKey { Name : * volumeSnapshotContents [ index ] . Spec . VolumeSnapshotClassName } , vsClass ) ; err != nil {
2022-03-31 10:09:35 +00:00
backupLog . Error ( err )
} else {
2022-10-18 06:58:03 +00:00
vsClassSet . Insert ( * volumeSnapshotContents [ index ] . Spec . VolumeSnapshotClassName )
2022-09-23 07:44:39 +00:00
volumeSnapshotClasses = append ( volumeSnapshotClasses , * vsClass )
2022-03-31 10:09:35 +00:00
}
}
2022-09-26 21:50:13 +00:00
}
2020-03-12 21:01:14 +00:00
}
2018-10-22 16:37:30 +00:00
backup . Status . VolumeSnapshotsAttempted = len ( backup . VolumeSnapshots )
for _ , snap := range backup . VolumeSnapshots {
if snap . Status . Phase == volume . SnapshotPhaseCompleted {
backup . Status . VolumeSnapshotsCompleted ++
}
}
2022-04-09 04:45:06 +00:00
backup . Status . CSIVolumeSnapshotsAttempted = len ( backup . CSISnapshots )
for _ , vs := range backup . CSISnapshots {
2022-05-02 21:23:06 +00:00
if vs . Status != nil && boolptr . IsSetToTrue ( vs . Status . ReadyToUse ) {
2022-04-09 04:45:06 +00:00
backup . Status . CSIVolumeSnapshotsCompleted ++
2022-04-08 16:57:28 +00:00
}
}
2023-01-24 17:59:53 +00:00
// Iterate over backup item operations and update progress.
// Any errors on operations at this point should be added to backup errors.
// If any operations are still not complete, then back will not be set to
// Completed yet.
inProgressOperations , _ , opsCompleted , opsFailed , errs := getBackupItemOperationProgress ( backup . Backup , pluginManager , * backup . GetItemOperationsList ( ) )
if len ( errs ) > 0 {
for err := range errs {
backupLog . Error ( err )
}
}
2023-03-08 14:37:07 +00:00
backup . Status . BackupItemOperationsAttempted = len ( * backup . GetItemOperationsList ( ) )
backup . Status . BackupItemOperationsCompleted = opsCompleted
backup . Status . BackupItemOperationsFailed = opsFailed
2023-01-24 17:59:53 +00:00
2021-10-28 17:19:05 +00:00
backup . Status . Warnings = logCounter . GetCount ( logrus . WarnLevel )
backup . Status . Errors = logCounter . GetCount ( logrus . ErrorLevel )
2023-02-16 04:27:53 +00:00
backupWarnings := logCounter . GetEntries ( logrus . WarnLevel )
backupErrors := logCounter . GetEntries ( logrus . ErrorLevel )
results := map [ string ] results . Result {
"warnings" : backupWarnings ,
"errors" : backupErrors ,
}
2023-03-10 00:59:40 +00:00
backupLog . DoneForPersist ( b . logger . WithField ( Backup , kubeutil . NamespaceAndName ( backup ) ) )
2019-04-26 16:14:26 +00:00
// Assign finalize phase as close to end as possible so that any errors
// logged to backupLog are captured. This is done before uploading the
// artifacts to object storage so that the JSON representation of the
// backup in object storage has the terminal phase set.
switch {
case len ( fatalErrs ) > 0 :
backup . Status . Phase = velerov1api . BackupPhaseFailed
case logCounter . GetCount ( logrus . ErrorLevel ) > 0 :
2023-01-24 17:59:53 +00:00
if inProgressOperations {
backup . Status . Phase = velerov1api . BackupPhaseWaitingForPluginOperationsPartiallyFailed
} else {
2023-03-08 14:37:07 +00:00
backup . Status . Phase = velerov1api . BackupPhaseFinalizingPartiallyFailed
2023-01-24 17:59:53 +00:00
}
2019-04-26 16:14:26 +00:00
default :
2023-01-24 17:59:53 +00:00
if inProgressOperations {
backup . Status . Phase = velerov1api . BackupPhaseWaitingForPluginOperations
} else {
2023-03-08 14:37:07 +00:00
backup . Status . Phase = velerov1api . BackupPhaseFinalizing
2023-01-24 17:59:53 +00:00
}
2019-04-26 16:14:26 +00:00
}
2023-01-24 17:59:53 +00:00
// Mark completion timestamp before serializing and uploading.
// Otherwise, the JSON file in object storage has a CompletionTimestamp of 'null'.
if backup . Status . Phase == velerov1api . BackupPhaseFailed ||
backup . Status . Phase == velerov1api . BackupPhasePartiallyFailed ||
backup . Status . Phase == velerov1api . BackupPhaseCompleted {
2023-03-10 00:59:40 +00:00
backup . Status . CompletionTimestamp = & metav1 . Time { Time : b . clock . Now ( ) }
2023-01-24 17:59:53 +00:00
}
2023-03-10 00:59:40 +00:00
recordBackupMetrics ( backupLog , backup . Backup , backupFile , b . metrics , false )
2019-04-26 16:14:26 +00:00
2020-05-27 23:03:52 +00:00
// re-instantiate the backup store because credentials could have changed since the original
// instantiation, if this was a long-running backup
backupLog . Info ( "Setting up backup store to persist the backup" )
2023-03-10 00:59:40 +00:00
backupStore , err = b . backupStoreGetter . Get ( backup . StorageLocation , pluginManager , backupLog )
2020-05-27 23:03:52 +00:00
if err != nil {
return err
}
2023-03-06 09:29:52 +00:00
if logFile , err := backupLog . GetPersistFile ( ) ; err != nil {
fatalErrs = append ( fatalErrs , errors . Wrap ( err , "error getting backup log file" ) )
} else {
if errs := persistBackup ( backup , backupFile , logFile , backupStore , volumeSnapshots , volumeSnapshotContents , volumeSnapshotClasses , results ) ; len ( errs ) > 0 {
fatalErrs = append ( fatalErrs , errs ... )
}
2019-04-26 16:14:26 +00:00
}
2018-10-12 17:55:02 +00:00
2023-03-10 00:59:40 +00:00
b . logger . WithField ( Backup , kubeutil . NamespaceAndName ( backup ) ) . Info ( "Backup completed" )
2018-10-12 17:55:02 +00:00
2019-04-26 16:14:26 +00:00
// if we return a non-nil error, the calling function will update
// the backup's phase to Failed.
return kerrors . NewAggregate ( fatalErrs )
2018-10-12 17:55:02 +00:00
}
2023-01-24 17:59:53 +00:00
func recordBackupMetrics ( log logrus . FieldLogger , backup * velerov1api . Backup , backupFile * os . File , serverMetrics * metrics . ServerMetrics , finalize bool ) {
2019-01-25 03:33:07 +00:00
backupScheduleName := backup . GetLabels ( ) [ velerov1api . ScheduleNameLabel ]
2017-08-02 17:27:17 +00:00
2023-01-24 17:59:53 +00:00
if backupFile != nil {
var backupSizeBytes int64
if backupFileStat , err := backupFile . Stat ( ) ; err != nil {
log . WithError ( errors . WithStack ( err ) ) . Error ( "Error getting backup file info" )
} else {
backupSizeBytes = backupFileStat . Size ( )
}
serverMetrics . SetBackupTarballSizeBytesGauge ( backupScheduleName , backupSizeBytes )
2018-06-06 21:35:06 +00:00
}
2022-04-08 16:57:28 +00:00
2023-01-24 17:59:53 +00:00
if backup . Status . CompletionTimestamp != nil {
backupDuration := backup . Status . CompletionTimestamp . Time . Sub ( backup . Status . StartTimestamp . Time )
backupDurationSeconds := float64 ( backupDuration / time . Second )
serverMetrics . RegisterBackupDuration ( backupScheduleName , backupDurationSeconds )
2022-04-08 16:57:28 +00:00
}
2023-01-24 17:59:53 +00:00
if ! finalize {
serverMetrics . RegisterVolumeSnapshotAttempts ( backupScheduleName , backup . Status . VolumeSnapshotsAttempted )
serverMetrics . RegisterVolumeSnapshotSuccesses ( backupScheduleName , backup . Status . VolumeSnapshotsCompleted )
serverMetrics . RegisterVolumeSnapshotFailures ( backupScheduleName , backup . Status . VolumeSnapshotsAttempted - backup . Status . VolumeSnapshotsCompleted )
2022-04-08 16:57:28 +00:00
2023-01-24 17:59:53 +00:00
if features . IsEnabled ( velerov1api . CSIFeatureFlag ) {
serverMetrics . RegisterCSISnapshotAttempts ( backupScheduleName , backup . Name , backup . Status . CSIVolumeSnapshotsAttempted )
serverMetrics . RegisterCSISnapshotSuccesses ( backupScheduleName , backup . Name , backup . Status . CSIVolumeSnapshotsCompleted )
serverMetrics . RegisterCSISnapshotFailures ( backupScheduleName , backup . Name , backup . Status . CSIVolumeSnapshotsAttempted - backup . Status . CSIVolumeSnapshotsCompleted )
}
if backup . Status . Progress != nil {
serverMetrics . RegisterBackupItemsTotalGauge ( backupScheduleName , backup . Status . Progress . TotalItems )
}
serverMetrics . RegisterBackupItemsErrorsGauge ( backupScheduleName , backup . Status . Errors )
2023-01-18 06:55:50 +00:00
2023-01-24 17:59:53 +00:00
if backup . Status . Warnings > 0 {
serverMetrics . RegisterBackupWarning ( backupScheduleName )
}
2023-01-18 06:55:50 +00:00
}
2018-10-12 17:55:02 +00:00
}
2020-03-12 21:01:14 +00:00
func persistBackup ( backup * pkgbackup . Request ,
backupContents , backupLog * os . File ,
backupStore persistence . BackupStore ,
2022-09-23 07:44:39 +00:00
csiVolumeSnapshots [ ] snapshotv1api . VolumeSnapshot ,
csiVolumeSnapshotContents [ ] snapshotv1api . VolumeSnapshotContent ,
csiVolumesnapshotClasses [ ] snapshotv1api . VolumeSnapshotClass ,
2023-02-16 04:27:53 +00:00
results map [ string ] results . Result ,
2020-03-12 21:01:14 +00:00
) [ ] error {
2020-04-17 22:26:41 +00:00
persistErrs := [ ] error { }
2018-10-12 17:55:02 +00:00
backupJSON := new ( bytes . Buffer )
2023-04-24 07:29:20 +00:00
if err := encode . To ( backup . Backup , "json" , backupJSON ) ; err != nil {
2020-04-20 16:33:21 +00:00
persistErrs = append ( persistErrs , errors . Wrap ( err , "error encoding backup" ) )
2017-08-02 17:27:17 +00:00
}
2020-04-13 17:38:11 +00:00
// Velero-native volume snapshots (as opposed to CSI ones)
2023-04-24 07:29:20 +00:00
nativeVolumeSnapshots , errs := encode . ToJSONGzip ( backup . VolumeSnapshots , "native volumesnapshots list" )
2020-04-17 22:26:41 +00:00
if errs != nil {
persistErrs = append ( persistErrs , errs ... )
2018-10-15 20:22:00 +00:00
}
2018-06-20 18:08:07 +00:00
2023-01-24 17:59:53 +00:00
var backupItemOperations * bytes . Buffer
2023-04-24 07:29:20 +00:00
backupItemOperations , errs = encode . ToJSONGzip ( backup . GetItemOperationsList ( ) , "backup item operations list" )
2023-01-24 17:59:53 +00:00
if errs != nil {
persistErrs = append ( persistErrs , errs ... )
}
2023-04-24 07:29:20 +00:00
podVolumeBackups , errs := encode . ToJSONGzip ( backup . PodVolumeBackups , "pod volume backups list" )
2020-04-17 22:26:41 +00:00
if errs != nil {
persistErrs = append ( persistErrs , errs ... )
2019-07-24 19:51:20 +00:00
}
2023-04-24 07:29:20 +00:00
csiSnapshotJSON , errs := encode . ToJSONGzip ( csiVolumeSnapshots , "csi volume snapshots list" )
2020-04-17 22:26:41 +00:00
if errs != nil {
persistErrs = append ( persistErrs , errs ... )
2020-03-12 21:01:14 +00:00
}
2023-04-24 07:29:20 +00:00
csiSnapshotContentsJSON , errs := encode . ToJSONGzip ( csiVolumeSnapshotContents , "csi volume snapshot contents list" )
2020-04-17 22:26:41 +00:00
if errs != nil {
persistErrs = append ( persistErrs , errs ... )
2020-03-12 21:01:14 +00:00
}
2023-04-24 07:29:20 +00:00
csiSnapshotClassesJSON , errs := encode . ToJSONGzip ( csiVolumesnapshotClasses , "csi volume snapshot classes list" )
2022-03-31 10:09:35 +00:00
if errs != nil {
persistErrs = append ( persistErrs , errs ... )
}
2020-03-12 21:01:14 +00:00
2023-04-24 07:29:20 +00:00
backupResourceList , errs := encode . ToJSONGzip ( backup . BackupResourceList ( ) , "backup resources list" )
2020-04-17 22:26:41 +00:00
if errs != nil {
persistErrs = append ( persistErrs , errs ... )
2019-08-05 17:15:55 +00:00
}
2023-04-24 07:29:20 +00:00
backupResult , errs := encode . ToJSONGzip ( results , "backup results" )
2023-02-16 04:27:53 +00:00
if errs != nil {
persistErrs = append ( persistErrs , errs ... )
}
2020-04-17 22:26:41 +00:00
if len ( persistErrs ) > 0 {
2018-10-12 17:55:02 +00:00
// Don't upload the JSON files or backup tarball if encoding to json fails.
backupJSON = nil
backupContents = nil
2020-03-12 21:01:14 +00:00
nativeVolumeSnapshots = nil
2023-01-24 17:59:53 +00:00
backupItemOperations = nil
2019-08-05 17:15:55 +00:00
backupResourceList = nil
2020-03-12 21:01:14 +00:00
csiSnapshotJSON = nil
2020-04-17 20:05:29 +00:00
csiSnapshotContentsJSON = nil
2022-03-31 10:09:35 +00:00
csiSnapshotClassesJSON = nil
2023-02-16 04:27:53 +00:00
backupResult = nil
2018-10-12 17:55:02 +00:00
}
2017-12-19 17:16:39 +00:00
2019-07-24 19:51:20 +00:00
backupInfo := persistence . BackupInfo {
2020-04-17 20:05:29 +00:00
Name : backup . Name ,
Metadata : backupJSON ,
Contents : backupContents ,
Log : backupLog ,
2023-02-16 04:27:53 +00:00
BackupResults : backupResult ,
2020-04-17 20:05:29 +00:00
PodVolumeBackups : podVolumeBackups ,
VolumeSnapshots : nativeVolumeSnapshots ,
2023-01-24 17:59:53 +00:00
BackupItemOperations : backupItemOperations ,
2020-04-17 20:05:29 +00:00
BackupResourceList : backupResourceList ,
CSIVolumeSnapshots : csiSnapshotJSON ,
CSIVolumeSnapshotContents : csiSnapshotContentsJSON ,
2022-03-31 10:09:35 +00:00
CSIVolumeSnapshotClasses : csiSnapshotClassesJSON ,
2019-07-24 19:51:20 +00:00
}
if err := backupStore . PutBackup ( backupInfo ) ; err != nil {
2020-04-17 22:26:41 +00:00
persistErrs = append ( persistErrs , err )
2018-10-12 17:55:02 +00:00
}
2020-04-17 22:26:41 +00:00
return persistErrs
2017-12-19 17:16:39 +00:00
}
func closeAndRemoveFile ( file * os . File , log logrus . FieldLogger ) {
2020-11-30 18:58:34 +00:00
if file == nil {
log . Debug ( "Skipping removal of file due to nil file pointer" )
return
}
2017-12-19 17:16:39 +00:00
if err := file . Close ( ) ; err != nil {
log . WithError ( err ) . WithField ( "file" , file . Name ( ) ) . Error ( "error closing file" )
2017-08-11 15:05:56 +00:00
}
2017-12-19 17:16:39 +00:00
if err := os . Remove ( file . Name ( ) ) ; err != nil {
log . WithError ( err ) . WithField ( "file" , file . Name ( ) ) . Error ( "error removing file" )
2017-08-02 17:27:17 +00:00
}
}
2020-04-13 17:38:11 +00:00
2023-02-02 09:31:35 +00:00
func oldAndNewFilterParametersUsedTogether ( backupSpec velerov1api . BackupSpec ) bool {
haveOldResourceFilterParameters := len ( backupSpec . IncludedResources ) > 0 ||
( len ( backupSpec . ExcludedResources ) > 0 ) ||
( backupSpec . IncludeClusterResources != nil )
2023-04-06 02:59:28 +00:00
haveNewResourceFilterParameters := len ( backupSpec . IncludedClusterScopedResources ) > 0 ||
( len ( backupSpec . ExcludedClusterScopedResources ) > 0 ) ||
( len ( backupSpec . IncludedNamespaceScopedResources ) > 0 ) ||
( len ( backupSpec . ExcludedNamespaceScopedResources ) > 0 )
2023-02-02 09:31:35 +00:00
return haveOldResourceFilterParameters && haveNewResourceFilterParameters
}