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

test: Add unit test to validate the instancetype cache #5917

Merged
merged 1 commit into from
Apr 10, 2024
Merged
Changes from all 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
141 changes: 141 additions & 0 deletions pkg/providers/instancetype/suite_test.go
Original file line number Diff line number Diff line change
@@ -19,13 +19,15 @@ import (
"fmt"
"math"
"net"
"reflect"
"sort"
"strings"
"testing"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/imdario/mergo"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/samber/lo"
@@ -1716,8 +1718,147 @@ var _ = Describe("InstanceTypes", func() {
})
})
})
Context("Provider Cache", func() {
// Keeping the Cache testing in one IT block to validate the combinatorial expansion of instance types generated by different configs
It("changes to kubelet configuration fields should result in a different set of instances types", func() {
// We should expect these kubelet configuration fields to change the result of the instance type call
// kubelet.kubeReserved
// kubelet.systemReserved
// kubelet.evictionHard
// kubelet.evictionSoft
// kubelet.maxPods
nodePool.Spec.Template.Spec.Kubelet = &corev1beta1.KubeletConfiguration{
KubeReserved: map[string]string{string(v1.ResourceCPU): "1"},
SystemReserved: map[string]string{string(v1.ResourceCPU): "1"},
EvictionHard: map[string]string{"memory.available": "5%"},
EvictionSoft: map[string]string{"nodefs.available": "10%"},
MaxPods: aws.Int32(10),
}
kubeletChanges := []*corev1beta1.KubeletConfiguration{
engedaam marked this conversation as resolved.
Show resolved Hide resolved
{}, // Testing the base case black EC2NodeClass
{KubeReserved: map[string]string{string(v1.ResourceCPU): "20"}},
{SystemReserved: map[string]string{string(v1.ResourceMemory): "10Gi"}},
{EvictionHard: map[string]string{"memory.available": "52%"}},
{EvictionSoft: map[string]string{"nodefs.available": "132%"}},
{MaxPods: aws.Int32(20)},
}
var instanceTypeResult [][]*corecloudprovider.InstanceType
ExpectApplied(ctx, env.Client, nodeClass)
// Adding the general set of to the instancetype into the cache
fullInstanceTypeList, err := cloudProvider.GetInstanceTypes(ctx, nodePool)
Expect(err).To(BeNil())
sort.Slice(fullInstanceTypeList, func(x int, y int) bool {
return fullInstanceTypeList[x].Name < fullInstanceTypeList[y].Name
})

sorted := nodePool.DeepCopy()
for _, change := range kubeletChanges {
nodePool = sorted.DeepCopy()
Expect(mergo.Merge(nodePool.Spec.Template.Spec.Kubelet, change, mergo.WithOverride, mergo.WithSliceDeepCopy)).To(BeNil())
// Calling the provider and storing the instance type list to the instancetype provider cache
_, err := awsEnv.InstanceTypesProvider.List(ctx, nodePool.Spec.Template.Spec.Kubelet, nodeClass)
Expect(err).To(BeNil())
// We are making sure to pull from the cache
instancetypes, err := awsEnv.InstanceTypesProvider.List(ctx, nodePool.Spec.Template.Spec.Kubelet, nodeClass)
Expect(err).To(BeNil())
sort.Slice(instancetypes, func(x int, y int) bool {
return instancetypes[x].Name < instancetypes[y].Name
})
instanceTypeResult = append(instanceTypeResult, instancetypes)
}

// Based on the nodeclass configuration, we expect to have 5 unique set of instance types
uniqueInstanceTypeList(instanceTypeResult)
})
It("changes to nodeclass fields should result in a different set of instances types", func() {
// We should expect these nodeclass fields to change the result of the instance type
// nodeClass.instanceStorePolicy
// nodeClass.amiFamily
// nodeClass.blockDeviceMapping.rootVolume
// nodeClass.blockDeviceMapping.volumeSize
// nodeClass.blockDeviceMapping.deviceName
nodeClass.Spec.BlockDeviceMappings = []*v1beta1.BlockDeviceMapping{
{
DeviceName: lo.ToPtr("/dev/xvda"),
EBS: &v1beta1.BlockDevice{VolumeSize: resource.NewScaledQuantity(10, resource.Giga)},
RootVolume: false,
},
}
nodeClassChanges := []*v1beta1.EC2NodeClass{
engedaam marked this conversation as resolved.
Show resolved Hide resolved
{}, // Testing the base case black EC2NodeClass
{Spec: v1beta1.EC2NodeClassSpec{InstanceStorePolicy: lo.ToPtr(v1beta1.InstanceStorePolicyRAID0)}},
{Spec: v1beta1.EC2NodeClassSpec{AMIFamily: &v1beta1.AMIFamilyUbuntu}},
{
Spec: v1beta1.EC2NodeClassSpec{BlockDeviceMappings: []*v1beta1.BlockDeviceMapping{
{
DeviceName: lo.ToPtr("/dev/sda1"),
EBS: &v1beta1.BlockDevice{VolumeSize: resource.NewScaledQuantity(10, resource.Giga)},
RootVolume: true,
},
},
}},
{
Spec: v1beta1.EC2NodeClassSpec{BlockDeviceMappings: []*v1beta1.BlockDeviceMapping{
{
DeviceName: lo.ToPtr("/dev/xvda"),
EBS: &v1beta1.BlockDevice{VolumeSize: resource.NewScaledQuantity(10, resource.Giga)},
RootVolume: true,
},
},
}},
{
Spec: v1beta1.EC2NodeClassSpec{BlockDeviceMappings: []*v1beta1.BlockDeviceMapping{
{
DeviceName: lo.ToPtr("/dev/xvda"),
EBS: &v1beta1.BlockDevice{VolumeSize: resource.NewScaledQuantity(20, resource.Giga)},
RootVolume: false,
},
},
}},
}
var instanceTypeResult [][]*corecloudprovider.InstanceType
ExpectApplied(ctx, env.Client, nodeClass)
nodePool.Spec.Template.Spec.NodeClassRef.Name = nodeClass.Name
// Adding the general set of to the instancetype into the cache
fullInstanceTypeList, err := cloudProvider.GetInstanceTypes(ctx, nodePool)
Expect(err).To(BeNil())
sort.Slice(fullInstanceTypeList, func(x int, y int) bool {
return fullInstanceTypeList[x].Name < fullInstanceTypeList[y].Name
})

sorted := nodeClass.DeepCopy()
for _, change := range nodeClassChanges {
nodeClass = sorted.DeepCopy()
Expect(mergo.Merge(nodeClass, change, mergo.WithOverride)).To(BeNil())
// Calling the provider and storing the instance type list to the instancetype provider cache
_, err := awsEnv.InstanceTypesProvider.List(ctx, nodePool.Spec.Template.Spec.Kubelet, nodeClass)
Expect(err).To(BeNil())
// We are making sure to pull from the cache
instanetypes, err := awsEnv.InstanceTypesProvider.List(ctx, nodePool.Spec.Template.Spec.Kubelet, nodeClass)
Expect(err).To(BeNil())
sort.Slice(instanetypes, func(x int, y int) bool {
return instanetypes[x].Name < instanetypes[y].Name
})
instanceTypeResult = append(instanceTypeResult, instanetypes)
}

// Based on the nodeclass configuration, we expect to have 5 unique set of instance types
uniqueInstanceTypeList(instanceTypeResult)
})
})
})

func uniqueInstanceTypeList(instanceTypesLists [][]*corecloudprovider.InstanceType) {
for x := range instanceTypesLists {
for y := range instanceTypesLists {
if x == y {
continue
}
Expect(reflect.DeepEqual(instanceTypesLists[x], instanceTypesLists[y])).To(BeFalse())
}
}
}

// generateSpotPricing creates a spot price history output for use in a mock that has all spot offerings discounted by 50%
// vs the on-demand offering.
func generateSpotPricing(cp *cloudprovider.CloudProvider, nodePool *corev1beta1.NodePool) *ec2.DescribeSpotPriceHistoryOutput {