Add a test which tests the e2e lifecycle of the pod controller
This uses the mock provider, so I moved the mock provider to a location where the node test can use it.
This commit is contained in:
225
node/mock_test.go
Normal file
225
node/mock_test.go
Normal file
@@ -0,0 +1,225 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/virtual-kubelet/virtual-kubelet/errdefs"
|
||||
"github.com/virtual-kubelet/virtual-kubelet/log"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
var (
|
||||
_ PodLifecycleHandler = (*mockV0Provider)(nil)
|
||||
_ PodNotifier = (*mockProvider)(nil)
|
||||
)
|
||||
|
||||
type mockV0Provider struct {
|
||||
creates int
|
||||
updates int
|
||||
deletes int
|
||||
|
||||
errorOnDelete error
|
||||
|
||||
pods map[string]*v1.Pod
|
||||
startTime time.Time
|
||||
notifier func(*v1.Pod)
|
||||
}
|
||||
|
||||
type mockProvider struct {
|
||||
*mockV0Provider
|
||||
}
|
||||
|
||||
// NewMockProviderMockConfig creates a new mockV0Provider. Mock legacy provider does not implement the new asynchronous podnotifier interface
|
||||
func newMockV0Provider() *mockV0Provider {
|
||||
provider := mockV0Provider{
|
||||
pods: make(map[string]*v1.Pod),
|
||||
startTime: time.Now(),
|
||||
// By default notifier is set to a function which is a no-op. In the event we've implemented the PodNotifier interface,
|
||||
// it will be set, and then we'll call a real underlying implementation.
|
||||
// This makes it easier in the sense we don't need to wrap each method.
|
||||
notifier: func(*v1.Pod) {},
|
||||
}
|
||||
|
||||
return &provider
|
||||
}
|
||||
|
||||
// NewMockProviderMockConfig creates a new MockProvider with the given config
|
||||
func newMockProvider() *mockProvider {
|
||||
|
||||
return &mockProvider{mockV0Provider: newMockV0Provider()}
|
||||
}
|
||||
|
||||
// CreatePod accepts a Pod definition and stores it in memory.
|
||||
func (p *mockV0Provider) CreatePod(ctx context.Context, pod *v1.Pod) error {
|
||||
log.G(ctx).Infof("receive CreatePod %q", pod.Name)
|
||||
|
||||
p.creates++
|
||||
key, err := buildKey(pod)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
now := metav1.NewTime(time.Now())
|
||||
pod.Status = v1.PodStatus{
|
||||
Phase: v1.PodRunning,
|
||||
HostIP: "1.2.3.4",
|
||||
PodIP: "5.6.7.8",
|
||||
StartTime: &now,
|
||||
Conditions: []v1.PodCondition{
|
||||
{
|
||||
Type: v1.PodInitialized,
|
||||
Status: v1.ConditionTrue,
|
||||
},
|
||||
{
|
||||
Type: v1.PodReady,
|
||||
Status: v1.ConditionTrue,
|
||||
},
|
||||
{
|
||||
Type: v1.PodScheduled,
|
||||
Status: v1.ConditionTrue,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, container := range pod.Spec.Containers {
|
||||
pod.Status.ContainerStatuses = append(pod.Status.ContainerStatuses, v1.ContainerStatus{
|
||||
Name: container.Name,
|
||||
Image: container.Image,
|
||||
Ready: true,
|
||||
RestartCount: 0,
|
||||
State: v1.ContainerState{
|
||||
Running: &v1.ContainerStateRunning{
|
||||
StartedAt: now,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
p.pods[key] = pod
|
||||
p.notifier(pod)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdatePod accepts a Pod definition and updates its reference.
|
||||
func (p *mockV0Provider) UpdatePod(ctx context.Context, pod *v1.Pod) error {
|
||||
log.G(ctx).Infof("receive UpdatePod %q", pod.Name)
|
||||
|
||||
p.updates++
|
||||
key, err := buildKey(pod)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.pods[key] = pod
|
||||
p.notifier(pod)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeletePod deletes the specified pod out of memory.
|
||||
func (p *mockV0Provider) DeletePod(ctx context.Context, pod *v1.Pod) (err error) {
|
||||
log.G(ctx).Infof("receive DeletePod %q", pod.Name)
|
||||
|
||||
if p.errorOnDelete != nil {
|
||||
return p.errorOnDelete
|
||||
}
|
||||
|
||||
p.deletes++
|
||||
key, err := buildKey(pod)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, exists := p.pods[key]; !exists {
|
||||
return errdefs.NotFound("pod not found")
|
||||
}
|
||||
|
||||
now := metav1.Now()
|
||||
delete(p.pods, key)
|
||||
pod.Status.Phase = v1.PodSucceeded
|
||||
pod.Status.Reason = "MockProviderPodDeleted"
|
||||
|
||||
for idx := range pod.Status.ContainerStatuses {
|
||||
pod.Status.ContainerStatuses[idx].Ready = false
|
||||
pod.Status.ContainerStatuses[idx].State = v1.ContainerState{
|
||||
Terminated: &v1.ContainerStateTerminated{
|
||||
Message: "Mock provider terminated container upon deletion",
|
||||
FinishedAt: now,
|
||||
Reason: "MockProviderPodContainerDeleted",
|
||||
StartedAt: pod.Status.ContainerStatuses[idx].State.Running.StartedAt,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
p.notifier(pod)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetPod returns a pod by name that is stored in memory.
|
||||
func (p *mockV0Provider) GetPod(ctx context.Context, namespace, name string) (pod *v1.Pod, err error) {
|
||||
log.G(ctx).Infof("receive GetPod %q", name)
|
||||
|
||||
key, err := buildKeyFromNames(namespace, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if pod, ok := p.pods[key]; ok {
|
||||
return pod, nil
|
||||
}
|
||||
return nil, errdefs.NotFoundf("pod \"%s/%s\" is not known to the provider", namespace, name)
|
||||
}
|
||||
|
||||
// GetPodStatus returns the status of a pod by name that is "running".
|
||||
// returns nil if a pod by that name is not found.
|
||||
func (p *mockV0Provider) GetPodStatus(ctx context.Context, namespace, name string) (*v1.PodStatus, error) {
|
||||
log.G(ctx).Infof("receive GetPodStatus %q", name)
|
||||
|
||||
pod, err := p.GetPod(ctx, namespace, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pod.Status, nil
|
||||
}
|
||||
|
||||
// GetPods returns a list of all pods known to be "running".
|
||||
func (p *mockV0Provider) GetPods(ctx context.Context) ([]*v1.Pod, error) {
|
||||
log.G(ctx).Info("receive GetPods")
|
||||
|
||||
var pods []*v1.Pod
|
||||
|
||||
for _, pod := range p.pods {
|
||||
pods = append(pods, pod)
|
||||
}
|
||||
|
||||
return pods, nil
|
||||
}
|
||||
|
||||
// NotifyPods is called to set a pod notifier callback function. This should be called before any operations are done
|
||||
// within the provider.
|
||||
func (p *mockProvider) NotifyPods(ctx context.Context, notifier func(*v1.Pod)) {
|
||||
p.notifier = notifier
|
||||
}
|
||||
|
||||
func buildKeyFromNames(namespace string, name string) (string, error) {
|
||||
return fmt.Sprintf("%s-%s", namespace, name), nil
|
||||
}
|
||||
|
||||
// buildKey is a helper for building the "key" for the providers pod store.
|
||||
func buildKey(pod *v1.Pod) (string, error) {
|
||||
if pod.ObjectMeta.Namespace == "" {
|
||||
return "", fmt.Errorf("pod namespace not found")
|
||||
}
|
||||
|
||||
if pod.ObjectMeta.Name == "" {
|
||||
return "", fmt.Errorf("pod name not found")
|
||||
}
|
||||
|
||||
return buildKeyFromNames(pod.ObjectMeta.Namespace, pod.ObjectMeta.Name)
|
||||
}
|
||||
Reference in New Issue
Block a user