Skip to content

Commit c96ee48

Browse files
committed
merge in master
2 parents 0131b5b + b2c1fdf commit c96ee48

File tree

11 files changed

+190
-49
lines changed

11 files changed

+190
-49
lines changed

.editorconfig

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,9 @@ insert_final_newline = true
99
indent_size = 4
1010
indent_style = space
1111

12-
[*.{js,py}]
12+
[*.{js,ts,py}]
1313
charset = utf-8
14-
15-
# 4 space indentation
16-
[*.py]
17-
indent_style = space
14+
max_line_length = 100
1815

1916
[Makefile]
2017
indent_style = tab

package.json

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1120,6 +1120,10 @@
11201120
"command": "aws.ec2.openTerminal",
11211121
"when": "aws.isDevMode"
11221122
},
1123+
{
1124+
"command": "aws.ec2.openTerminal",
1125+
"when": "aws.isDevMode"
1126+
},
11231127
{
11241128
"command": "aws.ec2.startInstance",
11251129
"when": "aws.isDevMode"
@@ -1349,7 +1353,42 @@
13491353
{
13501354
"command": "aws.ec2.rebootInstance",
13511355
"group": "0@1",
1352-
"when": "viewItem == awsEc2Node"
1356+
"when": "viewItem =~ /^(awsEc2(Parent|Running)Node)$/"
1357+
},
1358+
{
1359+
"command": "aws.ec2.openTerminal",
1360+
"group": "inline@1",
1361+
"when": "viewItem =~ /^(awsEc2(Parent|Running)Node)$/"
1362+
},
1363+
{
1364+
"command": "aws.ec2.startInstance",
1365+
"group": "0@1",
1366+
"when": "viewItem =~ /^(awsEc2(Stopped|Pending)Node)$/"
1367+
},
1368+
{
1369+
"command": "aws.ec2.startInstance",
1370+
"group": "inline@1",
1371+
"when": "viewItem =~ /^(awsEc2(Stopped|Pending)Node)$/"
1372+
},
1373+
{
1374+
"command": "aws.ec2.stopInstance",
1375+
"group": "0@1",
1376+
"when": "viewItem =~ /^(awsEc2(Running|Pending)Node)$/"
1377+
},
1378+
{
1379+
"command": "aws.ec2.stopInstance",
1380+
"group": "inline@1",
1381+
"when": "viewItem =~ /^(awsEc2(Running|Pending)Node)$/"
1382+
},
1383+
{
1384+
"command": "aws.ec2.rebootInstance",
1385+
"group": "0@1",
1386+
"when": "viewItem =~ /^(awsEc2RunningNode)$/"
1387+
},
1388+
{
1389+
"command": "aws.ec2.rebootInstance",
1390+
"group": "inline@1",
1391+
"when": "viewItem =~ /^(awsEc2RunningNode)$/"
13531392
},
13541393
{
13551394
"command": "aws.ecr.createRepository",
@@ -2122,6 +2161,7 @@
21222161
{
21232162
"command": "aws.ec2.startInstance",
21242163
"title": "%AWS.command.ec2.startInstance%",
2164+
"icon": "$(debug-start)",
21252165
"category": "%AWS.title%",
21262166
"cloud9": {
21272167
"cn": {
@@ -2132,6 +2172,7 @@
21322172
{
21332173
"command": "aws.ec2.stopInstance",
21342174
"title": "%AWS.command.ec2.stopInstance%",
2175+
"icon": "$(debug-stop)",
21352176
"category": "%AWS.title%",
21362177
"cloud9": {
21372178
"cn": {
@@ -2142,6 +2183,7 @@
21422183
{
21432184
"command": "aws.ec2.rebootInstance",
21442185
"title": "%AWS.command.ec2.rebootInstance%",
2186+
"icon": "$(debug-restart)",
21452187
"category": "%AWS.title%",
21462188
"cloud9": {
21472189
"cn": {

src/ec2/activation.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,14 @@ import { telemetry } from '../shared/telemetry/telemetry'
88
import { Ec2InstanceNode } from './explorer/ec2InstanceNode'
99
import { copyTextCommand } from '../awsexplorer/commands/copyText'
1010
import { Ec2Node } from './explorer/ec2ParentNode'
11-
import { openRemoteConnection, openTerminal, rebootInstance, startInstance, stopInstance } from './commands'
11+
import {
12+
openRemoteConnection,
13+
openTerminal,
14+
rebootInstance,
15+
startInstance,
16+
stopInstance,
17+
refreshExplorer,
18+
} from './commands'
1219

1320
export async function activate(ctx: ExtContext): Promise<void> {
1421
ctx.extensionContext.subscriptions.push(
@@ -29,14 +36,17 @@ export async function activate(ctx: ExtContext): Promise<void> {
2936

3037
Commands.register('aws.ec2.startInstance', async (node?: Ec2Node) => {
3138
await startInstance(node)
39+
refreshExplorer(node)
3240
}),
3341

3442
Commands.register('aws.ec2.stopInstance', async (node?: Ec2Node) => {
3543
await stopInstance(node)
44+
refreshExplorer(node)
3645
}),
3746

3847
Commands.register('aws.ec2.rebootInstance', async (node?: Ec2Node) => {
3948
await rebootInstance(node)
49+
refreshExplorer(node)
4050
})
4151
)
4252
}

src/ec2/commands.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,16 @@
66
import { Ec2InstanceNode } from './explorer/ec2InstanceNode'
77
import { Ec2Node } from './explorer/ec2ParentNode'
88
import { Ec2ConnectionManager } from './model'
9-
import { Ec2Prompter, instanceFilter } from './prompter'
10-
import { Ec2Selection } from './prompter'
11-
import { Ec2Client, Ec2Instance } from '../shared/clients/ec2Client'
9+
import { Ec2Prompter, instanceFilter, Ec2Selection } from './prompter'
10+
import { Ec2Instance, Ec2Client } from '../shared/clients/ec2Client'
1211
import { copyToClipboard } from '../shared/utilities/messages'
1312

13+
export function refreshExplorer(node?: Ec2Node) {
14+
if (node) {
15+
node instanceof Ec2InstanceNode ? node.parent.refreshNode() : node.refreshNode()
16+
}
17+
}
18+
1419
export async function openTerminal(node?: Ec2Node) {
1520
const selection = await getSelection(node)
1621

src/ec2/explorer/ec2InstanceNode.ts

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,58 @@
22
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
33
* SPDX-License-Identifier: Apache-2.0
44
*/
5-
6-
import { getNameOfInstance } from '../../shared/clients/ec2Client'
5+
import * as vscode from 'vscode'
6+
import { Ec2Client, 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'
1010
import globals from '../../shared/extensionGlobals'
11+
import { getIconCode } from '../utils'
1112
import { Ec2Selection } from '../prompter'
13+
import { Ec2ParentNode } from './ec2ParentNode'
14+
15+
export const Ec2InstanceRunningContext = 'awsEc2RunningNode'
16+
export const Ec2InstanceStoppedContext = 'awsEc2StoppedNode'
17+
export const Ec2InstancePendingContext = 'awsEc2PendingNode'
18+
19+
type Ec2InstanceNodeContext = 'awsEc2RunningNode' | 'awsEc2StoppedNode' | 'awsEc2PendingNode'
1220

1321
export class Ec2InstanceNode extends AWSTreeNodeBase implements AWSResourceNode {
1422
public constructor(
23+
public readonly parent: Ec2ParentNode,
24+
public readonly client: Ec2Client,
1525
public override readonly regionCode: string,
1626
private readonly partitionId: string,
17-
private instance: Ec2Instance,
18-
public override readonly contextValue: string
27+
protected instance: Ec2Instance
1928
) {
2029
super('')
21-
this.update(instance)
30+
this.updateInstance(instance)
2231
this.id = this.InstanceId
2332
}
2433

25-
public update(newInstance: Ec2Instance) {
34+
public updateInstance(newInstance: Ec2Instance) {
2635
this.setInstance(newInstance)
2736
this.label = `${this.name} (${this.InstanceId})`
28-
this.tooltip = `${this.name}\n${this.InstanceId}\n${this.arn}`
37+
this.contextValue = this.getContext()
38+
this.iconPath = new vscode.ThemeIcon(getIconCode(this.instance))
39+
this.tooltip = `${this.name}\n${this.InstanceId}\n${this.instance.status}\n${this.arn}`
40+
}
41+
42+
public async updateStatus() {
43+
const newStatus = await this.client.getInstanceStatus(this.InstanceId)
44+
this.updateInstance({ ...this.instance, status: newStatus })
45+
}
46+
47+
private getContext(): Ec2InstanceNodeContext {
48+
if (this.instance.status == 'running') {
49+
return Ec2InstanceRunningContext
50+
}
51+
52+
if (this.instance.status == 'stopped') {
53+
return Ec2InstanceStoppedContext
54+
}
55+
56+
return Ec2InstancePendingContext
2957
}
3058

3159
public setInstance(newInstance: Ec2Instance) {

src/ec2/explorer/ec2ParentNode.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,19 @@ import { PlaceholderNode } from '../../shared/treeview/nodes/placeholderNode'
99
import { Ec2InstanceNode } from './ec2InstanceNode'
1010
import { Ec2Client } from '../../shared/clients/ec2Client'
1111
import { updateInPlace } from '../../shared/utilities/collectionUtils'
12+
import { Commands } from '../../shared/vscode/commands'
1213

13-
export const contextValueEc2 = 'awsEc2Node'
14+
export const parentContextValue = 'awsEc2ParentNode'
1415
export type Ec2Node = Ec2InstanceNode | Ec2ParentNode
1516

1617
export class Ec2ParentNode extends AWSTreeNodeBase {
1718
protected readonly placeHolderMessage = '[No EC2 Instances Found]'
18-
protected readonly ec2InstanceNodes: Map<string, Ec2InstanceNode>
19-
public override readonly contextValue: string = contextValueEc2
19+
protected ec2InstanceNodes: Map<string, Ec2InstanceNode>
20+
public override readonly contextValue: string = parentContextValue
2021

2122
public constructor(
2223
public override readonly regionCode: string,
23-
private readonly partitionId: string,
24+
public readonly partitionId: string,
2425
protected readonly ec2Client: Ec2Client
2526
) {
2627
super('EC2', vscode.TreeItemCollapsibleState.Collapsed)
@@ -44,8 +45,17 @@ export class Ec2ParentNode extends AWSTreeNodeBase {
4445
updateInPlace(
4546
this.ec2InstanceNodes,
4647
ec2Instances.keys(),
47-
key => this.ec2InstanceNodes.get(key)!.update(ec2Instances.get(key)!),
48-
key => new Ec2InstanceNode(this.regionCode, this.partitionId, ec2Instances.get(key)!, contextValueEc2)
48+
key => this.ec2InstanceNodes.get(key)!.updateInstance(ec2Instances.get(key)!),
49+
key => new Ec2InstanceNode(this, this.ec2Client, this.regionCode, this.partitionId, ec2Instances.get(key)!)
4950
)
5051
}
52+
53+
public async clearChildren() {
54+
this.ec2InstanceNodes = new Map<string, Ec2InstanceNode>()
55+
}
56+
57+
public async refreshNode(): Promise<void> {
58+
this.clearChildren()
59+
Commands.vscode().execute('aws.refreshAwsExplorerNode', this)
60+
}
5161
}

src/ec2/prompter.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { Ec2Client, Ec2Instance } from '../shared/clients/ec2Client'
99
import { isValidResponse } from '../shared/wizards/wizard'
1010
import { CancellationError } from '../shared/utilities/timeoutUtils'
1111
import { AsyncCollection } from '../shared/utilities/asyncCollection'
12+
import { getIconCode } from './utils'
1213

1314
export type instanceFilter = (instance: Ec2Instance) => boolean
1415
export interface Ec2Selection {
@@ -20,8 +21,9 @@ export class Ec2Prompter {
2021
public constructor(protected filter?: instanceFilter) {}
2122

2223
protected static asQuickPickItem(instance: Ec2Instance): DataQuickPickItem<string> {
24+
const icon = `$(${getIconCode(instance)})`
2325
return {
24-
label: '$(terminal) \t' + (instance.name ?? '(no name)'),
26+
label: `${icon} \t ${instance.name ?? '(no name)'}`,
2527
detail: instance.InstanceId,
2628
data: instance.InstanceId,
2729
}

src/ec2/utils.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*!
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import { Ec2Instance } from '../shared/clients/ec2Client'
7+
8+
export function getIconCode(instance: Ec2Instance) {
9+
if (instance.status === 'running') {
10+
return 'check'
11+
}
12+
13+
if (instance.status === 'stopped') {
14+
return 'stop'
15+
}
16+
17+
return 'loading~spin'
18+
}

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

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,20 @@
44
*/
55

66
import * as assert from 'assert'
7-
import { Ec2InstanceNode } from '../../../ec2/explorer/ec2InstanceNode'
8-
import { Ec2Instance, getNameOfInstance } from '../../../shared/clients/ec2Client'
9-
import { contextValueEc2 } from '../../../ec2/explorer/ec2ParentNode'
7+
import {
8+
Ec2InstanceNode,
9+
Ec2InstancePendingContext,
10+
Ec2InstanceRunningContext,
11+
Ec2InstanceStoppedContext,
12+
} from '../../../ec2/explorer/ec2InstanceNode'
13+
import { Ec2Client, Ec2Instance, getNameOfInstance } from '../../../shared/clients/ec2Client'
14+
import { Ec2ParentNode } from '../../../ec2/explorer/ec2ParentNode'
1015

1116
describe('ec2InstanceNode', function () {
1217
let testNode: Ec2InstanceNode
1318
let testInstance: Ec2Instance
19+
const testRegion = 'testRegion'
20+
const testPartition = 'testPartition'
1421

1522
before(function () {
1623
testInstance = {
@@ -21,9 +28,15 @@ describe('ec2InstanceNode', function () {
2128
Value: 'testName',
2229
},
2330
],
31+
status: 'running',
2432
}
33+
const testClient = new Ec2Client('')
34+
const testParentNode = new Ec2ParentNode(testRegion, testPartition, testClient)
35+
testNode = new Ec2InstanceNode(testParentNode, testClient, 'testRegion', 'testPartition', testInstance)
36+
})
2537

26-
testNode = new Ec2InstanceNode('testRegion', 'testPartition', testInstance, contextValueEc2)
38+
this.beforeEach(function () {
39+
testNode.updateInstance(testInstance)
2740
})
2841

2942
it('instantiates without issue', async function () {
@@ -42,13 +55,37 @@ describe('ec2InstanceNode', function () {
4255
assert.strictEqual(testNode.name, getNameOfInstance(testInstance))
4356
})
4457

45-
it('initializes the tooltip', async function () {
46-
assert.strictEqual(testNode.tooltip, `${testNode.name}\n${testNode.InstanceId}\n${testNode.arn}`)
47-
})
48-
4958
it('has no children', async function () {
5059
const childNodes = await testNode.getChildren()
5160
assert.ok(childNodes)
5261
assert.strictEqual(childNodes.length, 0, 'Expected node to have no children')
5362
})
63+
64+
it('has an EC2ParentNode as parent', async function () {
65+
assert.ok(testNode.parent instanceof Ec2ParentNode)
66+
})
67+
68+
it('intializes the client', async function () {
69+
assert.ok(testNode.client instanceof Ec2Client)
70+
})
71+
72+
it('sets context value based on status', async function () {
73+
const stoppedInstance = { ...testInstance, status: 'stopped' }
74+
testNode.updateInstance(stoppedInstance)
75+
assert.strictEqual(testNode.contextValue, Ec2InstanceStoppedContext)
76+
77+
const runningInstance = { ...testInstance, status: 'running' }
78+
testNode.updateInstance(runningInstance)
79+
assert.strictEqual(testNode.contextValue, Ec2InstanceRunningContext)
80+
81+
const pendingInstance = { ...testInstance, status: 'pending' }
82+
testNode.updateInstance(pendingInstance)
83+
assert.strictEqual(testNode.contextValue, Ec2InstancePendingContext)
84+
})
85+
86+
it('updates label with new instance', async function () {
87+
const newIdInstance = { ...testInstance, InstanceId: 'testId2' }
88+
testNode.updateInstance(newIdInstance)
89+
assert.strictEqual(testNode.label, `${getNameOfInstance(newIdInstance)} (${newIdInstance.InstanceId})`)
90+
})
5491
})

0 commit comments

Comments
 (0)