Use LabelSelector in Action ResourceSelector

Instead of converting the unstructured item to check for the presence of
the `kube-aggregator.kubernetes.io/automanaged` label, use this label in
the `AppliesTo` to enable the restore logic to select the item. This
means that any item that matches the selector will have restore skipped.

Also add a new test case to the restore action test to check that label
selectors are applied correctly.

Signed-off-by: Bridget McErlean <bmcerlean@vmware.com>
This commit is contained in:
Bridget McErlean
2021-08-11 17:33:31 -04:00
parent 984176f156
commit 368098b727
3 changed files with 39 additions and 126 deletions
+5 -14
View File
@@ -17,10 +17,7 @@ limitations under the License.
package restore
import (
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/runtime"
apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
"k8s.io/kube-aggregator/pkg/controllers/autoregister"
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
@@ -41,20 +38,14 @@ func NewAPIServiceAction(logger logrus.FieldLogger) *APIServiceAction {
func (a *APIServiceAction) AppliesTo() (velero.ResourceSelector, error) {
return velero.ResourceSelector{
IncludedResources: []string{"apiservices"},
LabelSelector: autoregister.AutoRegisterManagedLabel,
}, nil
}
func (a *APIServiceAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) {
apiService := new(apiregistrationv1.APIService)
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(input.Item.UnstructuredContent(), apiService); err != nil {
return nil, errors.WithStack(err)
}
a.logger.Info("Executing APIServiceAction")
defer a.logger.Info("Done executing APIServiceAction")
output := velero.NewRestoreItemActionExecuteOutput(input.Item)
if _, ok := apiService.Labels[autoregister.AutoRegisterManagedLabel]; ok {
output = output.WithoutRestore()
}
return output, nil
a.logger.Infof("Skipping restore of APIService as it is managed by Kubernetes")
return velero.NewRestoreItemActionExecuteOutput(input.Item).WithoutRestore(), nil
}
+17 -111
View File
@@ -1,5 +1,5 @@
/*
Copyright 2017 the Velero contributors.
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.
@@ -19,129 +19,35 @@ package restore
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
"k8s.io/kube-aggregator/pkg/controllers/autoregister"
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
velerotest "github.com/vmware-tanzu/velero/pkg/test"
)
func TestAPIServiceActionExecute(t *testing.T) {
tests := []struct {
name string
obj apiregistrationv1.APIService
skipRestore bool
}{
{
name: "APIService with no labels should be restored without modification",
obj: apiregistrationv1.APIService{
ObjectMeta: metav1.ObjectMeta{
Name: "v1.foo.velero.io",
},
},
skipRestore: false,
},
{
name: "Non-Local APIService without Kubernetes managed label should be restored without modification",
obj: apiregistrationv1.APIService{
ObjectMeta: metav1.ObjectMeta{
Name: "v1.foo.velero.io",
Labels: map[string]string{
"component": "velero",
},
},
Spec: apiregistrationv1.APIServiceSpec{
Group: "velero.io",
Version: "v1",
Service: &apiregistrationv1.ServiceReference{
Namespace: "velero",
Name: "velero-aggregated-api-server",
},
},
},
skipRestore: false,
},
{
name: "APIService with Kubernetes managed label with 'true' value should not be restored",
obj: apiregistrationv1.APIService{
ObjectMeta: metav1.ObjectMeta{
Name: "v1.foo.velero.io",
Labels: map[string]string{
autoregister.AutoRegisterManagedLabel: "true",
},
},
},
skipRestore: true,
},
{
name: "APIService with Kubernetes managed label with 'onstart' value should not be restored",
obj: apiregistrationv1.APIService{
ObjectMeta: metav1.ObjectMeta{
Name: "v1.foo.velero.io",
Labels: map[string]string{
autoregister.AutoRegisterManagedLabel: "onstart",
},
},
},
skipRestore: true,
},
{
name: "APIService with Kubernetes managed label with any value should not be restored",
obj: apiregistrationv1.APIService{
ObjectMeta: metav1.ObjectMeta{
Name: "v1.foo.velero.io",
Labels: map[string]string{
autoregister.AutoRegisterManagedLabel: "randomvalue",
},
},
},
skipRestore: true,
},
{
name: "Non-Local APIService with Kubernetes managed label should not be restored",
obj: apiregistrationv1.APIService{
ObjectMeta: metav1.ObjectMeta{
Name: "v1.foo.velero.io",
Labels: map[string]string{
autoregister.AutoRegisterManagedLabel: "onstart",
},
},
Spec: apiregistrationv1.APIServiceSpec{
Group: "velero.io",
Version: "v1",
Service: &apiregistrationv1.ServiceReference{
Namespace: "velero",
Name: "velero-aggregated-api-server",
},
},
},
skipRestore: true,
func TestAPIServiceActionExecuteSkipsRestore(t *testing.T) {
obj := apiregistrationv1.APIService{
ObjectMeta: metav1.ObjectMeta{
Name: "v1.test.velero.io",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
action := NewAPIServiceAction(velerotest.NewLogger())
unstructuredAPIService, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&obj)
require.NoError(t, err)
unstructuredAPIService, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&test.obj)
require.NoError(t, err)
action := NewAPIServiceAction(velerotest.NewLogger())
res, err := action.Execute(&velero.RestoreItemActionExecuteInput{
Item: &unstructured.Unstructured{Object: unstructuredAPIService},
ItemFromBackup: &unstructured.Unstructured{Object: unstructuredAPIService},
})
require.NoError(t, err)
res, err := action.Execute(&velero.RestoreItemActionExecuteInput{
Item: &unstructured.Unstructured{Object: unstructuredAPIService},
ItemFromBackup: &unstructured.Unstructured{Object: unstructuredAPIService},
})
require.NoError(t, err)
var apiService apiregistrationv1.APIService
require.NoError(t, runtime.DefaultUnstructuredConverter.FromUnstructured(res.UpdatedItem.UnstructuredContent(), &apiService))
assert.Equal(t, test.obj, apiService)
assert.Equal(t, test.skipRestore, res.SkipRestore)
})
}
var apiService apiregistrationv1.APIService
require.NoError(t, runtime.DefaultUnstructuredConverter.FromUnstructured(res.UpdatedItem.UnstructuredContent(), &apiService))
require.Equal(t, obj, apiService)
require.Equal(t, true, res.SkipRestore)
}
+17 -1
View File
@@ -1,5 +1,5 @@
/*
Copyright 2019 the Velero contributors.
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.
@@ -1139,6 +1139,22 @@ func TestRestoreActionsRunForCorrectItems(t *testing.T) {
new(recordResourcesAction).ForNamespace("ns-1").ForResource("pods"): {"ns-1/pod-1"},
},
},
{
name: "single action with a resource and label selector runs only for resources matching that label",
restore: defaultRestore().Result(),
backup: defaultBackup().Result(),
tarball: test.NewTarWriter(t).
AddItems("pods",
builder.ForPod("ns-1", "pod-1").ObjectMeta(builder.WithLabels("restore-resource", "true")).Result(),
builder.ForPod("ns-1", "pod-2").ObjectMeta(builder.WithLabels("do-not-restore-resource", "true")).Result(),
builder.ForPod("ns-2", "pod-1").Result(),
builder.ForPod("ns-2", "pod-2").ObjectMeta(builder.WithLabels("restore-resource")).Result(),
).Done(),
apiResources: []*test.APIResource{test.Pods()},
actions: map[*recordResourcesAction][]string{
new(recordResourcesAction).ForResource("pods").ForLabelSelector("restore-resource"): {"ns-1/pod-1", "ns-2/pod-2"},
},
},
{
name: "multiple actions, each with a different resource selector using short name, run for matching resources",
restore: defaultRestore().Result(),