Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Unit tests] make it easier to create pending test pods #351

Merged
merged 7 commits into from
Apr 8, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
44 changes: 17 additions & 27 deletions pkg/controllers/provisioning/v1alpha1/allocation/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,15 +87,8 @@ 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}},
}),
}
for _, pod := range pods {
ps := []*v1.Pod{test.PendingPod(), test.PendingPod()}
JacobGabrielson marked this conversation as resolved.
Show resolved Hide resolved
for _, pod := range ps {
ExpectCreatedWithStatus(env.Client, pod)
}
ExpectCreated(env.Client, provisioner)
Expand All @@ -104,7 +97,7 @@ var _ = Describe("Allocation", func() {
nodes := &v1.NodeList{}
Expect(env.Client.List(ctx, nodes)).To(Succeed())
Expect(len(nodes.Items)).To(Equal(1))
for _, object := range pods {
for _, object := range ps {
pod := v1.Pod{}
Expect(env.Client.Get(ctx, client.ObjectKey{Name: object.GetName(), Namespace: object.GetNamespace()}, &pod)).To(Succeed())
Expect(pod.Spec.NodeName).To(Equal(nodes.Items[0].Name))
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) < 1 {
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},
}
}

// Pending creates a pending test pod with the minimal set of other
JacobGabrielson marked this conversation as resolved.
Show resolved Hide resolved
// fields defaulted to something sane.
func PendingPod() *v1.Pod {
return defaults(PodOptions{})
}

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

// With 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
}