1+ import { DescribeLaunchTemplateVersionsCommand , EC2Client } from '@aws-sdk/client-ec2' ;
12import { PutParameterCommand , SSMClient } from '@aws-sdk/client-ssm' ;
23import { mockClient } from 'aws-sdk-client-mock' ;
34import 'aws-sdk-client-mock-jest/vitest' ;
@@ -32,6 +33,7 @@ const mockOctokit = {
3233
3334const mockCreateRunner = vi . mocked ( createRunner ) ;
3435const mockListRunners = vi . mocked ( listEC2Runners ) ;
36+ const mockEC2Client = mockClient ( EC2Client ) ;
3537const mockSSMClient = mockClient ( SSMClient ) ;
3638const mockSSMgetParameter = vi . mocked ( getParameter ) ;
3739const mockPublishRetryMessage = vi . mocked ( publishRetryMessage ) ;
@@ -142,6 +144,22 @@ beforeEach(() => {
142144 vi . clearAllMocks ( ) ;
143145 setDefaults ( ) ;
144146
147+ mockEC2Client . reset ( ) ;
148+ mockEC2Client . on ( DescribeLaunchTemplateVersionsCommand ) . resolves ( {
149+ LaunchTemplateVersions : [
150+ {
151+ LaunchTemplateData : {
152+ BlockDeviceMappings : [
153+ {
154+ DeviceName : '/dev/sda1' ,
155+ Ebs : { } ,
156+ } ,
157+ ] ,
158+ } ,
159+ } ,
160+ ] ,
161+ } ) ;
162+
145163 defaultSSMGetParameterMockImpl ( ) ;
146164 defaultOctokitMockImpl ( ) ;
147165
@@ -650,6 +668,86 @@ describe('scaleUp with GHES', () => {
650668 ) ;
651669 } ) ;
652670
671+ it ( 'loads the launch template block device name for dynamic EBS labels without DeviceName' , async ( ) => {
672+ mockEC2Client . on ( DescribeLaunchTemplateVersionsCommand ) . resolves ( {
673+ LaunchTemplateVersions : [
674+ {
675+ LaunchTemplateData : {
676+ BlockDeviceMappings : [
677+ {
678+ DeviceName : '/dev/sdb' ,
679+ VirtualName : 'ephemeral0' ,
680+ } ,
681+ {
682+ DeviceName : '/dev/sdf' ,
683+ Ebs : { } ,
684+ } ,
685+ ] ,
686+ } ,
687+ } ,
688+ ] ,
689+ } ) ;
690+
691+ const testDataWithEbsLabels = [
692+ {
693+ ...TEST_DATA_SINGLE ,
694+ labels : [ 'ghr-ec2-ebs-volume-size:100' , 'ghr-ec2-ebs-volume-type:gp3' ] ,
695+ messageId : 'test-ebs-device-name' ,
696+ } ,
697+ ] ;
698+
699+ await scaleUpModule . scaleUp ( testDataWithEbsLabels ) ;
700+
701+ expect ( mockEC2Client ) . toHaveReceivedCommandWith ( DescribeLaunchTemplateVersionsCommand , {
702+ LaunchTemplateName : 'lt-1' ,
703+ Versions : [ '$Default' ] ,
704+ } ) ;
705+ expect ( createRunner ) . toBeCalledWith (
706+ expect . objectContaining ( {
707+ ec2OverrideConfig : expect . objectContaining ( {
708+ BlockDeviceMappings : [
709+ {
710+ DeviceName : '/dev/sdf' ,
711+ Ebs : {
712+ VolumeSize : 100 ,
713+ VolumeType : 'gp3' ,
714+ } ,
715+ } ,
716+ ] ,
717+ } ) ,
718+ } ) ,
719+ ) ;
720+ } ) ;
721+
722+ it ( 'does not load launch template block device name when DeviceName is provided by labels' , async ( ) => {
723+ const testDataWithEbsLabels = [
724+ {
725+ ...TEST_DATA_SINGLE ,
726+ labels : [ 'ghr-ec2-block-device-name:/dev/sdg' , 'ghr-ec2-ebs-volume-size:100' , 'ghr-ec2-ebs-volume-type:gp3' ] ,
727+ messageId : 'test-explicit-ebs-device-name' ,
728+ } ,
729+ ] ;
730+
731+ await scaleUpModule . scaleUp ( testDataWithEbsLabels ) ;
732+
733+ expect ( mockEC2Client ) . not . toHaveReceivedCommand ( DescribeLaunchTemplateVersionsCommand ) ;
734+ expect ( createRunner ) . toBeCalledWith (
735+ expect . objectContaining ( {
736+ ec2OverrideConfig : expect . objectContaining ( {
737+ BlockDeviceMappings : [
738+ {
739+ DeviceName : '/dev/sdg' ,
740+ Ebs : {
741+ VolumeSize : 100 ,
742+ VolumeType : 'gp3' ,
743+ } ,
744+ } ,
745+ ] ,
746+ } ) ,
747+ } ) ,
748+ ) ;
749+ } ) ;
750+
653751 it ( 'handles messages with no labels gracefully' , async ( ) => {
654752 const testDataWithNoLabels = [
655753 {
@@ -2553,11 +2651,16 @@ describe('parseEc2OverrideConfig', () => {
25532651 } ) ;
25542652
25552653 describe ( 'Placement' , ( ) => {
2556- it ( 'should parse placement-group label' , ( ) => {
2557- const result = scaleUpModule . parseEc2OverrideConfig ( [ 'ghr-ec2-placement-group:my-placement-group' ] ) ;
2654+ it ( 'should parse placement-group-name label' , ( ) => {
2655+ const result = scaleUpModule . parseEc2OverrideConfig ( [ 'ghr-ec2-placement-group-name :my-placement-group' ] ) ;
25582656 expect ( result ?. Placement ?. GroupName ) . toBe ( 'my-placement-group' ) ;
25592657 } ) ;
25602658
2659+ it ( 'should parse placement-group-id label' , ( ) => {
2660+ const result = scaleUpModule . parseEc2OverrideConfig ( [ 'ghr-ec2-placement-group-id:pg-1234567890abcdef0' ] ) ;
2661+ expect ( result ?. Placement ?. GroupId ) . toBe ( 'pg-1234567890abcdef0' ) ;
2662+ } ) ;
2663+
25612664 it ( 'should parse placement-tenancy label' , ( ) => {
25622665 const result = scaleUpModule . parseEc2OverrideConfig ( [ 'ghr-ec2-placement-tenancy:dedicated' ] ) ;
25632666 expect ( result ?. Placement ?. Tenancy ) . toBe ( 'dedicated' ) ;
@@ -2583,6 +2686,11 @@ describe('parseEc2OverrideConfig', () => {
25832686 expect ( result ?. Placement ?. AvailabilityZone ) . toBe ( 'us-west-2b' ) ;
25842687 } ) ;
25852688
2689+ it ( 'should parse placement-availability-zone-id label' , ( ) => {
2690+ const result = scaleUpModule . parseEc2OverrideConfig ( [ 'ghr-ec2-placement-availability-zone-id:use1-az1' ] ) ;
2691+ expect ( result ?. Placement ?. AvailabilityZoneId ) . toBe ( 'use1-az1' ) ;
2692+ } ) ;
2693+
25862694 it ( 'should parse placement-spread-domain label' , ( ) => {
25872695 const result = scaleUpModule . parseEc2OverrideConfig ( [ 'ghr-ec2-placement-spread-domain:my-spread-domain' ] ) ;
25882696 expect ( result ?. Placement ?. SpreadDomain ) . toBe ( 'my-spread-domain' ) ;
@@ -2599,7 +2707,7 @@ describe('parseEc2OverrideConfig', () => {
25992707
26002708 it ( 'should parse multiple placement labels' , ( ) => {
26012709 const result = scaleUpModule . parseEc2OverrideConfig ( [
2602- 'ghr-ec2-placement-group:group-1' ,
2710+ 'ghr-ec2-placement-group-name :group-1' ,
26032711 'ghr-ec2-placement-tenancy:dedicated' ,
26042712 'ghr-ec2-placement-availability-zone:us-east-1b' ,
26052713 ] ) ;
@@ -2610,6 +2718,17 @@ describe('parseEc2OverrideConfig', () => {
26102718 } ) ;
26112719
26122720 describe ( 'Block Device Mappings' , ( ) => {
2721+ it ( 'should parse block-device-name label' , ( ) => {
2722+ const result = scaleUpModule . parseEc2OverrideConfig ( [ 'ghr-ec2-block-device-name:/dev/sdg' ] ) ;
2723+ expect ( result ?. BlockDeviceMappings ?. [ 0 ] ?. DeviceName ) . toBe ( '/dev/sdg' ) ;
2724+ } ) ;
2725+
2726+ it ( 'should use default block device name when provided' , ( ) => {
2727+ const result = scaleUpModule . parseEc2OverrideConfig ( [ 'ghr-ec2-ebs-volume-size:100' ] , '/dev/sda1' ) ;
2728+ expect ( result ?. BlockDeviceMappings ?. [ 0 ] ?. DeviceName ) . toBe ( '/dev/sda1' ) ;
2729+ expect ( result ?. BlockDeviceMappings ?. [ 0 ] ?. Ebs ?. VolumeSize ) . toBe ( 100 ) ;
2730+ } ) ;
2731+
26132732 it ( 'should parse ebs-volume-size label as number' , ( ) => {
26142733 const result = scaleUpModule . parseEc2OverrideConfig ( [ 'ghr-ec2-ebs-volume-size:100' ] ) ;
26152734 expect ( result ?. BlockDeviceMappings ?. [ 0 ] ?. Ebs ?. VolumeSize ) . toBe ( 100 ) ;
@@ -2690,7 +2809,6 @@ describe('parseEc2OverrideConfig', () => {
26902809 it ( 'should initialize BlockDeviceMappings when not present' , ( ) => {
26912810 const result = scaleUpModule . parseEc2OverrideConfig ( [ 'ghr-ec2-ebs-volume-size:50' ] ) ;
26922811 expect ( result ?. BlockDeviceMappings ) . toBeDefined ( ) ;
2693- // expect(result?.BlockDeviceMappings?.[0]?.DeviceName).toBe('/dev/sda1');
26942812 } ) ;
26952813 } ) ;
26962814
@@ -3106,7 +3224,7 @@ describe('parseEc2OverrideConfig', () => {
31063224 'ghr-ec2-max-price:0.75' ,
31073225 'ghr-ec2-priority:1' ,
31083226 // Placement
3109- 'ghr-ec2-placement-group:my-group' ,
3227+ 'ghr-ec2-placement-group-name :my-group' ,
31103228 'ghr-ec2-placement-tenancy:dedicated' ,
31113229 // Block Device
31123230 'ghr-ec2-ebs-volume-size:200' ,
0 commit comments