@@ -23,6 +23,8 @@ import (
2323 testv1alpha1 "sigs.k8s.io/karpenter/pkg/test/v1alpha1"
2424
2525 "github.com/aws/aws-sdk-go-v2/aws"
26+ "github.com/aws/aws-sdk-go-v2/service/arczonalshift"
27+ arczonalshifttypes "github.com/aws/aws-sdk-go-v2/service/arczonalshift/types"
2628 "github.com/aws/aws-sdk-go-v2/service/ec2"
2729 ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
2830 "github.com/awslabs/operatorpkg/object"
@@ -665,4 +667,90 @@ var _ = Describe("InstanceProvider", func() {
665667 Expect (createdInstance .EFACount ).To (Equal (0 ))
666668 })
667669 })
670+ Context ("Zonal Shift" , func () {
671+ var instanceID string
672+ BeforeEach (func () {
673+ // Store an instance in the shifted zone and populate the cache
674+ ec2Instance := test .EC2Instance (ec2types.Instance {
675+ Placement : & ec2types.Placement {
676+ AvailabilityZone : aws .String ("test-zone-1a" ),
677+ AvailabilityZoneId : aws .String ("tstz1-1a" ),
678+ },
679+ })
680+ instanceID = aws .ToString (ec2Instance .InstanceId )
681+ awsEnv .EC2API .Instances .Store (instanceID , ec2Instance )
682+
683+ _ , err := awsEnv .InstanceProvider .Get (ctx , instanceID )
684+ Expect (err ).ToNot (HaveOccurred ())
685+ awsEnv .EC2API .DescribeInstancesBehavior .CalledWithInput .Reset ()
686+ awsEnv .EC2API .TerminateInstancesBehavior .CalledWithInput .Reset ()
687+ awsEnv .EC2API .CreateTagsBehavior .CalledWithInput .Reset ()
688+
689+ // Activate a zonal shift for tstz1-1a
690+ awsEnv .ARCZonalShiftAPI .GetManagedResourceBehavior .Output .Set (& arczonalshift.GetManagedResourceOutput {
691+ ZonalShifts : []arczonalshifttypes.ZonalShiftInResource {
692+ {
693+ AwayFrom : aws .String ("tstz1-1a" ),
694+ ExpiryTime : aws .Time (time .Now ().Add (time .Hour )),
695+ AppliedStatus : arczonalshifttypes .AppliedStatusApplied ,
696+ },
697+ },
698+ })
699+ Expect (awsEnv .ZonalShiftProvider .UpdateZonalShifts (ctx )).To (Succeed ())
700+ })
701+ It ("should not call DescribeInstances for instances in a zonally shifted AZ" , func () {
702+ inst , err := awsEnv .InstanceProvider .Get (ctx , instanceID , instance .SkipCache )
703+ Expect (err ).ToNot (HaveOccurred ())
704+ Expect (inst .ID ).To (Equal (instanceID ))
705+ Expect (inst .ZoneID ).To (Equal ("tstz1-1a" ))
706+ Expect (awsEnv .EC2API .DescribeInstancesBehavior .CalledWithInput .Len ()).To (Equal (0 ))
707+ })
708+ It ("should not call TerminateInstances for instances in a zonally shifted AZ" , func () {
709+ err := awsEnv .InstanceProvider .Delete (ctx , instanceID )
710+ Expect (err ).To (HaveOccurred ())
711+ Expect (err .Error ()).To (ContainSubstring ("zonally shifted" ))
712+ Expect (awsEnv .EC2API .DescribeInstancesBehavior .CalledWithInput .Len ()).To (Equal (0 ))
713+ Expect (awsEnv .EC2API .TerminateInstancesBehavior .CalledWithInput .Len ()).To (Equal (0 ))
714+ })
715+ It ("should not call CreateTags for instances in a zonally shifted AZ" , func () {
716+ err := awsEnv .InstanceProvider .CreateTags (ctx , instanceID , map [string ]string {"test-key" : "test-value" })
717+ Expect (err ).To (HaveOccurred ())
718+ Expect (err .Error ()).To (ContainSubstring ("zonally shifted" ))
719+ Expect (awsEnv .EC2API .CreateTagsBehavior .CalledWithInput .Len ()).To (Equal (0 ))
720+ })
721+ Context ("Cache Miss" , func () {
722+ // When the instance cache is cold, Get() calls DescribeInstances which populates
723+ // the cache with zone information. Delete() benefits from this since it calls Get()
724+ // first, so the zonal shift guard still applies. CreateTags() does not call Get(),
725+ // so its guard depends on a warm cache.
726+ var uncachedInstanceID string
727+ BeforeEach (func () {
728+ ec2Instance := test .EC2Instance (ec2types.Instance {
729+ Placement : & ec2types.Placement {
730+ AvailabilityZone : aws .String ("test-zone-1a" ),
731+ AvailabilityZoneId : aws .String ("tstz1-1a" ),
732+ },
733+ })
734+ uncachedInstanceID = aws .ToString (ec2Instance .InstanceId )
735+ // Store in EC2 but do NOT call Get() to populate the instance cache
736+ awsEnv .EC2API .Instances .Store (uncachedInstanceID , ec2Instance )
737+ awsEnv .EC2API .DescribeInstancesBehavior .CalledWithInput .Reset ()
738+ awsEnv .EC2API .TerminateInstancesBehavior .CalledWithInput .Reset ()
739+ awsEnv .EC2API .CreateTagsBehavior .CalledWithInput .Reset ()
740+ })
741+ It ("should not call TerminateInstances even with a cold cache since Delete calls Get first" , func () {
742+ err := awsEnv .InstanceProvider .Delete (ctx , uncachedInstanceID )
743+ Expect (err ).To (HaveOccurred ())
744+ Expect (err .Error ()).To (ContainSubstring ("zonally shifted" ))
745+ // DescribeInstances is called by Get() inside Delete() to fetch instance data
746+ Expect (awsEnv .EC2API .DescribeInstancesBehavior .CalledWithInput .Len ()).To (Equal (1 ))
747+ Expect (awsEnv .EC2API .TerminateInstancesBehavior .CalledWithInput .Len ()).To (Equal (0 ))
748+ })
749+ It ("should proceed with CreateTags when instance is not in the cache" , func () {
750+ err := awsEnv .InstanceProvider .CreateTags (ctx , uncachedInstanceID , map [string ]string {"test-key" : "test-value" })
751+ Expect (err ).ToNot (HaveOccurred ())
752+ Expect (awsEnv .EC2API .CreateTagsBehavior .CalledWithInput .Len ()).To (Equal (1 ))
753+ })
754+ })
755+ })
668756})
0 commit comments