@@ -30,6 +30,7 @@ import (
3030
3131 . "github.com/onsi/ginkgo/v2"
3232 . "github.com/onsi/gomega"
33+ "github.com/onsi/gomega/gstruct"
3334 . "sigs.k8s.io/karpenter/pkg/utils/testing"
3435
3536 "github.com/samber/lo"
@@ -45,6 +46,8 @@ import (
4546 "github.com/aws/karpenter-provider-aws/pkg/operator/options"
4647 "github.com/aws/karpenter-provider-aws/pkg/providers/amifamily"
4748 "github.com/aws/karpenter-provider-aws/pkg/test"
49+
50+ . "sigs.k8s.io/karpenter/pkg/test/expectations"
4851)
4952
5053var ctx context.Context
@@ -533,6 +536,219 @@ var _ = Describe("AMIProvider", func() {
533536 }))
534537 })
535538 })
539+ Context ("Provider Cache" , func () {
540+ It ("should resolve AMIs from cache that are filtered by id" , func () {
541+ awsEnv .EC2API .DescribeImagesOutput .Set (& ec2.DescribeImagesOutput {Images : []ec2types.Image {
542+ {
543+ Name : aws .String (coretest .RandomName ()),
544+ ImageId : aws .String ("ami-123" ),
545+ Architecture : "x86_64" ,
546+ Tags : []ec2types.Tag {{Key : lo .ToPtr ("test" ), Value : lo .ToPtr ("test" )}},
547+ CreationDate : aws .String ("2022-08-15T12:00:00Z" ),
548+ State : ec2types .ImageStateAvailable ,
549+ },
550+ {
551+ Name : aws .String (coretest .RandomName ()),
552+ ImageId : aws .String ("ami-456" ),
553+ Architecture : "arm64" ,
554+ Tags : []ec2types.Tag {{Key : lo .ToPtr ("test" ), Value : lo .ToPtr ("test" )}},
555+ CreationDate : aws .String ("2022-08-15T12:00:00Z" ),
556+ State : ec2types .ImageStateAvailable ,
557+ },
558+ }})
559+ nodeClass .Spec .AMISelectorTerms = []v1.AMISelectorTerm {
560+ {
561+ ID : "ami-123" ,
562+ },
563+ {
564+ ID : "ami-456" ,
565+ },
566+ }
567+ _ , err := awsEnv .AMIProvider .List (ctx , nodeClass )
568+ Expect (err ).To (BeNil ())
569+
570+ Expect (awsEnv .AMICache .Items ()).To (HaveLen (1 ))
571+ cachedImages := lo .Values (awsEnv .AMICache .Items ())[0 ].Object .(amifamily.AMIs )
572+ Expect (cachedImages ).To (ContainElements (
573+ gstruct .MatchFields (gstruct .IgnoreExtras , gstruct.Fields {
574+ "AmiID" : Equal ("ami-123" ),
575+ }),
576+ gstruct .MatchFields (gstruct .IgnoreExtras , gstruct.Fields {
577+ "AmiID" : Equal ("ami-456" ),
578+ }),
579+ ))
580+ })
581+ It ("should resolve AMIs from cache that are filtered by name" , func () {
582+ awsEnv .EC2API .DescribeImagesOutput .Set (& ec2.DescribeImagesOutput {Images : []ec2types.Image {
583+ {
584+ Name : aws .String ("ami-name-1" ),
585+ ImageId : aws .String ("ami-123" ),
586+ Architecture : "x86_64" ,
587+ Tags : []ec2types.Tag {{Key : lo .ToPtr ("test" ), Value : lo .ToPtr ("test" )}},
588+ CreationDate : aws .String ("2022-08-15T12:00:00Z" ),
589+ State : ec2types .ImageStateAvailable ,
590+ },
591+ {
592+ Name : aws .String ("ami-name-2" ),
593+ ImageId : aws .String ("ami-456" ),
594+ Architecture : "arm64" ,
595+ Tags : []ec2types.Tag {{Key : lo .ToPtr ("test" ), Value : lo .ToPtr ("test" )}},
596+ CreationDate : aws .String ("2022-08-15T12:00:00Z" ),
597+ State : ec2types .ImageStateAvailable ,
598+ },
599+ }})
600+ nodeClass .Spec .AMISelectorTerms = []v1.AMISelectorTerm {
601+ {
602+ Name : "ami-name-1" ,
603+ },
604+ {
605+ Name : "ami-name-2" ,
606+ },
607+ }
608+ _ , err := awsEnv .AMIProvider .List (ctx , nodeClass )
609+ Expect (err ).To (BeNil ())
610+
611+ Expect (awsEnv .AMICache .Items ()).To (HaveLen (1 ))
612+ cachedImages := lo .Values (awsEnv .AMICache .Items ())[0 ].Object .(amifamily.AMIs )
613+ Expect (cachedImages ).To (ContainElements (
614+ gstruct .MatchFields (gstruct .IgnoreExtras , gstruct.Fields {
615+ "Name" : Equal ("ami-name-1" ),
616+ }),
617+ gstruct .MatchFields (gstruct .IgnoreExtras , gstruct.Fields {
618+ "Name" : Equal ("ami-name-2" ),
619+ }),
620+ ))
621+ })
622+ It ("should resolve AMIs from cache that are filtered by tags" , func () {
623+ awsEnv .EC2API .DescribeImagesOutput .Set (& ec2.DescribeImagesOutput {Images : []ec2types.Image {
624+ {
625+ Name : aws .String ("ami-name-1" ),
626+ ImageId : aws .String ("ami-123" ),
627+ Architecture : "x86_64" ,
628+ Tags : []ec2types.Tag {{Key : lo .ToPtr ("test" ), Value : lo .ToPtr ("test" )}},
629+ CreationDate : aws .String ("2022-08-15T12:00:00Z" ),
630+ State : ec2types .ImageStateAvailable ,
631+ },
632+ {
633+ Name : aws .String ("ami-name-2" ),
634+ ImageId : aws .String ("ami-456" ),
635+ Architecture : "arm64" ,
636+ Tags : []ec2types.Tag {{Key : lo .ToPtr ("test" ), Value : lo .ToPtr ("test" )}},
637+ CreationDate : aws .String ("2022-08-15T12:00:00Z" ),
638+ State : ec2types .ImageStateAvailable ,
639+ },
640+ }})
641+ nodeClass .Spec .AMISelectorTerms = []v1.AMISelectorTerm {
642+ {
643+ Tags : map [string ]string {"test" : "test" },
644+ },
645+ }
646+ _ , err := awsEnv .AMIProvider .List (ctx , nodeClass )
647+ Expect (err ).To (BeNil ())
648+
649+ Expect (awsEnv .AMICache .Items ()).To (HaveLen (1 ))
650+ cachedImages := lo .Values (awsEnv .AMICache .Items ())[0 ].Object .(amifamily.AMIs )
651+ Expect (cachedImages ).To (ContainElements (
652+ gstruct .MatchFields (gstruct .IgnoreExtras , gstruct.Fields {
653+ "Name" : Equal ("ami-name-1" ),
654+ }),
655+ gstruct .MatchFields (gstruct .IgnoreExtras , gstruct.Fields {
656+ "Name" : Equal ("ami-name-2" ),
657+ }),
658+ ))
659+ })
660+ It ("should correctly disambiguate AND vs OR semantics for tags" , func () {
661+ // AND semantics
662+ awsEnv .EC2API .DescribeImagesOutput .Set (& ec2.DescribeImagesOutput {Images : []ec2types.Image {
663+ {
664+ Name : aws .String ("ami-name-3" ),
665+ ImageId : aws .String ("ami-789" ),
666+ Architecture : "x86_64" ,
667+ Tags : []ec2types.Tag {{Key : aws .String ("tag-key-1" ), Value : aws .String ("tag-value-1" )}, {Key : aws .String ("tag-key-2" ), Value : aws .String ("tag-value-2" )}},
668+ CreationDate : aws .String ("2022-08-15T12:00:00Z" ),
669+ State : ec2types .ImageStateAvailable ,
670+ },
671+ }})
672+ nodeClass .Spec .AMIFamily = & v1 .AMIFamilyAL2
673+ nodeClass .Spec .AMISelectorTerms = []v1.AMISelectorTerm {
674+ {
675+ Tags : map [string ]string {"tag-key-1" : "tag-value-1" , "tag-key-2" : "tag-value-2" },
676+ },
677+ }
678+ ExpectApplied (ctx , env .Client , nodeClass )
679+ amis , err := awsEnv .AMIProvider .List (ctx , nodeClass )
680+ Expect (err ).To (BeNil ())
681+
682+ Expect (amis ).To (ContainElements (
683+ gstruct .MatchFields (gstruct .IgnoreExtras , gstruct.Fields {
684+ "Name" : Equal ("ami-name-3" ),
685+ }),
686+ ))
687+
688+ // OR semantics
689+ awsEnv .EC2API .DescribeImagesOutput .Set (& ec2.DescribeImagesOutput {Images : []ec2types.Image {
690+ {
691+ Name : aws .String ("ami-name-1" ),
692+ ImageId : aws .String ("ami-123" ),
693+ Architecture : "x86_64" ,
694+ Tags : []ec2types.Tag {{Key : aws .String ("tag-key-1" ), Value : aws .String ("tag-value-1" )}},
695+ CreationDate : aws .String ("2022-08-15T12:00:00Z" ),
696+ State : ec2types .ImageStateAvailable ,
697+ },
698+ {
699+ Name : aws .String ("ami-name-2" ),
700+ ImageId : aws .String ("ami-456" ),
701+ Architecture : "arm64" ,
702+ Tags : []ec2types.Tag {{Key : aws .String ("tag-key-2" ), Value : aws .String ("tag-value-2" )}},
703+ CreationDate : aws .String ("2022-08-15T12:00:00Z" ),
704+ State : ec2types .ImageStateAvailable ,
705+ },
706+ }})
707+ nodeClass .Spec .AMISelectorTerms = []v1.AMISelectorTerm {
708+ {
709+ Tags : map [string ]string {"tag-key-1" : "tag-value-1" },
710+ },
711+ {
712+ Tags : map [string ]string {"tag-key-2" : "tag-value-2" },
713+ },
714+ }
715+ ExpectApplied (ctx , env .Client , nodeClass )
716+ amis , err = awsEnv .AMIProvider .List (ctx , nodeClass )
717+ Expect (err ).To (BeNil ())
718+
719+ Expect (amis ).To (ContainElements (
720+ gstruct .MatchFields (gstruct .IgnoreExtras , gstruct.Fields {
721+ "Name" : Equal ("ami-name-1" ),
722+ }),
723+ gstruct .MatchFields (gstruct .IgnoreExtras , gstruct.Fields {
724+ "Name" : Equal ("ami-name-2" ),
725+ }),
726+ ))
727+
728+ cacheItems := awsEnv .AMICache .Items ()
729+ Expect (cacheItems ).To (HaveLen (2 ))
730+ cachedImages := make ([]amifamily.AMIs , 0 , len (cacheItems ))
731+ for _ , item := range cacheItems {
732+ cachedImages = append (cachedImages , item .Object .(amifamily.AMIs ))
733+ }
734+
735+ Expect (cachedImages ).To (ConsistOf (
736+ ConsistOf (
737+ gstruct .MatchFields (gstruct .IgnoreExtras , gstruct.Fields {
738+ "Name" : Equal ("ami-name-3" ),
739+ }),
740+ ),
741+ ConsistOf (
742+ gstruct .MatchFields (gstruct .IgnoreExtras , gstruct.Fields {
743+ "Name" : Equal ("ami-name-1" ),
744+ }),
745+ gstruct .MatchFields (gstruct .IgnoreExtras , gstruct.Fields {
746+ "Name" : Equal ("ami-name-2" ),
747+ }),
748+ ),
749+ ))
750+ })
751+ })
536752 Context ("AMI Selectors" , func () {
537753 // When you tag public or shared resources, the tags you assign are available only to your AWS account; no other AWS account will have access to those tags
538754 // https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Using_Tags.html#tag-restrictions
0 commit comments