mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-09-05 15:47:15 +00:00
Merge branch 'main' into batch-delete-snapshot
This commit is contained in:
@@ -159,7 +159,7 @@ type BackupSpec struct {
|
||||
CSISnapshotTimeout metav1.Duration `json:"csiSnapshotTimeout,omitempty"`
|
||||
|
||||
// ItemOperationTimeout specifies the time used to wait for asynchronous BackupItemAction operations
|
||||
// The default value is 1 hour.
|
||||
// The default value is 4 hour.
|
||||
// +optional
|
||||
ItemOperationTimeout metav1.Duration `json:"itemOperationTimeout,omitempty"`
|
||||
// ResourcePolicy specifies the referenced resource policies that backup should follow
|
||||
|
||||
@@ -115,7 +115,7 @@ type RestoreSpec struct {
|
||||
ExistingResourcePolicy PolicyType `json:"existingResourcePolicy,omitempty"`
|
||||
|
||||
// ItemOperationTimeout specifies the time used to wait for RestoreItemAction operations
|
||||
// The default value is 1 hour.
|
||||
// The default value is 4 hour.
|
||||
// +optional
|
||||
ItemOperationTimeout metav1.Duration `json:"itemOperationTimeout,omitempty"`
|
||||
|
||||
|
||||
@@ -35,8 +35,8 @@ type DynamicFactory interface {
|
||||
// ClientForGroupVersionResource returns a Dynamic client for the given group/version
|
||||
// and resource for the given namespace.
|
||||
ClientForGroupVersionResource(gv schema.GroupVersion, resource metav1.APIResource, namespace string) (Dynamic, error)
|
||||
// DynamicSharedInformerFactoryForNamespace returns a DynamicSharedInformerFactory for the given namespace.
|
||||
DynamicSharedInformerFactoryForNamespace(namespace string) dynamicinformer.DynamicSharedInformerFactory
|
||||
// DynamicSharedInformerFactory returns a DynamicSharedInformerFactory.
|
||||
DynamicSharedInformerFactory() dynamicinformer.DynamicSharedInformerFactory
|
||||
}
|
||||
|
||||
// dynamicFactory implements DynamicFactory.
|
||||
@@ -55,8 +55,8 @@ func (f *dynamicFactory) ClientForGroupVersionResource(gv schema.GroupVersion, r
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (f *dynamicFactory) DynamicSharedInformerFactoryForNamespace(namespace string) dynamicinformer.DynamicSharedInformerFactory {
|
||||
return dynamicinformer.NewFilteredDynamicSharedInformerFactory(f.dynamicClient, time.Minute, namespace, nil)
|
||||
func (f *dynamicFactory) DynamicSharedInformerFactory() dynamicinformer.DynamicSharedInformerFactory {
|
||||
return dynamicinformer.NewDynamicSharedInformerFactory(f.dynamicClient, time.Minute)
|
||||
}
|
||||
|
||||
// Creator creates an object.
|
||||
|
||||
@@ -155,7 +155,7 @@ func NewInstallOptions() *Options {
|
||||
DefaultVolumesToFsBackup: false,
|
||||
UploaderType: uploader.KopiaType,
|
||||
DefaultSnapshotMoveData: false,
|
||||
DisableInformerCache: true,
|
||||
DisableInformerCache: false,
|
||||
ScheduleSkipImmediately: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,6 +171,12 @@ func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error {
|
||||
schedule.Spec.Template.ResourcePolicy = &v1.TypedLocalObjectReference{Kind: resourcepolicies.ConfigmapRefType, Name: o.BackupOptions.ResPoliciesConfigmap}
|
||||
}
|
||||
|
||||
if o.BackupOptions.ParallelFilesUpload > 0 {
|
||||
schedule.Spec.Template.UploaderConfig = &api.UploaderConfigForBackup{
|
||||
ParallelFilesUpload: o.BackupOptions.ParallelFilesUpload,
|
||||
}
|
||||
}
|
||||
|
||||
if printed, err := output.PrintWithFormat(c, schedule); printed || err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
corev1api "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
@@ -88,8 +89,8 @@ const (
|
||||
defaultResourceTerminatingTimeout = 10 * time.Minute
|
||||
|
||||
// server's client default qps and burst
|
||||
defaultClientQPS float32 = 20.0
|
||||
defaultClientBurst int = 30
|
||||
defaultClientQPS float32 = 100.0
|
||||
defaultClientBurst int = 100
|
||||
defaultClientPageSize int = 500
|
||||
|
||||
defaultProfilerAddress = "localhost:6060"
|
||||
@@ -467,7 +468,12 @@ func setDefaultBackupLocation(ctx context.Context, client ctrlclient.Client, nam
|
||||
|
||||
backupLocation := &velerov1api.BackupStorageLocation{}
|
||||
if err := client.Get(ctx, types.NamespacedName{Namespace: namespace, Name: defaultBackupLocation}, backupLocation); err != nil {
|
||||
return errors.WithStack(err)
|
||||
if apierrors.IsNotFound(err) {
|
||||
logger.WithField("backupStorageLocation", defaultBackupLocation).WithError(err).Warn("Failed to set default backup storage location at server start")
|
||||
return nil
|
||||
} else {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
}
|
||||
|
||||
if !backupLocation.Spec.Default {
|
||||
|
||||
@@ -409,4 +409,13 @@ func Test_setDefaultBackupLocation(t *testing.T) {
|
||||
nonDefaultLocation := &velerov1api.BackupStorageLocation{}
|
||||
require.Nil(t, c.Get(context.Background(), client.ObjectKey{Namespace: "velero", Name: "non-default"}, nonDefaultLocation))
|
||||
assert.False(t, nonDefaultLocation.Spec.Default)
|
||||
|
||||
// no default location specified
|
||||
c = fake.NewClientBuilder().WithScheme(scheme).Build()
|
||||
err := setDefaultBackupLocation(context.Background(), c, "velero", "", logrus.New())
|
||||
assert.NoError(t, err)
|
||||
|
||||
// no default location created
|
||||
err = setDefaultBackupLocation(context.Background(), c, "velero", "default", logrus.New())
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -252,8 +252,6 @@ func (b *backupReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr
|
||||
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)
|
||||
}
|
||||
// store ref to just-updated item for creating patch
|
||||
original = request.Backup.DeepCopy()
|
||||
|
||||
backupScheduleName := request.GetLabels()[velerov1api.ScheduleNameLabel]
|
||||
|
||||
@@ -265,6 +263,9 @@ func (b *backupReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
// store ref to just-updated item for creating patch
|
||||
original = request.Backup.DeepCopy()
|
||||
|
||||
b.backupTracker.Add(request.Namespace, request.Name)
|
||||
defer func() {
|
||||
switch request.Status.Phase {
|
||||
|
||||
@@ -76,7 +76,11 @@ func (r *BackupRepoReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
For(&velerov1api.BackupRepository{}).
|
||||
Watches(s, nil).
|
||||
Watches(&source.Kind{Type: &velerov1api.BackupStorageLocation{}}, kube.EnqueueRequestsFromMapUpdateFunc(r.invalidateBackupReposForBSL),
|
||||
builder.WithPredicates(kube.NewUpdateEventPredicate(r.needInvalidBackupRepo))).
|
||||
builder.WithPredicates(
|
||||
// When BSL updates, check if the backup repositories need to be invalidated
|
||||
kube.NewUpdateEventPredicate(r.needInvalidBackupRepo),
|
||||
// When BSL is created, invalidate any backup repositories that reference it
|
||||
kube.NewCreateEventPredicate(func(client.Object) bool { return true }))).
|
||||
Complete(r)
|
||||
}
|
||||
|
||||
@@ -90,13 +94,13 @@ func (r *BackupRepoReconciler) invalidateBackupReposForBSL(bslObj client.Object)
|
||||
}).AsSelector(),
|
||||
}
|
||||
if err := r.List(context.TODO(), list, options); err != nil {
|
||||
r.logger.WithField("BSL", bsl.Name).WithError(err).Error("unable to list BackupRepositorys")
|
||||
r.logger.WithField("BSL", bsl.Name).WithError(err).Error("unable to list BackupRepositories")
|
||||
return []reconcile.Request{}
|
||||
}
|
||||
|
||||
for i := range list.Items {
|
||||
r.logger.WithField("BSL", bsl.Name).Infof("Invalidating Backup Repository %s", list.Items[i].Name)
|
||||
if err := r.patchBackupRepository(context.Background(), &list.Items[i], repoNotReady("re-establish on BSL change")); err != nil {
|
||||
if err := r.patchBackupRepository(context.Background(), &list.Items[i], repoNotReady("re-establish on BSL change or create")); err != nil {
|
||||
r.logger.WithField("BSL", bsl.Name).WithError(err).Errorf("fail to patch BackupRepository %s", list.Items[i].Name)
|
||||
}
|
||||
}
|
||||
@@ -104,6 +108,7 @@ func (r *BackupRepoReconciler) invalidateBackupReposForBSL(bslObj client.Object)
|
||||
return []reconcile.Request{}
|
||||
}
|
||||
|
||||
// needInvalidBackupRepo returns true if the BSL's storage type, bucket, prefix, CACert, or config has changed
|
||||
func (r *BackupRepoReconciler) needInvalidBackupRepo(oldObj client.Object, newObj client.Object) bool {
|
||||
oldBSL := oldObj.(*velerov1api.BackupStorageLocation)
|
||||
newBSL := newObj.(*velerov1api.BackupStorageLocation)
|
||||
@@ -260,7 +265,7 @@ func (r *BackupRepoReconciler) getRepositoryMaintenanceFrequency(req *velerov1ap
|
||||
r.logger.WithError(err).WithField("returned frequency", frequency).Warn("Failed to get maitanance frequency, use the default one")
|
||||
frequency = defaultMaintainFrequency
|
||||
} else {
|
||||
r.logger.WithField("frequency", frequency).Info("Set matainenance according to repository suggestion")
|
||||
r.logger.WithField("frequency", frequency).Info("Set maintenance according to repository suggestion")
|
||||
}
|
||||
|
||||
return frequency
|
||||
|
||||
@@ -19,7 +19,13 @@ package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/credentials"
|
||||
"github.com/aws/aws-sdk-go-v2/credentials/stscreds"
|
||||
"github.com/aws/aws-sdk-go-v2/service/sts"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
awsconfig "github.com/aws/aws-sdk-go-v2/config"
|
||||
@@ -31,13 +37,13 @@ import (
|
||||
const (
|
||||
// AWS specific environment variable
|
||||
awsProfileEnvVar = "AWS_PROFILE"
|
||||
awsRoleEnvVar = "AWS_ROLE_ARN"
|
||||
awsKeyIDEnvVar = "AWS_ACCESS_KEY_ID"
|
||||
awsSecretKeyEnvVar = "AWS_SECRET_ACCESS_KEY"
|
||||
awsSessTokenEnvVar = "AWS_SESSION_TOKEN"
|
||||
awsProfileKey = "profile"
|
||||
awsCredentialsFileEnvVar = "AWS_SHARED_CREDENTIALS_FILE"
|
||||
awsConfigFileEnvVar = "AWS_CONFIG_FILE"
|
||||
awsDefaultProfile = "default"
|
||||
)
|
||||
|
||||
// GetS3ResticEnvVars gets the environment variables that restic
|
||||
@@ -72,10 +78,6 @@ func GetS3ResticEnvVars(config map[string]string) (map[string]string, error) {
|
||||
// GetS3Credentials gets the S3 credential values according to the information
|
||||
// of the provided config or the system's environment variables
|
||||
func GetS3Credentials(config map[string]string) (*aws.Credentials, error) {
|
||||
if os.Getenv(awsRoleEnvVar) != "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var opts []func(*awsconfig.LoadOptions) error
|
||||
credentialsFile := config[CredentialsFileKey]
|
||||
if credentialsFile == "" {
|
||||
@@ -93,6 +95,25 @@ func GetS3Credentials(config map[string]string) (*aws.Credentials, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if credentialsFile != "" && os.Getenv("AWS_WEB_IDENTITY_TOKEN_FILE") != "" && os.Getenv("AWS_ROLE_ARN") != "" {
|
||||
// Reset the config to use the credentials from the credentials/config file
|
||||
profile := config[awsProfileKey]
|
||||
if profile == "" {
|
||||
profile = awsDefaultProfile
|
||||
}
|
||||
sfp, err := awsconfig.LoadSharedConfigProfile(context.Background(), profile, func(o *awsconfig.LoadSharedConfigOptions) {
|
||||
o.ConfigFiles = []string{credentialsFile}
|
||||
o.CredentialsFiles = []string{credentialsFile}
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error loading config profile '%s': %v", profile, err)
|
||||
}
|
||||
if err := resolveCredsFromProfile(&cfg, &sfp); err != nil {
|
||||
return nil, fmt.Errorf("error resolving creds from profile '%s': %v", profile, err)
|
||||
}
|
||||
}
|
||||
|
||||
creds, err := cfg.Credentials.Retrieve(context.Background())
|
||||
|
||||
return &creds, err
|
||||
@@ -115,3 +136,44 @@ func GetAWSBucketRegion(bucket string) (string, error) {
|
||||
}
|
||||
return region, nil
|
||||
}
|
||||
|
||||
func resolveCredsFromProfile(cfg *aws.Config, sharedConfig *awsconfig.SharedConfig) error {
|
||||
var err error
|
||||
switch {
|
||||
case sharedConfig.Source != nil:
|
||||
// Assume IAM role with credentials source from a different profile.
|
||||
err = resolveCredsFromProfile(cfg, sharedConfig.Source)
|
||||
case sharedConfig.Credentials.HasKeys():
|
||||
// Static Credentials from Shared Config/Credentials file.
|
||||
cfg.Credentials = credentials.StaticCredentialsProvider{
|
||||
Value: sharedConfig.Credentials,
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(sharedConfig.RoleARN) > 0 {
|
||||
credsFromAssumeRole(cfg, sharedConfig)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func credsFromAssumeRole(cfg *aws.Config, sharedCfg *awsconfig.SharedConfig) {
|
||||
optFns := []func(*stscreds.AssumeRoleOptions){
|
||||
func(options *stscreds.AssumeRoleOptions) {
|
||||
options.RoleSessionName = sharedCfg.RoleSessionName
|
||||
if sharedCfg.RoleDurationSeconds != nil {
|
||||
if *sharedCfg.RoleDurationSeconds/time.Minute > 15 {
|
||||
options.Duration = *sharedCfg.RoleDurationSeconds
|
||||
}
|
||||
}
|
||||
if len(sharedCfg.ExternalID) > 0 {
|
||||
options.ExternalID = aws.String(sharedCfg.ExternalID)
|
||||
}
|
||||
if len(sharedCfg.MFASerial) != 0 {
|
||||
options.SerialNumber = aws.String(sharedCfg.MFASerial)
|
||||
}
|
||||
},
|
||||
}
|
||||
cfg.Credentials = stscreds.NewAssumeRoleProvider(sts.NewFromConfig(*cfg), sharedCfg.RoleARN, optFns...)
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ var getGCPCredentials = repoconfig.GetGCPCredentials
|
||||
var getS3BucketRegion = repoconfig.GetAWSBucketRegion
|
||||
|
||||
type localFuncTable struct {
|
||||
getStorageVariables func(*velerov1api.BackupStorageLocation, string, string, credentials.FileStore) (map[string]string, error)
|
||||
getStorageVariables func(*velerov1api.BackupStorageLocation, string, string) (map[string]string, error)
|
||||
getStorageCredentials func(*velerov1api.BackupStorageLocation, credentials.FileStore) (map[string]string, error)
|
||||
}
|
||||
|
||||
@@ -397,7 +397,7 @@ func (urp *unifiedRepoProvider) GetStoreOptions(param interface{}) (map[string]s
|
||||
return map[string]string{}, errors.Errorf("invalid parameter, expect %T, actual %T", RepoParam{}, param)
|
||||
}
|
||||
|
||||
storeVar, err := funcTable.getStorageVariables(repoParam.BackupLocation, urp.repoBackend, repoParam.BackupRepo.Spec.VolumeNamespace, urp.credentialGetter.FromFile)
|
||||
storeVar, err := funcTable.getStorageVariables(repoParam.BackupLocation, urp.repoBackend, repoParam.BackupRepo.Spec.VolumeNamespace)
|
||||
if err != nil {
|
||||
return map[string]string{}, errors.Wrap(err, "error to get storage variables")
|
||||
}
|
||||
@@ -488,8 +488,9 @@ func getStorageCredentials(backupLocation *velerov1api.BackupStorageLocation, cr
|
||||
result[udmrepo.StoreOptionS3Token] = credValue.SessionToken
|
||||
}
|
||||
case repoconfig.AzureBackend:
|
||||
// do nothing here, will retrieve the credential in Azure Storage
|
||||
return nil, nil
|
||||
if config[repoconfig.CredentialsFileKey] != "" {
|
||||
result[repoconfig.CredentialsFileKey] = config[repoconfig.CredentialsFileKey]
|
||||
}
|
||||
case repoconfig.GCPBackend:
|
||||
result[udmrepo.StoreOptionCredentialFile] = getGCPCredentials(config)
|
||||
}
|
||||
@@ -497,8 +498,7 @@ func getStorageCredentials(backupLocation *velerov1api.BackupStorageLocation, cr
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func getStorageVariables(backupLocation *velerov1api.BackupStorageLocation, repoBackend string, repoName string,
|
||||
credentialFileStore credentials.FileStore) (map[string]string, error) {
|
||||
func getStorageVariables(backupLocation *velerov1api.BackupStorageLocation, repoBackend string, repoName string) (map[string]string, error) {
|
||||
result := make(map[string]string)
|
||||
|
||||
backendType := repoconfig.GetBackendType(backupLocation.Spec.Provider, backupLocation.Spec.Config)
|
||||
@@ -510,13 +510,6 @@ func getStorageVariables(backupLocation *velerov1api.BackupStorageLocation, repo
|
||||
if config == nil {
|
||||
config = map[string]string{}
|
||||
}
|
||||
if backupLocation.Spec.Credential != nil {
|
||||
credsFile, err := credentialFileStore.Path(backupLocation.Spec.Credential)
|
||||
if err != nil {
|
||||
return map[string]string{}, errors.WithStack(err)
|
||||
}
|
||||
config[repoconfig.CredentialsFileKey] = credsFile
|
||||
}
|
||||
|
||||
bucket := strings.Trim(config["bucket"], "/")
|
||||
prefix := strings.Trim(config["prefix"], "/")
|
||||
@@ -555,7 +548,7 @@ func getStorageVariables(backupLocation *velerov1api.BackupStorageLocation, repo
|
||||
}
|
||||
|
||||
s3URL = url.Host
|
||||
disableTLS = (url.Scheme == "http")
|
||||
disableTLS = url.Scheme == "http"
|
||||
}
|
||||
|
||||
result[udmrepo.StoreOptionS3Endpoint] = strings.Trim(s3URL, "/")
|
||||
|
||||
@@ -168,7 +168,7 @@ func TestGetStorageCredentials(t *testing.T) {
|
||||
},
|
||||
},
|
||||
credFileStore: new(credmock.FileStore),
|
||||
expected: nil,
|
||||
expected: map[string]string{},
|
||||
},
|
||||
{
|
||||
name: "gcp, Credential section not exists in BSL",
|
||||
@@ -437,12 +437,11 @@ func TestGetStorageVariables(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
credFileStore := new(credmock.FileStore)
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
getS3BucketRegion = tc.getS3BucketRegion
|
||||
|
||||
actual, err := getStorageVariables(&tc.backupLocation, tc.repoBackend, tc.repoName, credFileStore)
|
||||
actual, err := getStorageVariables(&tc.backupLocation, tc.repoBackend, tc.repoName)
|
||||
|
||||
require.Equal(t, tc.expected, actual)
|
||||
|
||||
@@ -531,7 +530,7 @@ func TestGetStoreOptions(t *testing.T) {
|
||||
BackupRepo: &velerov1api.BackupRepository{},
|
||||
},
|
||||
funcTable: localFuncTable{
|
||||
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string, velerocredentials.FileStore) (map[string]string, error) {
|
||||
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string) (map[string]string, error) {
|
||||
return map[string]string{}, errors.New("fake-error-2")
|
||||
},
|
||||
},
|
||||
@@ -545,7 +544,7 @@ func TestGetStoreOptions(t *testing.T) {
|
||||
BackupRepo: &velerov1api.BackupRepository{},
|
||||
},
|
||||
funcTable: localFuncTable{
|
||||
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string, velerocredentials.FileStore) (map[string]string, error) {
|
||||
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string) (map[string]string, error) {
|
||||
return map[string]string{}, nil
|
||||
},
|
||||
getStorageCredentials: func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) {
|
||||
@@ -605,7 +604,7 @@ func TestPrepareRepo(t *testing.T) {
|
||||
repoService: new(reposervicenmocks.BackupRepoService),
|
||||
credStoreReturn: "fake-password",
|
||||
funcTable: localFuncTable{
|
||||
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string, velerocredentials.FileStore) (map[string]string, error) {
|
||||
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string) (map[string]string, error) {
|
||||
return map[string]string{}, errors.New("fake-store-option-error")
|
||||
},
|
||||
},
|
||||
@@ -616,7 +615,7 @@ func TestPrepareRepo(t *testing.T) {
|
||||
getter: new(credmock.SecretStore),
|
||||
credStoreReturn: "fake-password",
|
||||
funcTable: localFuncTable{
|
||||
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string, velerocredentials.FileStore) (map[string]string, error) {
|
||||
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string) (map[string]string, error) {
|
||||
return map[string]string{}, nil
|
||||
},
|
||||
getStorageCredentials: func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) {
|
||||
@@ -636,7 +635,7 @@ func TestPrepareRepo(t *testing.T) {
|
||||
getter: new(credmock.SecretStore),
|
||||
credStoreReturn: "fake-password",
|
||||
funcTable: localFuncTable{
|
||||
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string, velerocredentials.FileStore) (map[string]string, error) {
|
||||
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string) (map[string]string, error) {
|
||||
return map[string]string{}, nil
|
||||
},
|
||||
getStorageCredentials: func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) {
|
||||
@@ -657,7 +656,7 @@ func TestPrepareRepo(t *testing.T) {
|
||||
getter: new(credmock.SecretStore),
|
||||
credStoreReturn: "fake-password",
|
||||
funcTable: localFuncTable{
|
||||
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string, velerocredentials.FileStore) (map[string]string, error) {
|
||||
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string) (map[string]string, error) {
|
||||
return map[string]string{}, nil
|
||||
},
|
||||
getStorageCredentials: func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) {
|
||||
@@ -734,7 +733,7 @@ func TestForget(t *testing.T) {
|
||||
getter: new(credmock.SecretStore),
|
||||
credStoreReturn: "fake-password",
|
||||
funcTable: localFuncTable{
|
||||
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string, velerocredentials.FileStore) (map[string]string, error) {
|
||||
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string) (map[string]string, error) {
|
||||
return map[string]string{}, nil
|
||||
},
|
||||
getStorageCredentials: func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) {
|
||||
@@ -758,7 +757,7 @@ func TestForget(t *testing.T) {
|
||||
getter: new(credmock.SecretStore),
|
||||
credStoreReturn: "fake-password",
|
||||
funcTable: localFuncTable{
|
||||
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string, velerocredentials.FileStore) (map[string]string, error) {
|
||||
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string) (map[string]string, error) {
|
||||
return map[string]string{}, nil
|
||||
},
|
||||
getStorageCredentials: func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) {
|
||||
@@ -786,7 +785,7 @@ func TestForget(t *testing.T) {
|
||||
getter: new(credmock.SecretStore),
|
||||
credStoreReturn: "fake-password",
|
||||
funcTable: localFuncTable{
|
||||
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string, velerocredentials.FileStore) (map[string]string, error) {
|
||||
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string) (map[string]string, error) {
|
||||
return map[string]string{}, nil
|
||||
},
|
||||
getStorageCredentials: func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) {
|
||||
@@ -878,7 +877,7 @@ func TestInitRepo(t *testing.T) {
|
||||
getter: new(credmock.SecretStore),
|
||||
credStoreReturn: "fake-password",
|
||||
funcTable: localFuncTable{
|
||||
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string, velerocredentials.FileStore) (map[string]string, error) {
|
||||
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string) (map[string]string, error) {
|
||||
return map[string]string{}, nil
|
||||
},
|
||||
getStorageCredentials: func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) {
|
||||
@@ -896,7 +895,7 @@ func TestInitRepo(t *testing.T) {
|
||||
getter: new(credmock.SecretStore),
|
||||
credStoreReturn: "fake-password",
|
||||
funcTable: localFuncTable{
|
||||
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string, velerocredentials.FileStore) (map[string]string, error) {
|
||||
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string) (map[string]string, error) {
|
||||
return map[string]string{}, nil
|
||||
},
|
||||
getStorageCredentials: func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) {
|
||||
@@ -966,7 +965,7 @@ func TestConnectToRepo(t *testing.T) {
|
||||
getter: new(credmock.SecretStore),
|
||||
credStoreReturn: "fake-password",
|
||||
funcTable: localFuncTable{
|
||||
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string, velerocredentials.FileStore) (map[string]string, error) {
|
||||
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string) (map[string]string, error) {
|
||||
return map[string]string{}, nil
|
||||
},
|
||||
getStorageCredentials: func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) {
|
||||
@@ -984,7 +983,7 @@ func TestConnectToRepo(t *testing.T) {
|
||||
getter: new(credmock.SecretStore),
|
||||
credStoreReturn: "fake-password",
|
||||
funcTable: localFuncTable{
|
||||
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string, velerocredentials.FileStore) (map[string]string, error) {
|
||||
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string) (map[string]string, error) {
|
||||
return map[string]string{}, nil
|
||||
},
|
||||
getStorageCredentials: func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) {
|
||||
@@ -1058,7 +1057,7 @@ func TestBoostRepoConnect(t *testing.T) {
|
||||
getter: new(credmock.SecretStore),
|
||||
credStoreReturn: "fake-password",
|
||||
funcTable: localFuncTable{
|
||||
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string, velerocredentials.FileStore) (map[string]string, error) {
|
||||
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string) (map[string]string, error) {
|
||||
return map[string]string{}, nil
|
||||
},
|
||||
getStorageCredentials: func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) {
|
||||
@@ -1085,7 +1084,7 @@ func TestBoostRepoConnect(t *testing.T) {
|
||||
getter: new(credmock.SecretStore),
|
||||
credStoreReturn: "fake-password",
|
||||
funcTable: localFuncTable{
|
||||
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string, velerocredentials.FileStore) (map[string]string, error) {
|
||||
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string) (map[string]string, error) {
|
||||
return map[string]string{}, nil
|
||||
},
|
||||
getStorageCredentials: func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) {
|
||||
@@ -1111,7 +1110,7 @@ func TestBoostRepoConnect(t *testing.T) {
|
||||
getter: new(credmock.SecretStore),
|
||||
credStoreReturn: "fake-password",
|
||||
funcTable: localFuncTable{
|
||||
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string, velerocredentials.FileStore) (map[string]string, error) {
|
||||
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string) (map[string]string, error) {
|
||||
return map[string]string{}, nil
|
||||
},
|
||||
getStorageCredentials: func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) {
|
||||
@@ -1198,7 +1197,7 @@ func TestPruneRepo(t *testing.T) {
|
||||
getter: new(credmock.SecretStore),
|
||||
credStoreReturn: "fake-password",
|
||||
funcTable: localFuncTable{
|
||||
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string, velerocredentials.FileStore) (map[string]string, error) {
|
||||
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string) (map[string]string, error) {
|
||||
return map[string]string{}, nil
|
||||
},
|
||||
getStorageCredentials: func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) {
|
||||
@@ -1216,7 +1215,7 @@ func TestPruneRepo(t *testing.T) {
|
||||
getter: new(credmock.SecretStore),
|
||||
credStoreReturn: "fake-password",
|
||||
funcTable: localFuncTable{
|
||||
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string, velerocredentials.FileStore) (map[string]string, error) {
|
||||
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string) (map[string]string, error) {
|
||||
return map[string]string{}, nil
|
||||
},
|
||||
getStorageCredentials: func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) {
|
||||
|
||||
+45
-63
@@ -45,7 +45,6 @@ import (
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/dynamic/dynamicinformer"
|
||||
"k8s.io/client-go/informers"
|
||||
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
crclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
@@ -309,8 +308,6 @@ func (kr *kubernetesRestorer) RestoreWithResolvers(
|
||||
resourceTerminatingTimeout: kr.resourceTerminatingTimeout,
|
||||
resourceTimeout: kr.resourceTimeout,
|
||||
resourceClients: make(map[resourceClientKey]client.Dynamic),
|
||||
dynamicInformerFactories: make(map[string]*informerFactoryWithContext),
|
||||
resourceInformers: make(map[resourceClientKey]informers.GenericInformer),
|
||||
restoredItems: req.RestoredItems,
|
||||
renamedPVs: make(map[string]string),
|
||||
pvRenamer: kr.pvRenamer,
|
||||
@@ -362,8 +359,7 @@ type restoreContext struct {
|
||||
resourceTerminatingTimeout time.Duration
|
||||
resourceTimeout time.Duration
|
||||
resourceClients map[resourceClientKey]client.Dynamic
|
||||
dynamicInformerFactories map[string]*informerFactoryWithContext
|
||||
resourceInformers map[resourceClientKey]informers.GenericInformer
|
||||
dynamicInformerFactory *informerFactoryWithContext
|
||||
restoredItems map[itemKey]restoredItemStatus
|
||||
renamedPVs map[string]string
|
||||
pvRenamer func(string) (string, error)
|
||||
@@ -447,11 +443,16 @@ func (ctx *restoreContext) execute() (results.Result, results.Result) {
|
||||
|
||||
// Need to stop all informers if enabled
|
||||
if !ctx.disableInformerCache {
|
||||
context, cancel := signal.NotifyContext(go_context.Background(), os.Interrupt)
|
||||
ctx.dynamicInformerFactory = &informerFactoryWithContext{
|
||||
factory: ctx.dynamicFactory.DynamicSharedInformerFactory(),
|
||||
context: context,
|
||||
cancel: cancel,
|
||||
}
|
||||
|
||||
defer func() {
|
||||
// Call the cancel func to close the channel for each started informer
|
||||
for _, factory := range ctx.dynamicInformerFactories {
|
||||
factory.cancel()
|
||||
}
|
||||
ctx.dynamicInformerFactory.cancel()
|
||||
// After upgrading to client-go 0.27 or newer, also call Shutdown for each informer factory
|
||||
}()
|
||||
}
|
||||
@@ -579,28 +580,29 @@ func (ctx *restoreContext) execute() (results.Result, results.Result) {
|
||||
|
||||
// initialize informer caches for selected resources if enabled
|
||||
if !ctx.disableInformerCache {
|
||||
// CRD informer will have already been initialized if any CRDs were created,
|
||||
// but already-initialized informers aren't re-initialized because getGenericInformer
|
||||
// looks for an existing one first.
|
||||
factoriesToStart := make(map[string]*informerFactoryWithContext)
|
||||
for _, informerResource := range selectedResourceCollection {
|
||||
gr := schema.ParseGroupResource(informerResource.resource)
|
||||
if informerResource.totalItems == 0 {
|
||||
continue
|
||||
}
|
||||
version := ""
|
||||
for _, items := range informerResource.selectedItemsByNamespace {
|
||||
// don't use ns key since it represents original ns, not mapped ns
|
||||
if len(items) == 0 {
|
||||
continue
|
||||
}
|
||||
// use the first item in the list to initialize the informer. The rest of the list
|
||||
// should share the same gvr and namespace
|
||||
_, factory := ctx.getGenericInformerInternal(gr, items[0].version, items[0].targetNamespace)
|
||||
if factory != nil {
|
||||
factoriesToStart[items[0].targetNamespace] = factory
|
||||
}
|
||||
version = items[0].version
|
||||
break
|
||||
}
|
||||
gvr := schema.ParseGroupResource(informerResource.resource).WithVersion(version)
|
||||
_, _, err := ctx.discoveryHelper.ResourceFor(gvr)
|
||||
if err != nil {
|
||||
ctx.log.Infof("failed to create informer for %s: %v", gvr, err)
|
||||
continue
|
||||
}
|
||||
ctx.dynamicInformerFactory.factory.ForResource(gvr)
|
||||
}
|
||||
for _, factoryWithContext := range factoriesToStart {
|
||||
factoryWithContext.factory.WaitForCacheSync(factoryWithContext.context.Done())
|
||||
}
|
||||
ctx.dynamicInformerFactory.factory.Start(ctx.dynamicInformerFactory.context.Done())
|
||||
ctx.log.Info("waiting informer cache sync ...")
|
||||
ctx.dynamicInformerFactory.factory.WaitForCacheSync(ctx.dynamicInformerFactory.context.Done())
|
||||
}
|
||||
|
||||
// reset processedItems and totalItems before processing full resource list
|
||||
@@ -1061,47 +1063,23 @@ func (ctx *restoreContext) getResourceClient(groupResource schema.GroupResource,
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// if new informer is created, non-nil factory is returned
|
||||
func (ctx *restoreContext) getGenericInformerInternal(groupResource schema.GroupResource, version, namespace string) (informers.GenericInformer, *informerFactoryWithContext) {
|
||||
var returnFactory *informerFactoryWithContext
|
||||
func (ctx *restoreContext) getResourceLister(groupResource schema.GroupResource, obj *unstructured.Unstructured, namespace string) (cache.GenericNamespaceLister, error) {
|
||||
_, _, err := ctx.discoveryHelper.KindFor(obj.GroupVersionKind())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
informer := ctx.dynamicInformerFactory.factory.ForResource(groupResource.WithVersion(obj.GroupVersionKind().Version))
|
||||
// if the restore contains CRDs or the RIA returns new resources, need to make sure the corresponding informers are synced
|
||||
if !informer.Informer().HasSynced() {
|
||||
ctx.dynamicInformerFactory.factory.Start(ctx.dynamicInformerFactory.context.Done())
|
||||
ctx.log.Infof("waiting informer cache sync for %s, %s/%s ...", groupResource, namespace, obj.GetName())
|
||||
ctx.dynamicInformerFactory.factory.WaitForCacheSync(ctx.dynamicInformerFactory.context.Done())
|
||||
}
|
||||
|
||||
key := getResourceClientKey(groupResource, version, namespace)
|
||||
factoryWithContext, ok := ctx.dynamicInformerFactories[key.namespace]
|
||||
if !ok {
|
||||
factory := ctx.dynamicFactory.DynamicSharedInformerFactoryForNamespace(namespace)
|
||||
informerContext, informerCancel := signal.NotifyContext(go_context.Background(), os.Interrupt)
|
||||
factoryWithContext = &informerFactoryWithContext{
|
||||
factory: factory,
|
||||
context: informerContext,
|
||||
cancel: informerCancel,
|
||||
}
|
||||
ctx.dynamicInformerFactories[key.namespace] = factoryWithContext
|
||||
}
|
||||
informer, ok := ctx.resourceInformers[key]
|
||||
if !ok {
|
||||
ctx.log.Infof("[debug] Creating factory for %s in namespace %s", key.resource, key.namespace)
|
||||
informer = factoryWithContext.factory.ForResource(key.resource)
|
||||
factoryWithContext.factory.Start(factoryWithContext.context.Done())
|
||||
ctx.resourceInformers[key] = informer
|
||||
returnFactory = factoryWithContext
|
||||
}
|
||||
return informer, returnFactory
|
||||
}
|
||||
|
||||
func (ctx *restoreContext) getGenericInformer(groupResource schema.GroupResource, version, namespace string) informers.GenericInformer {
|
||||
informer, factoryWithContext := ctx.getGenericInformerInternal(groupResource, version, namespace)
|
||||
if factoryWithContext != nil {
|
||||
factoryWithContext.factory.WaitForCacheSync(factoryWithContext.context.Done())
|
||||
}
|
||||
return informer
|
||||
}
|
||||
func (ctx *restoreContext) getResourceLister(groupResource schema.GroupResource, obj *unstructured.Unstructured, namespace string) cache.GenericNamespaceLister {
|
||||
informer := ctx.getGenericInformer(groupResource, obj.GroupVersionKind().Version, namespace)
|
||||
if namespace == "" {
|
||||
return informer.Lister()
|
||||
} else {
|
||||
return informer.Lister().ByNamespace(namespace)
|
||||
return informer.Lister(), nil
|
||||
}
|
||||
return informer.Lister().ByNamespace(namespace), nil
|
||||
}
|
||||
|
||||
func getResourceID(groupResource schema.GroupResource, namespace, name string) string {
|
||||
@@ -1113,7 +1091,10 @@ func getResourceID(groupResource schema.GroupResource, namespace, name string) s
|
||||
}
|
||||
|
||||
func (ctx *restoreContext) getResource(groupResource schema.GroupResource, obj *unstructured.Unstructured, namespace, name string) (*unstructured.Unstructured, error) {
|
||||
lister := ctx.getResourceLister(groupResource, obj, namespace)
|
||||
lister, err := ctx.getResourceLister(groupResource, obj, namespace)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Error getting lister for %s", getResourceID(groupResource, namespace, name))
|
||||
}
|
||||
clusterObj, err := lister.Get(name)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error getting resource from lister for %s, %s/%s", groupResource, namespace, name)
|
||||
@@ -1123,6 +1104,7 @@ func (ctx *restoreContext) getResource(groupResource schema.GroupResource, obj *
|
||||
ctx.log.WithError(errors.WithStack(fmt.Errorf("expected *unstructured.Unstructured but got %T", u))).Error("unable to understand entry returned from client")
|
||||
return nil, fmt.Errorf("expected *unstructured.Unstructured but got %T", u)
|
||||
}
|
||||
ctx.log.Debugf("get %s, %s/%s from informer cache", groupResource, namespace, name)
|
||||
return u, nil
|
||||
}
|
||||
|
||||
@@ -1757,7 +1739,7 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso
|
||||
if groupResource == kuberesource.CustomResourceDefinitions {
|
||||
available, err := ctx.crdAvailable(name, resourceClient)
|
||||
if err != nil {
|
||||
errs.Add(namespace, errors.Wrapf(err, "error verifying custom resource definition is ready to use"))
|
||||
errs.Add(namespace, errors.Wrapf(err, "error verifying the CRD %s is ready to use", name))
|
||||
} else if !available {
|
||||
errs.Add(namespace, fmt.Errorf("the CRD %s is not available to use for custom resources", name))
|
||||
}
|
||||
|
||||
@@ -19,9 +19,6 @@ package test
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/discovery"
|
||||
discoveryfake "k8s.io/client-go/discovery/fake"
|
||||
@@ -76,7 +73,7 @@ func (c *DiscoveryClient) WithAPIResource(resource *APIResource) *DiscoveryClien
|
||||
Namespaced: resource.Namespaced,
|
||||
Group: resource.Group,
|
||||
Version: resource.Version,
|
||||
Kind: cases.Title(language.Und).String(strings.TrimSuffix(resource.Name, "s")),
|
||||
Kind: resource.Kind,
|
||||
Verbs: metav1.Verbs([]string{"list", "create", "get", "delete"}),
|
||||
ShortNames: []string{resource.ShortName},
|
||||
})
|
||||
|
||||
@@ -38,8 +38,8 @@ func (df *FakeDynamicFactory) ClientForGroupVersionResource(gv schema.GroupVersi
|
||||
return args.Get(0).(client.Dynamic), args.Error(1)
|
||||
}
|
||||
|
||||
func (df *FakeDynamicFactory) DynamicSharedInformerFactoryForNamespace(namespace string) dynamicinformer.DynamicSharedInformerFactory {
|
||||
args := df.Called(namespace)
|
||||
func (df *FakeDynamicFactory) DynamicSharedInformerFactory() dynamicinformer.DynamicSharedInformerFactory {
|
||||
args := df.Called()
|
||||
return args.Get(0).(dynamicinformer.DynamicSharedInformerFactory)
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ type APIResource struct {
|
||||
Group string
|
||||
Version string
|
||||
Name string
|
||||
Kind string
|
||||
ShortName string
|
||||
Namespaced bool
|
||||
Items []metav1.Object
|
||||
@@ -50,6 +51,7 @@ func Pods(items ...metav1.Object) *APIResource {
|
||||
ShortName: "po",
|
||||
Namespaced: true,
|
||||
Items: items,
|
||||
Kind: "Pod",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +61,7 @@ func PVCs(items ...metav1.Object) *APIResource {
|
||||
Version: "v1",
|
||||
Name: "persistentvolumeclaims",
|
||||
ShortName: "pvc",
|
||||
Kind: "PersistentVolumeClaim",
|
||||
Namespaced: true,
|
||||
Items: items,
|
||||
}
|
||||
@@ -70,6 +73,7 @@ func PVs(items ...metav1.Object) *APIResource {
|
||||
Version: "v1",
|
||||
Name: "persistentvolumes",
|
||||
ShortName: "pv",
|
||||
Kind: "PersistentVolume",
|
||||
Namespaced: false,
|
||||
Items: items,
|
||||
}
|
||||
@@ -81,6 +85,7 @@ func Secrets(items ...metav1.Object) *APIResource {
|
||||
Version: "v1",
|
||||
Name: "secrets",
|
||||
ShortName: "secrets",
|
||||
Kind: "Secret",
|
||||
Namespaced: true,
|
||||
Items: items,
|
||||
}
|
||||
@@ -92,6 +97,7 @@ func Deployments(items ...metav1.Object) *APIResource {
|
||||
Version: "v1",
|
||||
Name: "deployments",
|
||||
ShortName: "deploy",
|
||||
Kind: "Deployment",
|
||||
Namespaced: true,
|
||||
Items: items,
|
||||
}
|
||||
@@ -103,6 +109,7 @@ func ExtensionsDeployments(items ...metav1.Object) *APIResource {
|
||||
Version: "v1",
|
||||
Name: "deployments",
|
||||
ShortName: "deploy",
|
||||
Kind: "Deployment",
|
||||
Namespaced: true,
|
||||
Items: items,
|
||||
}
|
||||
@@ -115,6 +122,7 @@ func VeleroDeployments(items ...metav1.Object) *APIResource {
|
||||
Version: "v1",
|
||||
Name: "deployments",
|
||||
ShortName: "deploy",
|
||||
Kind: "Deployment",
|
||||
Namespaced: true,
|
||||
Items: items,
|
||||
}
|
||||
@@ -126,6 +134,7 @@ func Namespaces(items ...metav1.Object) *APIResource {
|
||||
Version: "v1",
|
||||
Name: "namespaces",
|
||||
ShortName: "ns",
|
||||
Kind: "Namespace",
|
||||
Namespaced: false,
|
||||
Items: items,
|
||||
}
|
||||
@@ -137,6 +146,7 @@ func ServiceAccounts(items ...metav1.Object) *APIResource {
|
||||
Version: "v1",
|
||||
Name: "serviceaccounts",
|
||||
ShortName: "sa",
|
||||
Kind: "ServiceAccount",
|
||||
Namespaced: true,
|
||||
Items: items,
|
||||
}
|
||||
@@ -148,6 +158,7 @@ func ConfigMaps(items ...metav1.Object) *APIResource {
|
||||
Version: "v1",
|
||||
Name: "configmaps",
|
||||
ShortName: "cm",
|
||||
Kind: "ConfigMap",
|
||||
Namespaced: true,
|
||||
Items: items,
|
||||
}
|
||||
@@ -159,6 +170,7 @@ func CRDs(items ...metav1.Object) *APIResource {
|
||||
Version: "v1beta1",
|
||||
Name: "customresourcedefinitions",
|
||||
ShortName: "crd",
|
||||
Kind: "CustomResourceDefinition",
|
||||
Namespaced: false,
|
||||
Items: items,
|
||||
}
|
||||
@@ -169,6 +181,7 @@ func VSLs(items ...metav1.Object) *APIResource {
|
||||
Group: "velero.io",
|
||||
Version: "v1",
|
||||
Name: "volumesnapshotlocations",
|
||||
Kind: "VolumeSnapshotLocation",
|
||||
Namespaced: true,
|
||||
Items: items,
|
||||
}
|
||||
@@ -179,6 +192,7 @@ func Backups(items ...metav1.Object) *APIResource {
|
||||
Group: "velero.io",
|
||||
Version: "v1",
|
||||
Name: "backups",
|
||||
Kind: "Backup",
|
||||
Namespaced: true,
|
||||
Items: items,
|
||||
}
|
||||
@@ -190,6 +204,7 @@ func Services(items ...metav1.Object) *APIResource {
|
||||
Version: "v1",
|
||||
Name: "services",
|
||||
ShortName: "svc",
|
||||
Kind: "Service",
|
||||
Namespaced: true,
|
||||
Items: items,
|
||||
}
|
||||
@@ -200,6 +215,7 @@ func DataUploads(items ...metav1.Object) *APIResource {
|
||||
Group: "velero.io",
|
||||
Version: "v2alpha1",
|
||||
Name: "datauploads",
|
||||
Kind: "DataUpload",
|
||||
Namespaced: true,
|
||||
Items: items,
|
||||
}
|
||||
|
||||
@@ -26,10 +26,8 @@ import (
|
||||
|
||||
type MapUpdateFunc func(client.Object) []reconcile.Request
|
||||
|
||||
// EnqueueRequestsFromMapUpdateFunc is for the same purpose with EnqueueRequestsFromMapFunc.
|
||||
// Merely, it is more friendly to updating the mapped objects in the MapUpdateFunc, because
|
||||
// on Update event, MapUpdateFunc is called for only once with the new object, so if MapUpdateFunc
|
||||
// does some update to the mapped objects, the update is done for once
|
||||
// EnqueueRequestsFromMapUpdateFunc has the same purpose with handler.EnqueueRequestsFromMapFunc.
|
||||
// MapUpdateFunc is simpler on Update event because mapAndEnqueue is called once with the new object. EnqueueRequestsFromMapFunc is called twice with the old and new object.
|
||||
func EnqueueRequestsFromMapUpdateFunc(fn MapUpdateFunc) handler.EventHandler {
|
||||
return &enqueueRequestsFromMapFunc{
|
||||
toRequests: fn,
|
||||
|
||||
Reference in New Issue
Block a user