2017-08-02 17:27:17 +00:00
/ *
2022-02-03 01:02:22 +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 backup
import (
"archive/tar"
"compress/gzip"
2018-02-28 01:35:35 +00:00
"context"
2020-04-13 20:53:58 +00:00
"encoding/json"
2017-08-24 23:44:01 +00:00
"fmt"
2017-08-02 17:27:17 +00:00
"io"
2020-04-13 20:53:58 +00:00
"io/ioutil"
"os"
2018-12-05 22:56:53 +00:00
"path/filepath"
2018-02-28 01:35:35 +00:00
"time"
2017-08-02 17:27:17 +00:00
2017-09-14 21:27:31 +00:00
"github.com/pkg/errors"
2017-10-05 23:36:04 +00:00
"github.com/sirupsen/logrus"
2020-04-13 20:53:58 +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"
2020-04-13 20:53:58 +00:00
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2017-08-02 17:27:17 +00:00
"k8s.io/apimachinery/pkg/runtime/schema"
2020-04-22 18:14:09 +00:00
"k8s.io/apimachinery/pkg/types"
2020-04-13 20:53:58 +00:00
kubeerrs "k8s.io/apimachinery/pkg/util/errors"
2017-08-02 17:27:17 +00:00
2020-07-22 21:26:14 +00:00
"github.com/vmware-tanzu/velero/internal/hook"
2020-04-13 20:53:58 +00:00
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
2019-09-30 21:26:56 +00:00
"github.com/vmware-tanzu/velero/pkg/client"
"github.com/vmware-tanzu/velero/pkg/discovery"
2020-04-22 18:14:09 +00:00
velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1"
2020-04-13 20:53:58 +00:00
"github.com/vmware-tanzu/velero/pkg/kuberesource"
2021-12-10 17:53:47 +00:00
"github.com/vmware-tanzu/velero/pkg/plugin/framework"
2019-09-30 21:26:56 +00:00
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
"github.com/vmware-tanzu/velero/pkg/podexec"
"github.com/vmware-tanzu/velero/pkg/restic"
2022-02-10 00:58:07 +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"
2017-08-02 17:27:17 +00:00
)
2020-05-01 19:54:57 +00:00
// BackupVersion is the current backup major version for Velero.
// Deprecated, use BackupFormatVersion
2018-12-05 22:56:53 +00:00
const BackupVersion = 1
2020-05-01 19:54:57 +00:00
// BackupFormatVersion is the current backup version for Velero, including major, minor, and patch.
const BackupFormatVersion = "1.1.0"
2017-08-02 17:27:17 +00:00
// Backupper performs backups.
type Backupper interface {
2020-04-13 20:53:58 +00:00
// Backup takes a backup using the specification in the velerov1api.Backup and writes backup and log data
2017-09-11 17:59:04 +00:00
// to the given writers.
2019-03-27 18:22:04 +00:00
Backup ( logger logrus . FieldLogger , backup * Request , backupFile io . Writer , actions [ ] velero . BackupItemAction , volumeSnapshotterGetter VolumeSnapshotterGetter ) error
2021-12-10 17:53:47 +00:00
BackupWithResolvers ( log logrus . FieldLogger , backupRequest * Request , backupFile io . Writer ,
backupItemActionResolver framework . BackupItemActionResolver , itemSnapshotterResolver framework . ItemSnapshotterResolver ,
volumeSnapshotterGetter VolumeSnapshotterGetter ) error
2017-08-02 17:27:17 +00:00
}
// kubernetesBackupper implements Backupper.
type kubernetesBackupper struct {
2020-04-22 18:14:09 +00:00
backupClient velerov1client . BackupsGetter
2018-02-28 01:35:35 +00:00
dynamicFactory client . DynamicFactory
discoveryHelper discovery . Helper
podCommandExecutor podexec . PodCommandExecutor
resticBackupperFactory restic . BackupperFactory
resticTimeout time . Duration
2020-06-15 22:26:44 +00:00
defaultVolumesToRestic bool
2021-08-18 01:49:41 +00:00
clientPageSize int
2017-08-24 23:44:01 +00:00
}
func ( i * itemKey ) String ( ) string {
return fmt . Sprintf ( "resource=%s,namespace=%s,name=%s" , i . resource , i . namespace , i . name )
2017-08-02 17:27:17 +00:00
}
2018-05-11 23:29:15 +00:00
func cohabitatingResources ( ) map [ string ] * cohabitatingResource {
return map [ string ] * cohabitatingResource {
"deployments" : newCohabitatingResource ( "deployments" , "extensions" , "apps" ) ,
"daemonsets" : newCohabitatingResource ( "daemonsets" , "extensions" , "apps" ) ,
"replicasets" : newCohabitatingResource ( "replicasets" , "extensions" , "apps" ) ,
"networkpolicies" : newCohabitatingResource ( "networkpolicies" , "extensions" , "networking.k8s.io" ) ,
"events" : newCohabitatingResource ( "events" , "" , "events.k8s.io" ) ,
}
2018-05-10 21:12:24 +00:00
}
2017-08-02 17:27:17 +00:00
// NewKubernetesBackupper creates a new kubernetesBackupper.
func NewKubernetesBackupper (
2020-04-22 18:14:09 +00:00
backupClient velerov1client . BackupsGetter ,
2017-08-02 17:27:17 +00:00
discoveryHelper discovery . Helper ,
dynamicFactory client . DynamicFactory ,
2018-02-28 01:35:35 +00:00
podCommandExecutor podexec . PodCommandExecutor ,
resticBackupperFactory restic . BackupperFactory ,
resticTimeout time . Duration ,
2020-06-15 22:26:44 +00:00
defaultVolumesToRestic bool ,
2021-08-18 01:49:41 +00:00
clientPageSize int ,
2017-08-02 17:27:17 +00:00
) ( Backupper , error ) {
return & kubernetesBackupper {
2020-04-22 18:14:09 +00:00
backupClient : backupClient ,
2018-02-28 01:35:35 +00:00
discoveryHelper : discoveryHelper ,
dynamicFactory : dynamicFactory ,
podCommandExecutor : podCommandExecutor ,
resticBackupperFactory : resticBackupperFactory ,
resticTimeout : resticTimeout ,
2020-06-15 22:26:44 +00:00
defaultVolumesToRestic : defaultVolumesToRestic ,
2021-08-18 01:49:41 +00:00
clientPageSize : clientPageSize ,
2017-08-02 17:27:17 +00:00
} , nil
}
// getNamespaceIncludesExcludes returns an IncludesExcludes list containing which namespaces to
// include and exclude from the backup.
2020-04-13 20:53:58 +00:00
func getNamespaceIncludesExcludes ( backup * velerov1api . Backup ) * collections . IncludesExcludes {
2017-08-02 17:27:17 +00:00
return collections . NewIncludesExcludes ( ) . Includes ( backup . Spec . IncludedNamespaces ... ) . Excludes ( backup . Spec . ExcludedNamespaces ... )
}
2020-07-22 21:26:14 +00:00
func getResourceHooks ( hookSpecs [ ] velerov1api . BackupResourceHookSpec , discoveryHelper discovery . Helper ) ( [ ] hook . ResourceHook , error ) {
resourceHooks := make ( [ ] hook . ResourceHook , 0 , len ( hookSpecs ) )
2017-10-02 20:53:08 +00:00
2018-01-04 15:27:12 +00:00
for _ , s := range hookSpecs {
h , err := getResourceHook ( s , discoveryHelper )
if err != nil {
2020-07-22 21:26:14 +00:00
return [ ] hook . ResourceHook { } , err
2017-10-02 20:53:08 +00:00
}
resourceHooks = append ( resourceHooks , h )
}
2017-08-02 17:27:17 +00:00
2017-10-02 20:53:08 +00:00
return resourceHooks , nil
2017-08-11 15:05:56 +00:00
}
2020-07-22 21:26:14 +00:00
func getResourceHook ( hookSpec velerov1api . BackupResourceHookSpec , discoveryHelper discovery . Helper ) ( hook . ResourceHook , error ) {
h := hook . ResourceHook {
2020-08-11 17:38:44 +00:00
Name : hookSpec . Name ,
Selector : hook . ResourceHookSelector {
Namespaces : collections . NewIncludesExcludes ( ) . Includes ( hookSpec . IncludedNamespaces ... ) . Excludes ( hookSpec . ExcludedNamespaces ... ) ,
2020-11-11 14:50:57 +00:00
Resources : collections . GetResourceIncludesExcludes ( discoveryHelper , hookSpec . IncludedResources , hookSpec . ExcludedResources ) ,
2020-08-11 17:38:44 +00:00
} ,
Pre : hookSpec . PreHooks ,
Post : hookSpec . PostHooks ,
2018-01-04 15:27:12 +00:00
}
if hookSpec . LabelSelector != nil {
labelSelector , err := metav1 . LabelSelectorAsSelector ( hookSpec . LabelSelector )
if err != nil {
2020-07-22 21:26:14 +00:00
return hook . ResourceHook { } , errors . WithStack ( err )
2018-01-04 15:27:12 +00:00
}
2020-08-11 17:38:44 +00:00
h . Selector . LabelSelector = labelSelector
2018-01-04 15:27:12 +00:00
}
return h , nil
}
2019-03-27 18:22:04 +00:00
type VolumeSnapshotterGetter interface {
GetVolumeSnapshotter ( name string ) ( velero . VolumeSnapshotter , error )
2018-09-26 22:18:45 +00:00
}
2017-08-02 17:27:17 +00:00
// Backup backs up the items specified in the Backup, placing them in a gzip-compressed tar file
2020-04-13 20:53:58 +00:00
// written to backupFile. The finalized velerov1api.Backup is written to metadata. Any error that represents
2019-04-26 16:14:26 +00:00
// a complete backup failure is returned. Errors that constitute partial failures (i.e. failures to
// back up individual resources that don't prevent the backup from continuing to be processed) are logged
// to the backup log.
2021-12-10 17:53:47 +00:00
func ( kb * kubernetesBackupper ) Backup ( log logrus . FieldLogger , backupRequest * Request , backupFile io . Writer ,
actions [ ] velero . BackupItemAction , volumeSnapshotterGetter VolumeSnapshotterGetter ) error {
backupItemActions := framework . NewBackupItemActionResolver ( actions )
itemSnapshotters := framework . NewItemSnapshotterResolver ( nil )
return kb . BackupWithResolvers ( log , backupRequest , backupFile , backupItemActions , itemSnapshotters ,
volumeSnapshotterGetter )
}
func ( kb * kubernetesBackupper ) BackupWithResolvers ( log logrus . FieldLogger ,
backupRequest * Request ,
backupFile io . Writer ,
backupItemActionResolver framework . BackupItemActionResolver ,
itemSnapshotterResolver framework . ItemSnapshotterResolver ,
volumeSnapshotterGetter VolumeSnapshotterGetter ) error {
2017-09-11 17:59:04 +00:00
gzippedData := gzip . NewWriter ( backupFile )
2017-08-11 15:05:56 +00:00
defer gzippedData . Close ( )
2017-08-02 17:27:17 +00:00
2017-08-11 15:05:56 +00:00
tw := tar . NewWriter ( gzippedData )
2017-08-02 17:27:17 +00:00
defer tw . Close ( )
2019-04-26 16:14:26 +00:00
log . Info ( "Writing backup version file" )
2018-12-05 22:56:53 +00:00
if err := kb . writeBackupVersion ( tw ) ; err != nil {
return errors . WithStack ( err )
}
2018-09-26 22:18:45 +00:00
backupRequest . NamespaceIncludesExcludes = getNamespaceIncludesExcludes ( backupRequest . Backup )
log . Infof ( "Including namespaces: %s" , backupRequest . NamespaceIncludesExcludes . IncludesString ( ) )
log . Infof ( "Excluding namespaces: %s" , backupRequest . NamespaceIncludesExcludes . ExcludesString ( ) )
2017-10-02 20:53:08 +00:00
2020-11-11 14:50:57 +00:00
backupRequest . ResourceIncludesExcludes = collections . GetResourceIncludesExcludes ( kb . discoveryHelper , backupRequest . Spec . IncludedResources , backupRequest . Spec . ExcludedResources )
2018-09-26 22:18:45 +00:00
log . Infof ( "Including resources: %s" , backupRequest . ResourceIncludesExcludes . IncludesString ( ) )
log . Infof ( "Excluding resources: %s" , backupRequest . ResourceIncludesExcludes . ExcludesString ( ) )
2022-02-10 00:58:07 +00:00
log . Infof ( "Backing up all pod volumes using Restic: %t" , boolptr . IsSetToTrue ( backupRequest . Backup . Spec . DefaultVolumesToRestic ) )
2020-06-05 22:34:02 +00:00
2018-09-26 22:18:45 +00:00
var err error
backupRequest . ResourceHooks , err = getResourceHooks ( backupRequest . Spec . Hooks . Resources , kb . discoveryHelper )
2017-10-02 20:53:08 +00:00
if err != nil {
return err
}
2021-12-10 17:53:47 +00:00
backupRequest . ResolvedActions , err = backupItemActionResolver . ResolveActions ( kb . discoveryHelper )
if err != nil {
return err
}
backupRequest . ResolvedItemSnapshotters , err = itemSnapshotterResolver . ResolveActions ( kb . discoveryHelper )
2017-11-15 02:35:02 +00:00
if err != nil {
return err
}
2019-08-05 17:15:55 +00:00
backupRequest . BackedUpItems = map [ itemKey ] struct { } { }
2018-02-28 01:35:35 +00:00
podVolumeTimeout := kb . resticTimeout
2020-04-13 20:53:58 +00:00
if val := backupRequest . Annotations [ velerov1api . PodVolumeOperationTimeoutAnnotation ] ; val != "" {
2018-02-28 01:35:35 +00:00
parsed , err := time . ParseDuration ( val )
if err != nil {
log . WithError ( errors . WithStack ( err ) ) . Errorf ( "Unable to parse pod volume timeout annotation %s, using server value." , val )
} else {
podVolumeTimeout = parsed
}
}
ctx , cancelFunc := context . WithTimeout ( context . Background ( ) , podVolumeTimeout )
defer cancelFunc ( )
var resticBackupper restic . Backupper
if kb . resticBackupperFactory != nil {
2018-09-26 22:18:45 +00:00
resticBackupper , err = kb . resticBackupperFactory . NewBackupper ( ctx , backupRequest . Backup )
2018-02-28 01:35:35 +00:00
if err != nil {
return errors . WithStack ( err )
}
}
2020-04-13 20:53:58 +00:00
// set up a temp dir for the itemCollector to use to temporarily
// store items as they're scraped from the API.
tempDir , err := ioutil . TempDir ( "" , "" )
if err != nil {
return errors . Wrap ( err , "error creating temp dir for backup" )
2017-08-02 17:27:17 +00:00
}
2020-04-13 20:53:58 +00:00
defer os . RemoveAll ( tempDir )
2017-08-02 17:27:17 +00:00
2020-04-13 20:53:58 +00:00
collector := & itemCollector {
2020-04-13 19:29:34 +00:00
log : log ,
backupRequest : backupRequest ,
discoveryHelper : kb . discoveryHelper ,
dynamicFactory : kb . dynamicFactory ,
cohabitatingResources : cohabitatingResources ( ) ,
2020-04-13 20:53:58 +00:00
dir : tempDir ,
2021-08-18 01:49:41 +00:00
pageSize : kb . clientPageSize ,
2020-04-13 20:53:58 +00:00
}
items := collector . getAllItems ( )
log . WithField ( "progress" , "" ) . Infof ( "Collected %d items matching the backup spec from the Kubernetes API (actual number of items backed up may be more or less depending on velero.io/exclude-from-backup annotation, plugins returning additional related items to back up, etc.)" , len ( items ) )
2020-05-21 21:40:59 +00:00
backupRequest . Status . Progress = & velerov1api . BackupProgress { TotalItems : len ( items ) }
2020-04-22 18:14:09 +00:00
patch := fmt . Sprintf ( ` { "status": { "progress": { "totalItems":%d}}} ` , len ( items ) )
k8s 1.18 import (#2651)
* k8s 1.18 import wip
backup, cmd, controller, generated, restic, restore, serverstatusrequest, test and util
Signed-off-by: Andrew Lavery <laverya@umich.edu>
* go mod tidy
Signed-off-by: Andrew Lavery <laverya@umich.edu>
* add changelog file
Signed-off-by: Andrew Lavery <laverya@umich.edu>
* go fmt
Signed-off-by: Andrew Lavery <laverya@umich.edu>
* update code-generator and controller-gen in CI
Signed-off-by: Andrew Lavery <laverya@umich.edu>
* checkout proper code-generator version, regen
Signed-off-by: Andrew Lavery <laverya@umich.edu>
* fix remaining calls
Signed-off-by: Andrew Lavery <laverya@umich.edu>
* regenerate CRDs with ./hack/update-generated-crd-code.sh
Signed-off-by: Andrew Lavery <laverya@umich.edu>
* use existing context in restic and server
Signed-off-by: Andrew Lavery <laverya@umich.edu>
* fix test cases by resetting resource version
also use main library go context, not golang.org/x/net/context, in pkg/restore/restore.go
Signed-off-by: Andrew Lavery <laverya@umich.edu>
* clarify changelog message
Signed-off-by: Andrew Lavery <laverya@umich.edu>
* use github.com/kubernetes-csi/external-snapshotter/v2@v2.2.0-rc1
Signed-off-by: Andrew Lavery <laverya@umich.edu>
* run 'go mod tidy' to remove old external-snapshotter version
Signed-off-by: Andrew Lavery <laverya@umich.edu>
2020-07-16 16:21:37 +00:00
if _ , err := kb . backupClient . Backups ( backupRequest . Namespace ) . Patch ( context . TODO ( ) , backupRequest . Name , types . MergePatchType , [ ] byte ( patch ) , metav1 . PatchOptions { } ) ; err != nil {
2020-04-22 18:14:09 +00:00
log . WithError ( errors . WithStack ( ( err ) ) ) . Warn ( "Got error trying to update backup's status.progress.totalItems" )
}
2020-04-13 20:53:58 +00:00
itemBackupper := & itemBackupper {
backupRequest : backupRequest ,
tarWriter : tw ,
dynamicFactory : kb . dynamicFactory ,
discoveryHelper : kb . discoveryHelper ,
resticBackupper : resticBackupper ,
resticSnapshotTracker : newPVCSnapshotTracker ( ) ,
volumeSnapshotterGetter : volumeSnapshotterGetter ,
2020-07-22 21:26:14 +00:00
itemHookHandler : & hook . DefaultItemHookHandler {
PodCommandExecutor : kb . podCommandExecutor ,
2020-04-13 20:53:58 +00:00
} ,
}
2020-04-22 18:14:09 +00:00
// helper struct to send current progress between the main
// backup loop and the gouroutine that periodically patches
// the backup CR with progress updates
type progressUpdate struct {
totalItems , itemsBackedUp int
}
// the main backup process will send on this channel once
// for every item it processes.
update := make ( chan progressUpdate )
// the main backup process will send on this channel when
// it's done sending progress updates
quit := make ( chan struct { } )
// This is the progress updater goroutine that receives
// progress updates on the 'update' channel. It patches
// the backup CR with progress updates at most every second,
// but it will not issue a patch if it hasn't received a new
// update since the previous patch. This goroutine exits
// when it receives on the 'quit' channel.
go func ( ) {
ticker := time . NewTicker ( 1 * time . Second )
var lastUpdate * progressUpdate
for {
select {
case <- quit :
ticker . Stop ( )
return
case val := <- update :
lastUpdate = & val
case <- ticker . C :
if lastUpdate != nil {
2020-05-21 21:40:59 +00:00
backupRequest . Status . Progress . TotalItems = lastUpdate . totalItems
backupRequest . Status . Progress . ItemsBackedUp = lastUpdate . itemsBackedUp
2020-04-22 18:14:09 +00:00
patch := fmt . Sprintf ( ` { "status": { "progress": { "totalItems":%d,"itemsBackedUp":%d}}} ` , lastUpdate . totalItems , lastUpdate . itemsBackedUp )
k8s 1.18 import (#2651)
* k8s 1.18 import wip
backup, cmd, controller, generated, restic, restore, serverstatusrequest, test and util
Signed-off-by: Andrew Lavery <laverya@umich.edu>
* go mod tidy
Signed-off-by: Andrew Lavery <laverya@umich.edu>
* add changelog file
Signed-off-by: Andrew Lavery <laverya@umich.edu>
* go fmt
Signed-off-by: Andrew Lavery <laverya@umich.edu>
* update code-generator and controller-gen in CI
Signed-off-by: Andrew Lavery <laverya@umich.edu>
* checkout proper code-generator version, regen
Signed-off-by: Andrew Lavery <laverya@umich.edu>
* fix remaining calls
Signed-off-by: Andrew Lavery <laverya@umich.edu>
* regenerate CRDs with ./hack/update-generated-crd-code.sh
Signed-off-by: Andrew Lavery <laverya@umich.edu>
* use existing context in restic and server
Signed-off-by: Andrew Lavery <laverya@umich.edu>
* fix test cases by resetting resource version
also use main library go context, not golang.org/x/net/context, in pkg/restore/restore.go
Signed-off-by: Andrew Lavery <laverya@umich.edu>
* clarify changelog message
Signed-off-by: Andrew Lavery <laverya@umich.edu>
* use github.com/kubernetes-csi/external-snapshotter/v2@v2.2.0-rc1
Signed-off-by: Andrew Lavery <laverya@umich.edu>
* run 'go mod tidy' to remove old external-snapshotter version
Signed-off-by: Andrew Lavery <laverya@umich.edu>
2020-07-16 16:21:37 +00:00
if _ , err := kb . backupClient . Backups ( backupRequest . Namespace ) . Patch ( context . TODO ( ) , backupRequest . Name , types . MergePatchType , [ ] byte ( patch ) , metav1 . PatchOptions { } ) ; err != nil {
2020-04-22 18:14:09 +00:00
log . WithError ( errors . WithStack ( ( err ) ) ) . Warn ( "Got error trying to update backup's status.progress" )
}
lastUpdate = nil
}
}
}
} ( )
2020-04-13 20:53:58 +00:00
backedUpGroupResources := map [ schema . GroupResource ] bool { }
2020-04-22 18:14:09 +00:00
totalItems := len ( items )
2020-04-13 20:53:58 +00:00
for i , item := range items {
log . WithFields ( map [ string ] interface { } {
"progress" : "" ,
"resource" : item . groupResource . String ( ) ,
"namespace" : item . namespace ,
"name" : item . name ,
2020-04-22 18:14:09 +00:00
} ) . Infof ( "Processing item" )
2020-04-13 20:53:58 +00:00
// use an anonymous func so we can defer-close/remove the file
// as soon as we're done with it
func ( ) {
var unstructured unstructured . Unstructured
f , err := os . Open ( item . path )
if err != nil {
log . WithError ( errors . WithStack ( err ) ) . Error ( "Error opening file containing item" )
return
}
defer f . Close ( )
defer os . Remove ( f . Name ( ) )
if err := json . NewDecoder ( f ) . Decode ( & unstructured ) ; err != nil {
log . WithError ( errors . WithStack ( err ) ) . Error ( "Error decoding JSON from file" )
return
}
if backedUp := kb . backupItem ( log , item . groupResource , itemBackupper , & unstructured , item . preferredGVR ) ; backedUp {
backedUpGroupResources [ item . groupResource ] = true
}
} ( )
2020-04-22 18:14:09 +00:00
// updated total is computed as "how many items we've backed up so far, plus
// how many items we know of that are remaining"
totalItems = len ( backupRequest . BackedUpItems ) + ( len ( items ) - ( i + 1 ) )
// send a progress update
update <- progressUpdate {
totalItems : totalItems ,
itemsBackedUp : len ( backupRequest . BackedUpItems ) ,
}
log . WithFields ( map [ string ] interface { } {
"progress" : "" ,
"resource" : item . groupResource . String ( ) ,
"namespace" : item . namespace ,
"name" : item . name ,
} ) . Infof ( "Backed up %d items out of an estimated total of %d (estimate will change throughout the backup)" , len ( backupRequest . BackedUpItems ) , totalItems )
2020-04-13 20:53:58 +00:00
}
2020-04-22 18:14:09 +00:00
// no more progress updates will be sent on the 'update' channel
quit <- struct { } { }
2020-04-13 20:53:58 +00:00
// back up CRD for resource if found. We should only need to do this if we've backed up at least
// one item for the resource and IncludeClusterResources is nil. If IncludeClusterResources is false
// we don't want to back it up, and if it's true it will already be included.
if backupRequest . Spec . IncludeClusterResources == nil {
for gr := range backedUpGroupResources {
kb . backupCRD ( log , gr , itemBackupper )
}
2020-04-13 19:29:34 +00:00
}
2020-04-22 18:14:09 +00:00
// do a final update on progress since we may have just added some CRDs and may not have updated
// for the last few processed items.
2020-05-21 21:40:59 +00:00
backupRequest . Status . Progress . TotalItems = len ( backupRequest . BackedUpItems )
backupRequest . Status . Progress . ItemsBackedUp = len ( backupRequest . BackedUpItems )
2020-04-22 18:14:09 +00:00
patch = fmt . Sprintf ( ` { "status": { "progress": { "totalItems":%d,"itemsBackedUp":%d}}} ` , len ( backupRequest . BackedUpItems ) , len ( backupRequest . BackedUpItems ) )
k8s 1.18 import (#2651)
* k8s 1.18 import wip
backup, cmd, controller, generated, restic, restore, serverstatusrequest, test and util
Signed-off-by: Andrew Lavery <laverya@umich.edu>
* go mod tidy
Signed-off-by: Andrew Lavery <laverya@umich.edu>
* add changelog file
Signed-off-by: Andrew Lavery <laverya@umich.edu>
* go fmt
Signed-off-by: Andrew Lavery <laverya@umich.edu>
* update code-generator and controller-gen in CI
Signed-off-by: Andrew Lavery <laverya@umich.edu>
* checkout proper code-generator version, regen
Signed-off-by: Andrew Lavery <laverya@umich.edu>
* fix remaining calls
Signed-off-by: Andrew Lavery <laverya@umich.edu>
* regenerate CRDs with ./hack/update-generated-crd-code.sh
Signed-off-by: Andrew Lavery <laverya@umich.edu>
* use existing context in restic and server
Signed-off-by: Andrew Lavery <laverya@umich.edu>
* fix test cases by resetting resource version
also use main library go context, not golang.org/x/net/context, in pkg/restore/restore.go
Signed-off-by: Andrew Lavery <laverya@umich.edu>
* clarify changelog message
Signed-off-by: Andrew Lavery <laverya@umich.edu>
* use github.com/kubernetes-csi/external-snapshotter/v2@v2.2.0-rc1
Signed-off-by: Andrew Lavery <laverya@umich.edu>
* run 'go mod tidy' to remove old external-snapshotter version
Signed-off-by: Andrew Lavery <laverya@umich.edu>
2020-07-16 16:21:37 +00:00
if _ , err := kb . backupClient . Backups ( backupRequest . Namespace ) . Patch ( context . TODO ( ) , backupRequest . Name , types . MergePatchType , [ ] byte ( patch ) , metav1 . PatchOptions { } ) ; err != nil {
2020-04-22 18:14:09 +00:00
log . WithError ( errors . WithStack ( ( err ) ) ) . Warn ( "Got error trying to update backup's status.progress" )
}
2020-04-13 20:53:58 +00:00
log . WithField ( "progress" , "" ) . Infof ( "Backed up a total of %d items" , len ( backupRequest . BackedUpItems ) )
2020-04-13 19:29:34 +00:00
2019-04-26 16:14:26 +00:00
return nil
2017-08-02 17:27:17 +00:00
}
2020-04-13 20:53:58 +00:00
func ( kb * kubernetesBackupper ) backupItem ( log logrus . FieldLogger , gr schema . GroupResource , itemBackupper * itemBackupper , unstructured * unstructured . Unstructured , preferredGVR schema . GroupVersionResource ) bool {
backedUpItem , err := itemBackupper . backupItem ( log , unstructured , gr , preferredGVR )
if aggregate , ok := err . ( kubeerrs . Aggregate ) ; ok {
log . WithField ( "name" , unstructured . GetName ( ) ) . Infof ( "%d errors encountered backup up item" , len ( aggregate . Errors ( ) ) )
// log each error separately so we get error location info in the log, and an
// accurate count of errors
for _ , err = range aggregate . Errors ( ) {
log . WithError ( err ) . WithField ( "name" , unstructured . GetName ( ) ) . Error ( "Error backing up item" )
}
return false
}
if err != nil {
log . WithError ( err ) . WithField ( "name" , unstructured . GetName ( ) ) . Error ( "Error backing up item" )
return false
}
return backedUpItem
}
// backupCRD checks if the resource is a custom resource, and if so, backs up the custom resource definition
// associated with it.
func ( kb * kubernetesBackupper ) backupCRD ( log logrus . FieldLogger , gr schema . GroupResource , itemBackupper * itemBackupper ) {
crdGroupResource := kuberesource . CustomResourceDefinitions
log . Debugf ( "Getting server preferred API version for %s" , crdGroupResource )
gvr , apiResource , err := kb . discoveryHelper . ResourceFor ( crdGroupResource . WithVersion ( "" ) )
if err != nil {
log . WithError ( errors . WithStack ( err ) ) . Errorf ( "Error getting resolved resource for %s" , crdGroupResource )
return
}
log . Debugf ( "Got server preferred API version %s for %s" , gvr . Version , crdGroupResource )
log . Debugf ( "Getting dynamic client for %s" , gvr . String ( ) )
crdClient , err := kb . dynamicFactory . ClientForGroupVersionResource ( gvr . GroupVersion ( ) , apiResource , "" )
if err != nil {
log . WithError ( errors . WithStack ( err ) ) . Errorf ( "Error getting dynamic client for %s" , crdGroupResource )
return
}
log . Debugf ( "Got dynamic client for %s" , gvr . String ( ) )
// try to get a CRD whose name matches the provided GroupResource
unstructured , err := crdClient . Get ( gr . String ( ) , metav1 . GetOptions { } )
if apierrors . IsNotFound ( err ) {
// not found: this means the GroupResource provided was not a
// custom resource, so there's no CRD to back up.
log . Debugf ( "No CRD found for GroupResource %s" , gr . String ( ) )
return
}
if err != nil {
log . WithError ( errors . WithStack ( err ) ) . Errorf ( "Error getting CRD %s" , gr . String ( ) )
return
}
log . Infof ( "Found associated CRD %s to add to backup" , gr . String ( ) )
kb . backupItem ( log , gvr . GroupResource ( ) , itemBackupper , unstructured , gvr )
}
2018-12-05 22:56:53 +00:00
func ( kb * kubernetesBackupper ) writeBackupVersion ( tw * tar . Writer ) error {
2020-04-13 20:53:58 +00:00
versionFile := filepath . Join ( velerov1api . MetadataDir , "version" )
2020-05-01 19:54:57 +00:00
versionString := fmt . Sprintf ( "%s\n" , BackupFormatVersion )
2018-12-05 22:56:53 +00:00
hdr := & tar . Header {
Name : versionFile ,
Size : int64 ( len ( versionString ) ) ,
Typeflag : tar . TypeReg ,
2019-01-31 23:27:13 +00:00
Mode : 0755 ,
2018-12-05 22:56:53 +00:00
ModTime : time . Now ( ) ,
}
if err := tw . WriteHeader ( hdr ) ; err != nil {
return errors . WithStack ( err )
}
if _ , err := tw . Write ( [ ] byte ( versionString ) ) ; err != nil {
return errors . WithStack ( err )
}
return nil
}
2017-08-02 17:27:17 +00:00
type tarWriter interface {
io . Closer
Write ( [ ] byte ) ( int , error )
WriteHeader ( * tar . Header ) error
}