@@ -28,7 +28,7 @@ import { showMessageWithCancel } from '../../shared/utilities/messages'
2828import { SshConfig } from '../../shared/sshConfig'
2929import { SshKeyPair } from './sshKeyPair'
3030import { Ec2SessionTracker } from './remoteSessionManager'
31- import { getEc2SsmEnv , getRemoveLinesCommand } from './utils'
31+ import { getEc2SsmEnv } from './utils'
3232
3333export type Ec2ConnectErrorCode = 'EC2SSMStatus' | 'EC2SSMPermission' | 'EC2SSMConnect' | 'EC2SSMAgentStatus'
3434
@@ -38,6 +38,12 @@ export interface Ec2RemoteEnv extends VscodeRemoteConnection {
3838 ssmSession : SSM . StartSessionResponse
3939}
4040
41+ export type Ec2OS = 'Amazon Linux' | 'Ubuntu' | 'macOS'
42+ interface RemoteUser {
43+ os : Ec2OS
44+ name : string
45+ }
46+
4147export class Ec2Connecter implements vscode . Disposable {
4248 protected ssmClient : SsmClient
4349 protected ec2Client : Ec2Client
@@ -195,20 +201,29 @@ export class Ec2Connecter implements vscode.Disposable {
195201 const remoteEnv = await this . prepareEc2RemoteEnvWithProgress ( selection , remoteUser )
196202
197203 try {
198- await startVscodeRemote ( remoteEnv . SessionProcess , remoteEnv . hostname , '/' , remoteEnv . vscPath , remoteUser )
204+ await startVscodeRemote (
205+ remoteEnv . SessionProcess ,
206+ remoteEnv . hostname ,
207+ '/' ,
208+ remoteEnv . vscPath ,
209+ remoteUser . name
210+ )
199211 } catch ( err ) {
200212 this . throwGeneralConnectionError ( selection , err as Error )
201213 }
202214 }
203215
204- public async prepareEc2RemoteEnvWithProgress ( selection : Ec2Selection , remoteUser : string ) : Promise < Ec2RemoteEnv > {
216+ public async prepareEc2RemoteEnvWithProgress (
217+ selection : Ec2Selection ,
218+ remoteUser : RemoteUser
219+ ) : Promise < Ec2RemoteEnv > {
205220 const timeout = new Timeout ( 60000 )
206221 await showMessageWithCancel ( 'AWS: Opening remote connection...' , timeout )
207222 const remoteEnv = await this . prepareEc2RemoteEnv ( selection , remoteUser ) . finally ( ( ) => timeout . cancel ( ) )
208223 return remoteEnv
209224 }
210225
211- public async prepareEc2RemoteEnv ( selection : Ec2Selection , remoteUser : string ) : Promise < Ec2RemoteEnv > {
226+ public async prepareEc2RemoteEnv ( selection : Ec2Selection , remoteUser : RemoteUser ) : Promise < Ec2RemoteEnv > {
212227 const logger = this . configureRemoteConnectionLogger ( selection . instanceId )
213228 const { ssm, vsc, ssh } = ( await ensureDependencies ( ) ) . unwrap ( )
214229 const keyPair = await this . configureSshKeys ( selection , remoteUser )
@@ -253,19 +268,23 @@ export class Ec2Connecter implements vscode.Disposable {
253268 return logger
254269 }
255270
256- public async configureSshKeys ( selection : Ec2Selection , remoteUser : string ) : Promise < SshKeyPair > {
271+ public async configureSshKeys ( selection : Ec2Selection , remoteUser : RemoteUser ) : Promise < SshKeyPair > {
257272 const keyPair = await SshKeyPair . getSshKeyPair ( `aws-ec2-key` , 30000 )
258273 await this . sendSshKeyToInstance ( selection , keyPair , remoteUser )
259274 return keyPair
260275 }
261276
262- private async attemptToCleanKeys ( instanceId : string , hintComment : string , remoteAuthorizedKeysPath : string ) {
277+ private async attemptToCleanKeys (
278+ instanceId : string ,
279+ hintComment : string ,
280+ hostOS : Ec2OS ,
281+ remoteAuthorizedKeysPath : string
282+ ) {
263283 try {
264- const deleteExistingKeyCommand = getRemoveLinesCommand ( hintComment , remoteAuthorizedKeysPath )
265- const result = await this . ssmClient . sendCommandAndWait ( instanceId , 'AWS-RunShellScript' , {
284+ const deleteExistingKeyCommand = getRemoveLinesCommand ( hintComment , hostOS , remoteAuthorizedKeysPath )
285+ await this . ssmClient . sendCommandAndWait ( instanceId , 'AWS-RunShellScript' , {
266286 commands : [ deleteExistingKeyCommand ] ,
267287 } )
268- console . log ( result )
269288 } catch ( e ) {
270289 getLogger ( ) . warn ( `ec2: failed to clean keys: %O` , e )
271290 }
@@ -274,32 +293,47 @@ export class Ec2Connecter implements vscode.Disposable {
274293 public async sendSshKeyToInstance (
275294 selection : Ec2Selection ,
276295 sshKeyPair : SshKeyPair ,
277- remoteUser : string
296+ remoteUser : RemoteUser
278297 ) : Promise < void > {
279298 const sshPubKey = await sshKeyPair . getPublicKey ( )
280299 const hintComment = '#AWSToolkitForVSCode'
281300
282- const remoteAuthorizedKeysPath = `/home/${ remoteUser } /.ssh/authorized_keys`
301+ const remoteAuthorizedKeysPath = `/home/${ remoteUser . name } /.ssh/authorized_keys`
283302
284303 const appendStr = ( s : string ) => `echo "${ s } " >> ${ remoteAuthorizedKeysPath } `
285304 const writeKeyCommand = appendStr ( [ sshPubKey . replace ( '\n' , '' ) , hintComment ] . join ( ' ' ) )
286305
287- await this . attemptToCleanKeys ( selection . instanceId , hintComment , remoteAuthorizedKeysPath )
306+ await this . attemptToCleanKeys ( selection . instanceId , hintComment , remoteUser . os , remoteAuthorizedKeysPath )
288307 await this . ssmClient . sendCommandAndWait ( selection . instanceId , 'AWS-RunShellScript' , {
289308 commands : [ writeKeyCommand ] ,
290309 } )
291310 }
292311
293- public async getRemoteUser ( instanceId : string ) {
294- const osName = await this . ssmClient . getTargetPlatformName ( instanceId )
295- if ( osName === 'Amazon Linux' ) {
296- return 'ec2-user'
312+ public async getRemoteUser ( instanceId : string ) : Promise < RemoteUser > {
313+ const os = await this . ssmClient . getTargetPlatformName ( instanceId )
314+ if ( os === 'Amazon Linux' ) {
315+ return { name : 'ec2-user' , os }
297316 }
298317
299- if ( osName === 'Ubuntu' ) {
300- return 'ubuntu'
318+ if ( os === 'Ubuntu' ) {
319+ return { name : 'ubuntu' , os }
301320 }
302321
303- throw new ToolkitError ( `Unrecognized OS name ${ osName } on instance ${ instanceId } ` , { code : 'UnknownEc2OS' } )
322+ throw new ToolkitError ( `Unrecognized OS name ${ os } on instance ${ instanceId } ` , { code : 'UnknownEc2OS' } )
304323 }
305324}
325+
326+ /**
327+ * Generate bash command (as string) to remove lines in file suffixed by `hintComment`.
328+ * @param hintComment suffix comment for deleted lines.
329+ * @param filepath filepath (as string) to target with the command.
330+ * @returns bash command to remove lines from file.
331+ */
332+ export function getRemoveLinesCommand ( hintComment : string , hostOS : Ec2OS , filepath : string ) : string {
333+ // Linux allows not passing extension to -i, whereas macOS requires zero length extension.
334+ return `sed -i ${ isLinux ( hostOS ) ? '' : "''" } /${ hintComment } /d ${ filepath } `
335+ }
336+
337+ function isLinux ( os : Ec2OS ) : boolean {
338+ return os === 'Amazon Linux' || os === 'Ubuntu'
339+ }
0 commit comments