Skip to content

Commit 04753ba

Browse files
Merge master into feature/emr
2 parents 8398a86 + b3b80ef commit 04753ba

File tree

86 files changed

+11824
-918
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

86 files changed

+11824
-918
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ src.gen/*
3434
**/src/auth/sso/oidcclientpkce.d.ts
3535
**/src/sagemakerunifiedstudio/shared/client/gluecatalogapi.d.ts
3636
**/src/sagemakerunifiedstudio/shared/client/sqlworkbench.d.ts
37+
**/src/sagemakerunifiedstudio/shared/client/datazonecustomclient.d.ts
3738

3839
# Generated by tests
3940
**/src/testFixtures/**/bin

packages/core/package.nls.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@
231231
"AWS.command.s3.uploadFileToParent": "Upload to Parent...",
232232
"AWS.command.smus.switchProject": "Switch Project",
233233
"AWS.command.smus.refreshProject": "Refresh Project",
234+
"AWS.command.smus.refresh": "Refresh",
234235
"AWS.command.smus.signOut": "Sign Out",
235236
"AWS.command.sagemaker.filterSpaces": "Filter Sagemaker Spaces",
236237
"AWS.command.stepFunctions.createStateMachineFromTemplate": "Create a new Step Functions state machine",

packages/core/scripts/build/generateServiceClient.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,10 @@ void (async () => {
249249
serviceJsonPath: 'src/sagemakerunifiedstudio/shared/client/sqlworkbench.json',
250250
serviceName: 'SQLWorkbench',
251251
},
252+
{
253+
serviceJsonPath: 'src/sagemakerunifiedstudio/shared/client/datazonecustomclient.json',
254+
serviceName: 'DataZoneCustomClient',
255+
},
252256
]
253257
await generateServiceClients(serviceClientDefinitions)
254258
})()

packages/core/src/auth/auth.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -598,7 +598,7 @@ export class Auth implements AuthService, ConnectionManager {
598598
}
599599

