Merge branch 'main' into block-uploader-snapshot-operations

This commit is contained in:
lyndon-li
2026-06-29 15:39:45 +08:00
committed by GitHub
22 changed files with 1435 additions and 152 deletions
+5
View File
@@ -80,6 +80,11 @@ const (
// timeout value for backup to plugins.
ResourceTimeoutAnnotation = "velero.io/resource-timeout"
// GlobalBackupVolumePolicyConfigMapAnnotation is the annotation key used to record the
// name of the cluster-wide global backup volume policies ConfigMap that contributed to a
// backup, so that `velero backup describe` can surface it.
GlobalBackupVolumePolicyConfigMapAnnotation = "velero.io/global-backup-volume-policy-configmap"
// AsyncOperationIDLabel is the label key used to identify the async operation ID
AsyncOperationIDLabel = "velero.io/async-operation-id"
+10
View File
@@ -125,6 +125,16 @@ type RestoreSpec struct {
// +nullable
ResourceModifier *corev1api.TypedLocalObjectReference `json:"resourceModifier,omitempty"`
// ResourcePolicy specifies the reference to a ConfigMap containing resource
// filter policies for this restore. The ConfigMap can contain a
// namespacedFilterPolicies section that specifies per-namespace resource type
// filters, label selectors, and resource name patterns, and a
// clusterScopedFilterPolicy section for per-kind filtering of cluster-scoped
// resources. The ConfigMap format is the same as for BackupSpec.ResourcePolicy.
// +optional
// +nullable
ResourcePolicy *corev1api.TypedLocalObjectReference `json:"resourcePolicy,omitempty"`
// UploaderConfig specifies the configuration for the restore.
// +optional
// +nullable
@@ -1415,6 +1415,11 @@ func (in *RestoreSpec) DeepCopyInto(out *RestoreSpec) {
*out = new(corev1.TypedLocalObjectReference)
(*in).DeepCopyInto(*out)
}
if in.ResourcePolicy != nil {
in, out := &in.ResourcePolicy, &out.ResourcePolicy
*out = new(corev1.TypedLocalObjectReference)
(*in).DeepCopyInto(*out)
}
if in.UploaderConfig != nil {
in, out := &in.UploaderConfig, &out.UploaderConfig
*out = new(UploaderConfigForRestore)
+10
View File
@@ -19,6 +19,7 @@ package builder
import (
"time"
corev1api "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
@@ -171,3 +172,12 @@ func (b *RestoreBuilder) ItemOperationTimeout(timeout time.Duration) *RestoreBui
b.object.Spec.ItemOperationTimeout.Duration = timeout
return b
}
// ResourcePoliciesConfigmap sets the Restore's resource policies configmap.
func (b *RestoreBuilder) ResourcePoliciesConfigmap(name string) *RestoreBuilder {
b.object.Spec.ResourcePolicy = &corev1api.TypedLocalObjectReference{
Kind: "configmap",
Name: name,
}
return b
}
+36
View File
@@ -0,0 +1,36 @@
/*
Copyright The Velero Contributors.
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 builder
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestRestoreBuilder_ResourcePoliciesConfigmap(t *testing.T) {
restore := ForRestore("velero", "my-restore").
ResourcePoliciesConfigmap("my-policy-cm").
Result()
assert.Equal(t, "velero", restore.Namespace)
assert.Equal(t, "my-restore", restore.Name)
assert.NotNil(t, restore.Spec.ResourcePolicy)
assert.Equal(t, "configmap", restore.Spec.ResourcePolicy.Kind)
assert.Equal(t, "my-policy-cm", restore.Spec.ResourcePolicy.Name)
assert.Equal(t, (*string)(nil), restore.Spec.ResourcePolicy.APIGroup)
}
+43 -36
View File
@@ -145,42 +145,43 @@ var (
)
type Config struct {
PluginDir string
MetricsAddress string
DefaultBackupLocation string // TODO(2.0) Deprecate defaultBackupLocation
BackupSyncPeriod time.Duration
PodVolumeOperationTimeout time.Duration
ResourceTerminatingTimeout time.Duration
DefaultBackupTTL time.Duration
DefaultVGSLabelKey string
StoreValidationFrequency time.Duration
DefaultCSISnapshotTimeout time.Duration
DefaultItemOperationTimeout time.Duration
ResourceTimeout time.Duration
RestoreResourcePriorities types.Priorities
DefaultVolumeSnapshotLocations flag.Map
RestoreOnly bool
DisabledControllers []string
ClientQPS float32
ClientBurst int
ClientPageSize int
ProfilerAddress string
LogLevel *logging.LevelFlag
LogFormat *logging.FormatFlag
RepoMaintenanceFrequency time.Duration
GarbageCollectionFrequency time.Duration
ItemOperationSyncFrequency time.Duration
DefaultVolumesToFsBackup bool
UploaderType string
MaxConcurrentK8SConnections int
DefaultSnapshotMoveData bool
DisableInformerCache bool
ScheduleSkipImmediately bool
CredentialsDirectory string
BackupRepoConfig string
RepoMaintenanceJobConfig string
ItemBlockWorkerCount int
ConcurrentBackups int
PluginDir string
MetricsAddress string
DefaultBackupLocation string // TODO(2.0) Deprecate defaultBackupLocation
BackupSyncPeriod time.Duration
PodVolumeOperationTimeout time.Duration
ResourceTerminatingTimeout time.Duration
DefaultBackupTTL time.Duration
DefaultVGSLabelKey string
StoreValidationFrequency time.Duration
DefaultCSISnapshotTimeout time.Duration
DefaultItemOperationTimeout time.Duration
ResourceTimeout time.Duration
RestoreResourcePriorities types.Priorities
DefaultVolumeSnapshotLocations flag.Map
RestoreOnly bool
DisabledControllers []string
ClientQPS float32
ClientBurst int
ClientPageSize int
ProfilerAddress string
LogLevel *logging.LevelFlag
LogFormat *logging.FormatFlag
RepoMaintenanceFrequency time.Duration
GarbageCollectionFrequency time.Duration
ItemOperationSyncFrequency time.Duration
DefaultVolumesToFsBackup bool
UploaderType string
MaxConcurrentK8SConnections int
DefaultSnapshotMoveData bool
DisableInformerCache bool
ScheduleSkipImmediately bool
CredentialsDirectory string
BackupRepoConfig string
RepoMaintenanceJobConfig string
ItemBlockWorkerCount int
ConcurrentBackups int
GlobalBackupVolumePoliciesConfigMap string
}
func GetDefaultConfig() *Config {
@@ -275,4 +276,10 @@ func (c *Config) BindFlags(flags *pflag.FlagSet) {
c.ConcurrentBackups,
"Number of backups to process concurrently. Default is one. Optional.",
)
flags.StringVar(
&c.GlobalBackupVolumePoliciesConfigMap,
"global-backup-volume-policies-configmap",
c.GlobalBackupVolumePoliciesConfigMap,
"The name of a ConfigMap in the Velero install namespace holding global backup volume policies that are merged into every backup. Optional.",
)
}
+12
View File
@@ -5,6 +5,7 @@ import (
"github.com/spf13/pflag"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestGetDefaultConfig(t *testing.T) {
@@ -17,3 +18,14 @@ func TestBindFlags(t *testing.T) {
config.BindFlags(pflag.CommandLine)
assert.Equal(t, 1, config.ItemBlockWorkerCount)
}
func TestGlobalBackupVolumePoliciesConfigMapFlag(t *testing.T) {
config := GetDefaultConfig()
// Opt-in: defaults to empty.
assert.Empty(t, config.GlobalBackupVolumePoliciesConfigMap)
flags := pflag.NewFlagSet("test", pflag.ContinueOnError)
config.BindFlags(flags)
require.NoError(t, flags.Parse([]string{"--global-backup-volume-policies-configmap", "global-volume-policy"}))
assert.Equal(t, "global-volume-policy", config.GlobalBackupVolumePoliciesConfigMap)
}
+10
View File
@@ -57,6 +57,7 @@ import (
"github.com/vmware-tanzu/velero/internal/credentials"
"github.com/vmware-tanzu/velero/internal/hook"
"github.com/vmware-tanzu/velero/internal/resourcepolicies"
"github.com/vmware-tanzu/velero/internal/storage"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
velerov2alpha1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1"
@@ -390,6 +391,14 @@ func (s *server) setupBeforeControllerRun() error {
if err := setDefaultBackupLocation(s.ctx, client, s.namespace, s.config.DefaultBackupLocation, s.logger); err != nil {
return err
}
// Validate the global backup volume policies ConfigMap early, so misconfigurations fail fast.
if s.config.GlobalBackupVolumePoliciesConfigMap != "" {
if _, err := resourcepolicies.GetGlobalResourcePolicies(client, s.namespace, s.config.GlobalBackupVolumePoliciesConfigMap, s.logger); err != nil {
return err
}
s.logger.WithField("configmap", s.config.GlobalBackupVolumePoliciesConfigMap).Info("Loaded global backup volume policies")
}
return nil
}
@@ -671,6 +680,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string
s.config.ItemBlockWorkerCount,
s.config.ConcurrentBackups,
s.crClient,
s.config.GlobalBackupVolumePoliciesConfigMap,
).SetupWithManager(s.mgr); err != nil {
s.logger.Fatal(err, "unable to create controller", "controller", constant.ControllerBackup)
}
+15
View File
@@ -99,6 +99,8 @@ func DescribeBackup(
DescribeFineGrainedFilterPolicies(ctx, kbClient, d, backup)
}
DescribeGlobalVolumePolicy(d, backup)
if backup.Spec.UploaderConfig != nil && backup.Spec.UploaderConfig.ParallelFilesUpload > 0 {
d.Println()
DescribeUploaderConfigForBackup(d, backup.Spec)
@@ -136,6 +138,19 @@ func DescribeResourcePolicies(d *Describer, resPolicies *corev1api.TypedLocalObj
d.Printf("\tName:\t%s\n", resPolicies.Name)
}
// DescribeGlobalVolumePolicy describes the cluster-wide global backup volume policies
// ConfigMap that contributed to the backup, if any.
func DescribeGlobalVolumePolicy(d *Describer, backup *velerov1api.Backup) {
name := backup.Annotations[velerov1api.GlobalBackupVolumePolicyConfigMapAnnotation]
if name == "" {
return
}
d.Println()
d.Printf("Global volume policies:\n")
d.Printf("\tType:\t%s\n", resourcepolicies.ConfigmapRefType)
d.Printf("\tName:\t%s\n", name)
}
// DescribeFineGrainedFilterPolicies describes cluster-scoped and namespace-scoped filter policies if present
func DescribeFineGrainedFilterPolicies(ctx context.Context, kbClient kbclient.Client, d *Describer, backup *velerov1api.Backup) {
if backup.Spec.ResourcePolicy == nil {
@@ -72,6 +72,34 @@ func TestDescribeResourcePolicies(t *testing.T) {
assert.Equal(t, expect, d.buf.String())
}
func TestDescribeGlobalVolumePolicy(t *testing.T) {
newDescriber := func() *Describer {
d := &Describer{out: &tabwriter.Writer{}, buf: &bytes.Buffer{}}
d.out.Init(d.buf, 0, 8, 2, ' ', 0)
return d
}
// No annotation: nothing is printed.
d := newDescriber()
DescribeGlobalVolumePolicy(d, builder.ForBackup("velero", "b").Result())
d.out.Flush()
assert.Empty(t, d.buf.String())
// Annotation present: ConfigMap name is surfaced.
d = newDescriber()
backup := builder.ForBackup("velero", "b").
ObjectMeta(builder.WithAnnotations(velerov1api.GlobalBackupVolumePolicyConfigMapAnnotation, "global-volume-policy")).
Result()
DescribeGlobalVolumePolicy(d, backup)
d.out.Flush()
expect := `
Global volume policies:
Type: configmap
Name: global-volume-policy
`
assert.Equal(t, expect, d.buf.String())
}
func TestDescribeBackupSpec(t *testing.T) {
input1 := builder.ForBackup("test-ns", "test-backup-1").
IncludedNamespaces("inc-ns-1", "inc-ns-2").
@@ -60,6 +60,8 @@ func DescribeBackupInSF(
DescribeFineGrainedFilterPoliciesInSF(ctx, kbClient, d, backup)
}
DescribeGlobalVolumePolicyInSF(d, backup)
status := backup.Status
if len(status.ValidationErrors) > 0 {
d.Describe("validationErrors", status.ValidationErrors)
@@ -699,6 +701,19 @@ func DescribeResourcePoliciesInSF(d *StructuredDescriber, resPolicies *corev1api
d.Describe("resourcePolicies", policiesInfo)
}
// DescribeGlobalVolumePolicyInSF describes the global backup volume policies ConfigMap that
// contributed to the backup, if any, in structured format.
func DescribeGlobalVolumePolicyInSF(d *StructuredDescriber, backup *velerov1api.Backup) {
name := backup.Annotations[velerov1api.GlobalBackupVolumePolicyConfigMapAnnotation]
if name == "" {
return
}
d.Describe("globalVolumePolicies", map[string]any{
"type": resourcepolicies.ConfigmapRefType,
"name": name,
})
}
func describeResultInSF(m map[string]any, result results.Result) {
m["velero"], m["cluster"], m["namespace"] = []string{}, []string{}, []string{}
@@ -627,6 +627,27 @@ func TestDescribeResourcePoliciesInSF(t *testing.T) {
assert.True(t, reflect.DeepEqual(sd.output, expect))
}
func TestDescribeGlobalVolumePolicyInSF(t *testing.T) {
// No annotation: nothing is added to the output.
sd := &StructuredDescriber{output: make(map[string]any), format: ""}
DescribeGlobalVolumePolicyInSF(sd, builder.ForBackup("velero", "b").Result())
assert.Empty(t, sd.output)
// Annotation present: the ConfigMap name is surfaced.
sd = &StructuredDescriber{output: make(map[string]any), format: ""}
backup := builder.ForBackup("velero", "b").
ObjectMeta(builder.WithAnnotations(velerov1api.GlobalBackupVolumePolicyConfigMapAnnotation, "global-volume-policy")).
Result()
DescribeGlobalVolumePolicyInSF(sd, backup)
expectGlobal := map[string]any{
"globalVolumePolicies": map[string]any{
"type": "configmap",
"name": "global-volume-policy",
},
}
assert.True(t, reflect.DeepEqual(sd.output, expectGlobal))
}
func TestDescribeBackupResultInSF(t *testing.T) {
input := results.Result{
Velero: []string{"msg-1", "msg-2"},
+60 -53
View File
@@ -84,32 +84,33 @@ var autoExcludeClusterScopedResources = []string{
}
type backupReconciler struct {
ctx context.Context
logger logrus.FieldLogger
discoveryHelper discovery.Helper
backupper pkgbackup.Backupper
kbClient kbclient.Client
clock clock.WithTickerAndDelayedExecution
backupLogLevel logrus.Level
newPluginManager func(logrus.FieldLogger) clientmgmt.Manager
backupTracker BackupTracker
defaultBackupLocation string
defaultVolumesToFsBackup bool
defaultBackupTTL time.Duration
defaultVGSLabelKey string
defaultCSISnapshotTimeout time.Duration
resourceTimeout time.Duration
defaultItemOperationTimeout time.Duration
defaultSnapshotLocations map[string]string
metrics *metrics.ServerMetrics
backupStoreGetter persistence.ObjectBackupStoreGetter
formatFlag logging.Format
credentialFileStore credentials.FileStore
maxConcurrentK8SConnections int
defaultSnapshotMoveData bool
globalCRClient kbclient.Client
itemBlockWorkerCount int
concurrentBackups int
ctx context.Context
logger logrus.FieldLogger
discoveryHelper discovery.Helper
backupper pkgbackup.Backupper
kbClient kbclient.Client
clock clock.WithTickerAndDelayedExecution
backupLogLevel logrus.Level
newPluginManager func(logrus.FieldLogger) clientmgmt.Manager
backupTracker BackupTracker
defaultBackupLocation string
defaultVolumesToFsBackup bool
defaultBackupTTL time.Duration
defaultVGSLabelKey string
defaultCSISnapshotTimeout time.Duration
resourceTimeout time.Duration
defaultItemOperationTimeout time.Duration
defaultSnapshotLocations map[string]string
metrics *metrics.ServerMetrics
backupStoreGetter persistence.ObjectBackupStoreGetter
formatFlag logging.Format
credentialFileStore credentials.FileStore
maxConcurrentK8SConnections int
defaultSnapshotMoveData bool
globalCRClient kbclient.Client
itemBlockWorkerCount int
concurrentBackups int
globalVolumePoliciesConfigMap string
}
func NewBackupReconciler(
@@ -138,34 +139,36 @@ func NewBackupReconciler(
itemBlockWorkerCount int,
concurrentBackups int,
globalCRClient kbclient.Client,
globalVolumePoliciesConfigMap string,
) *backupReconciler {
b := &backupReconciler{
ctx: ctx,
discoveryHelper: discoveryHelper,
backupper: backupper,
clock: &clock.RealClock{},
logger: logger,
backupLogLevel: backupLogLevel,
newPluginManager: newPluginManager,
backupTracker: backupTracker,
kbClient: kbClient,
defaultBackupLocation: defaultBackupLocation,
defaultVolumesToFsBackup: defaultVolumesToFsBackup,
defaultBackupTTL: defaultBackupTTL,
defaultVGSLabelKey: defaultVGSLabelKey,
defaultCSISnapshotTimeout: defaultCSISnapshotTimeout,
resourceTimeout: resourceTimeout,
defaultItemOperationTimeout: defaultItemOperationTimeout,
defaultSnapshotLocations: defaultSnapshotLocations,
metrics: metrics,
backupStoreGetter: backupStoreGetter,
formatFlag: formatFlag,
credentialFileStore: credentialStore,
maxConcurrentK8SConnections: maxConcurrentK8SConnections,
defaultSnapshotMoveData: defaultSnapshotMoveData,
itemBlockWorkerCount: itemBlockWorkerCount,
concurrentBackups: max(concurrentBackups, 1),
globalCRClient: globalCRClient,
ctx: ctx,
discoveryHelper: discoveryHelper,
backupper: backupper,
clock: &clock.RealClock{},
logger: logger,
backupLogLevel: backupLogLevel,
newPluginManager: newPluginManager,
backupTracker: backupTracker,
kbClient: kbClient,
defaultBackupLocation: defaultBackupLocation,
defaultVolumesToFsBackup: defaultVolumesToFsBackup,
defaultBackupTTL: defaultBackupTTL,
defaultVGSLabelKey: defaultVGSLabelKey,
defaultCSISnapshotTimeout: defaultCSISnapshotTimeout,
resourceTimeout: resourceTimeout,
defaultItemOperationTimeout: defaultItemOperationTimeout,
defaultSnapshotLocations: defaultSnapshotLocations,
metrics: metrics,
backupStoreGetter: backupStoreGetter,
formatFlag: formatFlag,
credentialFileStore: credentialStore,
maxConcurrentK8SConnections: maxConcurrentK8SConnections,
defaultSnapshotMoveData: defaultSnapshotMoveData,
itemBlockWorkerCount: itemBlockWorkerCount,
concurrentBackups: max(concurrentBackups, 1),
globalCRClient: globalCRClient,
globalVolumePoliciesConfigMap: globalVolumePoliciesConfigMap,
}
b.updateTotalBackupMetric()
return b
@@ -587,9 +590,13 @@ func (b *backupReconciler) prepareBackupRequest(ctx context.Context, backup *vel
request.Status.ValidationErrors = append(request.Status.ValidationErrors, "encountered labelSelector as well as orLabelSelectors in backup spec, only one can be specified")
}
resourcePolicies, err := resourcepolicies.GetResourcePoliciesFromBackup(*request.Backup, b.kbClient, logger)
resourcePolicies, err := resourcepolicies.GetResourcePoliciesFromBackupWithGlobal(
*request.Backup, b.kbClient, b.globalVolumePoliciesConfigMap, request.Namespace, logger)
if err != nil {
request.Status.ValidationErrors = append(request.Status.ValidationErrors, err.Error())
} else if b.globalVolumePoliciesConfigMap != "" {
// Record the contributing global volume policies ConfigMap so `velero backup describe` can surface it.
request.Annotations[velerov1api.GlobalBackupVolumePolicyConfigMapAnnotation] = b.globalVolumePoliciesConfigMap
}
if resourcePolicies != nil && resourcePolicies.GetIncludeExcludePolicy() != nil && collections.UseOldResourceFilters(request.Spec) {
request.Status.ValidationErrors = append(request.Status.ValidationErrors, "include-resources, exclude-resources and include-cluster-resources are old filter parameters.\n"+
+95
View File
@@ -46,6 +46,7 @@ import (
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
fakeClient "sigs.k8s.io/controller-runtime/pkg/client/fake"
"github.com/vmware-tanzu/velero/internal/resourcepolicies"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
pkgbackup "github.com/vmware-tanzu/velero/pkg/backup"
"github.com/vmware-tanzu/velero/pkg/builder"
@@ -2076,6 +2077,100 @@ namespacedFilterPolicies:
assert.True(t, hasTargetError, "expected validation error about namespacedFilterPolicies incompatibility with old-style filters, got: %v", res.Status.ValidationErrors)
}
// TestPrepareBackupRequest_GlobalVolumePolicies verifies that the cluster-wide global backup
// volume policies are merged into the request and that the contributing ConfigMap is recorded
// on the backup so `velero backup describe` can surface it.
func TestPrepareBackupRequest_GlobalVolumePolicies(t *testing.T) {
formatFlag := logging.FormatText
logger := logging.DefaultLogger(logrus.DebugLevel, formatFlag)
globalCM := &corev1api.ConfigMap{
ObjectMeta: metav1.ObjectMeta{Name: "global-volume-policy", Namespace: velerov1api.DefaultNamespace},
Data: map[string]string{"policies.yaml": `version: v1
volumePolicies:
- conditions:
storageClass:
- gp2
action:
type: skip
`},
}
fakeClient := velerotest.NewFakeControllerRuntimeClient(t, globalCM,
builder.ForBackupStorageLocation(velerov1api.DefaultNamespace, "loc-1").Result())
apiServer := velerotest.NewAPIServer(t)
discoveryHelper, err := discovery.NewHelper(apiServer.DiscoveryClient, logger)
require.NoError(t, err)
c := &backupReconciler{
logger: logger,
discoveryHelper: discoveryHelper,
kbClient: fakeClient,
clock: &clock.RealClock{},
formatFlag: formatFlag,
defaultBackupLocation: "loc-1",
globalVolumePoliciesConfigMap: "global-volume-policy",
}
backup := defaultBackup().StorageLocation("loc-1").Result()
res := c.prepareBackupRequest(ctx, backup, logger)
defer res.WorkerPool.Stop()
// The global volume policies must load cleanly (no policy-related validation error).
for _, e := range res.Status.ValidationErrors {
assert.NotContains(t, e, "global backup volume policies")
}
require.NotNil(t, res.ResPolicies)
assert.Equal(t, "global-volume-policy", res.Annotations[velerov1api.GlobalBackupVolumePolicyConfigMapAnnotation])
action, err := res.ResPolicies.GetMatchAction(resourcepolicies.VolumeFilterData{
PersistentVolume: &corev1api.PersistentVolume{Spec: corev1api.PersistentVolumeSpec{StorageClassName: "gp2"}},
})
require.NoError(t, err)
require.NotNil(t, action)
assert.Equal(t, resourcepolicies.Skip, action.Type)
}
// TestPrepareBackupRequest_GlobalVolumePolicies_LoadError verifies that when the configured
// global backup volume policies ConfigMap cannot be loaded, a validation error is recorded and
// the contributing-ConfigMap annotation is not set on the backup.
func TestPrepareBackupRequest_GlobalVolumePolicies_LoadError(t *testing.T) {
formatFlag := logging.FormatText
logger := logging.DefaultLogger(logrus.DebugLevel, formatFlag)
// No ConfigMap with this name exists, so loading the global policies fails.
fakeClient := velerotest.NewFakeControllerRuntimeClient(t,
builder.ForBackupStorageLocation(velerov1api.DefaultNamespace, "loc-1").Result())
apiServer := velerotest.NewAPIServer(t)
discoveryHelper, err := discovery.NewHelper(apiServer.DiscoveryClient, logger)
require.NoError(t, err)
c := &backupReconciler{
logger: logger,
discoveryHelper: discoveryHelper,
kbClient: fakeClient,
clock: &clock.RealClock{},
formatFlag: formatFlag,
defaultBackupLocation: "loc-1",
globalVolumePoliciesConfigMap: "missing-global-volume-policy",
}
backup := defaultBackup().StorageLocation("loc-1").Result()
res := c.prepareBackupRequest(ctx, backup, logger)
defer res.WorkerPool.Stop()
// The failure to load the global policies must surface as a validation error.
var hasGlobalPolicyError bool
for _, e := range res.Status.ValidationErrors {
if strings.Contains(e, "global backup volume policies") {
hasGlobalPolicyError = true
}
}
assert.True(t, hasGlobalPolicyError, "expected a validation error about global backup volume policies, got: %v", res.Status.ValidationErrors)
// The annotation is only set when the policies load successfully.
assert.Empty(t, res.Annotations[velerov1api.GlobalBackupVolumePolicyConfigMapAnnotation])
}
// TestPrepareBackupRequest_ClusterScopedFilterPolicyIncompatibleWithOldFilters verifies
// that a backup referencing a ResourcePolicy ConfigMap with clusterScopedFilterPolicy
// produces a validation error when old-style resource filters are also set on the spec.