Skip to content

Commit 76f87e8

Browse files
authored
fix: deterministic order for instance+LT BlockDeviceMappings merge (#68)
1 parent 7fc08f9 commit 76f87e8

1 file changed

Lines changed: 24 additions & 10 deletions

File tree

pkg/tree/aws/ec2/ec2.go

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -110,21 +110,35 @@ func (ec2 *EC2) PostProcess() {
110110
instance.Tenancy = lt.Tenancy
111111
}
112112

113-
launchTemplateBlockDeviceMap := make(map[string]BlockDeviceMapping)
113+
// Iterate the original slices for deterministic order — Go map
114+
// iteration is randomised, and downstream consumers index into this
115+
// slice to name sub-resources, so unstable order produces flaky
116+
// output (instance-defined and LT-only devices can swap positions).
117+
// Maps are only used for lookups during the merge.
118+
//
119+
// TODO: LT-derived block devices currently inherit their address
120+
// from the launch template (e.g. `aws_launch_template.X.block_device_mapping[0]`).
121+
// Downstream the providers package falls back to a synthetic
122+
// `ebs_block_device[N]` name for these because the address doesn't
123+
// start with the instance's. It would be more accurate to surface
124+
// the actual LT-prefixed address so users can see where the device
125+
// is really defined — left for a follow-up since it changes
126+
// user-visible output.
127+
launchTemplateBlockDeviceMap := make(map[string]BlockDeviceMapping, len(lt.BlockDeviceMappings))
114128
for _, blockDevice := range lt.BlockDeviceMappings {
115129
launchTemplateBlockDeviceMap[blockDevice.DeviceName.Value()] = blockDevice
116130
}
117131

118-
instanceBlockDeviceMap := make(map[string]BlockDeviceMapping)
132+
instanceBlockDeviceNames := make(map[string]struct{}, len(instance.BlockDeviceMappings))
119133
for _, blockDevice := range instance.BlockDeviceMappings {
120-
instanceBlockDeviceMap[blockDevice.DeviceName.Value()] = blockDevice
134+
instanceBlockDeviceNames[blockDevice.DeviceName.Value()] = struct{}{}
121135
}
122136

123-
blockDevices := []BlockDeviceMapping{}
137+
blockDevices := make([]BlockDeviceMapping, 0, len(instance.BlockDeviceMappings)+len(lt.BlockDeviceMappings))
124138

125-
// merge any defaults from the launch template into existing block devices
126-
for name, blockDevice := range instanceBlockDeviceMap {
127-
if launchTemplateBlockDevice, ok := launchTemplateBlockDeviceMap[name]; ok {
139+
// instance-defined first, merging any defaults from the launch template
140+
for _, blockDevice := range instance.BlockDeviceMappings {
141+
if launchTemplateBlockDevice, ok := launchTemplateBlockDeviceMap[blockDevice.DeviceName.Value()]; ok {
128142
if blockDevice.EBSVolume.Type.IsDefaultOrEmpty() {
129143
blockDevice.EBSVolume.Type = launchTemplateBlockDevice.EBSVolume.Type
130144
}
@@ -141,9 +155,9 @@ func (ec2 *EC2) PostProcess() {
141155
blockDevices = append(blockDevices, blockDevice)
142156
}
143157

144-
// add any new block devices from the launch template
145-
for name, blockDevice := range launchTemplateBlockDeviceMap {
146-
if _, ok := instanceBlockDeviceMap[name]; !ok {
158+
// LT-only block devices appended after, in launch template order
159+
for _, blockDevice := range lt.BlockDeviceMappings {
160+
if _, ok := instanceBlockDeviceNames[blockDevice.DeviceName.Value()]; !ok {
147161
blockDevices = append(blockDevices, blockDevice)
148162
}
149163
}

0 commit comments

Comments
 (0)