600600
@withTelemetryContext({ name: 'updateConnectionState', class: authClassName })
601-
private async updateConnectionState(id: Connection['id'], connectionState: ProfileMetadata['connectionState']) {
601+
public async updateConnectionState(id: Connection['id'], connectionState: ProfileMetadata['connectionState']) {
602602
getLogger().info(`auth: Updating connection state of ${id} to ${connectionState}`)
603603

604604
if (connectionState === 'authenticating') {

packages/core/src/awsService/sagemaker/credentialMapping.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { getLogger } from '../../shared/logger/logger'
1515
import { parseArn } from './detached-server/utils'
1616
import { SagemakerUnifiedStudioSpaceNode } from '../../sagemakerunifiedstudio/explorer/nodes/sageMakerUnifiedStudioSpaceNode'
1717
import { SageMakerUnifiedStudioSpacesParentNode } from '../../sagemakerunifiedstudio/explorer/nodes/sageMakerUnifiedStudioSpacesParentNode'
18+
import { isSmusSsoConnection } from '../../sagemakerunifiedstudio/auth/model'
1819

1920
const mappingFileName = '.sagemaker-space-profiles'
2021
const mappingFilePath = path.join(os.homedir(), '.aws', mappingFileName)
@@ -74,10 +75,11 @@ export async function persistLocalCredentials(spaceArn: string): Promise<void> {
7475
export async function persistSmusProjectCreds(spaceArn: string, node: SagemakerUnifiedStudioSpaceNode): Promise<void> {
7576
const nodeParent = node.getParent() as SageMakerUnifiedStudioSpacesParentNode
7677
const authProvider = nodeParent.getAuthProvider()
78+
const activeConnection = authProvider.activeConnection
7779
const projectId = nodeParent.getProjectId()
7880
const projectAuthProvider = await authProvider.getProjectCredentialProvider(projectId)
7981
await projectAuthProvider.getCredentials()
80-
await setSmusSpaceSsoProfile(spaceArn, projectId)
82+
await setSmusSpaceProfile(spaceArn, projectId, isSmusSsoConnection(activeConnection) ? 'sso' : 'iam')
8183
// Trigger SSH credential refresh for the project
8284
projectAuthProvider.startProactiveCredentialRefresh()
8385
}
@@ -177,11 +179,16 @@ export async function setSpaceSsoProfile(
177179
* Sets the SM Space to map to SageMaker Unified Studio Project.
178180
* @param spaceArn - The arn of the SageMaker Unified Studio space.
179181
* @param projectId - The project ID associated with the SageMaker Unified Studio space.
182+
* @param credentialType - The type of credential ('sso' or 'iam').
180183
*/
181-
export async function setSmusSpaceSsoProfile(spaceArn: string, projectId: string): Promise<void> {
184+
export async function setSmusSpaceProfile(
185+
spaceArn: string,
186+
projectId: string,
187+
credentialType: 'iam' | 'sso'
188+
): Promise<void> {
182189
const data = await loadMappings()
183190
data.localCredential ??= {}
184-
data.localCredential[spaceArn] = { type: 'sso', smusProjectId: projectId }
191+
data.localCredential[spaceArn] = { type: credentialType, smusProjectId: projectId }
185192
await saveMappings(data)
186193
}
187194

packages/core/src/awsService/sagemaker/detached-server/credentials.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,25 @@ export async function resolveCredentialsFor(connectionIdentifier: string): Promi
2929

3030
switch (profile.type) {
3131
case 'iam': {
32-
const name = profile.profileName?.split(':')[1]
33-
if (!name) {
34-
throw new Error(`Invalid IAM profile name for "${connectionIdentifier}"`)
32+
if ('profileName' in profile) {
33+
const name = profile.profileName?.split(':')[1]
34+
if (!name) {
35+
throw new Error(`Invalid IAM profile name for "${connectionIdentifier}"`)
36+
}
37+
return fromIni({ profile: name })
38+
} else if ('smusProjectId' in profile) {
39+
const { accessKey, secret, token } = mapping.smusProjects?.[profile.smusProjectId] || {}
40+
if (!accessKey || !secret || !token) {
41+
throw new Error(`Missing ProjectRole credentials for SMUS Space "${connectionIdentifier}"`)
42+
}
43+
return {
44+
accessKeyId: accessKey,
45+
secretAccessKey: secret,
46+
sessionToken: token,
47+
}
48+
} else {
49+
throw new Error(`Missing IAM credentials for "${connectionIdentifier}"`)
3550
}
36-
return fromIni({ profile: name })
3751
}
3852
case 'sso': {
3953
if ('accessKey' in profile && 'secret' in profile && 'token' in profile) {

packages/core/src/awsService/sagemaker/detached-server/errorPage.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,11 @@ export const getVSCodeErrorTitle = (error: SageMakerServiceException): string =>
3232
return ErrorText.StartSession[ExceptionType.DEFAULT].Title
3333
}
3434

35-
export const getVSCodeErrorText = (error: SageMakerServiceException, isSmus?: boolean): string => {
35+
export const getVSCodeErrorText = (
36+
error: SageMakerServiceException,
37+
isSmus?: boolean,
38+
isSmusIamConn?: boolean
39+
): string => {
3640
const exceptionType = error.name as ExceptionType
3741

3842
switch (exceptionType) {
@@ -41,9 +45,12 @@ export const getVSCodeErrorText = (error: SageMakerServiceException, isSmus?: bo
4145
return ErrorText.StartSession[exceptionType].Text.replace('{message}', error.message)
4246
case ExceptionType.EXPIRED_TOKEN:
4347
// Use SMUS-specific message if in SMUS context
44-
return isSmus
45-
? ErrorText.StartSession[ExceptionType.EXPIRED_TOKEN].SmusText
46-
: ErrorText.StartSession[exceptionType].Text
48+
if (isSmus) {
49+
return isSmusIamConn
50+
? ErrorText.StartSession[ExceptionType.EXPIRED_TOKEN].SmusIamText
51+
: ErrorText.StartSession[ExceptionType.EXPIRED_TOKEN].SmusSsoText
52+
}
53+
return ErrorText.StartSession[exceptionType].Text
4754
case ExceptionType.INTERNAL_FAILURE:
4855
case ExceptionType.RESOURCE_LIMIT_EXCEEDED:
4956
case ExceptionType.THROTTLING:
@@ -66,8 +73,10 @@ export const ErrorText = {
6673
[ExceptionType.EXPIRED_TOKEN]: {
6774
Title: 'Authentication expired',
6875
Text: 'Your session has expired. Please refresh your credentials and try again.',
69-
SmusText:
70-
'Your session has expired. This is likely due to network connectivity issues after machine sleep/resume. Please wait 10-30 seconds for automatic credential refresh, then try again. If the issue persists, try reconnecting through AWS Toolkit.',
76+
SmusSsoText:
77+
'Your session has expired. This is likely due to network connectivity issues after machine sleep/resume. Wait 10-30 seconds for automatic credential refresh, then try again. If the issue persists, try reconnecting through AWS Toolkit.',
78+
SmusIamText:
79+
'Your session has expired. Update the credentials associated with the IAM profile or use a valid IAM profile, then try again.',
7180
},
7281
[ExceptionType.INTERNAL_FAILURE]: {
7382
Title: 'Failed to connect remotely to VSCode',

packages/core/src/awsService/sagemaker/detached-server/routes/getSession.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
// Disabled: detached server files cannot import vscode.
77
/* eslint-disable aws-toolkits/no-console-log */
88
import { IncomingMessage, ServerResponse } from 'http'
9-
import { startSagemakerSession, parseArn, isSmusConnection } from '../utils'
9+
import { startSagemakerSession, parseArn, isSmusConnection, isSmusIamConnection } from '../utils'
1010
import { resolveCredentialsFor } from '../credentials'
1111
import url from 'url'
1212
import { SageMakerServiceException } from '@amzn/sagemaker-client'
@@ -35,6 +35,7 @@ export async function handleGetSession(req: IncomingMessage, res: ServerResponse
3535
const { region } = parseArn(connectionIdentifier)
3636
// Detect if this is a SMUS connection for specialized error handling
3737
const isSmus = await isSmusConnection(connectionIdentifier)
38+
const isSmusIamConn = await isSmusIamConnection(connectionIdentifier)
3839

3940
try {
4041
const session = await startSagemakerSession({ region, connectionIdentifier, credentials })
@@ -50,7 +51,7 @@ export async function handleGetSession(req: IncomingMessage, res: ServerResponse
5051
const error = err as SageMakerServiceException
5152
console.error(`Failed to start SageMaker session for ${connectionIdentifier}:`, err)
5253
const errorTitle = getVSCodeErrorTitle(error)
53-
const errorText = getVSCodeErrorText(error, isSmus)
54+
const errorText = getVSCodeErrorText(error, isSmus, isSmusIamConn)
5455
await openErrorPage(errorTitle, errorText)
5556
res.writeHead(500, { 'Content-Type': 'text/plain' })
5657
res.end('Failed to start SageMaker session')

packages/core/src/awsService/sagemaker/detached-server/utils.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,24 @@ export async function isSmusConnection(connectionIdentifier: string): Promise<bo
147147
}
148148
}
149149

150+
/**
151+
* Detects if the connection identifier is using SMUS IAM credentials
152+
* @param connectionIdentifier - The connection identifier to check
153+
* @returns Promise<boolean> - true if SMUS IAM connection, false otherwise
154+
*/
155+
export async function isSmusIamConnection(connectionIdentifier: string): Promise<boolean> {
156+
try {
157+
const mapping = await readMapping()
158+
const profile = mapping.localCredential?.[connectionIdentifier]
159+
160+
// Check if profile exists, has smusProjectId, and type is 'iam'
161+
return profile && 'smusProjectId' in profile && profile.type === 'iam'
162+
} catch (err) {
163+
// If we can't detect it is iam connection, assume not SMUS IAM to avoid breaking existing functionality
164+
return false
165+
}
166+
}
167+
150168
/**
151169
* Writes the mapping to a temp file and atomically renames it to the target path.
152170
* Uses a queue to prevent race conditions when multiple requests try to write simultaneously.

packages/core/src/awsService/sagemaker/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export interface SpaceMappings {
1212
export type LocalCredentialProfile =
1313
| { type: 'iam'; profileName: string }
1414
| { type: 'sso'; accessKey: string; secret: string; token: string }
15-
| { type: 'sso'; smusProjectId: string }
15+
| { type: 'sso' | 'iam'; smusProjectId: string }
1616

1717
export interface DeeplinkSession {
1818
requests: Record<string, SsmConnectionInfo>

0 commit comments

Comments
 (0)