Skip to content

Commit 1dac097

Browse files
authored
fix(ec2): explorer assumes names are unique (#3640)
Problem: In the explorer, assuming the names are unique has the following consequences: it is used as a key in the map connecting parent -> child nodes. it is displayed by name, meaning each instance with same name appears identical. Solution: Remove this assumption, and use the instanceIds to key the child nodes. add instance Ids following the names in explorer.
1 parent 0c5ac5f commit 1dac097

File tree

4 files changed

+42
-25
lines changed

4 files changed

+42
-25
lines changed

src/ec2/explorer/ec2InstanceNode.ts

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import { getNameOfInstance } from '../../shared/clients/ec2Client'
77
import { AWSResourceNode } from '../../shared/treeview/nodes/awsResourceNode'
88
import { AWSTreeNodeBase } from '../../shared/treeview/nodes/awsTreeNodeBase'
99
import { Ec2Instance } from '../../shared/clients/ec2Client'
10-
import { build } from '@aws-sdk/util-arn-parser'
1110
import globals from '../../shared/extensionGlobals'
1211
import { Ec2Selection } from '../utils'
1312

@@ -24,8 +23,8 @@ export class Ec2InstanceNode extends AWSTreeNodeBase implements AWSResourceNode
2423

2524
public update(newInstance: Ec2Instance) {
2625
this.setInstance(newInstance)
27-
this.label = this.name
28-
this.tooltip = this.InstanceId
26+
this.label = `${this.name} (${this.InstanceId})`
27+
this.tooltip = `${this.name}\n${this.InstanceId}\n${this.arn}`
2928
}
3029

3130
public setInstance(newInstance: Ec2Instance) {
@@ -40,20 +39,16 @@ export class Ec2InstanceNode extends AWSTreeNodeBase implements AWSResourceNode
4039
}
4140

4241
public get name(): string {
43-
return getNameOfInstance(this.instance) ?? `${this.InstanceId} (no name)`
42+
return getNameOfInstance(this.instance) ?? `(no name)`
4443
}
4544

4645
public get InstanceId(): string {
4746
return this.instance.InstanceId!
4847
}
4948

5049
public get arn(): string {
51-
return build({
52-
partition: this.partitionId,
53-
service: 'ec2',
54-
region: this.regionCode,
55-
accountId: globals.awsContext.getCredentialAccountId()!,
56-
resource: 'instance',
57-
})
50+
return `arn:${this.partitionId}:ec2:${
51+
this.regionCode
52+
}:${globals.awsContext.getCredentialAccountId()}:instance/${this.InstanceId}`
5853
}
5954
}

src/ec2/explorer/ec2ParentNode.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import { makeChildrenNodes } from '../../shared/treeview/utils'
88
import { PlaceholderNode } from '../../shared/treeview/nodes/placeholderNode'
99
import { Ec2InstanceNode } from './ec2InstanceNode'
1010
import { Ec2Client } from '../../shared/clients/ec2Client'
11-
import { getNameOfInstance } from '../../shared/clients/ec2Client'
1211
import { updateInPlace } from '../../shared/utilities/collectionUtils'
1312

1413
export const contextValueEc2 = 'awsEc2Node'
@@ -39,9 +38,7 @@ export class Ec2ParentNode extends AWSTreeNodeBase {
3938
}
4039

