Skip to content

Commit

Permalink
[Unit tests] make it easier to create pending test pods (#351)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jacob Gabrielson authored Apr 8, 2021
1 parent 8438c2e commit 30ba533
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 86 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/Pallinder/go-randomdata v1.2.0
github.com/aws/aws-sdk-go v1.38.11
github.com/go-logr/zapr v0.2.0
github.com/imdario/mergo v0.3.10
github.com/mitchellh/hashstructure/v2 v2.0.1
github.com/onsi/ginkgo v1.14.2
github.com/onsi/gomega v1.10.3
Expand Down
23 changes: 5 additions & 18 deletions pkg/cloudprovider/aws/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,7 @@ var _ = Describe("Allocation", func() {
{SubnetId: aws.String("test-subnet-3"), AvailabilityZone: aws.String("test-zone-1c")},
}}
// Setup
pod := test.PodWith(test.PodOptions{
Conditions: []v1.PodCondition{{Type: v1.PodScheduled, Reason: v1.PodReasonUnschedulable, Status: v1.ConditionFalse}},
})
pod := test.PendingPod()
ExpectCreatedWithStatus(env.Client, pod)
ExpectCreated(env.Client, provisioner)
ExpectEventuallyReconciled(env.Client, provisioner)
Expand Down Expand Up @@ -180,9 +178,7 @@ var _ = Describe("Allocation", func() {
}}
// Setup
provisioner.Spec.Zones = []string{"test-zone-1a", "test-zone-1b"}
pod := test.PodWith(test.PodOptions{
Conditions: []v1.PodCondition{{Type: v1.PodScheduled, Reason: v1.PodReasonUnschedulable, Status: v1.ConditionFalse}},
})
pod := test.PendingPod()
ExpectCreatedWithStatus(env.Client, pod)
ExpectCreated(env.Client, provisioner)
ExpectEventuallyReconciled(env.Client, provisioner)
Expand Down Expand Up @@ -213,10 +209,7 @@ var _ = Describe("Allocation", func() {
}}
// Setup
provisioner.Spec.Zones = []string{"test-zone-1a", "test-zone-1b"}
pod := test.PodWith(test.PodOptions{
NodeSelector: map[string]string{v1alpha1.ZoneLabelKey: "test-zone-1c"},
Conditions: []v1.PodCondition{{Type: v1.PodScheduled, Reason: v1.PodReasonUnschedulable, Status: v1.ConditionFalse}},
})
pod := test.PendingPodWith(test.PodOptions{NodeSelector: map[string]string{v1alpha1.ZoneLabelKey: "test-zone-1c"}})
ExpectCreatedWithStatus(env.Client, pod)
ExpectCreated(env.Client, provisioner)
ExpectEventuallyReconciled(env.Client, provisioner)
Expand All @@ -237,14 +230,8 @@ var _ = Describe("Allocation", func() {
})
It("should launch separate instances for pods with different node selectors", func() {
// Setup
pod1Options := test.PodOptions{
NodeSelector: map[string]string{"node.k8s.aws/launch-template-id": "abc123"},
Conditions: []v1.PodCondition{{Type: v1.PodScheduled, Reason: v1.PodReasonUnschedulable, Status: v1.ConditionFalse}}}
pod2Options := test.PodOptions{
NodeSelector: map[string]string{"node.k8s.aws/launch-template-id": "34sy4s"},
Conditions: []v1.PodCondition{{Type: v1.PodScheduled, Reason: v1.PodReasonUnschedulable, Status: v1.ConditionFalse}}}
pod1 := test.PodWith(pod1Options)
pod2 := test.PodWith(pod2Options)
pod1 := test.PendingPodWith(test.PodOptions{NodeSelector: map[string]string{"node.k8s.aws/launch-template-id": "abc123"}})
pod2 := test.PendingPodWith(test.PodOptions{NodeSelector: map[string]string{"node.k8s.aws/launch-template-id": "34sy4s"}})
ExpectCreatedWithStatus(env.Client, pod1, pod2)
ExpectCreated(env.Client, provisioner)
ExpectEventuallyReconciled(env.Client, provisioner)
Expand Down
40 changes: 15 additions & 25 deletions pkg/controllers/provisioning/v1alpha1/allocation/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,7 @@ var _ = Describe("Allocation", func() {

Context("Reconcilation", func() {
It("should provision nodes for unconstrained pods", func() {
pods := []*v1.Pod{
test.PodWith(test.PodOptions{
Conditions: []v1.PodCondition{{Type: v1.PodScheduled, Reason: v1.PodReasonUnschedulable, Status: v1.ConditionFalse}},
}),
test.PodWith(test.PodOptions{
Conditions: []v1.PodCondition{{Type: v1.PodScheduled, Reason: v1.PodReasonUnschedulable, Status: v1.ConditionFalse}},
}),
}
pods := []*v1.Pod{test.PendingPod(), test.PendingPod()}
for _, pod := range pods {
ExpectCreatedWithStatus(env.Client, pod)
}
Expand All @@ -113,45 +106,42 @@ var _ = Describe("Allocation", func() {
It("should provision nodes for pods with supported node selectors", func() {
coschedulable := []client.Object{
// Unconstrained
test.PodWith(test.PodOptions{
Conditions: []v1.PodCondition{{Type: v1.PodScheduled, Reason: v1.PodReasonUnschedulable, Status: v1.ConditionFalse}},
}),
test.PendingPod(),
// Constrained by provisioner
test.PodWith(test.PodOptions{
test.PendingPodWith(test.PodOptions{
NodeSelector: map[string]string{v1alpha1.ProvisionerNameLabelKey: provisioner.Name, v1alpha1.ProvisionerNamespaceLabelKey: provisioner.Namespace},
Conditions: []v1.PodCondition{{Type: v1.PodScheduled, Reason: v1.PodReasonUnschedulable, Status: v1.ConditionFalse}},
}),
}
schedulable := []client.Object{
// Constrained by zone
test.PodWith(test.PodOptions{
test.PendingPodWith(test.PodOptions{
NodeSelector: map[string]string{v1alpha1.ZoneLabelKey: "test-zone-1"},
Conditions: []v1.PodCondition{{Type: v1.PodScheduled, Reason: v1.PodReasonUnschedulable, Status: v1.ConditionFalse}},
}),
// Constrained by instanceType
test.PodWith(test.PodOptions{
test.PendingPodWith(test.PodOptions{
NodeSelector: map[string]string{v1alpha1.InstanceTypeLabelKey: "test-instance-type-1"},
Conditions: []v1.PodCondition{{Type: v1.PodScheduled, Reason: v1.PodReasonUnschedulable, Status: v1.ConditionFalse}},
}),
// Constrained by architecture
test.PodWith(test.PodOptions{
test.PendingPodWith(test.PodOptions{
NodeSelector: map[string]string{v1alpha1.ArchitectureLabelKey: "test-architecture-1"},
Conditions: []v1.PodCondition{{Type: v1.PodScheduled, Reason: v1.PodReasonUnschedulable, Status: v1.ConditionFalse}},
}),
// Constrained by operating system
test.PodWith(test.PodOptions{
test.PendingPodWith(test.PodOptions{
NodeSelector: map[string]string{v1alpha1.OperatingSystemLabelKey: "test-operating-system-1"},
Conditions: []v1.PodCondition{{Type: v1.PodScheduled, Reason: v1.PodReasonUnschedulable, Status: v1.ConditionFalse}},
}),
// Constrained by arbitrary label
test.PodWith(test.PodOptions{
test.PendingPodWith(test.PodOptions{
NodeSelector: map[string]string{"foo": "bar"},
Conditions: []v1.PodCondition{{Type: v1.PodScheduled, Reason: v1.PodReasonUnschedulable, Status: v1.ConditionFalse}},
}),
}
unschedulable := []client.Object{
// Ignored, matches another provisioner
test.PodWith(test.PodOptions{
test.PendingPodWith(test.PodOptions{
NodeSelector: map[string]string{v1alpha1.ProvisionerNameLabelKey: "test", v1alpha1.ProvisionerNamespaceLabelKey: "test"},
Conditions: []v1.PodCondition{{Type: v1.PodScheduled, Reason: v1.PodReasonUnschedulable, Status: v1.ConditionFalse}},
}),
Expand Down Expand Up @@ -184,33 +174,33 @@ var _ = Describe("Allocation", func() {
provisioner.Spec.Taints = []v1.Taint{{Key: "test-key", Value: "test-value", Effect: v1.TaintEffectNoSchedule}}
schedulable := []client.Object{
// Tolerates with OpExists
test.PodWith(test.PodOptions{
test.PendingPodWith(test.PodOptions{
Tolerations: []v1.Toleration{{Key: "test-key", Operator: v1.TolerationOpExists}},
Conditions: []v1.PodCondition{{Type: v1.PodScheduled, Reason: v1.PodReasonUnschedulable, Status: v1.ConditionFalse}},
}),
// Tolerates with OpEqual
test.PodWith(test.PodOptions{
test.PendingPodWith(test.PodOptions{
Tolerations: []v1.Toleration{{Key: "test-key", Value: "test-value", Operator: v1.TolerationOpEqual}},
Conditions: []v1.PodCondition{{Type: v1.PodScheduled, Reason: v1.PodReasonUnschedulable, Status: v1.ConditionFalse}},
}),
}
unschedulable := []client.Object{
// Missing toleration
test.PodWith(test.PodOptions{
test.PendingPodWith(test.PodOptions{
Conditions: []v1.PodCondition{{Type: v1.PodScheduled, Reason: v1.PodReasonUnschedulable, Status: v1.ConditionFalse}},
}),
// key mismatch with OpExists
test.PodWith(test.PodOptions{
test.PendingPodWith(test.PodOptions{
Tolerations: []v1.Toleration{{Key: "invalid", Operator: v1.TolerationOpExists}},
Conditions: []v1.PodCondition{{Type: v1.PodScheduled, Reason: v1.PodReasonUnschedulable, Status: v1.ConditionFalse}},
}),
// value mismatch with OpEqual
test.PodWith(test.PodOptions{
test.PendingPodWith(test.PodOptions{
Tolerations: []v1.Toleration{{Key: "test-key", Value: "invalid", Operator: v1.TolerationOpEqual}},
Conditions: []v1.PodCondition{{Type: v1.PodScheduled, Reason: v1.PodReasonUnschedulable, Status: v1.ConditionFalse}},
}),
// key mismatch with OpEqual
test.PodWith(test.PodOptions{
test.PendingPodWith(test.PodOptions{
Tolerations: []v1.Toleration{{Key: "invalid", Value: "test-value", Operator: v1.TolerationOpEqual}},
Conditions: []v1.PodCondition{{Type: v1.PodScheduled, Reason: v1.PodReasonUnschedulable, Status: v1.ConditionFalse}},
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ var _ = Describe("Reallocation", func() {
v1alpha1.ProvisionerTTLKey: time.Now().Add(time.Duration(100) * time.Second).Format(time.RFC3339),
},
})
pod := test.PodWith(test.PodOptions{
pod := test.PendingPodWith(test.PodOptions{
Name: strings.ToLower(randomdata.SillyName()),
Namespace: provisioner.Namespace,
NodeName: node.Name,
Expand Down
42 changes: 0 additions & 42 deletions pkg/test/objects.go → pkg/test/nodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,48 +22,6 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

type PodOptions struct {
Name string
Namespace string
Image string
NodeName string
ResourceRequests v1.ResourceList
NodeSelector map[string]string
Tolerations []v1.Toleration
Conditions []v1.PodCondition
}

func PodWith(options PodOptions) *v1.Pod {
if options.Name == "" {
options.Name = strings.ToLower(randomdata.SillyName())
}
if options.Namespace == "" {
options.Namespace = "default"
}
if options.Image == "" {
options.Image = "k8s.gcr.io/pause"
}
return &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: options.Name,
Namespace: options.Namespace,
},
Spec: v1.PodSpec{
NodeSelector: options.NodeSelector,
Tolerations: options.Tolerations,
Containers: []v1.Container{{
Name: options.Name,
Image: options.Image,
Resources: v1.ResourceRequirements{
Requests: options.ResourceRequests,
},
}},
NodeName: options.NodeName,
},
Status: v1.PodStatus{Conditions: options.Conditions},
}
}

type NodeOptions struct {
Name string
Labels map[string]string
Expand Down
78 changes: 78 additions & 0 deletions pkg/test/pods.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package test

import (
"fmt"
"strings"

"github.com/Pallinder/go-randomdata"
"github.com/imdario/mergo"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// PodOptions customizes a Pod.
type PodOptions struct {
Name string
Namespace string
Image string
NodeName string
ResourceRequests v1.ResourceList
NodeSelector map[string]string
Tolerations []v1.Toleration
Conditions []v1.PodCondition
}

func defaults(options PodOptions) *v1.Pod {
if options.Name == "" {
options.Name = strings.ToLower(randomdata.SillyName())
}
if options.Namespace == "" {
options.Namespace = "default"
}
if options.Image == "" {
options.Image = "k8s.gcr.io/pause"
}
if len(options.Conditions) == 0 {
options.Conditions = []v1.PodCondition{{Type: v1.PodScheduled, Reason: v1.PodReasonUnschedulable, Status: v1.ConditionFalse}}
}
return &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: options.Name,
Namespace: options.Namespace,
},
Spec: v1.PodSpec{
NodeSelector: options.NodeSelector,
Tolerations: options.Tolerations,
Containers: []v1.Container{{
Name: options.Name,
Image: options.Image,
Resources: v1.ResourceRequirements{
Requests: options.ResourceRequests,
},
}},
NodeName: options.NodeName,
},
Status: v1.PodStatus{Conditions: options.Conditions},
}
}

// PendingPod creates a pending test pod with the minimal set of other
// fields defaulted to something sane.
func PendingPod() *v1.Pod {
return defaults(PodOptions{})
}

// PendingPodWith creates a pending test pod with fields overridden by
// options.
func PendingPodWith(options PodOptions) *v1.Pod {
return PodWith(PendingPod(), options)
}

// PodWith overrides, in-place, pod with any non-zero elements of
// options. It returns the same pod simply for ease of use.
func PodWith(pod *v1.Pod, options PodOptions) *v1.Pod {
if err := mergo.Merge(pod, defaults(options), mergo.WithOverride); err != nil {
panic(fmt.Sprintf("unexpected error in test code: %v", err))
}
return pod
}

0 comments on commit 30ba533

Please sign in to comment.