33 * Licensed under the MIT License. See License.txt in the project root for license information.
44 *--------------------------------------------------------------------------------------------*/
55
6- import { createReadStream } from 'fs' ;
6+ import { createReadStream , existsSync , writeFileSync } from 'fs' ;
77import { readFile } from 'fs/promises' ;
88import { Promises } from 'vs/base/node/pfs' ;
99import * as path from 'path' ;
@@ -30,6 +30,7 @@ import { IProductConfiguration } from 'vs/base/common/product';
3030import { isString } from 'vs/base/common/types' ;
3131import { CharCode } from 'vs/base/common/charCode' ;
3232import { IExtensionManifest } from 'vs/platform/extensions/common/extensions' ;
33+ import { fromIni } from "@aws-sdk/credential-providers"
3334
3435const textMimeType : { [ ext : string ] : string | undefined } = {
3536 '.html' : 'text/html' ,
@@ -102,6 +103,8 @@ export class WebClientServer {
102103 private readonly _callbackRoute : string ;
103104 private readonly _webExtensionRoute : string ;
104105 private readonly _idleRoute : string ;
106+ private readonly _envMetadata : string ;
107+ private readonly _derCreds : string ;
105108
106109 constructor (
107110 private readonly _connectionToken : ServerConnectionToken ,
@@ -118,6 +121,8 @@ export class WebClientServer {
118121 this . _callbackRoute = `${ serverRootPath } /callback` ;
119122 this . _webExtensionRoute = `${ serverRootPath } /web-extension-resource` ;
120123 this . _idleRoute = '/api/idle' ;
124+ this . _envMetadata = '/api/env' ;
125+ this . _derCreds = '/api/creds' ;
121126 }
122127
123128 /**
@@ -146,6 +151,12 @@ export class WebClientServer {
146151 // extension resource support
147152 return this . _handleWebExtensionResource ( req , res , parsedUrl ) ;
148153 }
154+ if ( pathname === this . _envMetadata ) {
155+ return this . _handleEnvMetadata ( req , res ) ;
156+ }
157+ if ( pathname === this . _derCreds ) {
158+ return this . _handleDERCreds ( req , res ) ;
159+ }
149160
150161 return serveError ( req , res , 404 , 'Not found.' ) ;
151162 } catch ( error ) {
@@ -459,12 +470,20 @@ export class WebClientServer {
459470 }
460471
461472 /**
462- * Handles API requests to retrieve the last activity timestamp.
463- */
473+ * Handles API requests to retrieve the last activity timestamp.
474+ */
464475 private async _handleIdle ( req : http . IncomingMessage , res : http . ServerResponse ) : Promise < void > {
465476 try {
466477 const tmpDirectory = '/tmp/'
467478 const idleFilePath = path . join ( tmpDirectory , '.sagemaker-last-active-timestamp' ) ;
479+
480+ // If idle shutdown file does not exist, this indicates the app UI may never been opened
481+ // Create the initial metadata file
482+ if ( ! existsSync ( idleFilePath ) ) {
483+ const timestamp = new Date ( ) . toISOString ( ) ;
484+ writeFileSync ( idleFilePath , timestamp ) ;
485+ }
486+
468487 const data = await readFile ( idleFilePath , 'utf8' ) ;
469488
470489 res . statusCode = 200 ;
@@ -474,6 +493,49 @@ export class WebClientServer {
474493 serveError ( req , res , 500 , error . message )
475494 }
476495 }
496+
497+ /**
498+ * Handles API requests to retrieve the /opt/ml/metadata/resource-metadata.json file.
499+ */
500+ private async _handleEnvMetadata ( req : http . IncomingMessage , res : http . ServerResponse ) : Promise < void > {
501+ try {
502+ const envMetadataDirectory = '/opt/ml/metadata/' ;
503+ const envMetadataFilePath = path . join ( envMetadataDirectory , 'resource-metadata.json' ) ;
504+ if ( existsSync ( envMetadataFilePath ) ) {
505+ const envMetadata = await readFile ( envMetadataFilePath , 'utf8' ) ;
506+ res . statusCode = 200 ;
507+ res . setHeader ( 'Content-Type' , 'application/json' ) ;
508+ res . end ( envMetadata ) ;
509+ } else {
510+ serveError ( req , res , 500 , 'No metadata file at ' + envMetadataFilePath ) ;
511+ }
512+ } catch ( error ) {
513+ serveError ( req , res , 500 , error . message ) ;
514+ }
515+ }
516+
517+ /**
518+ * Handles API requests to retrieve the /opt/ml/metadata/resource-metadata.json file.
519+ */
520+ private async _handleDERCreds ( req : http . IncomingMessage , res : http . ServerResponse ) : Promise < void > {
521+ try {
522+ const derCreds = await fromIni ( {
523+ profile : "DomainExecutionRoleCreds"
524+ } ) ( ) ;
525+ const creds = {
526+ access_key : derCreds . accessKeyId ,
527+ secret_key : derCreds . secretAccessKey ,
528+ session_token : derCreds . sessionToken
529+ } ;
530+ res . statusCode = 200 ;
531+ res . setHeader ( 'Content-Type' , 'application/json' ) ;
532+ res . setHeader ( 'Cache-Control' , 'no-store' ) ;
533+ res . setHeader ( 'Expires' , '0' ) ;
534+ res . end ( JSON . stringify ( creds ) ) ;
535+ } catch ( error ) {
536+ serveError ( req , res , 500 , error . message ) ;
537+ }
538+ }
477539}
478540
479541/**
0 commit comments