diff --git a/.github/workflows/e2e-matrix.yaml b/.github/workflows/e2e-matrix.yaml index d0dfedeaa..3cebe5d65 100644 --- a/.github/workflows/e2e-matrix.yaml +++ b/.github/workflows/e2e-matrix.yaml @@ -33,7 +33,7 @@ jobs: strategy: fail-fast: false matrix: - suite: [Nonbehavioral, Utilization, GPU, Drift, Integration] + suite: [Nonbehavioral, Utilization, GPU, Drift, Integration, NodeClaim] uses: ./.github/workflows/e2e.yaml with: git_ref: ${{ inputs.git_ref }} diff --git a/test/suites/nodeclaim/nodeclaim_test.go b/test/suites/nodeclaim/nodeclaim_test.go new file mode 100644 index 000000000..8f6d9b12d --- /dev/null +++ b/test/suites/nodeclaim/nodeclaim_test.go @@ -0,0 +1,154 @@ +/* +Portions Copyright (c) Microsoft Corporation. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package nodeclaim_test + +import ( + "github.com/Azure/karpenter/pkg/apis/v1alpha2" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + corev1beta1 "github.com/aws/karpenter-core/pkg/apis/v1beta1" + "github.com/aws/karpenter-core/pkg/test" + "github.com/aws/karpenter-core/pkg/utils/resources" +) + +var _ = Describe("StandaloneNodeClaim", func() { + It("should create a standard NodeClaim within the 'D' sku family", func() { + nodeClaim := test.NodeClaim(corev1beta1.NodeClaim{ + Spec: corev1beta1.NodeClaimSpec{ + Requirements: []v1.NodeSelectorRequirement{ + { + Key: v1alpha2.LabelSKUFamily, + Operator: v1.NodeSelectorOpIn, + Values: []string{"D"}, + }, + { + Key: corev1beta1.CapacityTypeLabelKey, + Operator: v1.NodeSelectorOpIn, + Values: []string{corev1beta1.CapacityTypeOnDemand}, + }, + }, + NodeClassRef: &corev1beta1.NodeClassReference{ + Name: nodeClass.Name, + }, + }, + }) + env.ExpectCreated(nodeClass, nodeClaim) + node := env.EventuallyExpectInitializedNodeCount("==", 1)[0] + nodeClaim = env.EventuallyExpectCreatedNodeClaimCount("==", 1)[0] + Expect(node.Labels).To(HaveKeyWithValue(v1alpha2.LabelSKUFamily, "D")) + env.EventuallyExpectNodeClaimsReady(nodeClaim) + }) + It("should create a standard NodeClaim based on resource requests", func() { + nodeClaim := test.NodeClaim(corev1beta1.NodeClaim{ + Spec: corev1beta1.NodeClaimSpec{ + Resources: corev1beta1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("1"), + v1.ResourceMemory: resource.MustParse("16Gi"), + }, + }, + NodeClassRef: &corev1beta1.NodeClassReference{ + Name: nodeClass.Name, + }, + }, + }) + env.ExpectCreated(nodeClass, nodeClaim) + node := env.EventuallyExpectInitializedNodeCount("==", 1)[0] + nodeClaim = env.EventuallyExpectCreatedNodeClaimCount("==", 1)[0] + Expect(resources.Fits(nodeClaim.Spec.Resources.Requests, node.Status.Allocatable)) + env.EventuallyExpectNodeClaimsReady(nodeClaim) + }) + It("should create a NodeClaim propagating all the NodeClaim spec details", func() { + nodeClaim := test.NodeClaim(corev1beta1.NodeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + "custom-annotation": "custom-value", + }, + Labels: map[string]string{ + "custom-label": "custom-value", + }, + }, + Spec: corev1beta1.NodeClaimSpec{ + Taints: []v1.Taint{ + { + Key: "custom-taint", + Effect: v1.TaintEffectNoSchedule, + Value: "custom-value", + }, + { + Key: "other-custom-taint", + Effect: v1.TaintEffectNoExecute, + Value: "other-custom-value", + }, + }, + NodeClassRef: &corev1beta1.NodeClassReference{ + Name: nodeClass.Name, + }, + }, + }) + env.ExpectCreated(nodeClass, nodeClaim) + node := env.EventuallyExpectInitializedNodeCount("==", 1)[0] + Expect(node.Annotations).To(HaveKeyWithValue("custom-annotation", "custom-value")) + Expect(node.Labels).To(HaveKeyWithValue("custom-label", "custom-value")) + Expect(node.Spec.Taints).To(ContainElements( + v1.Taint{ + Key: "custom-taint", + Effect: v1.TaintEffectNoSchedule, + Value: "custom-value", + }, + v1.Taint{ + Key: "other-custom-taint", + Effect: v1.TaintEffectNoExecute, + Value: "other-custom-value", + }, + )) + env.EventuallyExpectCreatedNodeClaimCount("==", 1) + env.EventuallyExpectNodeClaimsReady(nodeClaim) + }) + It("should remove the cloudProvider NodeClaim when the cluster NodeClaim is deleted", func() { + nodeClaim := test.NodeClaim(corev1beta1.NodeClaim{ + Spec: corev1beta1.NodeClaimSpec{ + Requirements: []v1.NodeSelectorRequirement{ + { + Key: v1alpha2.LabelSKUFamily, + Operator: v1.NodeSelectorOpIn, + Values: []string{"D"}, + }, + { + Key: corev1beta1.CapacityTypeLabelKey, + Operator: v1.NodeSelectorOpIn, + Values: []string{corev1beta1.CapacityTypeOnDemand}, + }, + }, + NodeClassRef: &corev1beta1.NodeClassReference{ + Name: nodeClass.Name, + }, + }, + }) + env.ExpectCreated(nodeClass, nodeClaim) + node := env.EventuallyExpectInitializedNodeCount("==", 1)[0] + nodeClaim = env.EventuallyExpectCreatedNodeClaimCount("==", 1)[0] + + // Node is deleted and now should be not found + env.ExpectDeleted(nodeClaim) + env.EventuallyExpectNotFound(nodeClaim, node) + }) +}) diff --git a/test/suites/nodeclaim/suite_test.go b/test/suites/nodeclaim/suite_test.go new file mode 100644 index 000000000..ac11eea59 --- /dev/null +++ b/test/suites/nodeclaim/suite_test.go @@ -0,0 +1,51 @@ +/* +Portions Copyright (c) Microsoft Corporation. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package nodeclaim_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/Azure/karpenter/pkg/apis/v1alpha2" + "github.com/Azure/karpenter/test/pkg/environment/azure" + corev1beta1 "github.com/aws/karpenter-core/pkg/apis/v1beta1" +) + +var env *azure.Environment +var nodeClass *v1alpha2.AKSNodeClass +var nodePool *corev1beta1.NodePool + +func TestNodeClaim(t *testing.T) { + RegisterFailHandler(Fail) + BeforeSuite(func() { + env = azure.NewEnvironment(t) + }) + AfterSuite(func() { + env.Stop() + }) + RunSpecs(t, "NodeClaim") +} + +var _ = BeforeEach(func() { + env.BeforeEach() + nodeClass = env.DefaultAKSNodeClass() + nodePool = env.DefaultNodePool(nodeClass) +}) +var _ = AfterEach(func() { env.Cleanup() }) +var _ = AfterEach(func() { env.AfterEach() })