diff --git a/pkg/cloudprovider/aws/apis/v1alpha1/suite_test.go b/pkg/cloudprovider/aws/apis/v1alpha1/suite_test.go new file mode 100644 index 000000000000..9a92cd7d5e5e --- /dev/null +++ b/pkg/cloudprovider/aws/apis/v1alpha1/suite_test.go @@ -0,0 +1,325 @@ +package v1alpha1_test + +import ( + "context" + "math" + "testing" + + "github.com/Pallinder/go-randomdata" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/karpenter/pkg/apis/provisioning/v1alpha5" + "github.com/aws/karpenter/pkg/cloudprovider/aws/apis/v1alpha1" + "github.com/aws/karpenter/pkg/test" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/api/resource" + . "knative.dev/pkg/logging/testing" +) + +var ctx context.Context + +func TestAPIs(t *testing.T) { + ctx = TestContextWithLogger(t) + RegisterFailHandler(Fail) + RunSpecs(t, "cloudprovider/aws/apis/v1alpha1") +} + +var _ = Describe("Validation", func() { + var provisioner v1alpha1.Provisioner + BeforeEach(func() { + provisioner = Provisioner(test.ProvisionerOptions{}) + }) + + It("should validate", func() { + Expect(provisioner.Validate(ctx)).To(Succeed()) + }) + It("should succeed if provider undefined", func() { + provisioner.Spec.Provider = nil + Expect(provisioner.Validate(ctx)).To(Succeed()) + }) + + Context("SubnetSelector", func() { + It("should not allow empty string keys or values", func() { + provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) + Expect(err).ToNot(HaveOccurred()) + for key, value := range map[string]string{ + "": "value", + "key": "", + } { + provider.SubnetSelector = map[string]string{key: value} + provisioner := Provisioner(test.ProvisionerOptions{Provider: provider}) + Expect(provisioner.Validate(ctx)).ToNot(Succeed()) + } + }) + }) + Context("SecurityGroupSelector", func() { + It("should not allow with a custom launch template", func() { + provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) + Expect(err).ToNot(HaveOccurred()) + provider.LaunchTemplateName = aws.String("my-lt") + provider.SecurityGroupSelector = map[string]string{"key": "value"} + provisioner := Provisioner(test.ProvisionerOptions{Provider: provider}) + Expect(provisioner.Validate(ctx)).ToNot(Succeed()) + }) + It("should not allow empty string keys or values", func() { + provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) + Expect(err).ToNot(HaveOccurred()) + for key, value := range map[string]string{ + "": "value", + "key": "", + } { + provider.SecurityGroupSelector = map[string]string{key: value} + provisioner := Provisioner(test.ProvisionerOptions{Provider: provider}) + Expect(provisioner.Validate(ctx)).ToNot(Succeed()) + } + }) + }) + Context("Labels", func() { + It("should not allow unrecognized labels with the aws label prefix", func() { + Expect(v1alpha5.RestrictedLabelDomains.List()).To(ContainElement(v1alpha1.LabelDomain)) + }) + It("should support well known labels", func() { + Expect(v1alpha5.WellKnownLabels.List()).To(ContainElements( + v1alpha1.LabelInstanceHypervisor, + v1alpha1.LabelInstanceFamily, + v1alpha1.LabelInstanceSize, + v1alpha1.LabelInstanceCPU, + v1alpha1.LabelInstanceMemory, + v1alpha1.LabelInstanceGPUName, + v1alpha1.LabelInstanceGPUManufacturer, + v1alpha1.LabelInstanceGPUCount, + v1alpha1.LabelInstanceGPUMemory, + )) + }) + }) + Context("MetadataOptions", func() { + It("should not allow with a custom launch template", func() { + provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) + Expect(err).ToNot(HaveOccurred()) + provider.LaunchTemplateName = aws.String("my-lt") + provider.MetadataOptions = &v1alpha1.MetadataOptions{} + provisioner := Provisioner(test.ProvisionerOptions{Provider: provider}) + Expect(provisioner.Validate(ctx)).ToNot(Succeed()) + }) + It("should allow missing values", func() { + provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) + Expect(err).ToNot(HaveOccurred()) + provider.MetadataOptions = &v1alpha1.MetadataOptions{} + provisioner := Provisioner(test.ProvisionerOptions{Provider: provider}) + Expect(provisioner.Validate(ctx)).To(Succeed()) + }) + Context("HTTPEndpoint", func() { + It("should allow enum values", func() { + provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) + Expect(err).ToNot(HaveOccurred()) + for _, value := range ec2.LaunchTemplateInstanceMetadataEndpointState_Values() { + provider.MetadataOptions = &v1alpha1.MetadataOptions{ + HTTPEndpoint: &value, + } + provisioner := Provisioner(test.ProvisionerOptions{Provider: provider}) + Expect(provisioner.Validate(ctx)).To(Succeed()) + } + }) + It("should not allow non-enum values", func() { + provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) + Expect(err).ToNot(HaveOccurred()) + provider.MetadataOptions = &v1alpha1.MetadataOptions{ + HTTPEndpoint: aws.String(randomdata.SillyName()), + } + provisioner := Provisioner(test.ProvisionerOptions{Provider: provider}) + Expect(provisioner.Validate(ctx)).ToNot(Succeed()) + }) + }) + Context("HTTPProtocolIpv6", func() { + It("should allow enum values", func() { + provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) + Expect(err).ToNot(HaveOccurred()) + for _, value := range ec2.LaunchTemplateInstanceMetadataProtocolIpv6_Values() { + provider.MetadataOptions = &v1alpha1.MetadataOptions{ + HTTPProtocolIPv6: &value, + } + provisioner := Provisioner(test.ProvisionerOptions{Provider: provider}) + Expect(provisioner.Validate(ctx)).To(Succeed()) + } + }) + It("should not allow non-enum values", func() { + provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) + Expect(err).ToNot(HaveOccurred()) + provider.MetadataOptions = &v1alpha1.MetadataOptions{ + HTTPProtocolIPv6: aws.String(randomdata.SillyName()), + } + provisioner := Provisioner(test.ProvisionerOptions{Provider: provider}) + Expect(provisioner.Validate(ctx)).ToNot(Succeed()) + }) + }) + Context("HTTPPutResponseHopLimit", func() { + It("should validate inside accepted range", func() { + provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) + Expect(err).ToNot(HaveOccurred()) + provider.MetadataOptions = &v1alpha1.MetadataOptions{ + HTTPPutResponseHopLimit: aws.Int64(int64(randomdata.Number(1, 65))), + } + provisioner := Provisioner(test.ProvisionerOptions{Provider: provider}) + Expect(provisioner.Validate(ctx)).To(Succeed()) + }) + It("should not validate outside accepted range", func() { + provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) + Expect(err).ToNot(HaveOccurred()) + provider.MetadataOptions = &v1alpha1.MetadataOptions{} + // We expect to be able to invalidate any hop limit between + // [math.MinInt64, 1). But, to avoid a panic here, we can't + // exceed math.MaxInt for the difference between bounds of + // the random number range. So we divide the range + // approximately in half and test on both halves. + provider.MetadataOptions.HTTPPutResponseHopLimit = aws.Int64(int64(randomdata.Number(math.MinInt64, math.MinInt64/2))) + provisioner := Provisioner(test.ProvisionerOptions{Provider: provider}) + Expect(provisioner.Validate(ctx)).ToNot(Succeed()) + provider.MetadataOptions.HTTPPutResponseHopLimit = aws.Int64(int64(randomdata.Number(math.MinInt64/2, 1))) + provisioner = Provisioner(test.ProvisionerOptions{Provider: provider}) + Expect(provisioner.Validate(ctx)).ToNot(Succeed()) + + provider.MetadataOptions.HTTPPutResponseHopLimit = aws.Int64(int64(randomdata.Number(65, math.MaxInt64))) + provisioner = Provisioner(test.ProvisionerOptions{Provider: provider}) + Expect(provisioner.Validate(ctx)).ToNot(Succeed()) + }) + }) + Context("HTTPTokens", func() { + It("should allow enum values", func() { + provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) + Expect(err).ToNot(HaveOccurred()) + for _, value := range ec2.LaunchTemplateHttpTokensState_Values() { + provider.MetadataOptions = &v1alpha1.MetadataOptions{ + HTTPTokens: aws.String(value), + } + provisioner := Provisioner(test.ProvisionerOptions{Provider: provider}) + Expect(provisioner.Validate(ctx)).To(Succeed()) + } + }) + It("should not allow non-enum values", func() { + provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) + Expect(err).ToNot(HaveOccurred()) + provider.MetadataOptions = &v1alpha1.MetadataOptions{ + HTTPTokens: aws.String(randomdata.SillyName()), + } + provisioner := Provisioner(test.ProvisionerOptions{Provider: provider}) + Expect(provisioner.Validate(ctx)).ToNot(Succeed()) + }) + }) + Context("BlockDeviceMappings", func() { + It("should not allow with a custom launch template", func() { + provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) + Expect(err).ToNot(HaveOccurred()) + provider.LaunchTemplateName = aws.String("my-lt") + provider.BlockDeviceMappings = []*v1alpha1.BlockDeviceMapping{{ + DeviceName: aws.String("/dev/xvda"), + EBS: &v1alpha1.BlockDevice{ + VolumeSize: resource.NewScaledQuantity(1, resource.Giga), + }, + }} + provisioner := Provisioner(test.ProvisionerOptions{Provider: provider}) + Expect(provisioner.Validate(ctx)).ToNot(Succeed()) + }) + It("should validate minimal device mapping", func() { + provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) + Expect(err).ToNot(HaveOccurred()) + provider.BlockDeviceMappings = []*v1alpha1.BlockDeviceMapping{{ + DeviceName: aws.String("/dev/xvda"), + EBS: &v1alpha1.BlockDevice{ + VolumeSize: resource.NewScaledQuantity(1, resource.Giga), + }, + }} + provisioner := Provisioner(test.ProvisionerOptions{Provider: provider}) + Expect(provisioner.Validate(ctx)).To(Succeed()) + }) + It("should validate ebs device mapping with snapshotID only", func() { + provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) + Expect(err).ToNot(HaveOccurred()) + provider.BlockDeviceMappings = []*v1alpha1.BlockDeviceMapping{{ + DeviceName: aws.String("/dev/xvda"), + EBS: &v1alpha1.BlockDevice{ + SnapshotID: aws.String("snap-0123456789"), + }, + }} + provisioner := Provisioner(test.ProvisionerOptions{Provider: provider}) + Expect(provisioner.Validate(ctx)).To(Succeed()) + }) + It("should not allow volume size below minimum", func() { + provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) + Expect(err).ToNot(HaveOccurred()) + provider.BlockDeviceMappings = []*v1alpha1.BlockDeviceMapping{{ + DeviceName: aws.String("/dev/xvda"), + EBS: &v1alpha1.BlockDevice{ + VolumeSize: resource.NewScaledQuantity(100, resource.Mega), + }, + }} + provisioner := Provisioner(test.ProvisionerOptions{Provider: provider}) + Expect(provisioner.Validate(ctx)).ToNot(Succeed()) + }) + It("should not allow volume size above max", func() { + provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) + Expect(err).ToNot(HaveOccurred()) + provider.BlockDeviceMappings = []*v1alpha1.BlockDeviceMapping{{ + DeviceName: aws.String("/dev/xvda"), + EBS: &v1alpha1.BlockDevice{ + VolumeSize: resource.NewScaledQuantity(65, resource.Tera), + }, + }} + provisioner := Provisioner(test.ProvisionerOptions{Provider: provider}) + Expect(provisioner.Validate(ctx)).ToNot(Succeed()) + }) + It("should not allow nil device name", func() { + provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) + Expect(err).ToNot(HaveOccurred()) + provider.BlockDeviceMappings = []*v1alpha1.BlockDeviceMapping{{ + EBS: &v1alpha1.BlockDevice{ + VolumeSize: resource.NewScaledQuantity(65, resource.Tera), + }, + }} + provisioner := Provisioner(test.ProvisionerOptions{Provider: provider}) + Expect(provisioner.Validate(ctx)).ToNot(Succeed()) + }) + It("should not allow nil volume size", func() { + provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) + Expect(err).ToNot(HaveOccurred()) + provider.BlockDeviceMappings = []*v1alpha1.BlockDeviceMapping{{ + DeviceName: aws.String("/dev/xvda"), + EBS: &v1alpha1.BlockDevice{}, + }} + provisioner := Provisioner(test.ProvisionerOptions{Provider: provider}) + Expect(provisioner.Validate(ctx)).ToNot(Succeed()) + }) + It("should not allow empty ebs block", func() { + provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) + Expect(err).ToNot(HaveOccurred()) + provider.BlockDeviceMappings = []*v1alpha1.BlockDeviceMapping{{ + DeviceName: aws.String("/dev/xvda"), + }} + provisioner := Provisioner(test.ProvisionerOptions{Provider: provider}) + Expect(provisioner.Validate(ctx)).ToNot(Succeed()) + }) + }) + }) +}) + +func Provisioner(options test.ProvisionerOptions) v1alpha1.Provisioner { + if options.Provider == nil { + options.Provider = &v1alpha1.AWS{} + } + provider := options.Provider.(*v1alpha1.AWS) + if provider.AMIFamily == nil { + provider.AMIFamily = aws.String(v1alpha1.AMIFamilyAL2) + } + if provider.SubnetSelector == nil { + provider.SubnetSelector = map[string]string{"foo": "bar"} + } + if provider.SecurityGroupSelector == nil { + provider.SecurityGroupSelector = map[string]string{"foo": "bar"} + } + from := test.Provisioner(options) + to := v1alpha1.Provisioner(*from) + // Apply provider specific defaults + to.SetDefaults(context.Background()) + return to +} diff --git a/pkg/cloudprovider/aws/suite_test.go b/pkg/cloudprovider/aws/suite_test.go index ae3e7ee0b299..f3a50876e9a3 100644 --- a/pkg/cloudprovider/aws/suite_test.go +++ b/pkg/cloudprovider/aws/suite_test.go @@ -20,7 +20,6 @@ import ( "errors" "fmt" "io/ioutil" - "math" "strings" "testing" "time" @@ -163,7 +162,7 @@ var _ = Describe("Allocation", func() { SubnetSelector: map[string]string{"foo": "bar"}, SecurityGroupSelector: map[string]string{"foo": "bar"}, } - provisioner = test.Provisioner(test.ProvisionerOptions{Provider: provider}) + provisioner = Provisioner(test.ProvisionerOptions{Provider: provider}) fakeEC2API.Reset() fakePricingAPI.Reset() launchTemplateCache.Flush() @@ -552,12 +551,6 @@ var _ = Describe("Allocation", func() { }) }) Context("CapacityType", func() { - It("should default to on-demand", func() { - ExpectApplied(ctx, env.Client, provisioner) - pod := ExpectProvisioned(ctx, env.Client, controller, test.UnschedulablePod())[0] - node := ExpectScheduled(ctx, env.Client, pod) - Expect(node.Labels).To(HaveKeyWithValue(v1alpha5.LabelCapacityType, v1alpha1.CapacityTypeOnDemand)) - }) It("should launch spot capacity if flexible to both spot and on demand", func() { provisioner.Spec.Requirements = []v1.NodeSelectorRequirement{ {Key: v1alpha5.LabelCapacityType, Operator: v1.NodeSelectorOpIn, Values: []string{v1alpha1.CapacityTypeSpot, v1alpha1.CapacityTypeOnDemand}}} @@ -645,7 +638,7 @@ var _ = Describe("Allocation", func() { }) It("should tag with provisioner name", func() { provisionerName := "the-provisioner" - ExpectApplied(ctx, env.Client, test.Provisioner(test.ProvisionerOptions{Provider: provider, ObjectMeta: metav1.ObjectMeta{Name: provisionerName}})) + ExpectApplied(ctx, env.Client, Provisioner(test.ProvisionerOptions{Provider: provider, ObjectMeta: metav1.ObjectMeta{Name: provisionerName}})) pod := ExpectProvisioned(ctx, env.Client, controller, test.UnschedulablePod())[0] ExpectScheduled(ctx, env.Client, pod) Expect(fakeEC2API.CalledWithCreateFleetInput.Len()).To(Equal(1)) @@ -668,7 +661,7 @@ var _ = Describe("Allocation", func() { "tag1": "tag1value", "tag2": "tag2value", } - ExpectApplied(ctx, env.Client, test.Provisioner(test.ProvisionerOptions{Provider: provider})) + ExpectApplied(ctx, env.Client, Provisioner(test.ProvisionerOptions{Provider: provider})) pod := ExpectProvisioned(ctx, env.Client, controller, test.UnschedulablePod())[0] ExpectScheduled(ctx, env.Client, pod) Expect(fakeEC2API.CalledWithCreateFleetInput.Len()).To(Equal(1)) @@ -690,8 +683,8 @@ var _ = Describe("Allocation", func() { "Name": "myname", } - ExpectApplied(ctx, env.Client, test.Provisioner(test.ProvisionerOptions{Provider: provider})) - ExpectApplied(ctx, env.Client, test.Provisioner(test.ProvisionerOptions{Provider: provider})) + ExpectApplied(ctx, env.Client, Provisioner(test.ProvisionerOptions{Provider: provider})) + ExpectApplied(ctx, env.Client, Provisioner(test.ProvisionerOptions{Provider: provider})) pod := ExpectProvisioned(ctx, env.Client, controller, test.UnschedulablePod())[0] ExpectScheduled(ctx, env.Client, pod) Expect(fakeEC2API.CalledWithCreateFleetInput.Len()).To(Equal(1)) @@ -728,7 +721,7 @@ var _ = Describe("Allocation", func() { It("should allow a launch template to be specified", func() { provider.LaunchTemplateName = aws.String("test-launch-template") provider.SecurityGroupSelector = nil - ExpectApplied(ctx, env.Client, test.Provisioner(test.ProvisionerOptions{Provider: provider})) + ExpectApplied(ctx, env.Client, Provisioner(test.ProvisionerOptions{Provider: provider})) pod := ExpectProvisioned(ctx, env.Client, controller, test.UnschedulablePod())[0] ExpectScheduled(ctx, env.Client, pod) Expect(fakeEC2API.CalledWithCreateFleetInput.Len()).To(Equal(1)) @@ -762,7 +755,7 @@ var _ = Describe("Allocation", func() { }) Context("Subnets", func() { It("should default to the cluster's subnets", func() { - ExpectApplied(ctx, env.Client, test.Provisioner(test.ProvisionerOptions{Provider: provider})) + ExpectApplied(ctx, env.Client, Provisioner(test.ProvisionerOptions{Provider: provider})) pod := ExpectProvisioned(ctx, env.Client, controller, test.UnschedulablePod( test.PodOptions{NodeSelector: map[string]string{v1.LabelArchStable: v1alpha5.ArchitectureAmd64}}))[0] ExpectScheduled(ctx, env.Client, pod) @@ -800,7 +793,7 @@ var _ = Describe("Allocation", func() { }) It("should discover subnet by ID", func() { provider.SubnetSelector = map[string]string{"aws-ids": "subnet-test1"} - ExpectApplied(ctx, env.Client, test.Provisioner(test.ProvisionerOptions{Provider: provider})) + ExpectApplied(ctx, env.Client, Provisioner(test.ProvisionerOptions{Provider: provider})) pod := ExpectProvisioned(ctx, env.Client, controller, test.UnschedulablePod())[0] ExpectScheduled(ctx, env.Client, pod) createFleetInput := fakeEC2API.CalledWithCreateFleetInput.Pop() @@ -808,7 +801,7 @@ var _ = Describe("Allocation", func() { }) It("should discover subnets by IDs", func() { provider.SubnetSelector = map[string]string{"aws-ids": "subnet-test1,subnet-test2"} - ExpectApplied(ctx, env.Client, test.Provisioner(test.ProvisionerOptions{Provider: provider})) + ExpectApplied(ctx, env.Client, Provisioner(test.ProvisionerOptions{Provider: provider})) pod := ExpectProvisioned(ctx, env.Client, controller, test.UnschedulablePod())[0] ExpectScheduled(ctx, env.Client, pod) createFleetInput := fakeEC2API.CalledWithCreateFleetInput.Pop() @@ -819,7 +812,7 @@ var _ = Describe("Allocation", func() { }) It("should discover subnets by IDs and tags", func() { provider.SubnetSelector = map[string]string{"aws-ids": "subnet-test1,subnet-test2", "foo": "bar"} - ExpectApplied(ctx, env.Client, test.Provisioner(test.ProvisionerOptions{Provider: provider})) + ExpectApplied(ctx, env.Client, Provisioner(test.ProvisionerOptions{Provider: provider})) pod := ExpectProvisioned(ctx, env.Client, controller, test.UnschedulablePod())[0] ExpectScheduled(ctx, env.Client, pod) createFleetInput := fakeEC2API.CalledWithCreateFleetInput.Pop() @@ -830,7 +823,7 @@ var _ = Describe("Allocation", func() { }) It("should discover subnets by IDs intersected with tags", func() { provider.SubnetSelector = map[string]string{"aws-ids": "subnet-test2", "foo": "bar"} - ExpectApplied(ctx, env.Client, test.Provisioner(test.ProvisionerOptions{Provider: provider})) + ExpectApplied(ctx, env.Client, Provisioner(test.ProvisionerOptions{Provider: provider})) pod := ExpectProvisioned(ctx, env.Client, controller, test.UnschedulablePod())[0] ExpectScheduled(ctx, env.Client, pod) createFleetInput := fakeEC2API.CalledWithCreateFleetInput.Pop() @@ -841,7 +834,7 @@ var _ = Describe("Allocation", func() { }) Context("Security Groups", func() { It("should default to the clusters security groups", func() { - ExpectApplied(ctx, env.Client, test.Provisioner(test.ProvisionerOptions{Provider: provider})) + ExpectApplied(ctx, env.Client, Provisioner(test.ProvisionerOptions{Provider: provider})) pod := ExpectProvisioned(ctx, env.Client, controller, test.UnschedulablePod())[0] ExpectScheduled(ctx, env.Client, pod) Expect(fakeEC2API.CalledWithCreateLaunchTemplateInput.Len()).To(Equal(1)) @@ -857,7 +850,7 @@ var _ = Describe("Allocation", func() { {GroupId: aws.String("test-sg-1"), Tags: []*ec2.Tag{{Key: aws.String("kubernetes.io/cluster/test-cluster"), Value: aws.String("test-sg-1")}}}, {GroupId: aws.String("test-sg-2"), Tags: []*ec2.Tag{{Key: aws.String("kubernetes.io/cluster/test-cluster"), Value: aws.String("test-sg-2")}}}, }}) - ExpectApplied(ctx, env.Client, test.Provisioner(test.ProvisionerOptions{Provider: provider})) + ExpectApplied(ctx, env.Client, Provisioner(test.ProvisionerOptions{Provider: provider})) pod := ExpectProvisioned(ctx, env.Client, controller, test.UnschedulablePod())[0] ExpectScheduled(ctx, env.Client, pod) Expect(fakeEC2API.CalledWithCreateLaunchTemplateInput.Len()).To(Equal(1)) @@ -869,7 +862,7 @@ var _ = Describe("Allocation", func() { }) It("should discover security groups by ID", func() { provider.SecurityGroupSelector = map[string]string{"aws-ids": "sg-test1"} - ExpectApplied(ctx, env.Client, test.Provisioner(test.ProvisionerOptions{Provider: provider})) + ExpectApplied(ctx, env.Client, Provisioner(test.ProvisionerOptions{Provider: provider})) pod := ExpectProvisioned(ctx, env.Client, controller, test.UnschedulablePod())[0] ExpectScheduled(ctx, env.Client, pod) Expect(fakeEC2API.CalledWithCreateLaunchTemplateInput.Len()).To(Equal(1)) @@ -880,7 +873,7 @@ var _ = Describe("Allocation", func() { }) It("should discover security groups by IDs", func() { provider.SecurityGroupSelector = map[string]string{"aws-ids": "sg-test1,sg-test2"} - ExpectApplied(ctx, env.Client, test.Provisioner(test.ProvisionerOptions{Provider: provider})) + ExpectApplied(ctx, env.Client, Provisioner(test.ProvisionerOptions{Provider: provider})) pod := ExpectProvisioned(ctx, env.Client, controller, test.UnschedulablePod())[0] ExpectScheduled(ctx, env.Client, pod) Expect(fakeEC2API.CalledWithCreateLaunchTemplateInput.Len()).To(Equal(1)) @@ -892,7 +885,7 @@ var _ = Describe("Allocation", func() { }) It("should discover security groups by IDs and tags", func() { provider.SecurityGroupSelector = map[string]string{"aws-ids": "sg-test1,sg-test2", "foo": "bar"} - ExpectApplied(ctx, env.Client, test.Provisioner(test.ProvisionerOptions{Provider: provider})) + ExpectApplied(ctx, env.Client, Provisioner(test.ProvisionerOptions{Provider: provider})) pod := ExpectProvisioned(ctx, env.Client, controller, test.UnschedulablePod())[0] ExpectScheduled(ctx, env.Client, pod) Expect(fakeEC2API.CalledWithCreateLaunchTemplateInput.Len()).To(Equal(1)) @@ -904,7 +897,7 @@ var _ = Describe("Allocation", func() { }) It("should discover security groups by IDs intersected with tags", func() { provider.SecurityGroupSelector = map[string]string{"aws-ids": "sg-test2", "foo": "bar"} - ExpectApplied(ctx, env.Client, test.Provisioner(test.ProvisionerOptions{Provider: provider})) + ExpectApplied(ctx, env.Client, Provisioner(test.ProvisionerOptions{Provider: provider})) pod := ExpectProvisioned(ctx, env.Client, controller, test.UnschedulablePod())[0] ExpectScheduled(ctx, env.Client, pod) Expect(fakeEC2API.CalledWithCreateLaunchTemplateInput.Len()).To(Equal(1)) @@ -918,7 +911,7 @@ var _ = Describe("Allocation", func() { It("should not specify --use-max-pods=false when using ENI-based pod density", func() { opts.AWSENILimitedPodDensity = true controller := provisioning.NewController(injection.WithOptions(ctx, opts), cfg, env.Client, clientSet.CoreV1(), recorder, cloudProvider, cluster) - ExpectApplied(ctx, env.Client, test.Provisioner(test.ProvisionerOptions{Provider: provider})) + ExpectApplied(ctx, env.Client, Provisioner(test.ProvisionerOptions{Provider: provider})) pod := ExpectProvisioned(ctx, env.Client, controller, test.UnschedulablePod())[0] ExpectScheduled(ctx, env.Client, pod) Expect(fakeEC2API.CalledWithCreateLaunchTemplateInput.Len()).To(Equal(1)) @@ -930,7 +923,7 @@ var _ = Describe("Allocation", func() { opts.AWSENILimitedPodDensity = false controller := provisioning.NewController(injection.WithOptions(ctx, opts), cfg, env.Client, clientSet.CoreV1(), recorder, cloudProvider, cluster) - ExpectApplied(ctx, env.Client, test.Provisioner(test.ProvisionerOptions{Provider: provider})) + ExpectApplied(ctx, env.Client, Provisioner(test.ProvisionerOptions{Provider: provider})) pod := ExpectProvisioned(ctx, env.Client, controller, test.UnschedulablePod())[0] ExpectScheduled(ctx, env.Client, pod) Expect(fakeEC2API.CalledWithCreateLaunchTemplateInput.Len()).To(Equal(1)) @@ -940,7 +933,7 @@ var _ = Describe("Allocation", func() { Expect(string(userData)).To(ContainSubstring("--max-pods=110")) }) It("should specify --container-runtime containerd by default", func() { - ExpectApplied(ctx, env.Client, test.Provisioner(test.ProvisionerOptions{Provider: provider})) + ExpectApplied(ctx, env.Client, Provisioner(test.ProvisionerOptions{Provider: provider})) pod := ExpectProvisioned(ctx, env.Client, controller, test.UnschedulablePod())[0] ExpectScheduled(ctx, env.Client, pod) Expect(fakeEC2API.CalledWithCreateLaunchTemplateInput.Len()).To(Equal(1)) @@ -949,7 +942,7 @@ var _ = Describe("Allocation", func() { Expect(string(userData)).To(ContainSubstring("--container-runtime containerd")) }) It("should specify dockerd if specified in the provisionerSpec", func() { - ExpectApplied(ctx, env.Client, test.Provisioner(test.ProvisionerOptions{ + ExpectApplied(ctx, env.Client, Provisioner(test.ProvisionerOptions{ Provider: provider, Kubelet: &v1alpha5.KubeletConfiguration{ContainerRuntime: aws.String("dockerd")}, })) @@ -961,7 +954,7 @@ var _ = Describe("Allocation", func() { Expect(string(userData)).To(ContainSubstring("--container-runtime dockerd")) }) It("should specify --container-runtime docker when using Neuron GPUs", func() { - ExpectApplied(ctx, env.Client, test.Provisioner(test.ProvisionerOptions{Provider: provider})) + ExpectApplied(ctx, env.Client, Provisioner(test.ProvisionerOptions{Provider: provider})) pod := ExpectProvisioned(ctx, env.Client, controller, test.UnschedulablePod(test.PodOptions{ ResourceRequirements: v1.ResourceRequirements{ Requests: map[v1.ResourceName]resource.Quantity{ @@ -980,7 +973,7 @@ var _ = Describe("Allocation", func() { Expect(string(userData)).To(ContainSubstring("--container-runtime docker")) }) It("should specify --container-runtime containerd when using Nvidia GPUs", func() { - ExpectApplied(ctx, env.Client, test.Provisioner(test.ProvisionerOptions{Provider: provider})) + ExpectApplied(ctx, env.Client, Provisioner(test.ProvisionerOptions{Provider: provider})) pod := ExpectProvisioned(ctx, env.Client, controller, test.UnschedulablePod(test.PodOptions{ ResourceRequirements: v1.ResourceRequirements{ Requests: map[v1.ResourceName]resource.Quantity{ @@ -1014,7 +1007,7 @@ var _ = Describe("Allocation", func() { ObjectMeta: metav1.ObjectMeta{Name: providerRefName}}) ExpectApplied(ctx, env.Client, nodeTemplate) controller := provisioning.NewController(injection.WithOptions(ctx, opts), cfg, env.Client, clientSet.CoreV1(), recorder, cloudProvider, cluster) - newProvisioner := test.Provisioner(test.ProvisionerOptions{ProviderRef: providerRef}) + newProvisioner := Provisioner(test.ProvisionerOptions{ProviderRef: providerRef}) ExpectApplied(ctx, env.Client, newProvisioner) env.Client.Get(ctx, client.ObjectKeyFromObject(newProvisioner), newProvisioner) pod := ExpectProvisioned(ctx, env.Client, controller, test.UnschedulablePod())[0] @@ -1042,7 +1035,7 @@ var _ = Describe("Allocation", func() { ObjectMeta: metav1.ObjectMeta{Name: providerRefName}}) ExpectApplied(ctx, env.Client, nodeTemplate) controller := provisioning.NewController(injection.WithOptions(ctx, opts), cfg, env.Client, clientSet.CoreV1(), recorder, cloudProvider, cluster) - newProvisioner := test.Provisioner(test.ProvisionerOptions{ProviderRef: providerRef}) + newProvisioner := Provisioner(test.ProvisionerOptions{ProviderRef: providerRef}) ExpectApplied(ctx, env.Client, newProvisioner) pod := ExpectProvisioned(ctx, env.Client, controller, test.UnschedulablePod())[0] ExpectScheduled(ctx, env.Client, pod) @@ -1062,7 +1055,7 @@ var _ = Describe("Allocation", func() { Name: "doesnotexist", } controller := provisioning.NewController(ctx, cfg, env.Client, clientSet.CoreV1(), recorder, cloudProvider, cluster) - newProvisioner := test.Provisioner(test.ProvisionerOptions{ProviderRef: providerRef}) + newProvisioner := Provisioner(test.ProvisionerOptions{ProviderRef: providerRef}) ExpectApplied(ctx, env.Client, newProvisioner) pod := ExpectProvisioned(ctx, env.Client, controller, test.UnschedulablePod())[0] // This will not be scheduled since we were pointed to a non-existent awsnodetemplate resource. @@ -1081,7 +1074,7 @@ var _ = Describe("Allocation", func() { ObjectMeta: metav1.ObjectMeta{Name: providerRefName}}) ExpectApplied(ctx, env.Client, nodeTemplate) controller := provisioning.NewController(ctx, cfg, env.Client, clientSet.CoreV1(), recorder, cloudProvider, cluster) - newProvisioner := test.Provisioner(test.ProvisionerOptions{ProviderRef: providerRef}) + newProvisioner := Provisioner(test.ProvisionerOptions{ProviderRef: providerRef}) ExpectApplied(ctx, env.Client, newProvisioner) pod := ExpectProvisioned(ctx, env.Client, controller, test.UnschedulablePod())[0] // This will not be scheduled since userData cannot be generated for the prospective node. @@ -1103,7 +1096,7 @@ var _ = Describe("Allocation", func() { ObjectMeta: metav1.ObjectMeta{Name: providerRefName}}) ExpectApplied(ctx, env.Client, nodeTemplate) controller := provisioning.NewController(injection.WithOptions(ctx, opts), cfg, env.Client, clientSet.CoreV1(), recorder, cloudProvider, cluster) - newProvisioner := test.Provisioner(test.ProvisionerOptions{ProviderRef: providerRef}) + newProvisioner := Provisioner(test.ProvisionerOptions{ProviderRef: providerRef}) ExpectApplied(ctx, env.Client, newProvisioner) pod := ExpectProvisioned(ctx, env.Client, controller, test.UnschedulablePod())[0] ExpectScheduled(ctx, env.Client, pod) @@ -1127,7 +1120,7 @@ var _ = Describe("Allocation", func() { ObjectMeta: metav1.ObjectMeta{Name: providerRefName}}) ExpectApplied(ctx, env.Client, nodeTemplate) controller := provisioning.NewController(injection.WithOptions(ctx, opts), cfg, env.Client, clientSet.CoreV1(), recorder, cloudProvider, cluster) - newProvisioner := test.Provisioner(test.ProvisionerOptions{ProviderRef: providerRef}) + newProvisioner := Provisioner(test.ProvisionerOptions{ProviderRef: providerRef}) ExpectApplied(ctx, env.Client, newProvisioner) pod := ExpectProvisioned(ctx, env.Client, controller, test.UnschedulablePod())[0] ExpectScheduled(ctx, env.Client, pod) @@ -1151,7 +1144,7 @@ var _ = Describe("Allocation", func() { ObjectMeta: metav1.ObjectMeta{Name: providerRefName}}) ExpectApplied(ctx, env.Client, nodeTemplate) controller := provisioning.NewController(injection.WithOptions(ctx, opts), cfg, env.Client, clientSet.CoreV1(), recorder, cloudProvider, cluster) - newProvisioner := test.Provisioner(test.ProvisionerOptions{ProviderRef: providerRef}) + newProvisioner := Provisioner(test.ProvisionerOptions{ProviderRef: providerRef}) ExpectApplied(ctx, env.Client, newProvisioner) pod := ExpectProvisioned(ctx, env.Client, controller, test.UnschedulablePod())[0] // This will not be scheduled since userData cannot be generated for the prospective node. @@ -1160,7 +1153,7 @@ var _ = Describe("Allocation", func() { }) Context("Kubelet Args", func() { It("should specify the --dns-cluster-ip flag when clusterDNSIP is set", func() { - ExpectApplied(ctx, env.Client, test.Provisioner(test.ProvisionerOptions{ + ExpectApplied(ctx, env.Client, Provisioner(test.ProvisionerOptions{ Kubelet: &v1alpha5.KubeletConfiguration{ClusterDNS: []string{"10.0.10.100"}}, Provider: provider, })) @@ -1174,7 +1167,7 @@ var _ = Describe("Allocation", func() { }) Context("Instance Profile", func() { It("should use the default instance profile if none specified on the Provisioner", func() { - ExpectApplied(ctx, env.Client, test.Provisioner(test.ProvisionerOptions{Provider: provider})) + ExpectApplied(ctx, env.Client, Provisioner(test.ProvisionerOptions{Provider: provider})) pod := ExpectProvisioned(ctx, env.Client, controller, test.UnschedulablePod())[0] ExpectScheduled(ctx, env.Client, pod) Expect(fakeEC2API.CalledWithCreateLaunchTemplateInput.Len()).To(Equal(1)) @@ -1183,7 +1176,7 @@ var _ = Describe("Allocation", func() { }) It("should use the instance profile on the Provisioner when specified", func() { provider.InstanceProfile = aws.String("overridden-profile") - ExpectApplied(ctx, env.Client, test.Provisioner(test.ProvisionerOptions{Provider: provider})) + ExpectApplied(ctx, env.Client, Provisioner(test.ProvisionerOptions{Provider: provider})) pod := ExpectProvisioned(ctx, env.Client, controller, test.UnschedulablePod())[0] ExpectScheduled(ctx, env.Client, pod) Expect(fakeEC2API.CalledWithCreateLaunchTemplateInput.Len()).To(Equal(1)) @@ -1213,7 +1206,7 @@ var _ = Describe("Allocation", func() { HTTPPutResponseHopLimit: aws.Int64(1), HTTPTokens: aws.String(ec2.LaunchTemplateHttpTokensStateOptional), } - ExpectApplied(ctx, env.Client, test.Provisioner(test.ProvisionerOptions{Provider: provider})) + ExpectApplied(ctx, env.Client, Provisioner(test.ProvisionerOptions{Provider: provider})) pod := ExpectProvisioned(ctx, env.Client, controller, test.UnschedulablePod())[0] ExpectScheduled(ctx, env.Client, pod) Expect(fakeEC2API.CalledWithCreateLaunchTemplateInput.Len()).To(Equal(1)) @@ -1228,7 +1221,7 @@ var _ = Describe("Allocation", func() { It("should default AL2 block device mappings", func() { provider, _ := v1alpha1.Deserialize(provisioner.Spec.Provider) provider.AMIFamily = &v1alpha1.AMIFamilyAL2 - ExpectApplied(ctx, env.Client, test.Provisioner(test.ProvisionerOptions{Provider: provider})) + ExpectApplied(ctx, env.Client, Provisioner(test.ProvisionerOptions{Provider: provider})) pod := ExpectProvisioned(ctx, env.Client, controller, test.UnschedulablePod())[0] ExpectScheduled(ctx, env.Client, pod) Expect(fakeEC2API.CalledWithCreateLaunchTemplateInput.Len()).To(Equal(1)) @@ -1254,7 +1247,7 @@ var _ = Describe("Allocation", func() { }, }, } - ExpectApplied(ctx, env.Client, test.Provisioner(test.ProvisionerOptions{Provider: provider})) + ExpectApplied(ctx, env.Client, Provisioner(test.ProvisionerOptions{Provider: provider})) pod := ExpectProvisioned(ctx, env.Client, controller, test.UnschedulablePod())[0] ExpectScheduled(ctx, env.Client, pod) Expect(fakeEC2API.CalledWithCreateLaunchTemplateInput.Len()).To(Equal(1)) @@ -1270,7 +1263,7 @@ var _ = Describe("Allocation", func() { It("should default bottlerocket second volume with root volume size", func() { provider, _ := v1alpha1.Deserialize(provisioner.Spec.Provider) provider.AMIFamily = &v1alpha1.AMIFamilyBottlerocket - ExpectApplied(ctx, env.Client, test.Provisioner(test.ProvisionerOptions{Provider: provider})) + ExpectApplied(ctx, env.Client, Provisioner(test.ProvisionerOptions{Provider: provider})) pod := ExpectProvisioned(ctx, env.Client, controller, test.UnschedulablePod())[0] ExpectScheduled(ctx, env.Client, pod) Expect(fakeEC2API.CalledWithCreateLaunchTemplateInput.Len()).To(Equal(1)) @@ -1386,7 +1379,7 @@ var _ = Describe("Allocation", func() { VolumeSize: resource.NewScaledQuantity(50, resource.Giga), }, }} - ExpectApplied(ctx, env.Client, test.Provisioner(test.ProvisionerOptions{Provider: provider})) + ExpectApplied(ctx, env.Client, Provisioner(test.ProvisionerOptions{Provider: provider})) pod := ExpectProvisioned(ctx, env.Client, controller, test.UnschedulablePod(test.PodOptions{ResourceRequirements: v1.ResourceRequirements{ Requests: map[v1.ResourceName]resource.Quantity{ @@ -1400,6 +1393,30 @@ var _ = Describe("Allocation", func() { }) }) }) + Context("EC2 Context", func() { + It("should set context on the CreateFleet request if specified on the Provisioner", func() { + provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) + Expect(err).ToNot(HaveOccurred()) + provider.Context = aws.String("context-1234") + provisioner := Provisioner(test.ProvisionerOptions{Provider: provider}) + provisioner.SetDefaults(ctx) + ExpectApplied(ctx, env.Client, provisioner) + pod := ExpectProvisioned(ctx, env.Client, controller, test.UnschedulablePod())[0] + ExpectScheduled(ctx, env.Client, pod) + Expect(fakeEC2API.CalledWithCreateFleetInput.Len()).To(Equal(1)) + createFleetInput := fakeEC2API.CalledWithCreateFleetInput.Pop() + Expect(aws.StringValue(createFleetInput.Context)).To(Equal("context-1234")) + }) + It("should default to no EC2 Context", func() { + provisioner.SetDefaults(ctx) + ExpectApplied(ctx, env.Client, provisioner) + pod := ExpectProvisioned(ctx, env.Client, controller, test.UnschedulablePod())[0] + ExpectScheduled(ctx, env.Client, pod) + Expect(fakeEC2API.CalledWithCreateFleetInput.Len()).To(Equal(1)) + createFleetInput := fakeEC2API.CalledWithCreateFleetInput.Pop() + Expect(createFleetInput.Context).To(BeNil()) + }) + }) Context("Defaulting", func() { // Intent here is that if updates occur on the controller, the Provisioner doesn't need to be recreated It("should not set the InstanceProfile with the default if none provided in Provisioner", func() { @@ -1423,305 +1440,6 @@ var _ = Describe("Allocation", func() { })) }) }) - Context("Validation", func() { - It("should validate", func() { - Expect(provisioner.Validate(ctx)).To(Succeed()) - }) - It("should succeed if provider undefined", func() { - provisioner.Spec.Provider = nil - Expect(provisioner.Validate(ctx)).To(Succeed()) - }) - - Context("SubnetSelector", func() { - It("should not allow empty string keys or values", func() { - provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) - Expect(err).ToNot(HaveOccurred()) - for key, value := range map[string]string{ - "": "value", - "key": "", - } { - provider.SubnetSelector = map[string]string{key: value} - provisioner := test.Provisioner(test.ProvisionerOptions{Provider: provider}) - Expect(provisioner.Validate(ctx)).ToNot(Succeed()) - } - }) - }) - Context("SecurityGroupSelector", func() { - It("should not allow with a custom launch template", func() { - provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) - Expect(err).ToNot(HaveOccurred()) - provider.LaunchTemplateName = aws.String("my-lt") - provider.SecurityGroupSelector = map[string]string{"key": "value"} - provisioner := test.Provisioner(test.ProvisionerOptions{Provider: provider}) - Expect(provisioner.Validate(ctx)).ToNot(Succeed()) - }) - It("should not allow empty string keys or values", func() { - provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) - Expect(err).ToNot(HaveOccurred()) - for key, value := range map[string]string{ - "": "value", - "key": "", - } { - provider.SecurityGroupSelector = map[string]string{key: value} - provisioner := test.Provisioner(test.ProvisionerOptions{Provider: provider}) - Expect(provisioner.Validate(ctx)).ToNot(Succeed()) - } - }) - }) - Context("EC2 Context", func() { - It("should set context on the CreateFleet request if specified on the Provisioner", func() { - provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) - Expect(err).ToNot(HaveOccurred()) - provider.Context = aws.String("context-1234") - provisioner := test.Provisioner(test.ProvisionerOptions{Provider: provider}) - provisioner.SetDefaults(ctx) - ExpectApplied(ctx, env.Client, provisioner) - pod := ExpectProvisioned(ctx, env.Client, controller, test.UnschedulablePod())[0] - ExpectScheduled(ctx, env.Client, pod) - Expect(fakeEC2API.CalledWithCreateFleetInput.Len()).To(Equal(1)) - createFleetInput := fakeEC2API.CalledWithCreateFleetInput.Pop() - Expect(aws.StringValue(createFleetInput.Context)).To(Equal("context-1234")) - }) - It("should default to no EC2 Context", func() { - provisioner.SetDefaults(ctx) - ExpectApplied(ctx, env.Client, provisioner) - pod := ExpectProvisioned(ctx, env.Client, controller, test.UnschedulablePod())[0] - ExpectScheduled(ctx, env.Client, pod) - Expect(fakeEC2API.CalledWithCreateFleetInput.Len()).To(Equal(1)) - createFleetInput := fakeEC2API.CalledWithCreateFleetInput.Pop() - Expect(createFleetInput.Context).To(BeNil()) - }) - }) - Context("Labels", func() { - It("should not allow unrecognized labels with the aws label prefix", func() { - provisioner.Spec.Labels = map[string]string{v1alpha1.LabelDomain + "/" + randomdata.SillyName(): randomdata.SillyName()} - Expect(provisioner.Validate(ctx)).ToNot(Succeed()) - }) - It("should support well known labels", func() { - for _, label := range []string{ - v1alpha1.LabelInstanceHypervisor, - v1alpha1.LabelInstanceFamily, - v1alpha1.LabelInstanceSize, - v1alpha1.LabelInstanceCPU, - v1alpha1.LabelInstanceMemory, - v1alpha1.LabelInstanceGPUName, - v1alpha1.LabelInstanceGPUManufacturer, - v1alpha1.LabelInstanceGPUCount, - v1alpha1.LabelInstanceGPUMemory, - } { - provisioner.Spec.Labels = map[string]string{label: randomdata.SillyName()} - Expect(provisioner.Validate(ctx)).To(Succeed()) - } - }) - }) - Context("MetadataOptions", func() { - It("should not allow with a custom launch template", func() { - provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) - Expect(err).ToNot(HaveOccurred()) - provider.LaunchTemplateName = aws.String("my-lt") - provider.MetadataOptions = &v1alpha1.MetadataOptions{} - provisioner := test.Provisioner(test.ProvisionerOptions{Provider: provider}) - Expect(provisioner.Validate(ctx)).ToNot(Succeed()) - }) - It("should allow missing values", func() { - provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) - Expect(err).ToNot(HaveOccurred()) - provider.MetadataOptions = &v1alpha1.MetadataOptions{} - provisioner := test.Provisioner(test.ProvisionerOptions{Provider: provider}) - Expect(provisioner.Validate(ctx)).To(Succeed()) - }) - Context("HTTPEndpoint", func() { - It("should allow enum values", func() { - provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) - Expect(err).ToNot(HaveOccurred()) - for _, value := range ec2.LaunchTemplateInstanceMetadataEndpointState_Values() { - provider.MetadataOptions = &v1alpha1.MetadataOptions{ - HTTPEndpoint: &value, - } - provisioner := test.Provisioner(test.ProvisionerOptions{Provider: provider}) - Expect(provisioner.Validate(ctx)).To(Succeed()) - } - }) - It("should not allow non-enum values", func() { - provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) - Expect(err).ToNot(HaveOccurred()) - provider.MetadataOptions = &v1alpha1.MetadataOptions{ - HTTPEndpoint: aws.String(randomdata.SillyName()), - } - provisioner := test.Provisioner(test.ProvisionerOptions{Provider: provider}) - Expect(provisioner.Validate(ctx)).ToNot(Succeed()) - }) - }) - Context("HTTPProtocolIpv6", func() { - It("should allow enum values", func() { - provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) - Expect(err).ToNot(HaveOccurred()) - for _, value := range ec2.LaunchTemplateInstanceMetadataProtocolIpv6_Values() { - provider.MetadataOptions = &v1alpha1.MetadataOptions{ - HTTPProtocolIPv6: &value, - } - provisioner := test.Provisioner(test.ProvisionerOptions{Provider: provider}) - Expect(provisioner.Validate(ctx)).To(Succeed()) - } - }) - It("should not allow non-enum values", func() { - provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) - Expect(err).ToNot(HaveOccurred()) - provider.MetadataOptions = &v1alpha1.MetadataOptions{ - HTTPProtocolIPv6: aws.String(randomdata.SillyName()), - } - provisioner := test.Provisioner(test.ProvisionerOptions{Provider: provider}) - Expect(provisioner.Validate(ctx)).ToNot(Succeed()) - }) - }) - Context("HTTPPutResponseHopLimit", func() { - It("should validate inside accepted range", func() { - provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) - Expect(err).ToNot(HaveOccurred()) - provider.MetadataOptions = &v1alpha1.MetadataOptions{ - HTTPPutResponseHopLimit: aws.Int64(int64(randomdata.Number(1, 65))), - } - provisioner := test.Provisioner(test.ProvisionerOptions{Provider: provider}) - Expect(provisioner.Validate(ctx)).To(Succeed()) - }) - It("should not validate outside accepted range", func() { - provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) - Expect(err).ToNot(HaveOccurred()) - provider.MetadataOptions = &v1alpha1.MetadataOptions{} - // We expect to be able to invalidate any hop limit between - // [math.MinInt64, 1). But, to avoid a panic here, we can't - // exceed math.MaxInt for the difference between bounds of - // the random number range. So we divide the range - // approximately in half and test on both halves. - provider.MetadataOptions.HTTPPutResponseHopLimit = aws.Int64(int64(randomdata.Number(math.MinInt64, math.MinInt64/2))) - provisioner := test.Provisioner(test.ProvisionerOptions{Provider: provider}) - Expect(provisioner.Validate(ctx)).ToNot(Succeed()) - provider.MetadataOptions.HTTPPutResponseHopLimit = aws.Int64(int64(randomdata.Number(math.MinInt64/2, 1))) - provisioner = test.Provisioner(test.ProvisionerOptions{Provider: provider}) - Expect(provisioner.Validate(ctx)).ToNot(Succeed()) - - provider.MetadataOptions.HTTPPutResponseHopLimit = aws.Int64(int64(randomdata.Number(65, math.MaxInt64))) - provisioner = test.Provisioner(test.ProvisionerOptions{Provider: provider}) - Expect(provisioner.Validate(ctx)).ToNot(Succeed()) - }) - }) - Context("HTTPTokens", func() { - It("should allow enum values", func() { - provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) - Expect(err).ToNot(HaveOccurred()) - for _, value := range ec2.LaunchTemplateHttpTokensState_Values() { - provider.MetadataOptions = &v1alpha1.MetadataOptions{ - HTTPTokens: aws.String(value), - } - provisioner := test.Provisioner(test.ProvisionerOptions{Provider: provider}) - Expect(provisioner.Validate(ctx)).To(Succeed()) - } - }) - It("should not allow non-enum values", func() { - provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) - Expect(err).ToNot(HaveOccurred()) - provider.MetadataOptions = &v1alpha1.MetadataOptions{ - HTTPTokens: aws.String(randomdata.SillyName()), - } - provisioner := test.Provisioner(test.ProvisionerOptions{Provider: provider}) - Expect(provisioner.Validate(ctx)).ToNot(Succeed()) - }) - }) - Context("BlockDeviceMappings", func() { - It("should not allow with a custom launch template", func() { - provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) - Expect(err).ToNot(HaveOccurred()) - provider.LaunchTemplateName = aws.String("my-lt") - provider.BlockDeviceMappings = []*v1alpha1.BlockDeviceMapping{{ - DeviceName: aws.String("/dev/xvda"), - EBS: &v1alpha1.BlockDevice{ - VolumeSize: resource.NewScaledQuantity(1, resource.Giga), - }, - }} - provisioner := test.Provisioner(test.ProvisionerOptions{Provider: provider}) - Expect(provisioner.Validate(ctx)).ToNot(Succeed()) - }) - It("should validate minimal device mapping", func() { - provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) - Expect(err).ToNot(HaveOccurred()) - provider.BlockDeviceMappings = []*v1alpha1.BlockDeviceMapping{{ - DeviceName: aws.String("/dev/xvda"), - EBS: &v1alpha1.BlockDevice{ - VolumeSize: resource.NewScaledQuantity(1, resource.Giga), - }, - }} - provisioner := test.Provisioner(test.ProvisionerOptions{Provider: provider}) - Expect(provisioner.Validate(ctx)).To(Succeed()) - }) - It("should validate ebs device mapping with snapshotID only", func() { - provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) - Expect(err).ToNot(HaveOccurred()) - provider.BlockDeviceMappings = []*v1alpha1.BlockDeviceMapping{{ - DeviceName: aws.String("/dev/xvda"), - EBS: &v1alpha1.BlockDevice{ - SnapshotID: aws.String("snap-0123456789"), - }, - }} - provisioner := test.Provisioner(test.ProvisionerOptions{Provider: provider}) - Expect(provisioner.Validate(ctx)).To(Succeed()) - }) - It("should not allow volume size below minimum", func() { - provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) - Expect(err).ToNot(HaveOccurred()) - provider.BlockDeviceMappings = []*v1alpha1.BlockDeviceMapping{{ - DeviceName: aws.String("/dev/xvda"), - EBS: &v1alpha1.BlockDevice{ - VolumeSize: resource.NewScaledQuantity(100, resource.Mega), - }, - }} - provisioner := test.Provisioner(test.ProvisionerOptions{Provider: provider}) - Expect(provisioner.Validate(ctx)).ToNot(Succeed()) - }) - It("should not allow volume size above max", func() { - provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) - Expect(err).ToNot(HaveOccurred()) - provider.BlockDeviceMappings = []*v1alpha1.BlockDeviceMapping{{ - DeviceName: aws.String("/dev/xvda"), - EBS: &v1alpha1.BlockDevice{ - VolumeSize: resource.NewScaledQuantity(65, resource.Tera), - }, - }} - provisioner := test.Provisioner(test.ProvisionerOptions{Provider: provider}) - Expect(provisioner.Validate(ctx)).ToNot(Succeed()) - }) - It("should not allow nil device name", func() { - provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) - Expect(err).ToNot(HaveOccurred()) - provider.BlockDeviceMappings = []*v1alpha1.BlockDeviceMapping{{ - EBS: &v1alpha1.BlockDevice{ - VolumeSize: resource.NewScaledQuantity(65, resource.Tera), - }, - }} - provisioner := test.Provisioner(test.ProvisionerOptions{Provider: provider}) - Expect(provisioner.Validate(ctx)).ToNot(Succeed()) - }) - It("should not allow nil volume size", func() { - provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) - Expect(err).ToNot(HaveOccurred()) - provider.BlockDeviceMappings = []*v1alpha1.BlockDeviceMapping{{ - DeviceName: aws.String("/dev/xvda"), - EBS: &v1alpha1.BlockDevice{}, - }} - provisioner := test.Provisioner(test.ProvisionerOptions{Provider: provider}) - Expect(provisioner.Validate(ctx)).ToNot(Succeed()) - }) - It("should not allow empty ebs block", func() { - provider, err := v1alpha1.Deserialize(provisioner.Spec.Provider) - Expect(err).ToNot(HaveOccurred()) - provider.BlockDeviceMappings = []*v1alpha1.BlockDeviceMapping{{ - DeviceName: aws.String("/dev/xvda"), - }} - provisioner := test.Provisioner(test.ProvisionerOptions{Provider: provider}) - Expect(provisioner.Validate(ctx)).ToNot(Succeed()) - }) - }) - }) - }) }) var _ = Describe("Pricing", func() { @@ -1802,6 +1520,15 @@ var _ = Describe("Pricing", func() { }) }) +func Provisioner(options test.ProvisionerOptions) *v1alpha5.Provisioner { + from := test.Provisioner(options) + to := v1alpha1.Provisioner(*from) + // Apply provider specific defaults + to.SetDefaults(context.Background()) + provisioner := v1alpha5.Provisioner(to) + return &provisioner +} + // ExpectTags verifies that the expected tags are a subset of the tags found func ExpectTags(tags []*ec2.Tag, expected map[string]string) { existingTags := map[string]string{}