Convert Pod Volume Restore resource/controller to the Kubebuilder framework

Convert Pod Volume Restore resource/controller to the Kubebuilder framework

Fixes #4134

Signed-off-by: Wenkai Yin(尹文开) <yinw@vmware.com>
This commit is contained in:
Wenkai Yin(尹文开)
2022-04-26 16:23:38 +08:00
parent 3ec96e2eac
commit c2c211fefb
13 changed files with 381 additions and 694 deletions
@@ -17,64 +17,64 @@ limitations under the License.
package controller
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"k8s.io/apimachinery/pkg/runtime"
"github.com/sirupsen/logrus"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
"github.com/stretchr/testify/assert"
corev1api "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/sets"
corev1listers "k8s.io/client-go/listers/core/v1"
"k8s.io/client-go/tools/cache"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
velerofake "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/fake"
veleroinformers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions"
velerov1listers "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1"
"github.com/vmware-tanzu/velero/pkg/restic"
velerotest "github.com/vmware-tanzu/velero/pkg/test"
)
func TestPVRHandler(t *testing.T) {
func TestShouldProcess(t *testing.T) {
controllerNode := "foo"
tests := []struct {
name string
obj *velerov1api.PodVolumeRestore
pod *corev1api.Pod
shouldEnqueue bool
name string
obj *velerov1api.PodVolumeRestore
pod *corev1api.Pod
shouldProcessed bool
}{
{
name: "InProgress phase pvr should not be enqueued",
name: "InProgress phase pvr should not be processed",
obj: &velerov1api.PodVolumeRestore{
Status: velerov1api.PodVolumeRestoreStatus{
Phase: velerov1api.PodVolumeRestorePhaseInProgress,
},
},
shouldEnqueue: false,
shouldProcessed: false,
},
{
name: "Completed phase pvr should not be enqueued",
name: "Completed phase pvr should not be processed",
obj: &velerov1api.PodVolumeRestore{
Status: velerov1api.PodVolumeRestoreStatus{
Phase: velerov1api.PodVolumeRestorePhaseCompleted,
},
},
shouldEnqueue: false,
shouldProcessed: false,
},
{
name: "Failed phase pvr should not be enqueued",
name: "Failed phase pvr should not be processed",
obj: &velerov1api.PodVolumeRestore{
Status: velerov1api.PodVolumeRestoreStatus{
Phase: velerov1api.PodVolumeRestorePhaseFailed,
},
},
shouldEnqueue: false,
shouldProcessed: false,
},
{
name: "Unable to get pvr's pod should not be enqueued",
name: "Unable to get pvr's pod should not be processed",
obj: &velerov1api.PodVolumeRestore{
Spec: velerov1api.PodVolumeRestoreSpec{
Pod: corev1api.ObjectReference{
@@ -86,50 +86,10 @@ func TestPVRHandler(t *testing.T) {
Phase: "",
},
},
shouldEnqueue: false,
shouldProcessed: false,
},
{
name: "Empty phase pvr with pod not on node running init container should not be enqueued",
obj: &velerov1api.PodVolumeRestore{
Spec: velerov1api.PodVolumeRestoreSpec{
Pod: corev1api.ObjectReference{
Namespace: "ns-1",
Name: "pod-1",
},
},
Status: velerov1api.PodVolumeRestoreStatus{
Phase: "",
},
},
pod: &corev1api.Pod{
ObjectMeta: metav1.ObjectMeta{
Namespace: "ns-1",
Name: "pod-1",
},
Spec: corev1api.PodSpec{
NodeName: "some-other-node",
InitContainers: []corev1api.Container{
{
Name: restic.InitContainer,
},
},
},
Status: corev1api.PodStatus{
InitContainerStatuses: []corev1api.ContainerStatus{
{
State: corev1api.ContainerState{
Running: &corev1api.ContainerStateRunning{
StartedAt: metav1.Time{Time: time.Now()},
},
},
},
},
},
},
shouldEnqueue: false,
},
{
name: "Empty phase pvr with pod on node not running init container should not be enqueued",
name: "Empty phase pvr with pod on node not running init container should not be processed",
obj: &velerov1api.PodVolumeRestore{
Spec: velerov1api.PodVolumeRestoreSpec{
Pod: corev1api.ObjectReference{
@@ -162,7 +122,7 @@ func TestPVRHandler(t *testing.T) {
},
},
},
shouldEnqueue: false,
shouldProcessed: false,
},
{
name: "Empty phase pvr with pod on node running init container should be enqueued",
@@ -202,220 +162,23 @@ func TestPVRHandler(t *testing.T) {
},
},
},
shouldEnqueue: true,
shouldProcessed: true,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
var (
podInformer = cache.NewSharedIndexInformer(nil, new(corev1api.Pod), 0, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
c = &podVolumeRestoreController{
genericController: newGenericController(PodVolumeRestore, velerotest.NewLogger()),
podLister: corev1listers.NewPodLister(podInformer.GetIndexer()),
nodeName: controllerNode,
}
)
builder := fake.NewClientBuilder()
if test.pod != nil {
require.NoError(t, podInformer.GetStore().Add(test.pod))
builder.WithObjects(test.pod)
}
c := &PodVolumeRestoreReconciler{
logger: logrus.New(),
Client: builder.Build(),
}
c.pvrHandler(test.obj)
if !test.shouldEnqueue {
assert.Equal(t, 0, c.queue.Len())
return
}
require.Equal(t, 1, c.queue.Len())
})
}
}
func TestPodHandler(t *testing.T) {
controllerNode := "foo"
tests := []struct {
name string
pod *corev1api.Pod
podVolumeRestores []*velerov1api.PodVolumeRestore
expectedEnqueues sets.String
}{
{
name: "pod on controller node running restic init container with multiple PVRs has new ones enqueued",
pod: &corev1api.Pod{
ObjectMeta: metav1.ObjectMeta{
Namespace: "ns-1",
Name: "pod-1",
UID: types.UID("uid"),
},
Spec: corev1api.PodSpec{
NodeName: controllerNode,
InitContainers: []corev1api.Container{
{
Name: restic.InitContainer,
},
},
},
Status: corev1api.PodStatus{
InitContainerStatuses: []corev1api.ContainerStatus{
{
State: corev1api.ContainerState{
Running: &corev1api.ContainerStateRunning{StartedAt: metav1.Time{Time: time.Now()}},
},
},
},
},
},
podVolumeRestores: []*velerov1api.PodVolumeRestore{
{
ObjectMeta: metav1.ObjectMeta{
Namespace: "ns-1",
Name: "pvr-1",
Labels: map[string]string{
velerov1api.PodUIDLabel: "uid",
},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Namespace: "ns-1",
Name: "pvr-2",
Labels: map[string]string{
velerov1api.PodUIDLabel: "uid",
},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Namespace: "ns-1",
Name: "pvr-3",
Labels: map[string]string{
velerov1api.PodUIDLabel: "uid",
},
},
Status: velerov1api.PodVolumeRestoreStatus{
Phase: velerov1api.PodVolumeRestorePhaseInProgress,
},
},
{
ObjectMeta: metav1.ObjectMeta{
Namespace: "ns-1",
Name: "pvr-4",
Labels: map[string]string{
velerov1api.PodUIDLabel: "some-other-pod",
},
},
},
},
expectedEnqueues: sets.NewString("ns-1/pvr-1", "ns-1/pvr-2"),
},
{
name: "pod on controller node not running restic init container doesn't have PVRs enqueued",
pod: &corev1api.Pod{
ObjectMeta: metav1.ObjectMeta{
Namespace: "ns-1",
Name: "pod-1",
UID: types.UID("uid"),
},
Spec: corev1api.PodSpec{
NodeName: controllerNode,
InitContainers: []corev1api.Container{
{
Name: restic.InitContainer,
},
},
},
Status: corev1api.PodStatus{
InitContainerStatuses: []corev1api.ContainerStatus{
{
State: corev1api.ContainerState{},
},
},
},
},
podVolumeRestores: []*velerov1api.PodVolumeRestore{
{
ObjectMeta: metav1.ObjectMeta{
Namespace: "ns-1",
Name: "pvr-1",
Labels: map[string]string{
velerov1api.PodUIDLabel: "uid",
},
},
},
},
},
{
name: "pod not running on controller node doesn't have PVRs enqueued",
pod: &corev1api.Pod{
ObjectMeta: metav1.ObjectMeta{
Namespace: "ns-1",
Name: "pod-1",
UID: types.UID("uid"),
},
Spec: corev1api.PodSpec{
NodeName: "some-other-node",
InitContainers: []corev1api.Container{
{
Name: restic.InitContainer,
},
},
},
Status: corev1api.PodStatus{
InitContainerStatuses: []corev1api.ContainerStatus{
{
State: corev1api.ContainerState{
Running: &corev1api.ContainerStateRunning{StartedAt: metav1.Time{Time: time.Now()}},
},
},
},
},
},
podVolumeRestores: []*velerov1api.PodVolumeRestore{
{
ObjectMeta: metav1.ObjectMeta{
Namespace: "ns-1",
Name: "pvr-1",
Labels: map[string]string{
velerov1api.PodUIDLabel: "uid",
},
},
},
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
var (
client = velerofake.NewSimpleClientset()
informers = veleroinformers.NewSharedInformerFactory(client, 0)
pvrInformer = informers.Velero().V1().PodVolumeRestores()
c = &podVolumeRestoreController{
genericController: newGenericController(PodVolumeRestore, velerotest.NewLogger()),
podVolumeRestoreLister: velerov1listers.NewPodVolumeRestoreLister(pvrInformer.Informer().GetIndexer()),
nodeName: controllerNode,
}
)
if len(test.podVolumeRestores) > 0 {
for _, pvr := range test.podVolumeRestores {
require.NoError(t, pvrInformer.Informer().GetStore().Add(pvr))
}
}
c.podHandler(test.pod)
require.Equal(t, len(test.expectedEnqueues), c.queue.Len())
itemCount := c.queue.Len()
for i := 0; i < itemCount; i++ {
item, _ := c.queue.Get()
assert.True(t, test.expectedEnqueues.Has(item.(string)))
}
shouldProcess, _, _ := c.shouldProcess(context.Background(), c.logger, test.obj)
require.Equal(t, test.shouldProcessed, shouldProcess)
})
}
}
@@ -437,17 +200,6 @@ func TestIsPVRNew(t *testing.T) {
}
}
func TestIsPodOnNode(t *testing.T) {
pod := &corev1api.Pod{}
assert.False(t, isPodOnNode(pod, "bar"))
pod.Spec.NodeName = "foo"
assert.False(t, isPodOnNode(pod, "bar"))
pod.Spec.NodeName = "bar"
assert.True(t, isPodOnNode(pod, "bar"))
}
func TestIsResticContainerRunning(t *testing.T) {
tests := []struct {
name string
@@ -720,3 +472,44 @@ func TestGetResticInitContainerIndex(t *testing.T) {
})
}
}
func TestFindVolumeRestoresForPod(t *testing.T) {
pod := &corev1api.Pod{}
pod.UID = "uid"
scheme := runtime.NewScheme()
scheme.AddKnownTypes(velerov1api.SchemeGroupVersion, &velerov1api.PodVolumeRestore{}, &velerov1api.PodVolumeRestoreList{})
clientBuilder := fake.NewClientBuilder().WithScheme(scheme)
// no matching PVR
reconciler := &PodVolumeRestoreReconciler{
Client: clientBuilder.Build(),
logger: logrus.New(),
}
requests := reconciler.findVolumeRestoresForPod(pod)
assert.Len(t, requests, 0)
// contain one matching PVR
reconciler.Client = clientBuilder.WithLists(&velerov1api.PodVolumeRestoreList{
Items: []velerov1api.PodVolumeRestore{
{
ObjectMeta: metav1.ObjectMeta{
Name: "pvr1",
Labels: map[string]string{
velerov1api.PodUIDLabel: string(pod.GetUID()),
},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "pvr2",
Labels: map[string]string{
velerov1api.PodUIDLabel: "non-matching-uid",
},
},
},
},
}).Build()
requests = reconciler.findVolumeRestoresForPod(pod)
assert.Len(t, requests, 1)
}