Skip to main content
Glama
ami_test.go27.1 kB
/* 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 nodeclass_test import ( "fmt" "time" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/ec2" ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/samber/lo" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/util/version" karpv1 "sigs.k8s.io/karpenter/pkg/apis/v1" v1 "github.com/aws/karpenter-provider-aws/pkg/apis/v1" "github.com/aws/karpenter-provider-aws/pkg/test" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" . "sigs.k8s.io/karpenter/pkg/test/expectations" ) var _ = Describe("NodeClass AMI Status Controller", func() { var k8sVersion string BeforeEach(func() { k8sVersion = awsEnv.VersionProvider.Get(ctx) nodeClass = test.EC2NodeClass(v1.EC2NodeClass{ Spec: v1.EC2NodeClassSpec{ SubnetSelectorTerms: []v1.SubnetSelectorTerm{ { Tags: map[string]string{"*": "*"}, }, }, SecurityGroupSelectorTerms: []v1.SecurityGroupSelectorTerm{ { Tags: map[string]string{"*": "*"}, }, }, AMIFamily: lo.ToPtr(v1.AMIFamilyCustom), AMISelectorTerms: []v1.AMISelectorTerm{ { Tags: map[string]string{"*": "*"}, }, }, }, }) awsEnv.EC2API.DescribeImagesOutput.Set(&ec2.DescribeImagesOutput{ Images: []ec2types.Image{ { Name: aws.String("amd64-standard"), ImageId: aws.String("ami-amd64-standard"), CreationDate: aws.String(time.Now().Format(time.RFC3339)), Architecture: "x86_64", Tags: []ec2types.Tag{ {Key: aws.String("Name"), Value: aws.String("amd64-standard")}, {Key: aws.String("foo"), Value: aws.String("bar")}, }, State: ec2types.ImageStateAvailable, }, { Name: aws.String("amd64-standard-new"), ImageId: aws.String("ami-amd64-standard-new"), CreationDate: aws.String(time.Now().Add(time.Minute).Format(time.RFC3339)), Architecture: "x86_64", Tags: []ec2types.Tag{ {Key: aws.String("Name"), Value: aws.String("amd64-standard")}, {Key: aws.String("foo"), Value: aws.String("bar")}, }, State: ec2types.ImageStateAvailable, }, { Name: aws.String("amd64-nvidia"), ImageId: aws.String("ami-amd64-nvidia"), CreationDate: aws.String(time.Now().Format(time.RFC3339)), Architecture: "x86_64", Tags: []ec2types.Tag{ {Key: aws.String("Name"), Value: aws.String("amd64-nvidia")}, {Key: aws.String("foo"), Value: aws.String("bar")}, }, State: ec2types.ImageStateAvailable, }, { Name: aws.String("amd64-neuron"), ImageId: aws.String("ami-amd64-neuron"), CreationDate: aws.String(time.Now().Format(time.RFC3339)), Architecture: "x86_64", Tags: []ec2types.Tag{ {Key: aws.String("Name"), Value: aws.String("amd64-neuron")}, {Key: aws.String("foo"), Value: aws.String("bar")}, }, State: ec2types.ImageStateAvailable, }, { Name: aws.String("arm64-standard"), ImageId: aws.String("ami-arm64-standard"), CreationDate: aws.String(time.Now().Format(time.RFC3339)), Architecture: "arm64", Tags: []ec2types.Tag{ {Key: aws.String("Name"), Value: aws.String("arm64-standard")}, {Key: aws.String("foo"), Value: aws.String("bar")}, }, State: ec2types.ImageStateAvailable, }, { Name: aws.String("arm64-nvidia"), ImageId: aws.String("ami-arm64-nvidia"), CreationDate: aws.String(time.Now().Format(time.RFC3339)), Architecture: "arm64", Tags: []ec2types.Tag{ {Key: aws.String("Name"), Value: aws.String("arm64-nvidia")}, {Key: aws.String("foo"), Value: aws.String("bar")}, }, State: ec2types.ImageStateAvailable, }, }, }) }) Context("Aliases", func() { It("Should resolve all AMIs with correct requirements for AL2023", func() { awsEnv.SSMAPI.Parameters = map[string]string{ fmt.Sprintf("/aws/service/eks/optimized-ami/%s/amazon-linux-2023/x86_64/standard/recommended/image_id", k8sVersion): "ami-amd64-standard", fmt.Sprintf("/aws/service/eks/optimized-ami/%s/amazon-linux-2023/x86_64/nvidia/recommended/image_id", k8sVersion): "ami-amd64-nvidia", fmt.Sprintf("/aws/service/eks/optimized-ami/%s/amazon-linux-2023/x86_64/neuron/recommended/image_id", k8sVersion): "ami-amd64-neuron", fmt.Sprintf("/aws/service/eks/optimized-ami/%s/amazon-linux-2023/arm64/standard/recommended/image_id", k8sVersion): "ami-arm64-standard", fmt.Sprintf("/aws/service/eks/optimized-ami/%s/amazon-linux-2023/arm64/nvidia/recommended/image_id", k8sVersion): "ami-arm64-nvidia", } nodeClass.Spec.AMISelectorTerms = []v1.AMISelectorTerm{{Alias: "al2023@latest"}} ExpectApplied(ctx, env.Client, nodeClass) ExpectObjectReconciled(ctx, env.Client, controller, nodeClass) nodeClass = ExpectExists(ctx, env.Client, nodeClass) Expect(len(nodeClass.Status.AMIs)).To(Equal(5)) Expect(nodeClass.Status.AMIs).To(ContainElements([]v1.AMI{ { Name: "amd64-standard", ID: "ami-amd64-standard", Requirements: []corev1.NodeSelectorRequirement{ { Key: corev1.LabelArchStable, Operator: corev1.NodeSelectorOpIn, Values: []string{karpv1.ArchitectureAmd64}, }, { Key: v1.LabelInstanceGPUCount, Operator: corev1.NodeSelectorOpDoesNotExist, }, { Key: v1.LabelInstanceAcceleratorCount, Operator: corev1.NodeSelectorOpDoesNotExist, }, }, }, { Name: "amd64-nvidia", ID: "ami-amd64-nvidia", Requirements: []corev1.NodeSelectorRequirement{ { Key: corev1.LabelArchStable, Operator: corev1.NodeSelectorOpIn, Values: []string{karpv1.ArchitectureAmd64}, }, { Key: v1.LabelInstanceGPUCount, Operator: corev1.NodeSelectorOpExists, }, }, }, { Name: "amd64-neuron", ID: "ami-amd64-neuron", Requirements: []corev1.NodeSelectorRequirement{ { Key: corev1.LabelArchStable, Operator: corev1.NodeSelectorOpIn, Values: []string{karpv1.ArchitectureAmd64}, }, { Key: v1.LabelInstanceAcceleratorCount, Operator: corev1.NodeSelectorOpExists, }, }, }, { Name: "arm64-standard", ID: "ami-arm64-standard", Requirements: []corev1.NodeSelectorRequirement{ { Key: corev1.LabelArchStable, Operator: corev1.NodeSelectorOpIn, Values: []string{karpv1.ArchitectureArm64}, }, { Key: v1.LabelInstanceGPUCount, Operator: corev1.NodeSelectorOpDoesNotExist, }, { Key: v1.LabelInstanceAcceleratorCount, Operator: corev1.NodeSelectorOpDoesNotExist, }, }, }, { Name: "arm64-nvidia", ID: "ami-arm64-nvidia", Requirements: []corev1.NodeSelectorRequirement{ { Key: corev1.LabelArchStable, Operator: corev1.NodeSelectorOpIn, Values: []string{karpv1.ArchitectureArm64}, }, { Key: v1.LabelInstanceGPUCount, Operator: corev1.NodeSelectorOpExists, }, }, }, })) Expect(nodeClass.StatusConditions().IsTrue(v1.ConditionTypeAMIsReady)).To(BeTrue()) }) It("Should resolve all AMIs with correct requirements for AL2", func() { if version.MustParseGeneric(k8sVersion).Minor() > 32 { Skip("AL2 is not supported on versions > 1.32") } awsEnv.SSMAPI.Parameters = map[string]string{ fmt.Sprintf("/aws/service/eks/optimized-ami/%s/amazon-linux-2/recommended/image_id", k8sVersion): "ami-amd64-standard", fmt.Sprintf("/aws/service/eks/optimized-ami/%s/amazon-linux-2-gpu/recommended/image_id", k8sVersion): "ami-amd64-nvidia", fmt.Sprintf("/aws/service/eks/optimized-ami/%s/amazon-linux-2-arm64/recommended/image_id", k8sVersion): "ami-arm64-standard", } nodeClass.Spec.AMISelectorTerms = []v1.AMISelectorTerm{{Alias: "al2@latest"}} ExpectApplied(ctx, env.Client, nodeClass) ExpectObjectReconciled(ctx, env.Client, controller, nodeClass) nodeClass = ExpectExists(ctx, env.Client, nodeClass) Expect(len(nodeClass.Status.AMIs)).To(Equal(4)) Expect(nodeClass.Status.AMIs).To(ContainElements([]v1.AMI{ { Name: "amd64-standard", ID: "ami-amd64-standard", Requirements: []corev1.NodeSelectorRequirement{ { Key: corev1.LabelArchStable, Operator: corev1.NodeSelectorOpIn, Values: []string{karpv1.ArchitectureAmd64}, }, { Key: v1.LabelInstanceGPUCount, Operator: corev1.NodeSelectorOpDoesNotExist, }, { Key: v1.LabelInstanceAcceleratorCount, Operator: corev1.NodeSelectorOpDoesNotExist, }, }, }, { Name: "amd64-nvidia", ID: "ami-amd64-nvidia", Requirements: []corev1.NodeSelectorRequirement{ { Key: corev1.LabelArchStable, Operator: corev1.NodeSelectorOpIn, Values: []string{karpv1.ArchitectureAmd64}, }, { Key: v1.LabelInstanceGPUCount, Operator: corev1.NodeSelectorOpExists, }, }, }, // Note: AL2 uses the same AMI for nvidia and neuron, we use the nvidia AMI here { Name: "amd64-nvidia", ID: "ami-amd64-nvidia", Requirements: []corev1.NodeSelectorRequirement{ { Key: corev1.LabelArchStable, Operator: corev1.NodeSelectorOpIn, Values: []string{karpv1.ArchitectureAmd64}, }, { Key: v1.LabelInstanceAcceleratorCount, Operator: corev1.NodeSelectorOpExists, }, }, }, { Name: "arm64-standard", ID: "ami-arm64-standard", Requirements: []corev1.NodeSelectorRequirement{ { Key: corev1.LabelArchStable, Operator: corev1.NodeSelectorOpIn, Values: []string{karpv1.ArchitectureArm64}, }, { Key: v1.LabelInstanceGPUCount, Operator: corev1.NodeSelectorOpDoesNotExist, }, { Key: v1.LabelInstanceAcceleratorCount, Operator: corev1.NodeSelectorOpDoesNotExist, }, }, }, })) Expect(nodeClass.StatusConditions().IsTrue(v1.ConditionTypeAMIsReady)).To(BeTrue()) }) It("Should resolve all AMIs with correct requirements for Bottlerocket", func() { awsEnv.SSMAPI.Parameters = map[string]string{ fmt.Sprintf("/aws/service/bottlerocket/aws-k8s-%s/x86_64/latest/image_id", k8sVersion): "ami-amd64-standard", fmt.Sprintf("/aws/service/bottlerocket/aws-k8s-%s/arm64/latest/image_id", k8sVersion): "ami-arm64-standard", fmt.Sprintf("/aws/service/bottlerocket/aws-k8s-%s-nvidia/x86_64/latest/image_id", k8sVersion): "ami-amd64-nvidia", fmt.Sprintf("/aws/service/bottlerocket/aws-k8s-%s-nvidia/arm64/latest/image_id", k8sVersion): "ami-arm64-nvidia", } nodeClass.Spec.AMISelectorTerms = []v1.AMISelectorTerm{{Alias: "bottlerocket@latest"}} ExpectApplied(ctx, env.Client, nodeClass) ExpectObjectReconciled(ctx, env.Client, controller, nodeClass) nodeClass = ExpectExists(ctx, env.Client, nodeClass) Expect(len(nodeClass.Status.AMIs)).To(Equal(5)) Expect(nodeClass.Status.AMIs).To(ContainElements([]v1.AMI{ { Name: "amd64-standard", ID: "ami-amd64-standard", Requirements: []corev1.NodeSelectorRequirement{ { Key: corev1.LabelArchStable, Operator: corev1.NodeSelectorOpIn, Values: []string{karpv1.ArchitectureAmd64}, }, { Key: v1.LabelInstanceGPUCount, Operator: corev1.NodeSelectorOpDoesNotExist, }, { Key: v1.LabelInstanceAcceleratorCount, Operator: corev1.NodeSelectorOpDoesNotExist, }, }, }, { Name: "arm64-standard", ID: "ami-arm64-standard", Requirements: []corev1.NodeSelectorRequirement{ { Key: corev1.LabelArchStable, Operator: corev1.NodeSelectorOpIn, Values: []string{karpv1.ArchitectureArm64}, }, { Key: v1.LabelInstanceGPUCount, Operator: corev1.NodeSelectorOpDoesNotExist, }, { Key: v1.LabelInstanceAcceleratorCount, Operator: corev1.NodeSelectorOpDoesNotExist, }, }, }, // Note: Bottlerocket uses the same AMI for standard and neuron { Name: "amd64-standard", ID: "ami-amd64-standard", Requirements: []corev1.NodeSelectorRequirement{ { Key: corev1.LabelArchStable, Operator: corev1.NodeSelectorOpIn, Values: []string{karpv1.ArchitectureAmd64}, }, { Key: v1.LabelInstanceAcceleratorCount, Operator: corev1.NodeSelectorOpExists, }, }, }, { Name: "amd64-nvidia", ID: "ami-amd64-nvidia", Requirements: []corev1.NodeSelectorRequirement{ { Key: corev1.LabelArchStable, Operator: corev1.NodeSelectorOpIn, Values: []string{karpv1.ArchitectureAmd64}, }, { Key: v1.LabelInstanceGPUCount, Operator: corev1.NodeSelectorOpExists, }, }, }, { Name: "arm64-nvidia", ID: "ami-arm64-nvidia", Requirements: []corev1.NodeSelectorRequirement{ { Key: corev1.LabelArchStable, Operator: corev1.NodeSelectorOpIn, Values: []string{karpv1.ArchitectureArm64}, }, { Key: v1.LabelInstanceGPUCount, Operator: corev1.NodeSelectorOpExists, }, }, }, })) Expect(nodeClass.StatusConditions().IsTrue(v1.ConditionTypeAMIsReady)).To(BeTrue()) }) It("Should resolve all AMIs with correct requirements for Windows2019", func() { awsEnv.SSMAPI.Parameters = map[string]string{ fmt.Sprintf("/aws/service/ami-windows-latest/Windows_Server-2019-English-Core-EKS_Optimized-%s/image_id", k8sVersion): "ami-amd64-standard", } nodeClass.Spec.AMISelectorTerms = []v1.AMISelectorTerm{{Alias: "windows2019@latest"}} ExpectApplied(ctx, env.Client, nodeClass) ExpectObjectReconciled(ctx, env.Client, controller, nodeClass) nodeClass = ExpectExists(ctx, env.Client, nodeClass) Expect(len(nodeClass.Status.AMIs)).To(Equal(1)) Expect(nodeClass.Status.AMIs).To(ContainElements([]v1.AMI{ { Name: "amd64-standard", ID: "ami-amd64-standard", Requirements: []corev1.NodeSelectorRequirement{ { Key: corev1.LabelOSStable, Operator: corev1.NodeSelectorOpIn, Values: []string{string(corev1.Windows)}, }, { Key: corev1.LabelArchStable, Operator: corev1.NodeSelectorOpIn, Values: []string{karpv1.ArchitectureAmd64}, }, { Key: corev1.LabelWindowsBuild, Operator: corev1.NodeSelectorOpIn, Values: []string{v1.Windows2019Build}, }, }, }, })) Expect(nodeClass.StatusConditions().IsTrue(v1.ConditionTypeAMIsReady)).To(BeTrue()) }) It("Should resolve all AMIs with correct requirements for Windows2022", func() { awsEnv.SSMAPI.Parameters = map[string]string{ fmt.Sprintf("/aws/service/ami-windows-latest/Windows_Server-2022-English-Core-EKS_Optimized-%s/image_id", k8sVersion): "ami-amd64-standard", } nodeClass.Spec.AMISelectorTerms = []v1.AMISelectorTerm{{Alias: "windows2022@latest"}} ExpectApplied(ctx, env.Client, nodeClass) ExpectObjectReconciled(ctx, env.Client, controller, nodeClass) nodeClass = ExpectExists(ctx, env.Client, nodeClass) Expect(len(nodeClass.Status.AMIs)).To(Equal(1)) Expect(nodeClass.Status.AMIs).To(ContainElements([]v1.AMI{ { Name: "amd64-standard", ID: "ami-amd64-standard", Requirements: []corev1.NodeSelectorRequirement{ { Key: corev1.LabelOSStable, Operator: corev1.NodeSelectorOpIn, Values: []string{string(corev1.Windows)}, }, { Key: corev1.LabelArchStable, Operator: corev1.NodeSelectorOpIn, Values: []string{karpv1.ArchitectureAmd64}, }, { Key: corev1.LabelWindowsBuild, Operator: corev1.NodeSelectorOpIn, Values: []string{v1.Windows2022Build}, }, }, }, })) Expect(nodeClass.StatusConditions().IsTrue(v1.ConditionTypeAMIsReady)).To(BeTrue()) }) }) It("should resolve amiSelector AMIs and requirements into status when all SSM parameters don't resolve", func() { // This parameter set doesn't include any of the Nvidia AMIs awsEnv.SSMAPI.Parameters = map[string]string{ fmt.Sprintf("/aws/service/bottlerocket/aws-k8s-%s/x86_64/latest/image_id", k8sVersion): "ami-amd64-standard", fmt.Sprintf("/aws/service/bottlerocket/aws-k8s-%s/arm64/latest/image_id", k8sVersion): "ami-arm64-standard", } nodeClass.Spec.AMISelectorTerms = []v1.AMISelectorTerm{{ Alias: "bottlerocket@latest", }} ExpectApplied(ctx, env.Client, nodeClass) ExpectObjectReconciled(ctx, env.Client, controller, nodeClass) nodeClass = ExpectExists(ctx, env.Client, nodeClass) Expect(len(nodeClass.Status.AMIs)).To(Equal(3)) Expect(nodeClass.Status.AMIs).To(ContainElements([]v1.AMI{ { Name: "arm64-standard", ID: "ami-arm64-standard", Requirements: []corev1.NodeSelectorRequirement{ { Key: corev1.LabelArchStable, Operator: corev1.NodeSelectorOpIn, Values: []string{karpv1.ArchitectureArm64}, }, { Key: v1.LabelInstanceGPUCount, Operator: corev1.NodeSelectorOpDoesNotExist, }, { Key: v1.LabelInstanceAcceleratorCount, Operator: corev1.NodeSelectorOpDoesNotExist, }, }, }, { Name: "amd64-standard", ID: "ami-amd64-standard", Requirements: []corev1.NodeSelectorRequirement{ { Key: corev1.LabelArchStable, Operator: corev1.NodeSelectorOpIn, Values: []string{karpv1.ArchitectureAmd64}, }, { Key: v1.LabelInstanceGPUCount, Operator: corev1.NodeSelectorOpDoesNotExist, }, { Key: v1.LabelInstanceAcceleratorCount, Operator: corev1.NodeSelectorOpDoesNotExist, }, }, }, })) Expect(nodeClass.StatusConditions().IsTrue(v1.ConditionTypeAMIsReady)).To(BeTrue()) }) It("should resolve a valid AMI selector", func() { nodeClass.Spec.AMISelectorTerms = []v1.AMISelectorTerm{{ Tags: map[string]string{"Name": "amd64-standard"}, }} ExpectApplied(ctx, env.Client, nodeClass) ExpectObjectReconciled(ctx, env.Client, controller, nodeClass) nodeClass = ExpectExists(ctx, env.Client, nodeClass) Expect(nodeClass.Status.AMIs).To(Equal( []v1.AMI{ { Name: "amd64-standard-new", ID: "ami-amd64-standard-new", Requirements: []corev1.NodeSelectorRequirement{{ Key: corev1.LabelArchStable, Operator: corev1.NodeSelectorOpIn, Values: []string{karpv1.ArchitectureAmd64}, }}, }, }, )) Expect(nodeClass.StatusConditions().IsTrue(v1.ConditionTypeAMIsReady)).To(BeTrue()) }) It("should get error when resolving AMIs and have status condition set to false", func() { awsEnv.EC2API.NextError.Set(fmt.Errorf("unable to resolve AMI")) ExpectApplied(ctx, env.Client, nodeClass) _ = ExpectObjectReconcileFailed(ctx, env.Client, controller, nodeClass) nodeClass = ExpectExists(ctx, env.Client, nodeClass) Expect(nodeClass.StatusConditions().IsTrue(v1.ConditionTypeAMIsReady)).To(BeFalse()) }) Context("NodeClass AMI Status", func() { BeforeEach(func() { // Set time using the injectable/fake clock to now awsEnv.Clock.SetTime(time.Now()) nodeClass.Spec.AMISelectorTerms = []v1.AMISelectorTerm{ { Tags: map[string]string{"*": "*"}, }, } awsEnv.EC2API.DescribeImagesOutput.Set(&ec2.DescribeImagesOutput{ Images: []ec2types.Image{ { Name: aws.String("test-ami-3"), ImageId: aws.String("ami-id-789"), CreationDate: aws.String("2021-08-31T00:12:42.000Z"), DeprecationTime: aws.String(awsEnv.Clock.Now().Add(30 * time.Minute).Format(time.RFC3339)), Architecture: "x86_64", Tags: []ec2types.Tag{ {Key: aws.String("Name"), Value: aws.String("test-ami-3")}, {Key: aws.String("foo"), Value: aws.String("bar")}, }, State: ec2types.ImageStateAvailable, }, { Name: aws.String("test-ami-2"), ImageId: aws.String("ami-id-456"), CreationDate: aws.String("2021-08-31T00:12:42.000Z"), Architecture: "arm64", Tags: []ec2types.Tag{ {Key: aws.String("Name"), Value: aws.String("test-ami-2")}, {Key: aws.String("foo"), Value: aws.String("bar")}, }, State: ec2types.ImageStateAvailable, }, }, }) }) It("should update nodeclass AMI status with correct deprecation value and conditions", func() { ExpectApplied(ctx, env.Client, nodeClass) ExpectObjectReconciled(ctx, env.Client, controller, nodeClass) nodeClass = ExpectExists(ctx, env.Client, nodeClass) Expect(len(nodeClass.Status.AMIs)).To(Equal(2)) Expect(nodeClass.Status.AMIs).To(Equal( []v1.AMI{ { Name: "test-ami-2", ID: "ami-id-456", Requirements: []corev1.NodeSelectorRequirement{ { Key: corev1.LabelArchStable, Operator: corev1.NodeSelectorOpIn, Values: []string{karpv1.ArchitectureArm64}, }, }, }, { Name: "test-ami-3", ID: "ami-id-789", Requirements: []corev1.NodeSelectorRequirement{ { Key: corev1.LabelArchStable, Operator: corev1.NodeSelectorOpIn, Values: []string{karpv1.ArchitectureAmd64}, }, }, }, }, )) Expect(nodeClass.StatusConditions().IsTrue(v1.ConditionTypeAMIsReady)).To(BeTrue()) // Increment clock to simulate status updates on deprecated AMIs awsEnv.Clock.Step(40 * time.Minute) // Flush Cache awsEnv.AMICache.Flush() ExpectObjectReconciled(ctx, env.Client, controller, nodeClass) nodeClass = ExpectExists(ctx, env.Client, nodeClass) Expect(len(nodeClass.Status.AMIs)).To(Equal(2)) Expect(nodeClass.Status.AMIs).To(Equal( []v1.AMI{ { Name: "test-ami-2", ID: "ami-id-456", Requirements: []corev1.NodeSelectorRequirement{{ Key: corev1.LabelArchStable, Operator: corev1.NodeSelectorOpIn, Values: []string{karpv1.ArchitectureArm64}, }, }, }, { Name: "test-ami-3", ID: "ami-id-789", // Adds deprecated field to the AMI status on the NodeClass Deprecated: true, Requirements: []corev1.NodeSelectorRequirement{{ Key: corev1.LabelArchStable, Operator: corev1.NodeSelectorOpIn, Values: []string{karpv1.ArchitectureAmd64}, }, }, }, }, )) Expect(nodeClass.StatusConditions().IsTrue(v1.ConditionTypeAMIsReady)).To(BeTrue()) }) It("should remove AMIDeprecated status condition when non deprecated AMIs are discovered", func() { // Increment clock to simulate status updates on deprecated AMIs awsEnv.Clock.Step(40 * time.Minute) // Initial reconcile discovers AMIs which are deprecated ExpectApplied(ctx, env.Client, nodeClass) ExpectObjectReconciled(ctx, env.Client, controller, nodeClass) nodeClass = ExpectExists(ctx, env.Client, nodeClass) Expect(len(nodeClass.Status.AMIs)).To(Equal(2)) Expect(nodeClass.Status.AMIs).To(Equal( []v1.AMI{ { Name: "test-ami-2", ID: "ami-id-456", Requirements: []corev1.NodeSelectorRequirement{{ Key: corev1.LabelArchStable, Operator: corev1.NodeSelectorOpIn, Values: []string{karpv1.ArchitectureArm64}, }}, }, { Name: "test-ami-3", ID: "ami-id-789", // Adds deprecated field to the AMI status on the NodeClass Deprecated: true, Requirements: []corev1.NodeSelectorRequirement{{ Key: corev1.LabelArchStable, Operator: corev1.NodeSelectorOpIn, Values: []string{karpv1.ArchitectureAmd64}, }}, }, }, )) // Checks if both AMIsReady and AMIsDeprecated status conditions are set Expect(nodeClass.StatusConditions().IsTrue(v1.ConditionTypeAMIsReady)).To(BeTrue()) // rediscover AMIs again and reconcile awsEnv.EC2API.DescribeImagesOutput.Set(&ec2.DescribeImagesOutput{ Images: []ec2types.Image{ { Name: aws.String("test-ami-4"), ImageId: aws.String("ami-id-123"), CreationDate: aws.String("2021-08-31T00:12:42.000Z"), DeprecationTime: aws.String(awsEnv.Clock.Now().Add(30 * time.Minute).Format(time.RFC3339)), Architecture: "x86_64", Tags: []ec2types.Tag{ {Key: aws.String("Name"), Value: aws.String("test-ami-4")}, {Key: aws.String("foo"), Value: aws.String("bar")}, }, State: ec2types.ImageStateAvailable, }, { Name: aws.String("test-ami-2"), ImageId: aws.String("ami-id-456"), CreationDate: aws.String("2021-08-31T00:12:42.000Z"), Architecture: "arm64", Tags: []ec2types.Tag{ {Key: aws.String("Name"), Value: aws.String("test-ami-2")}, {Key: aws.String("foo"), Value: aws.String("bar")}, }, State: ec2types.ImageStateAvailable, }, }, }) awsEnv.AMICache.Flush() ExpectApplied(ctx, env.Client, nodeClass) ExpectObjectReconciled(ctx, env.Client, controller, nodeClass) nodeClass = ExpectExists(ctx, env.Client, nodeClass) Expect(len(nodeClass.Status.AMIs)).To(Equal(2)) Expect(nodeClass.Status.AMIs).To(Equal( []v1.AMI{ { Name: "test-ami-4", ID: "ami-id-123", Requirements: []corev1.NodeSelectorRequirement{{ Key: corev1.LabelArchStable, Operator: corev1.NodeSelectorOpIn, Values: []string{karpv1.ArchitectureAmd64}, }, }, }, { Name: "test-ami-2", ID: "ami-id-456", Requirements: []corev1.NodeSelectorRequirement{{ Key: corev1.LabelArchStable, Operator: corev1.NodeSelectorOpIn, Values: []string{karpv1.ArchitectureArm64}, }, }, }, }, )) // Since all AMIs discovered are non deprecated, the status conditions should remove AMIsDeprecated and only set AMIsReady Expect(nodeClass.StatusConditions().IsTrue(v1.ConditionTypeAMIsReady)).To(BeTrue()) }) }) })

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/mengfwan/test-mcp-glama'

If you have feedback or need assistance with the MCP directory API, please join our Discord server