Merge branch 'main' into block-uploader-restore-implementation

This commit is contained in:
Lyndon-Li
2026-07-27 14:12:06 +08:00
17 changed files with 578 additions and 553 deletions
+7 -9
View File
@@ -1428,22 +1428,20 @@ func resolveClusterScopedFilterPolicy(
}
func resolveResourceFilter(rf resourcepolicies.ResourceFilter) (*ResolvedResourceFilter, error) {
var selector labels.Selector
if len(rf.LabelSelector) > 0 {
var err error
selector, err = labels.ValidatedSelectorFromSet(labels.Set(rf.LabelSelector))
if err != nil {
return nil, fmt.Errorf("invalid label selector in resource filter: %w", err)
}
selector, err := resourcepolicies.SelectorFromPolicyLabelSelector(rf.LabelSelector)
if err != nil {
return nil, fmt.Errorf("invalid label selector in resource filter: %w", err)
}
var orSelectors []labels.Selector
for _, ols := range rf.OrLabelSelectors {
s, err := labels.ValidatedSelectorFromSet(labels.Set(ols))
s, err := resourcepolicies.SelectorFromPolicyLabelSelector(ols)
if err != nil {
return nil, fmt.Errorf("invalid OR label selector in resource filter: %w", err)
}
orSelectors = append(orSelectors, s)
if s != nil {
orSelectors = append(orSelectors, s)
}
}
var nameIE *collections.IncludesExcludes
+77 -15
View File
@@ -5741,7 +5741,7 @@ func TestResolveResourceFilter(t *testing.T) {
{
name: "valid label selector",
rf: resourcepolicies.ResourceFilter{
LabelSelector: map[string]string{"app": "foo"},
LabelSelector: &resourcepolicies.PolicyLabelSelector{MatchLabels: map[string]string{"app": "foo"}},
},
expectErr: false,
checkResult: func(t *testing.T, r *ResolvedResourceFilter) {
@@ -5754,16 +5754,16 @@ func TestResolveResourceFilter(t *testing.T) {
{
name: "invalid label selector",
rf: resourcepolicies.ResourceFilter{
LabelSelector: map[string]string{"invalid/label/key": "value"},
LabelSelector: &resourcepolicies.PolicyLabelSelector{MatchLabels: map[string]string{"invalid/label/key": "value"}},
},
expectErr: true,
},
{
name: "valid or label selectors",
rf: resourcepolicies.ResourceFilter{
OrLabelSelectors: []map[string]string{
{"app": "foo"},
{"app": "bar"},
OrLabelSelectors: []*resourcepolicies.PolicyLabelSelector{
{MatchLabels: map[string]string{"app": "foo"}},
{MatchLabels: map[string]string{"app": "bar"}},
},
},
expectErr: false,
@@ -5776,8 +5776,8 @@ func TestResolveResourceFilter(t *testing.T) {
{
name: "invalid or label selectors",
rf: resourcepolicies.ResourceFilter{
OrLabelSelectors: []map[string]string{
{"invalid/label/key": "value"},
OrLabelSelectors: []*resourcepolicies.PolicyLabelSelector{
{MatchLabels: map[string]string{"invalid/label/key": "value"}},
},
},
expectErr: true,
@@ -5797,6 +5797,68 @@ func TestResolveResourceFilter(t *testing.T) {
assert.False(t, r.NameIE.ShouldInclude("exc1"))
},
},
{
name: "empty labelSelector is no filter",
rf: resourcepolicies.ResourceFilter{
LabelSelector: &resourcepolicies.PolicyLabelSelector{},
},
expectErr: false,
checkResult: func(t *testing.T, r *ResolvedResourceFilter) {
t.Helper()
require.NotNil(t, r)
assert.Nil(t, r.LabelSelector)
},
},
{
name: "set-based In and DoesNotExist",
rf: resourcepolicies.ResourceFilter{
LabelSelector: &resourcepolicies.PolicyLabelSelector{
MatchExpressions: []resourcepolicies.PolicyLabelSelectorRequirement{
{Key: "environment", Operator: "In", Values: []string{"prod", "staging"}},
{Key: "do-not-backup", Operator: "DoesNotExist"},
},
},
},
expectErr: false,
checkResult: func(t *testing.T, r *ResolvedResourceFilter) {
t.Helper()
require.NotNil(t, r.LabelSelector)
assert.True(t, r.LabelSelector.Matches(labels.Set{"environment": "prod"}))
assert.True(t, r.LabelSelector.Matches(labels.Set{"environment": "staging"}))
assert.False(t, r.LabelSelector.Matches(labels.Set{"environment": "dev"}))
assert.False(t, r.LabelSelector.Matches(labels.Set{"environment": "prod", "do-not-backup": "true"}))
},
},
{
name: "set-based NotIn and Exists",
rf: resourcepolicies.ResourceFilter{
LabelSelector: &resourcepolicies.PolicyLabelSelector{
MatchExpressions: []resourcepolicies.PolicyLabelSelectorRequirement{
{Key: "tier", Operator: "NotIn", Values: []string{"debug"}},
{Key: "app", Operator: "Exists"},
},
},
},
expectErr: false,
checkResult: func(t *testing.T, r *ResolvedResourceFilter) {
t.Helper()
require.NotNil(t, r.LabelSelector)
assert.True(t, r.LabelSelector.Matches(labels.Set{"app": "web", "tier": "frontend"}))
assert.False(t, r.LabelSelector.Matches(labels.Set{"app": "web", "tier": "debug"}))
assert.False(t, r.LabelSelector.Matches(labels.Set{"tier": "frontend"}))
},
},
{
name: "invalid operator",
rf: resourcepolicies.ResourceFilter{
LabelSelector: &resourcepolicies.PolicyLabelSelector{
MatchExpressions: []resourcepolicies.PolicyLabelSelectorRequirement{
{Key: "env", Operator: "Equals", Values: []string{"prod"}},
},
},
},
expectErr: true,
},
}
for _, tc := range tests {
@@ -5834,11 +5896,11 @@ func TestResolveClusterScopedFilterPolicy(t *testing.T) {
ResourceFilters: []resourcepolicies.ResourceFilter{
{
Kinds: []string{"pods", "secrets"},
LabelSelector: map[string]string{"app": "foo"},
LabelSelector: &resourcepolicies.PolicyLabelSelector{MatchLabels: map[string]string{"app": "foo"}},
},
{
Kinds: []string{"invalid-kind"},
LabelSelector: map[string]string{"invalid/label/key": "value"},
LabelSelector: &resourcepolicies.PolicyLabelSelector{MatchLabels: map[string]string{"invalid/label/key": "value"}},
},
},
}
@@ -5852,7 +5914,7 @@ func TestResolveClusterScopedFilterPolicy(t *testing.T) {
ResourceFilters: []resourcepolicies.ResourceFilter{
{
Kinds: []string{"pods", "secrets"},
LabelSelector: map[string]string{"app": "foo"},
LabelSelector: &resourcepolicies.PolicyLabelSelector{MatchLabels: map[string]string{"app": "foo"}},
},
},
}
@@ -5900,11 +5962,11 @@ func TestResolveNamespacedFilterPolicies(t *testing.T) {
ResourceFilters: []resourcepolicies.ResourceFilter{
{
Kinds: []string{"pods"},
LabelSelector: map[string]string{"app": "foo"},
LabelSelector: &resourcepolicies.PolicyLabelSelector{MatchLabels: map[string]string{"app": "foo"}},
},
{
Kinds: []string{"*"},
LabelSelector: map[string]string{"catch": "all"},
LabelSelector: &resourcepolicies.PolicyLabelSelector{MatchLabels: map[string]string{"catch": "all"}},
},
},
},
@@ -5932,7 +5994,7 @@ func TestResolveNamespacedFilterPolicies(t *testing.T) {
ResourceFilters: []resourcepolicies.ResourceFilter{
{
Kinds: []string{"pods"},
LabelSelector: map[string]string{"invalid/label/key": "value"},
LabelSelector: &resourcepolicies.PolicyLabelSelector{MatchLabels: map[string]string{"invalid/label/key": "value"}},
},
},
},
@@ -6016,7 +6078,7 @@ func TestBackupWithResPoliciesLogs(t *testing.T) {
ResourceFilters: []resourcepolicies.ResourceFilter{
{
Kinds: []string{"pods"},
LabelSelector: map[string]string{"invalid/label/key": "value"},
LabelSelector: &resourcepolicies.PolicyLabelSelector{MatchLabels: map[string]string{"invalid/label/key": "value"}},
},
},
}
@@ -6035,7 +6097,7 @@ func TestBackupWithResPoliciesLogs(t *testing.T) {
ResourceFilters: []resourcepolicies.ResourceFilter{
{
Kinds: []string{"pods"},
LabelSelector: map[string]string{"invalid/label/key": "value"},
LabelSelector: &resourcepolicies.PolicyLabelSelector{MatchLabels: map[string]string{"invalid/label/key": "value"}},
},
},
},
-118
View File
@@ -21,7 +21,6 @@ import (
"context"
"encoding/json"
"fmt"
"io"
"sort"
"strconv"
"strings"
@@ -31,7 +30,6 @@ import (
"github.com/cockroachdb/errors"
snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v8/apis/volumesnapshot/v1"
"github.com/sirupsen/logrus"
"github.com/fatih/color"
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
@@ -94,9 +92,6 @@ func DescribeBackup(
if backup.Spec.ResourcePolicy != nil {
d.Println()
DescribeResourcePolicies(d, backup.Spec.ResourcePolicy)
// Display fine-grained filter policies if they exist
DescribeFineGrainedFilterPolicies(ctx, kbClient, d, backup)
}
DescribeGlobalVolumePolicy(d, backup)
@@ -151,119 +146,6 @@ func DescribeGlobalVolumePolicy(d *Describer, backup *velerov1api.Backup) {
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 {
return
}
// Create a discard logger for the resource policies function since this is CLI output context
discardLogger := logrus.New()
discardLogger.Out = io.Discard
resourcePolicies, err := resourcepolicies.GetResourcePoliciesFromBackup(*backup, kbClient, discardLogger)
if err != nil {
// Don't fail the describe if we can't read policies, just skip
return
}
if resourcePolicies == nil {
return
}
clusterScopedFilterPolicy := resourcePolicies.GetClusterScopedFilterPolicy()
if clusterScopedFilterPolicy != nil {
d.Printf("\nCluster Scoped Filter Policy:\n")
d.Printf(" Resource Filters:\n")
for _, rf := range clusterScopedFilterPolicy.ResourceFilters {
kindsStr := strings.Join(rf.Kinds, ", ")
d.Printf(" %s:\n", kindsStr)
// Label selector
if len(rf.LabelSelector) > 0 {
selectorStr := formatLabelMap(rf.LabelSelector)
d.Printf(" Label selector: %s\n", selectorStr)
} else if len(rf.OrLabelSelectors) > 0 {
var orStrs []string
for _, ols := range rf.OrLabelSelectors {
orStrs = append(orStrs, formatLabelMap(ols))
}
d.Printf(" OR label selectors: [%s]\n", strings.Join(orStrs, ", "))
} else {
d.Printf(" Label selector: <none>\n")
}
// Name patterns
if len(rf.Names) > 0 {
d.Printf(" Included names: [%s]\n", strings.Join(rf.Names, ", "))
} else {
d.Printf(" Included names: <none>\n")
}
if len(rf.ExcludedNames) > 0 {
d.Printf(" Excluded names: [%s]\n", strings.Join(rf.ExcludedNames, ", "))
} else {
d.Printf(" Excluded names: <none>\n")
}
}
}
nfPolicies := resourcePolicies.GetNamespacedFilterPolicies()
if len(nfPolicies) > 0 {
d.Printf("\nNamespace-Scoped Filter Policies:\n")
for _, policy := range nfPolicies {
for _, ns := range policy.Namespaces {
d.Printf(" %s:\n", ns)
d.Printf(" Resource Filters:\n")
for _, rf := range policy.ResourceFilters {
var kindsStr string
if rf.IsCatchAll() {
kindsStr = "<catch-all> (all other kinds)"
} else {
kindsStr = strings.Join(rf.Kinds, ", ")
}
d.Printf(" %s:\n", kindsStr)
// Label selector
if len(rf.LabelSelector) > 0 {
selectorStr := formatLabelMap(rf.LabelSelector)
d.Printf(" Label selector: %s\n", selectorStr)
} else if len(rf.OrLabelSelectors) > 0 {
var orStrs []string
for _, ols := range rf.OrLabelSelectors {
orStrs = append(orStrs, formatLabelMap(ols))
}
d.Printf(" OR label selectors: [%s]\n", strings.Join(orStrs, ", "))
} else {
d.Printf(" Label selector: <none>\n")
}
// Name patterns
if len(rf.Names) > 0 {
d.Printf(" Included names: [%s]\n", strings.Join(rf.Names, ", "))
} else {
d.Printf(" Included names: <none>\n")
}
if len(rf.ExcludedNames) > 0 {
d.Printf(" Excluded names: [%s]\n", strings.Join(rf.ExcludedNames, ", "))
} else {
d.Printf(" Excluded names: <none>\n")
}
}
}
}
}
}
func formatLabelMap(labelMap map[string]string) string {
var pairs []string
for k, v := range labelMap {
pairs = append(pairs, fmt.Sprintf("%s=%s", k, v))
}
return strings.Join(pairs, ",")
}
// DescribeUploaderConfigForBackup describes uploader config in human-readable format
func DescribeUploaderConfigForBackup(d *Describer, spec velerov1api.BackupSpec) {
d.Printf("Uploader config:\n")
@@ -18,7 +18,6 @@ package output
import (
"bytes"
"context"
"testing"
"text/tabwriter"
"time"
@@ -26,8 +25,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
corev1api "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
"github.com/vmware-tanzu/velero/internal/volume"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
@@ -897,85 +894,3 @@ func TestDescribeBackupItemOperation(t *testing.T) {
d.out.Flush()
assert.Equal(t, expected, d.buf.String())
}
func TestDescribeFineGrainedFilterPolicies(t *testing.T) {
yamlData := `
version: v1
clusterScopedFilterPolicy:
resourceFilters:
- kinds: ["StorageClass"]
labelSelector: {"app": "velero"}
- kinds: ["ClusterRole"]
orLabelSelectors:
- {"app": "velero"}
- {"app": "test"}
names: ["role1"]
excludedNames: ["role2"]
namespacedFilterPolicies:
- namespaces: ["ns1", "ns2"]
resourceFilters:
- kinds: ["Pod", "ConfigMap"]
labelSelector: {"app": "velero"}
- kinds: ["*"]
`
cm := &corev1api.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "test-policy",
Namespace: "velero",
},
Data: map[string]string{
"policy.yaml": yamlData,
},
}
client := fake.NewClientBuilder().WithRuntimeObjects(cm).Build()
backup := builder.ForBackup("velero", "test-backup").
ResourcePolicies("test-policy").Result()
d := &Describer{
Prefix: "",
out: &tabwriter.Writer{},
buf: &bytes.Buffer{},
}
d.out.Init(d.buf, 0, 8, 2, ' ', 0)
DescribeFineGrainedFilterPolicies(context.Background(), client, d, backup)
d.out.Flush()
expected := `
Cluster Scoped Filter Policy:
Resource Filters:
StorageClass:
Label selector: app=velero
Included names: <none>
Excluded names: <none>
ClusterRole:
OR label selectors: [app=velero, app=test]
Included names: [role1]
Excluded names: [role2]
Namespace-Scoped Filter Policies:
ns1:
Resource Filters:
Pod, ConfigMap:
Label selector: app=velero
Included names: <none>
Excluded names: <none>
<catch-all> (all other kinds):
Label selector: <none>
Included names: <none>
Excluded names: <none>
ns2:
Resource Filters:
Pod, ConfigMap:
Label selector: app=velero
Included names: <none>
Excluded names: <none>
<catch-all> (all other kinds):
Label selector: <none>
Included names: <none>
Excluded names: <none>
`
assert.Equal(t, expected, d.buf.String())
}
@@ -21,10 +21,8 @@ import (
"context"
"encoding/json"
"fmt"
"io"
"strings"
"github.com/sirupsen/logrus"
corev1api "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -57,7 +55,6 @@ func DescribeBackupInSF(
if backup.Spec.ResourcePolicy != nil {
DescribeResourcePoliciesInSF(d, backup.Spec.ResourcePolicy)
DescribeFineGrainedFilterPoliciesInSF(ctx, kbClient, d, backup)
}
DescribeGlobalVolumePolicyInSF(d, backup)
@@ -228,88 +225,6 @@ func DescribeBackupSpecInSF(d *StructuredDescriber, spec velerov1api.BackupSpec)
d.Describe("spec", backupSpecInfo)
}
// DescribeFineGrainedFilterPoliciesInSF adds the clusterScopedFilterPolicy
// and namespacedFilterPolicies sections to the structured describer output when present
// in the ResourcePolicy ConfigMap referenced by the backup.
func DescribeFineGrainedFilterPoliciesInSF(ctx context.Context, kbClient kbclient.Client, d *StructuredDescriber, backup *velerov1api.Backup) {
if backup.Spec.ResourcePolicy == nil {
return
}
discardLogger := logrus.New()
discardLogger.Out = io.Discard
resPolicies, err := resourcepolicies.GetResourcePoliciesFromBackup(*backup, kbClient, discardLogger)
if err != nil || resPolicies == nil {
return
}
clusterScopedFilterPolicy := resPolicies.GetClusterScopedFilterPolicy()
if clusterScopedFilterPolicy != nil {
var clusterScopedFilters []map[string]any
for _, rf := range clusterScopedFilterPolicy.ResourceFilters {
entry := map[string]any{
"kinds": rf.Kinds,
}
if len(rf.LabelSelector) > 0 {
entry["labelSelector"] = rf.LabelSelector
}
if len(rf.OrLabelSelectors) > 0 {
entry["orLabelSelectors"] = rf.OrLabelSelectors
}
if len(rf.Names) > 0 {
entry["names"] = rf.Names
}
if len(rf.ExcludedNames) > 0 {
entry["excludedNames"] = rf.ExcludedNames
}
clusterScopedFilters = append(clusterScopedFilters, entry)
}
d.Describe("clusterScopedFilterPolicy", map[string]any{
"resourceFilters": clusterScopedFilters,
})
}
nfPolicies := resPolicies.GetNamespacedFilterPolicies()
if len(nfPolicies) == 0 {
return
}
var structuredPolicies []map[string]any
for _, policy := range nfPolicies {
for _, ns := range policy.Namespaces {
var rfEntries []map[string]any
for _, rf := range policy.ResourceFilters {
entry := map[string]any{}
if rf.IsCatchAll() {
entry["kinds"] = []string{}
entry["isCatchAll"] = true
} else {
entry["kinds"] = rf.Kinds
}
if len(rf.LabelSelector) > 0 {
entry["labelSelector"] = rf.LabelSelector
}
if len(rf.OrLabelSelectors) > 0 {
entry["orLabelSelectors"] = rf.OrLabelSelectors
}
if len(rf.Names) > 0 {
entry["names"] = rf.Names
}
if len(rf.ExcludedNames) > 0 {
entry["excludedNames"] = rf.ExcludedNames
}
rfEntries = append(rfEntries, entry)
}
structuredPolicies = append(structuredPolicies, map[string]any{
"namespace": ns,
"resourceFilters": rfEntries,
})
}
}
d.Describe("namespacedFilterPolicies", structuredPolicies)
}
// DescribeBackupStatusInSF describes a backup status in structured format.
func DescribeBackupStatusInSF(ctx context.Context, kbClient kbclient.Client, d *StructuredDescriber, backup *velerov1api.Backup, details bool,
insecureSkipTLSVerify bool, caCertPath string, podVolumeBackups []velerov1api.PodVolumeBackup) {
@@ -17,7 +17,6 @@ limitations under the License.
package output
import (
"context"
"reflect"
"testing"
"time"
@@ -25,8 +24,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
corev1api "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
"github.com/vmware-tanzu/velero/internal/volume"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
@@ -731,96 +728,3 @@ func TestDescribeDeleteBackupRequestsInSF(t *testing.T) {
})
}
}
func TestDescribeFineGrainedFilterPoliciesInSF(t *testing.T) {
yamlData := `
version: v1
clusterScopedFilterPolicy:
resourceFilters:
- kinds: ["StorageClass"]
labelSelector: {"app": "velero"}
- kinds: ["ClusterRole"]
orLabelSelectors:
- {"app": "velero"}
- {"app": "test"}
names: ["role1"]
excludedNames: ["role2"]
namespacedFilterPolicies:
- namespaces: ["ns1", "ns2"]
resourceFilters:
- kinds: ["Pod", "ConfigMap"]
labelSelector: {"app": "velero"}
- kinds: ["*"]
`
cm := &corev1api.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "test-policy",
Namespace: "velero",
},
Data: map[string]string{
"policy.yaml": yamlData,
},
}
client := fake.NewClientBuilder().WithRuntimeObjects(cm).Build()
backup := builder.ForBackup("velero", "test-backup").
ResourcePolicies("test-policy").Result()
sd := &StructuredDescriber{
output: make(map[string]any),
format: "",
}
DescribeFineGrainedFilterPoliciesInSF(context.Background(), client, sd, backup)
expect := map[string]any{
"clusterScopedFilterPolicy": map[string]any{
"resourceFilters": []map[string]any{
{
"kinds": []string{"StorageClass"},
"labelSelector": map[string]string{"app": "velero"},
},
{
"kinds": []string{"ClusterRole"},
"orLabelSelectors": []map[string]string{
{"app": "velero"},
{"app": "test"},
},
"names": []string{"role1"},
"excludedNames": []string{"role2"},
},
},
},
"namespacedFilterPolicies": []map[string]any{
{
"namespace": "ns1",
"resourceFilters": []map[string]any{
{
"kinds": []string{"Pod", "ConfigMap"},
"labelSelector": map[string]string{"app": "velero"},
},
{
"kinds": []string{},
"isCatchAll": true,
},
},
},
{
"namespace": "ns2",
"resourceFilters": []map[string]any{
{
"kinds": []string{"Pod", "ConfigMap"},
"labelSelector": map[string]string{"app": "velero"},
},
{
"kinds": []string{},
"isCatchAll": true,
},
},
},
},
}
assert.True(t, reflect.DeepEqual(sd.output, expect))
}
+7 -9
View File
@@ -638,21 +638,19 @@ func resolveRestoreNamespacedFilterPolicies(
func resolveResourceFilter(
rf resourcepolicies.ResourceFilter,
) (*resolvedResourceFilter, error) {
var selector labels.Selector
if len(rf.LabelSelector) > 0 {
var err error
selector, err = labels.ValidatedSelectorFromSet(labels.Set(rf.LabelSelector))
if err != nil {
return nil, fmt.Errorf("invalid label selector in resource filter: %w", err)
}
selector, err := resourcepolicies.SelectorFromPolicyLabelSelector(rf.LabelSelector)
if err != nil {
return nil, fmt.Errorf("invalid label selector in resource filter: %w", err)
}
var orSelectors []labels.Selector
for _, ols := range rf.OrLabelSelectors {
s, err := labels.ValidatedSelectorFromSet(labels.Set(ols))
s, err := resourcepolicies.SelectorFromPolicyLabelSelector(ols)
if err != nil {
return nil, fmt.Errorf("invalid OR label selector in resource filter: %w", err)
}
orSelectors = append(orSelectors, s)
if s != nil {
orSelectors = append(orSelectors, s)
}
}
var nameIE *collections.IncludesExcludes
if len(rf.Names) > 0 || len(rf.ExcludedNames) > 0 {
+2 -1
View File
@@ -170,7 +170,8 @@ namespacedFilterPolicies:
- kinds:
- '*'
labelSelector:
app: test
matchLabels:
app: test
`,
tarball: test.NewTarWriter(t).
AddItems("pods",