@@ -31,6 +31,7 @@ import (
3131
3232 . "github.com/onsi/ginkgo/v2"
3333 . "github.com/onsi/gomega"
34+ "github.com/onsi/gomega/gstruct"
3435 . "sigs.k8s.io/karpenter/pkg/utils/testing"
3536
3637 "github.com/samber/lo"
@@ -51,6 +52,8 @@ import (
5152 "github.com/aws/karpenter-provider-aws/pkg/operator/options"
5253 "github.com/aws/karpenter-provider-aws/pkg/providers/amifamily"
5354 "github.com/aws/karpenter-provider-aws/pkg/test"
55+
56+ . "sigs.k8s.io/karpenter/pkg/test/expectations"
5457)
5558
5659var ctx context.Context
@@ -579,6 +582,219 @@ var _ = Describe("AMIProvider", func() {
579582 }))
580583 })
581584 })
585+ Context ("Provider Cache" , func () {
586+ It ("should resolve AMIs from cache that are filtered by id" , func () {
587+ awsEnv .EC2API .DescribeImagesOutput .Set (& ec2.DescribeImagesOutput {Images : []ec2types.Image {
588+ {
589+ Name : aws .String (coretest .RandomName ()),
590+ ImageId : aws .String ("ami-123" ),
591+ Architecture : "x86_64" ,
592+ Tags : []ec2types.Tag {{Key : lo .ToPtr ("test" ), Value : lo .ToPtr ("test" )}},
593+ CreationDate : aws .String ("2022-08-15T12:00:00Z" ),
594+ State : ec2types .ImageStateAvailable ,
595+ },
596+ {
597+ Name : aws .String (coretest .RandomName ()),
598+ ImageId : aws .String ("ami-456" ),
599+ Architecture : "arm64" ,
600+ Tags : []ec2types.Tag {{Key : lo .ToPtr ("test" ), Value : lo .ToPtr ("test" )}},
601+ CreationDate : aws .String ("2022-08-15T12:00:00Z" ),
602+ State : ec2types .ImageStateAvailable ,
603+ },
604+ }})
605+ nodeClass .Spec .AMISelectorTerms = []v1.AMISelectorTerm {
606+ {
607+ ID : "ami-123" ,
608+ },
609+ {
610+ ID : "ami-456" ,
611+ },
612+ }
613+ _ , err := awsEnv .AMIProvider .List (ctx , nodeClass )
614+ Expect (err ).To (BeNil ())
615+
616+ Expect (awsEnv .AMICache .Items ()).To (HaveLen (1 ))
617+ cachedImages := lo .Values (awsEnv .AMICache .Items ())[0 ].Object .(amifamily.AMIs )
618+ Expect (cachedImages ).To (ContainElements (
619+ gstruct .MatchFields (gstruct .IgnoreExtras , gstruct.Fields {
620+ "AmiID" : Equal ("ami-123" ),
621+ }),
622+ gstruct .MatchFields (gstruct .IgnoreExtras , gstruct.Fields {
623+ "AmiID" : Equal ("ami-456" ),
624+ }),
625+ ))
626+ })
627+ It ("should resolve AMIs from cache that are filtered by name" , func () {
628+ awsEnv .EC2API .DescribeImagesOutput .Set (& ec2.DescribeImagesOutput {Images : []ec2types.Image {
629+ {
630+ Name : aws .String ("ami-name-1" ),
631+ ImageId : aws .String ("ami-123" ),
632+ Architecture : "x86_64" ,
633+ Tags : []ec2types.Tag {{Key : lo .ToPtr ("test" ), Value : lo .ToPtr ("test" )}},
634+ CreationDate : aws .String ("2022-08-15T12:00:00Z" ),
635+ State : ec2types .ImageStateAvailable ,
636+ },
637+ {
638+ Name : aws .String ("ami-name-2" ),
639+ ImageId : aws .String ("ami-456" ),
640+ Architecture : "arm64" ,
641+ Tags : []ec2types.Tag {{Key : lo .ToPtr ("test" ), Value : lo .ToPtr ("test" )}},
642+ CreationDate : aws .String ("2022-08-15T12:00:00Z" ),
643+ State : ec2types .ImageStateAvailable ,
644+ },
645+ }})
646+ nodeClass .Spec .AMISelectorTerms = []v1.AMISelectorTerm {
647+ {
648+ Name : "ami-name-1" ,
649+ },
650+ {
651+ Name : "ami-name-2" ,
652+ },
653+ }
654+ _ , err := awsEnv .AMIProvider .List (ctx , nodeClass )
655+ Expect (err ).To (BeNil ())
656+
657+ Expect (awsEnv .AMICache .Items ()).To (HaveLen (1 ))
658+ cachedImages := lo .Values (awsEnv .AMICache .Items ())[0 ].Object .(amifamily.AMIs )
659+ Expect (cachedImages ).To (ContainElements (
660+ gstruct .MatchFields (gstruct .IgnoreExtras , gstruct.Fields {
661+ "Name" : Equal ("ami-name-1" ),
662+ }),
663+ gstruct .MatchFields (gstruct .IgnoreExtras , gstruct.Fields {
664+ "Name" : Equal ("ami-name-2" ),
665+ }),
666+ ))
667+ })
668+ It ("should resolve AMIs from cache that are filtered by tags" , func () {
669+ awsEnv .EC2API .DescribeImagesOutput .Set (& ec2.DescribeImagesOutput {Images : []ec2types.Image {
670+ {
671+ Name : aws .String ("ami-name-1" ),
672+ ImageId : aws .String ("ami-123" ),
673+ Architecture : "x86_64" ,
674+ Tags : []ec2types.Tag {{Key : lo .ToPtr ("test" ), Value : lo .ToPtr ("test" )}},
675+ CreationDate : aws .String ("2022-08-15T12:00:00Z" ),
676+ State : ec2types .ImageStateAvailable ,
677+ },
678+ {
679+ Name : aws .String ("ami-name-2" ),
680+ ImageId : aws .String ("ami-456" ),
681+ Architecture : "arm64" ,
682+ Tags : []ec2types.Tag {{Key : lo .ToPtr ("test" ), Value : lo .ToPtr ("test" )}},
683+ CreationDate : aws .String ("2022-08-15T12:00:00Z" ),
684+ State : ec2types .ImageStateAvailable ,
685+ },
686+ }})
687+ nodeClass .Spec .AMISelectorTerms = []v1.AMISelectorTerm {
688+ {
689+ Tags : map [string ]string {"test" : "test" },
690+ },
691+ }
692+ _ , err := awsEnv .AMIProvider .List (ctx , nodeClass )
693+ Expect (err ).To (BeNil ())
694+
695+ Expect (awsEnv .AMICache .Items ()).To (HaveLen (1 ))
696+ cachedImages := lo .Values (awsEnv .AMICache .Items ())[0 ].Object .(amifamily.AMIs )
697+ Expect (cachedImages ).To (ContainElements (
698+ gstruct .MatchFields (gstruct .IgnoreExtras , gstruct.Fields {
699+ "Name" : Equal ("ami-name-1" ),
700+ }),
701+ gstruct .MatchFields (gstruct .IgnoreExtras , gstruct.Fields {
702+ "Name" : Equal ("ami-name-2" ),
703+ }),
704+ ))
705+ })
706+ It ("should correctly disambiguate AND vs OR semantics for tags" , func () {
707+ // AND semantics
708+ awsEnv .EC2API .DescribeImagesOutput .Set (& ec2.DescribeImagesOutput {Images : []ec2types.Image {
709+ {
710+ Name : aws .String ("ami-name-3" ),
711+ ImageId : aws .String ("ami-789" ),
712+ Architecture : "x86_64" ,
713+ 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" )}},
714+ CreationDate : aws .String ("2022-08-15T12:00:00Z" ),
715+ State : ec2types .ImageStateAvailable ,
716+ },
717+ }})
718+ nodeClass .Spec .AMIFamily = & v1 .AMIFamilyAL2
719+ nodeClass .Spec .AMISelectorTerms = []v1.AMISelectorTerm {
720+ {
721+ Tags : map [string ]string {"tag-key-1" : "tag-value-1" , "tag-key-2" : "tag-value-2" },
722+ },
723+ }
724+ ExpectApplied (ctx , env .Client , nodeClass )
725+ amis , err := awsEnv .AMIProvider .List (ctx , nodeClass )
726+ Expect (err ).To (BeNil ())
727+
728+ Expect (amis ).To (ContainElements (
729+ gstruct .MatchFields (gstruct .IgnoreExtras , gstruct.Fields {
730+ "Name" : Equal ("ami-name-3" ),
731+ }),
732+ ))
733+
734+ // OR semantics
735+ awsEnv .EC2API .DescribeImagesOutput .Set (& ec2.DescribeImagesOutput {Images : []ec2types.Image {
736+ {
737+ Name : aws .String ("ami-name-1" ),
738+ ImageId : aws .String ("ami-123" ),
739+ Architecture : "x86_64" ,
740+ Tags : []ec2types.Tag {{Key : aws .String ("tag-key-1" ), Value : aws .String ("tag-value-1" )}},
741+ CreationDate : aws .String ("2022-08-15T12:00:00Z" ),
742+ State : ec2types .ImageStateAvailable ,
743+ },
744+ {
745+ Name : aws .String ("ami-name-2" ),
746+ ImageId : aws .String ("ami-456" ),
747+ Architecture : "arm64" ,
748+ Tags : []ec2types.Tag {{Key : aws .String ("tag-key-2" ), Value : aws .String ("tag-value-2" )}},
749+ CreationDate : aws .String ("2022-08-15T12:00:00Z" ),
750+ State : ec2types .ImageStateAvailable ,
751+ },
752+ }})
753+ nodeClass .Spec .AMISelectorTerms = []v1.AMISelectorTerm {
754+ {
755+ Tags : map [string ]string {"tag-key-1" : "tag-value-1" },
756+ },
757+ {
758+ Tags : map [string ]string {"tag-key-2" : "tag-value-2" },
759+ },
760+ }
761+ ExpectApplied (ctx , env .Client , nodeClass )
762+ amis , err = awsEnv .AMIProvider .List (ctx , nodeClass )
763+ Expect (err ).To (BeNil ())
764+
765+ Expect (amis ).To (ContainElements (
766+ gstruct .MatchFields (gstruct .IgnoreExtras , gstruct.Fields {
767+ "Name" : Equal ("ami-name-1" ),
768+ }),
769+ gstruct .MatchFields (gstruct .IgnoreExtras , gstruct.Fields {
770+ "Name" : Equal ("ami-name-2" ),
771+ }),
772+ ))
773+
774+ cacheItems := awsEnv .AMICache .Items ()
775+ Expect (cacheItems ).To (HaveLen (2 ))
776+ cachedImages := make ([]amifamily.AMIs , 0 , len (cacheItems ))
777+ for _ , item := range cacheItems {
778+ cachedImages = append (cachedImages , item .Object .(amifamily.AMIs ))
779+ }
780+
781+ Expect (cachedImages ).To (ConsistOf (
782+ ConsistOf (
783+ gstruct .MatchFields (gstruct .IgnoreExtras , gstruct.Fields {
784+ "Name" : Equal ("ami-name-3" ),
785+ }),
786+ ),
787+ ConsistOf (
788+ gstruct .MatchFields (gstruct .IgnoreExtras , gstruct.Fields {
789+ "Name" : Equal ("ami-name-1" ),
790+ }),
791+ gstruct .MatchFields (gstruct .IgnoreExtras , gstruct.Fields {
792+ "Name" : Equal ("ami-name-2" ),
793+ }),
794+ ),
795+ ))
796+ })
797+ })
582798 Context ("AMI Selectors" , func () {
583799 // 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
584800 // https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Using_Tags.html#tag-restrictions
0 commit comments