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