mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-12-24 23:19:39 -05:00
[node operator] PendingNode controller unit test
Signed-off-by: Malte Poll <mp@edgeless.systems>
This commit is contained in:
parent
627b9e7ae8
commit
19568d400b
@ -0,0 +1,251 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/event"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||||
|
|
||||||
|
updatev1alpha1 "github.com/edgelesssys/constellation/operators/constellation-node-operator/api/v1alpha1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNodeStateChangePredicate(t *testing.T) {
|
||||||
|
updateTestCases := map[string]struct {
|
||||||
|
event event.UpdateEvent
|
||||||
|
wantProcessing bool
|
||||||
|
}{
|
||||||
|
"old object is not a node": {
|
||||||
|
event: event.UpdateEvent{
|
||||||
|
ObjectNew: &corev1.Node{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"new object is not a node": {
|
||||||
|
event: event.UpdateEvent{
|
||||||
|
ObjectOld: &corev1.Node{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"status is unchanged": {
|
||||||
|
event: event.UpdateEvent{
|
||||||
|
ObjectOld: &corev1.Node{},
|
||||||
|
ObjectNew: &corev1.Node{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"node became ready": {
|
||||||
|
event: event.UpdateEvent{
|
||||||
|
ObjectOld: &corev1.Node{
|
||||||
|
Status: corev1.NodeStatus{
|
||||||
|
Conditions: []corev1.NodeCondition{
|
||||||
|
{Type: corev1.NodeReady, Status: corev1.ConditionFalse},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ObjectNew: &corev1.Node{
|
||||||
|
Status: corev1.NodeStatus{
|
||||||
|
Conditions: []corev1.NodeCondition{
|
||||||
|
{Type: corev1.NodeReady, Status: corev1.ConditionTrue},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantProcessing: true,
|
||||||
|
},
|
||||||
|
"node acquired provider id": {
|
||||||
|
event: event.UpdateEvent{
|
||||||
|
ObjectOld: &corev1.Node{},
|
||||||
|
ObjectNew: &corev1.Node{
|
||||||
|
Spec: corev1.NodeSpec{
|
||||||
|
ProviderID: "provider-id",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantProcessing: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range updateTestCases {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
predicate := nodeStateChangePredicate()
|
||||||
|
assert.Equal(tc.wantProcessing, predicate.Update(tc.event))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("create", func(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
predicate := nodeStateChangePredicate()
|
||||||
|
assert.True(predicate.Create(event.CreateEvent{}))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("delete", func(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
predicate := nodeStateChangePredicate()
|
||||||
|
assert.True(predicate.Delete(event.DeleteEvent{}))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("generic", func(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
predicate := nodeStateChangePredicate()
|
||||||
|
assert.False(predicate.Generic(event.GenericEvent{}))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFindObjectsForNode(t *testing.T) {
|
||||||
|
testCases := map[string]struct {
|
||||||
|
pendingNode client.Object
|
||||||
|
listPendingNodesErr error
|
||||||
|
wantRequests []reconcile.Request
|
||||||
|
}{
|
||||||
|
"getting the corresponding pending nodes fails": {
|
||||||
|
listPendingNodesErr: errors.New("get-pending-nodes-err"),
|
||||||
|
},
|
||||||
|
"pending nodes reconcile request is returned": {
|
||||||
|
pendingNode: &updatev1alpha1.PendingNode{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "pending-node"},
|
||||||
|
},
|
||||||
|
wantRequests: []reconcile.Request{
|
||||||
|
{
|
||||||
|
NamespacedName: types.NamespacedName{
|
||||||
|
Name: "pending-node",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range testCases {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
reconciler := PendingNodeReconciler{
|
||||||
|
Client: newStubReaderClient(t, []runtime.Object{tc.pendingNode}, nil, tc.listPendingNodesErr),
|
||||||
|
}
|
||||||
|
requests := reconciler.findObjectsForNode(&corev1.Node{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pending-node",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
assert.ElementsMatch(tc.wantRequests, requests)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReachedGoal(t *testing.T) {
|
||||||
|
testCases := map[string]struct {
|
||||||
|
pendingNode updatev1alpha1.PendingNode
|
||||||
|
nodeState updatev1alpha1.CSPNodeState
|
||||||
|
getPendingNodeErr error
|
||||||
|
wantErr bool
|
||||||
|
wantGoalReached bool
|
||||||
|
}{
|
||||||
|
"join: getting the corresponding k8s node fails": {
|
||||||
|
pendingNode: updatev1alpha1.PendingNode{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "pending-node"},
|
||||||
|
Spec: updatev1alpha1.PendingNodeSpec{Goal: updatev1alpha1.NodeGoalJoin},
|
||||||
|
},
|
||||||
|
nodeState: updatev1alpha1.NodeStateReady,
|
||||||
|
getPendingNodeErr: errors.New("get-pending-node-err"),
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
"join: node not found": {
|
||||||
|
pendingNode: updatev1alpha1.PendingNode{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "pending-node"},
|
||||||
|
Spec: updatev1alpha1.PendingNodeSpec{Goal: updatev1alpha1.NodeGoalJoin},
|
||||||
|
},
|
||||||
|
nodeState: updatev1alpha1.NodeStateReady,
|
||||||
|
getPendingNodeErr: &apierrors.StatusError{
|
||||||
|
ErrStatus: metav1.Status{
|
||||||
|
Status: "Failure",
|
||||||
|
Reason: "NotFound",
|
||||||
|
Code: http.StatusNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"join: csp states node is not ready": {
|
||||||
|
pendingNode: updatev1alpha1.PendingNode{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "pending-node"},
|
||||||
|
Spec: updatev1alpha1.PendingNodeSpec{Goal: updatev1alpha1.NodeGoalJoin},
|
||||||
|
},
|
||||||
|
nodeState: updatev1alpha1.NodeStateFailed,
|
||||||
|
},
|
||||||
|
"join: node joined": {
|
||||||
|
pendingNode: updatev1alpha1.PendingNode{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "pending-node"},
|
||||||
|
Spec: updatev1alpha1.PendingNodeSpec{Goal: updatev1alpha1.NodeGoalJoin},
|
||||||
|
},
|
||||||
|
nodeState: updatev1alpha1.NodeStateReady,
|
||||||
|
wantGoalReached: true,
|
||||||
|
},
|
||||||
|
"leave: node still exists": {
|
||||||
|
pendingNode: updatev1alpha1.PendingNode{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "pending-node"},
|
||||||
|
Spec: updatev1alpha1.PendingNodeSpec{Goal: updatev1alpha1.NodeGoalLeave},
|
||||||
|
},
|
||||||
|
nodeState: updatev1alpha1.NodeStateReady,
|
||||||
|
},
|
||||||
|
"leave: node terminated": {
|
||||||
|
pendingNode: updatev1alpha1.PendingNode{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "pending-node"},
|
||||||
|
Spec: updatev1alpha1.PendingNodeSpec{Goal: updatev1alpha1.NodeGoalLeave},
|
||||||
|
},
|
||||||
|
nodeState: updatev1alpha1.NodeStateTerminated,
|
||||||
|
wantGoalReached: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range testCases {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
require := require.New(t)
|
||||||
|
|
||||||
|
reconciler := PendingNodeReconciler{
|
||||||
|
Client: newStubReaderClient(t, []runtime.Object{&tc.pendingNode}, tc.getPendingNodeErr, nil),
|
||||||
|
}
|
||||||
|
reachedGoal, err := reconciler.reachedGoal(context.Background(), tc.pendingNode, tc.nodeState)
|
||||||
|
if tc.wantErr {
|
||||||
|
assert.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
require.NoError(err)
|
||||||
|
assert.Equal(tc.wantGoalReached, reachedGoal)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type stubNodeStateGetter struct {
|
||||||
|
sync.RWMutex
|
||||||
|
nodeState updatev1alpha1.CSPNodeState
|
||||||
|
nodeStateErr error
|
||||||
|
deleteNodeErr error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *stubNodeStateGetter) GetNodeState(ctx context.Context, providerID string) (updatev1alpha1.CSPNodeState, error) {
|
||||||
|
g.RLock()
|
||||||
|
defer g.RUnlock()
|
||||||
|
return g.nodeState, g.nodeStateErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *stubNodeStateGetter) DeleteNode(ctx context.Context, providerID string) error {
|
||||||
|
g.RLock()
|
||||||
|
defer g.RUnlock()
|
||||||
|
return g.deleteNodeErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// thread safe methods to update the stub while in use
|
||||||
|
|
||||||
|
func (g *stubNodeStateGetter) setNodeState(nodeState updatev1alpha1.CSPNodeState) {
|
||||||
|
g.Lock()
|
||||||
|
defer g.Unlock()
|
||||||
|
g.nodeState = nodeState
|
||||||
|
}
|
@ -6,6 +6,7 @@ require (
|
|||||||
github.com/medik8s/node-maintenance-operator v0.13.0
|
github.com/medik8s/node-maintenance-operator v0.13.0
|
||||||
github.com/onsi/ginkgo v1.16.5
|
github.com/onsi/ginkgo v1.16.5
|
||||||
github.com/onsi/gomega v1.18.1
|
github.com/onsi/gomega v1.18.1
|
||||||
|
github.com/stretchr/testify v1.7.0
|
||||||
k8s.io/api v0.24.0
|
k8s.io/api v0.24.0
|
||||||
k8s.io/apimachinery v0.24.0
|
k8s.io/apimachinery v0.24.0
|
||||||
k8s.io/client-go v0.24.0
|
k8s.io/client-go v0.24.0
|
||||||
@ -52,6 +53,7 @@ require (
|
|||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/nxadm/tail v1.4.8 // indirect
|
github.com/nxadm/tail v1.4.8 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/prometheus/client_golang v1.12.1 // indirect
|
github.com/prometheus/client_golang v1.12.1 // indirect
|
||||||
github.com/prometheus/client_model v0.2.0 // indirect
|
github.com/prometheus/client_model v0.2.0 // indirect
|
||||||
github.com/prometheus/common v0.32.1 // indirect
|
github.com/prometheus/common v0.32.1 // indirect
|
||||||
|
Loading…
Reference in New Issue
Block a user