4140
public async updateChildren(): Promise<void> {
42-
const ec2Instances = await (
43-
await this.ec2Client.getInstances()
44-
).toMap(instance => getNameOfInstance(instance) ?? instance.InstanceId)
41+
const ec2Instances = await (await this.ec2Client.getInstances()).toMap(instance => instance.InstanceId)
4542
updateInPlace(
4643
this.ec2InstanceNodes,
4744
ec2Instances.keys(),

src/test/ec2/explorer/ec2InstanceNode.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,15 @@ describe('ec2InstanceNode', function () {
3535
})
3636

3737
it('initializes the label', async function () {
38-
assert.strictEqual(testNode.label, getNameOfInstance(testInstance))
38+
assert.strictEqual(testNode.label, `${getNameOfInstance(testInstance)} (${testInstance.InstanceId})`)
3939
})
4040

4141
it('initializes the functionName', async function () {
4242
assert.strictEqual(testNode.name, getNameOfInstance(testInstance))
4343
})
4444

4545
it('initializes the tooltip', async function () {
46-
assert.strictEqual(testNode.tooltip, testInstance.InstanceId)
46+
assert.strictEqual(testNode.tooltip, `${testNode.name}\n${testNode.InstanceId}\n${testNode.arn}`)
4747
})
4848

4949
it('has no children', async function () {

src/test/ec2/explorer/ec2ParentNode.test.ts

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import * as assert from 'assert'
77
import { Ec2ParentNode, contextValueEc2 } from '../../../ec2/explorer/ec2ParentNode'
88
import { stub } from '../../utilities/stubber'
9-
import { Ec2Client } from '../../../shared/clients/ec2Client'
9+
import { Ec2Client, Ec2Instance } from '../../../shared/clients/ec2Client'
1010
import { intoCollection } from '../../../shared/utilities/collectionUtils'
1111
import {
1212
assertNodeListOnlyHasErrorNode,
@@ -16,28 +16,35 @@ import { Ec2InstanceNode } from '../../../ec2/explorer/ec2InstanceNode'
1616

1717
describe('ec2ParentNode', function () {
1818
let testNode: Ec2ParentNode
19-
let instanceNames: string[]
19+
let instances: Ec2Instance[]
2020
const testRegion = 'testRegion'
2121
const testPartition = 'testPartition'
2222

2323
function createClient() {
2424
const client = stub(Ec2Client, { regionCode: testRegion })
2525
client.getInstances.callsFake(async () =>
2626
intoCollection(
27-
instanceNames.map(name => ({ InstanceId: name + name, Tags: [{ Key: 'Name', Value: name }] }))
27+
instances.map(instance => ({
28+
InstanceId: instance.InstanceId,
29+
Tags: [{ Key: 'Name', Value: instance.name }],
30+
}))
2831
)
2932
)
3033

3134
return client
3235
}
3336

3437
beforeEach(function () {
35-
instanceNames = ['firstOne', 'secondOne']
38+
instances = [
39+
{ name: 'firstOne', InstanceId: '0' },
40+
{ name: 'secondOne', InstanceId: '1' },
41+
]
42+
3643
testNode = new Ec2ParentNode(testRegion, testPartition, createClient())
3744
})
3845

3946
it('returns placeholder node if no children are present', async function () {
40-
instanceNames = []
47+
instances = []
4148

4249
const childNodes = await testNode.getChildren()
4350

@@ -47,7 +54,7 @@ describe('ec2ParentNode', function () {
4754
it('has instance child nodes', async function () {
4855
const childNodes = await testNode.getChildren()
4956

50-
assert.strictEqual(childNodes.length, instanceNames.length, 'Unexpected child count')
57+
assert.strictEqual(childNodes.length, instances.length, 'Unexpected child count')
5158

5259
childNodes.forEach(node =>
5360
assert.ok(node instanceof Ec2InstanceNode, 'Expected child node to be Ec2InstanceNode')
@@ -64,11 +71,18 @@ describe('ec2ParentNode', function () {
6471

6572
it('sorts child nodes', async function () {
6673
const sortedText = ['aa', 'ab', 'bb', 'bc', 'cc', 'cd']
67-
instanceNames = ['ab', 'bb', 'bc', 'aa', 'cc', 'cd']
74+
instances = [
75+
{ name: 'ab', InstanceId: '0' },
76+
{ name: 'bb', InstanceId: '1' },
77+
{ name: 'bc', InstanceId: '2' },
78+
{ name: 'aa', InstanceId: '3' },
79+
{ name: 'cc', InstanceId: '4' },
80+
{ name: 'cd', InstanceId: '5' },
81+
]
6882

6983
const childNodes = await testNode.getChildren()
7084

71-
const actualChildOrder = childNodes.map(node => node.label)
85+
const actualChildOrder = childNodes.map(node => (node instanceof Ec2InstanceNode ? node.name : undefined))
7286
assert.deepStrictEqual(actualChildOrder, sortedText, 'Unexpected child sort order')
7387
})
7488

@@ -79,4 +93,15 @@ describe('ec2ParentNode', function () {
7993
const node = new Ec2ParentNode(testRegion, testPartition, client)
8094
assertNodeListOnlyHasErrorNode(await node.getChildren())
8195
})
96+
97+
it('is able to handle children with duplicate names', async function () {
98+
instances = [
99+
{ name: 'firstOne', InstanceId: '0' },
100+
{ name: 'secondOne', InstanceId: '1' },
101+
{ name: 'firstOne', InstanceId: '2' },
102+
]
103+
104+
const childNodes = await testNode.getChildren()
105+
assert.strictEqual(childNodes.length, instances.length, 'Unexpected child count')
106+
})
82107
})

0 commit comments

Comments
 (0)