Skip to content

Commit bdb5d6c

Browse files
Merge master into feature/console-session-profile
2 parents ae29517 + 151f9f0 commit bdb5d6c

File tree

4 files changed

+172
-58
lines changed

4 files changed

+172
-58
lines changed

packages/core/src/lambda/vue/remoteInvoke/invokeLambda.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -284,12 +284,10 @@ export class RemoteInvokeWebview extends VueWebview {
284284
await telemetry.lambda_invokeRemote.run(async (span) => {
285285
try {
286286
let funcResponse
287-
const snapStartDisabled =
288-
!this.data.LambdaFunctionNode?.configuration.SnapStart &&
289-
this.data.LambdaFunctionNode?.configuration.State !== 'Active'
287+
const isLMI = (this.data.LambdaFunctionNode?.configuration as any)?.CapacityProviderConfig
290288
if (remoteDebugEnabled) {
291289
funcResponse = await this.clientDebug.invoke(this.data.FunctionArn, input, qualifier)
292-
} else if (snapStartDisabled) {
290+
} else if (isLMI) {
293291
funcResponse = await this.client.invoke(this.data.FunctionArn, input, qualifier, 'None')
294292
} else {
295293
funcResponse = await this.client.invoke(this.data.FunctionArn, input, qualifier, 'Tail')
@@ -300,7 +298,7 @@ export class RemoteInvokeWebview extends VueWebview {
300298
const payload = decodedPayload || JSON.stringify({})
301299

302300
this.channel.appendLine(`Invocation result for ${this.data.FunctionArn}`)
303-
if (!snapStartDisabled) {
301+
if (!isLMI) {
304302
this.channel.appendLine('Logs:')
305303
this.channel.appendLine(logs)
306304
this.channel.appendLine('')

packages/core/src/lambda/vue/remoteInvoke/remoteInvoke.vue

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,7 @@
5757
:disabled="
5858
!initialData.runtimeSupportsRemoteDebug ||
5959
!initialData.remoteDebugLayer ||
60-
(!initialData.LambdaFunctionNode?.configuration.SnapStart &&
61-
initialData.LambdaFunctionNode?.configuration.State !== 'Active')
60+
(initialData.LambdaFunctionNode?.configuration as any)?.CapacityProviderConfig
6261
"
6362
class="remote-debug-checkbox"
6463
/>
@@ -95,13 +94,10 @@
9594
Region {{ initialData.FunctionRegion }} doesn't support remote debugging yet
9695
</info>
9796
<info
98-
v-else-if="
99-
!initialData.LambdaFunctionNode?.configuration.SnapStart &&
100-
initialData.LambdaFunctionNode?.configuration.State !== 'Active'
101-
"
97+
v-else-if="(initialData.LambdaFunctionNode?.configuration as any)?.CapacityProviderConfig"
10298
style="color: var(--vscode-errorForeground)"
10399
>
104-
Doesn't support remote debugging yet
100+
Lambda Managed Instances Function doesn't support remote debugging yet
105101
</info>
106102
</div>
107103
</div>

packages/core/src/test/lambda/remoteDebugging/ldkClient.test.ts

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,111 @@ import {
2424
TunnelStatus,
2525
} from '@aws-sdk/client-iotsecuretunneling'
2626
import { AwsStub, mockClient } from 'aws-sdk-client-mock'
27+
import * as http from 'http'
28+
import { AWSClientBuilderV3 } from '../../../shared/awsClientBuilderV3'
29+
import { FakeAwsContext } from '../../utilities/fakeAwsContext'
30+
import { Any } from '../../../shared/utilities/typeConstructors'
31+
32+
describe('Remote Debugging User-Agent test', () => {
33+
let sandbox: sinon.SinonSandbox
34+
let ldkClient: LdkClient
35+
let mockServer: http.Server
36+
let capturedHeaders: http.IncomingHttpHeaders | undefined
37+
let sdkBuilderTmp: Any
38+
let mockLocalProxy: any
39+
40+
before(async () => {
41+
sdkBuilderTmp = globals.sdkClientBuilderV3
42+
43+
mockServer = http.createServer((req, res) => {
44+
capturedHeaders = req.headers
45+
res.writeHead(200, { 'Content-Type': 'application/json' })
46+
res.end()
47+
})
48+
49+
// Start the mock server
50+
await new Promise<void>((resolve) => {
51+
mockServer.listen(0, '127.0.0.1', () => {
52+
resolve()
53+
})
54+
})
55+
56+
const port = (mockServer.address() as any).port
57+
globals.sdkClientBuilderV3 = new AWSClientBuilderV3(
58+
new FakeAwsContext({
59+
contextCredentials: {
60+
endpointUrl: `http://127.0.0.1:${port}`,
61+
credentials: undefined as any,
62+
credentialsId: '',
63+
},
64+
})
65+
)
66+
})
67+
68+
beforeEach(() => {
69+
sandbox = sinon.createSandbox()
70+
sandbox.stub(telemetryUtil, 'getClientId').returns('test-client-id')
71+
capturedHeaders = undefined
72+
// Mock LocalProxy
73+
mockLocalProxy = {
74+
start: sandbox.stub(),
75+
stop: sandbox.stub(),
76+
}
77+
sandbox.stub(LocalProxy.prototype, 'start').callsFake(mockLocalProxy.start)
78+
sandbox.stub(LocalProxy.prototype, 'stop').callsFake(mockLocalProxy.stop)
79+
ldkClient = LdkClient.instance
80+
;(ldkClient as any).localProxy = mockLocalProxy
81+
ldkClient.dispose()
82+
})
83+
84+
afterEach(() => {
85+
sandbox.restore()
86+
})
87+
88+
after(async () => {
89+
globals.sdkClientBuilderV3 = sdkBuilderTmp
90+
// Close the server
91+
mockServer.close()
92+
})
93+
94+
for (const scenario of ['Lambda', 'IoT']) {
95+
it(`should send ${scenario} request with correct User-Agent header to mock server`, async () => {
96+
try {
97+
switch (scenario) {
98+
case 'Lambda':
99+
await ldkClient.getFunctionDetail('arn:aws:lambda:us-east-1:123456789012:function:testFunction')
100+
break
101+
case 'IoT':
102+
await ldkClient.createOrReuseTunnel('us-east-1')
103+
break
104+
}
105+
} catch (e) {
106+
// Ignore errors from the mock response, we just want to capture headers
107+
}
108+
109+
// Verify the User-Agent header was sent correctly
110+
assert(capturedHeaders, 'Should have captured request headers')
111+
const userAgent = capturedHeaders!['user-agent'] || capturedHeaders!['User-Agent']
112+
assert(userAgent, 'Should have User-Agent header')
113+
114+
// The User-Agent should contain our custom user agent pairs
115+
assert(
116+
userAgent.includes('LAMBDA-DEBUG/1.0.0'),
117+
`User-Agent should include LAMBDA-DEBUG/1.0.0, got: ${userAgent}`
118+
)
119+
// Check for presence of other user agent components without checking specific values
120+
assert(
121+
userAgent.includes('AWS-Toolkit-For-VSCode/'),
122+
`User-Agent should include AWS-Toolkit-For-VSCode/, got: ${userAgent}`
123+
)
124+
assert(
125+
userAgent.includes('Visual-Studio-Code'),
126+
`User-Agent should include Visual-Studio-Code, got: ${userAgent}`
127+
)
128+
assert(userAgent.includes('ClientId/'), `User-Agent should include ClientId/, got: ${userAgent}`)
129+
})
130+
}
131+
})
27132

28133
describe('LdkClient', () => {
29134
let sandbox: sinon.SinonSandbox

packages/core/src/test/lambda/vue/remoteInvoke/remoteInvoke.test.ts

Lines changed: 61 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -14,59 +14,74 @@ import { InvocationResponse } from '@aws-sdk/client-lambda'
1414

1515
describe('RemoteInvokeWebview', function () {
1616
let client: SinonStubbedInstance<LambdaClient>
17-
let remoteInvokeWebview: RemoteInvokeWebview
1817
let outputChannel: vscode.OutputChannel
19-
let mockData: any
20-
before(async () => {
21-
client = createStubInstance(DefaultLambdaClient)
18+
const mockData = {
19+
FunctionArn: 'arn:aws:lambda:us-west-2:123456789012:function:my-function',
20+
} as any
21+
const mockDataLMI = {
22+
FunctionArn: 'arn:aws:lambda:us-west-2:123456789012:function:my-function',
23+
LambdaFunctionNode: {
24+
configuration: {
25+
CapacityProviderConfig: {
26+
blah: 'blah',
27+
},
28+
},
29+
},
30+
} as any
31+
const input = '{"key": "value"}'
32+
const mockResponse = {
33+
LogResult: Buffer.from('Test log').toString('base64'),
34+
Payload: new TextEncoder().encode('{"result": "success"}'),
35+
} satisfies InvocationResponse
36+
37+
before(() => {
2238
outputChannel = {
2339
appendLine: (line: string) => {},
2440
show: () => {},
2541
} as vscode.OutputChannel
26-
mockData = {
27-
FunctionArn: 'arn:aws:lambda:us-west-2:123456789012:function:my-function',
28-
}
29-
remoteInvokeWebview = new RemoteInvokeWebview(outputChannel, client, client, mockData)
3042
})
31-
describe('Invoke Remote Lambda Function with Payload', () => {
32-
it('should invoke with a simple payload', async function () {
33-
const input = '{"key": "value"}'
34-
const mockResponse = {
35-
LogResult: Buffer.from('Test log').toString('base64'),
36-
Payload: new TextEncoder().encode('{"result": "success"}'),
37-
} satisfies InvocationResponse
38-
client.invoke.resolves(mockResponse)
39-
await remoteInvokeWebview.invokeLambda(input)
40-
sinon.assert.calledOnce(client.invoke)
41-
sinon.assert.calledWith(client.invoke, mockData.FunctionArn, input)
42-
})
43+
beforeEach(() => {
44+
client = createStubInstance(DefaultLambdaClient)
45+
})
46+
afterEach(() => {
47+
sinon.restore()
48+
})
49+
it('should invoke with a simple payload', async function () {
50+
const remoteInvokeWebview = new RemoteInvokeWebview(outputChannel, client, client, mockData)
51+
client.invoke.resolves(mockResponse)
52+
await remoteInvokeWebview.invokeLambda(input)
53+
sinon.assert.calledOnce(client.invoke)
54+
sinon.assert.calledWith(client.invoke, mockData.FunctionArn, input, undefined, 'Tail')
4355
})
44-
describe('Invoke Remote Lambda Function with Saved Events Payload', () => {
45-
const mockEvent = {
46-
name: 'TestEvent',
47-
arn: 'arn:aws:lambda:us-west-2:123456789012:function:myFunction',
48-
region: 'us-west-2',
49-
}
50-
const expectedParams = {
51-
name: mockEvent.name,
52-
operation: TestEventsOperation.Get,
53-
functionArn: mockEvent.arn,
54-
region: mockEvent.region,
55-
}
56-
const mockResponse = 'true'
57-
let runSamCliRemoteTestEventsStub: sinon.SinonStub
58-
beforeEach(() => {
59-
runSamCliRemoteTestEventsStub = sinon.stub(samCliRemoteTestEvent, 'runSamCliRemoteTestEvents')
60-
})
61-
afterEach(() => {
62-
sinon.restore()
63-
})
64-
it('should get saved event and invoke with it', async function () {
65-
runSamCliRemoteTestEventsStub.resolves(mockResponse)
66-
await remoteInvokeWebview.getRemoteTestEvents(mockEvent)
6756

68-
sinon.assert.calledOnce(runSamCliRemoteTestEventsStub)
69-
sinon.assert.calledWith(runSamCliRemoteTestEventsStub, expectedParams)
70-
})
57+
it('should invoke with no tail in LMI', async function () {
58+
const remoteInvokeWebview = new RemoteInvokeWebview(outputChannel, client, client, mockDataLMI)
59+
client.invoke.resolves(mockResponse)
60+
await remoteInvokeWebview.invokeLambda(input)
61+
sinon.assert.calledOnce(client.invoke)
62+
sinon.assert.calledWith(client.invoke, mockData.FunctionArn, input, undefined, 'None')
63+
})
64+
65+
const mockEvent = {
66+
name: 'TestEvent',
67+
arn: 'arn:aws:lambda:us-west-2:123456789012:function:myFunction',
68+
region: 'us-west-2',
69+
}
70+
const expectedParams = {
71+
name: mockEvent.name,
72+
operation: TestEventsOperation.Get,
73+
functionArn: mockEvent.arn,
74+
region: mockEvent.region,
75+
}
76+
const mockEventResponse = 'true'
77+
78+
it('should get saved event and invoke with it', async function () {
79+
const remoteInvokeWebview = new RemoteInvokeWebview(outputChannel, client, client, mockData)
80+
const runSamCliRemoteTestEventsStub = sinon.stub(samCliRemoteTestEvent, 'runSamCliRemoteTestEvents')
81+
runSamCliRemoteTestEventsStub.resolves(mockEventResponse)
82+
await remoteInvokeWebview.getRemoteTestEvents(mockEvent)
83+
84+
sinon.assert.calledOnce(runSamCliRemoteTestEventsStub)
85+
sinon.assert.calledWith(runSamCliRemoteTestEventsStub, expectedParams)
7186
})
7287
})

0 commit comments

Comments
 (0)