Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(toolkit): attach lifetime to generated ssh keys. #5578

Merged
merged 217 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from 207 commits
Commits
Show all changes
217 commits
Select commit Hold shift + click to select a range
b7399c5
rename existing command to openTerminal
Hweinstock Jul 7, 2023
588594d
add new command for ec2 instance remote-connect
Hweinstock Jul 7, 2023
1c57e45
register new command so that it does not throw error
Hweinstock Jul 7, 2023
814ba96
refactor to make terminal distinction clearer
Hweinstock Jul 7, 2023
b5d2c88
add prompt for new selection
Hweinstock Jul 7, 2023
08ba106
enable connection to ec2, start generalizing CodeCatalyst work
Hweinstock Jul 10, 2023
3caba57
abstract general error msg to its own function
Hweinstock Jul 10, 2023
f2894ba
refactor more code catalyst code
Hweinstock Jul 10, 2023
f0f5086
refactor code to better mirror the code catalyst implementaton
Hweinstock Jul 10, 2023
5be1f8d
add a cancellable loading bar on open
Hweinstock Jul 10, 2023
3c096cd
rename variable to be more explicit
Hweinstock Jul 10, 2023
13b2bc0
refactor to have a with-progress method layer
Hweinstock Jul 10, 2023
8fbbc8d
refactor ssh config into the ssh file
Hweinstock Jul 10, 2023
57dbde6
convert regExp property to abstract
Hweinstock Jul 10, 2023
fb9f1e8
reformat string in proxycommand
Hweinstock Jul 11, 2023
f94fe64
implement basic tests on sshconfig mock object
Hweinstock Jul 11, 2023
fdec798
split up verify ssh host into pieces
Hweinstock Jul 11, 2023
545b8a7
refactor the sshconfig to make more testable, add more tests
Hweinstock Jul 11, 2023
4f8c680
refactor tests to distinguish between command and proxyCommand
Hweinstock Jul 11, 2023
222fcfd
avoid hard-coding in test
Hweinstock Jul 11, 2023
0e5f7a2
avoid other hard-coding in tests
Hweinstock Jul 11, 2023
e87f24d
change simple function to be in lined
Hweinstock Jul 11, 2023
3764b4a
change command wording to be less clunky.
Hweinstock Jul 11, 2023
1c3be08
change command phrasing to mirror ssh extension
Hweinstock Jul 11, 2023
93e23f0
Merge branch 'hkobew/ec2/remoteConnect/newCommand' into hkobew/ec2/re…
Hweinstock Jul 11, 2023
d742eef
Merge branch 'hkobew/ec2/remoteConnect/connect' into hkobew/ec2/remot…
Hweinstock Jul 11, 2023
056e807
remove commented out code
Hweinstock Jul 12, 2023
153bf62
update command file with icon
Hweinstock Jul 13, 2023
bd71166
update context value for parent node
Hweinstock Jul 13, 2023
e027e40
handle case where parent node is passed to command
Hweinstock Jul 13, 2023
9d62154
change outdated prompter text
Hweinstock Jul 13, 2023
f28809b
refactor commands to own file
Hweinstock Jul 13, 2023
3cc9aa6
add start command to package.json files
Hweinstock Jul 13, 2023
732778f
add command to start the instance
Hweinstock Jul 13, 2023
2cbb723
fix typo in which method was being invoked
Hweinstock Jul 13, 2023
26faa78
merge upstream changes
Hweinstock Jul 13, 2023
ea30d86
refactor prompter and commands file
Hweinstock Jul 13, 2023
52a2fde
add tests for new structure of prompter
Hweinstock Jul 13, 2023
8fac525
refactor prompter code to be more testable
Hweinstock Jul 13, 2023
004ac0b
add baseline test to item provider
Hweinstock Jul 13, 2023
f85f0ee
add instance filter and tests for it
Hweinstock Jul 13, 2023
29d6b62
remove duplication of determining if instance is running
Hweinstock Jul 13, 2023
506be9a
backtrack filter idea because it breaks pagination
Hweinstock Jul 13, 2023
2022004
implement start command
Hweinstock Jul 13, 2023
8560da2
move bulk of work to new file
Hweinstock Jul 13, 2023
9022ec2
add command for stopping instance
Hweinstock Jul 13, 2023
fd1703d
add functionality to stop instances
Hweinstock Jul 13, 2023
9e0c16e
refactor start/stop commands into a class
Hweinstock Jul 13, 2023
1f53a3f
add testing for checking instance status
Hweinstock Jul 13, 2023
eb92609
add reboot command
Hweinstock Jul 13, 2023
cec3ede
implement reboot extension
Hweinstock Jul 13, 2023
024eb7b
refactor to remove duplicate code
Hweinstock Jul 13, 2023
38559be
remove unnecessary async
Hweinstock Jul 13, 2023
b217377
expose command on explorer
Hweinstock Jul 13, 2023
19ad794
refactor to eliminate some duplicate implementation
Hweinstock Jul 14, 2023
7794b67
handle undefined node consistently
Hweinstock Jul 14, 2023
adae37b
add method to append status to instance retriever
Hweinstock Jul 14, 2023
d3a4276
refactor how we add fields to the ec2 instances
Hweinstock Jul 14, 2023
0772dd2
add tests for new helper functions in Ec2Client
Hweinstock Jul 14, 2023
b4c03c3
refactor tests to use the same test-data
Hweinstock Jul 14, 2023
b76e22f
add test for filter functionality
Hweinstock Jul 14, 2023
98f03fb
change prompter to filter based on instance status
Hweinstock Jul 14, 2023
6cc4cc5
add icons to QuickPick
Hweinstock Jul 14, 2023
d147b22
disallow rebooting stopped instance
Hweinstock Jul 14, 2023
d72f9c9
add icon entrypoints to start/stop/restart commands
Hweinstock Jul 14, 2023
bd709da
move icon determiner function to seperate file
Hweinstock Jul 14, 2023
bffa367
add icons to show if its running in explorer
Hweinstock Jul 14, 2023
8062ed6
limit commands to cases where they are dont throw error
Hweinstock Jul 14, 2023
f7999b1
limit explorer icons and refresh after
Hweinstock Jul 14, 2023
f2a7786
fix outdated test
Hweinstock Jul 17, 2023
cf05c8e
remove commands from command palette
Hweinstock Jul 17, 2023
f0900d7
refactor such that it only updates ec2 parent node
Hweinstock Jul 17, 2023
10566c1
update outdated test
Hweinstock Jul 17, 2023
2b749a3
add testing to ec2InstanceNode
Hweinstock Jul 17, 2023
bf25145
add another test for the update functionality
Hweinstock Jul 17, 2023
de97b43
update tests to use icons
Hweinstock Jul 17, 2023
1914fe8
implement core logic with basic tests
Hweinstock Jul 17, 2023
ce7beb9
refactor to key by node, rather than string
Hweinstock Jul 17, 2023
a5cf7f3
switch back to using instanceids to add more testing
Hweinstock Jul 17, 2023
177aa07
ensure timer stops with tests
Hweinstock Jul 18, 2023
dd82ff9
refactor tests to avoid polling before tests start
Hweinstock Jul 18, 2023
76eb0fd
refactor such that only instance node is refreshed
Hweinstock Jul 18, 2023
5ad533f
no stopping/starting on pending instances
Hweinstock Jul 18, 2023
6efd4fd
refactor sshConfig changes to their own new file
Hweinstock Jul 18, 2023
4a68497
move the creation of ssh section to parent abstract class
Hweinstock Jul 18, 2023
ba21411
move ensureConnect to shared file
Hweinstock Jul 18, 2023
5ec5496
move ensure valid method to shared file
Hweinstock Jul 18, 2023
6c03515
remove sub-classes
Hweinstock Jul 18, 2023
7e6c56e
switch scriptPrefix to a parameter
Hweinstock Jul 18, 2023
19d3e2c
use variable to name prefix of script
Hweinstock Jul 18, 2023
886a41e
add test for section created by sshConfig
Hweinstock Jul 18, 2023
a4a4626
construct regexp from script name
Hweinstock Jul 18, 2023
8b36799
move logFile location generator to general file
Hweinstock Jul 18, 2023
72ae902
refactor the ec2_connect script
Hweinstock Jul 18, 2023
b08bbdb
refactor script to include token for session
Hweinstock Jul 18, 2023
c4348b4
include session tokens in script env
Hweinstock Jul 18, 2023
02b2a2b
adding suport for documents in start session
Hweinstock Jul 18, 2023
e373be8
remove port number
Hweinstock Jul 18, 2023
0f2c01d
remove log file location where unused
Hweinstock Jul 19, 2023
718c402
clean up state after tests
Hweinstock Jul 19, 2023
a21d6f8
hide start/stop/reboot from command palette
Hweinstock Jul 19, 2023
60566f5
add functionality to generate keys
Hweinstock Jul 19, 2023
e15c0a2
implement sending keys to target instance, with hard coded dest file
Hweinstock Jul 19, 2023
41eade4
implement reading public key from KeyPair
Hweinstock Jul 20, 2023
33fbd79
add comment about hard coded path
Hweinstock Jul 20, 2023
8cb386d
fix some spacing
Hweinstock Jul 20, 2023
84fa815
update config to get it working
Hweinstock Jul 20, 2023
dca607e
cleanup some small things
Hweinstock Jul 20, 2023
f6ac8e3
add some testing
Hweinstock Jul 20, 2023
e15e184
clean up handling of key parameters to ssh config
Hweinstock Jul 21, 2023
6950712
updates tests to utilize sinon stub
Hweinstock Jul 21, 2023
d951675
remove references to code catatlyst in error/log messages
Hweinstock Jul 21, 2023
2a63ae3
pass remote username down from above
Hweinstock Jul 21, 2023
1087678
increase testing coverage for sshConfigSection
Hweinstock Jul 21, 2023
84ea20a
implement method to determine remote user
Hweinstock Jul 21, 2023
eed3a09
implement method for guessing the os
Hweinstock Jul 21, 2023
4b94c83
refactor to use ssm sdk to determine os
Hweinstock Jul 21, 2023
65ad6ac
check for permissions before trying the ssm sdk
Hweinstock Jul 21, 2023
febac95
fix to work on ubuntu
Hweinstock Jul 21, 2023
ee149b7
remove leftover log statement
Hweinstock Jul 21, 2023
d70c415
merge in master
Hweinstock Jul 21, 2023
aa56c93
refactor tests to use stub only where needed
Hweinstock Jul 24, 2023
90b248b
remove double restore in test file
Hweinstock Jul 24, 2023
950ca3c
test commenting out sshConfig tests
Hweinstock Jul 24, 2023
f17580d
add comment to import so that it runs
Hweinstock Jul 24, 2023
ac4e711
comment out other use of sinon stub for testing
Hweinstock Jul 24, 2023
7441de1
comment out test for reading ssh keys
Hweinstock Jul 24, 2023
ef427f2
comment out tests in the model file
Hweinstock Jul 24, 2023
a0cea9d
comment out entire codecatalyst tools file
Hweinstock Jul 24, 2023
740baf8
comment out all tests modified
Hweinstock Jul 24, 2023
c60d540
uncomment test files
Hweinstock Jul 24, 2023
2a08bb7
move ssh tests to their own file
Hweinstock Jul 24, 2023
24a25d0
uncomment tests related to the model
Hweinstock Jul 24, 2023
27179ab
move the rest of the the tests to the sshConfig file
Hweinstock Jul 24, 2023
a8a92ae
add codecatalyst label to relevant tests
Hweinstock Jul 24, 2023
d6896c1
rename file to mirror main class name in file
Hweinstock Jul 24, 2023
e201693
rename test file
Hweinstock Jul 24, 2023
3bbabd1
change path import structure
Hweinstock Jul 24, 2023
8a733e6
implement function to map instance profile to instance role
Hweinstock Jul 26, 2023
8a7e1ae
refactor error handling
Hweinstock Jul 26, 2023
9ac421f
clean up error handling
Hweinstock Jul 26, 2023
819203f
update tests to match the new behavior
Hweinstock Jul 26, 2023
65133a4
be extra explicit that Instance Profile is different from role.
Hweinstock Jul 26, 2023
de04def
throw if no roles associated with instance profile
Hweinstock Jul 26, 2023
9a55520
refactor to avoid duplicate service calls
Hweinstock Jul 26, 2023
b0c1f02
update and refactor tests to use sinon instead of homemade mocks
Hweinstock Jul 26, 2023
59e9f9c
merge in master
Hweinstock Jul 27, 2023
53a678b
Merge branch 'hkobew/ec2/remoteConnect/sshKeysNew' into hkobew/ec2/te…
Hweinstock Jul 27, 2023
0a26c22
merge in necessary pre-req
Hweinstock Jul 27, 2023
fc4af9e
refactor test file to use mocking framework
Hweinstock Jul 27, 2023
2f393c8
add coverage for the getAttachedIamRole method
Hweinstock Jul 27, 2023
6eb3450
reformat and rearrange tests
Hweinstock Jul 27, 2023
f9c5d91
refactor out shared piece
Hweinstock Jul 27, 2023
f72acc7
implement new version using permissions
Hweinstock Jul 27, 2023
19eb5d1
fix logic error
Hweinstock Jul 27, 2023
ca86046
update error message
Hweinstock Jul 27, 2023
57ad523
handle merge conflicts
Hweinstock Aug 5, 2023
dd05a0d
remove duplicate commands
Hweinstock Aug 5, 2023
40abd17
merge in upstream changes
Hweinstock Aug 7, 2023
ae3851c
merge in master
Hweinstock Aug 8, 2023
9cbcb48
remove duplicate commands in package.json
Hweinstock Aug 8, 2023
4d6cd41
remove duplicate terminal command
Hweinstock Aug 8, 2023
d4a2244
merge in master
Hweinstock Aug 10, 2023
ac6d473
make instance readonly on instance node
Hweinstock Aug 10, 2023
6cab577
add comment about not-readonly
Hweinstock Aug 10, 2023
a034dc3
refactor to allow for readonly instance with comment
Hweinstock Aug 10, 2023
2854015
merge in master
Hweinstock Aug 11, 2023
f21201f
merge in master, update imports
Sep 5, 2024
c9d6eff
update vscode.command usage
Hweinstock Sep 5, 2024
49efa8b
add changelog
Hweinstock Sep 5, 2024
0cc83e3
start to generalize polling set
Hweinstock Sep 5, 2024
996362c
minimize code dupe
Hweinstock Sep 5, 2024
1e6cdcf
cleanup
Hweinstock Sep 5, 2024
4e97666
generalize further, extend tests
Hweinstock Sep 5, 2024
9d9fa8d
fix linting problems
Hweinstock Sep 5, 2024
79b8f81
fix linting 2
Hweinstock Sep 6, 2024
681771e
Merge branch 'master' into hkobew/ec2/explorerUpdate
Hweinstock Sep 6, 2024
955308c
clean up inlining
Hweinstock Sep 6, 2024
341bff3
merge in upstream changes
Hweinstock Sep 6, 2024
e2ac9c8
remove merge relic
Hweinstock Sep 6, 2024
1a47575
add removed changes from merge
Hweinstock Sep 6, 2024
65b2ff2
implement w/ timeout
Hweinstock Sep 11, 2024
234a26d
remove outdated test case
Hweinstock Sep 11, 2024
8a16fa7
Merge branch 'hkobew/ec2/useActions' into hkobew/ec2/noDiskKey
Hweinstock Sep 11, 2024
b1f05f5
add deletion tests
Hweinstock Sep 11, 2024
57e887c
add progress bar
Hweinstock Sep 11, 2024
656a58c
attach lifetime to keys
Hweinstock Sep 11, 2024
61eb6bb
add tests for key expiration
Hweinstock Sep 11, 2024
cf70ff6
update key file permissions to match that used in aws ec2 instance co…
Hweinstock Sep 12, 2024
b77dc8d
move test files over
Hweinstock Sep 12, 2024
548f69e
move rest of changes from other branch
Hweinstock Sep 12, 2024
f152943
fix imports
Hweinstock Sep 12, 2024
1af46cd
update changelog
Hweinstock Sep 12, 2024
dda254c
merge in upstream changes
Hweinstock Sep 12, 2024
28798b6
move over changes
Hweinstock Sep 12, 2024
0e9d0e8
Merge branch 'hkobew/ec2/useActions' into hkobew/ec2/useActionsTemp
Hweinstock Sep 12, 2024
cdf23cb
remove system utilities since they are not used anymore
Hweinstock Sep 12, 2024
c3bfbf2
fix bug with priv/pub
Hweinstock Sep 12, 2024
987c9cf
force key overwrite
Hweinstock Sep 13, 2024
e37a46f
merge in upstream changes
Hweinstock Sep 16, 2024
eab2f7d
update changelog
Hweinstock Sep 16, 2024
a8051de
remove code related to add inline policies
Hweinstock Sep 16, 2024
3efa868
remove outdated import
Hweinstock Sep 16, 2024
05b107b
Merge branch 'master' into hkobew/ec2/useActions
Hweinstock Sep 17, 2024
2e7c79f
Merge branch 'hkobew/ec2/useActions' into hkobew/ec2/noDiskKey
Hweinstock Sep 17, 2024
841165b
Merge branch 'master' into hkobew/ec2/noDiskKey
Hweinstock Sep 19, 2024
64da02e
remove outdated comment
Hweinstock Sep 19, 2024
085103c
use custom fs wrapper
Hweinstock Sep 20, 2024
90fa76a
add test case to ensure key generation is working
Hweinstock Sep 20, 2024
ce7706a
force file permissions
Hweinstock Sep 20, 2024
efc63be
bring back use of chmod
Hweinstock Sep 20, 2024
c02697c
resolve conflicts with main branch
Hweinstock Sep 20, 2024
72d6730
remove flaky file permissions test
Hweinstock Sep 20, 2024
20a2212
increase timeout on test keys
Hweinstock Sep 20, 2024
bc89e7f
use existing utility function
Hweinstock Sep 20, 2024
7b2bafc
delete private key first
Hweinstock Sep 20, 2024
cdf5d13
avoid use of fs-extra
Hweinstock Sep 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions packages/core/src/awsService/ec2/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export type Ec2ConnectErrorCode = 'EC2SSMStatus' | 'EC2SSMPermission' | 'EC2SSMC

interface Ec2RemoteEnv extends VscodeRemoteConnection {
selection: Ec2Selection
keyPair: SshKeyPair
}

export class Ec2ConnectionManager {
Expand Down Expand Up @@ -195,9 +196,9 @@ export class Ec2ConnectionManager {
public async prepareEc2RemoteEnv(selection: Ec2Selection, remoteUser: string): Promise<Ec2RemoteEnv> {
const logger = this.configureRemoteConnectionLogger(selection.instanceId)
const { ssm, vsc, ssh } = (await ensureDependencies()).unwrap()
const keyPath = await this.configureSshKeys(selection, remoteUser)
const keyPair = await this.configureSshKeys(selection, remoteUser)
const hostNamePrefix = 'aws-ec2-'
const sshConfig = new SshConfig(ssh, hostNamePrefix, 'ec2_connect', keyPath)
const sshConfig = new SshConfig(ssh, hostNamePrefix, 'ec2_connect', keyPair.getPrivateKeyPath())

const config = await sshConfig.ensureValid()
if (config.isErr()) {
Expand All @@ -224,6 +225,7 @@ export class Ec2ConnectionManager {
vscPath: vsc,
SessionProcess,
selection,
keyPair,
}
}

Expand All @@ -233,11 +235,11 @@ export class Ec2ConnectionManager {
return logger
}

public async configureSshKeys(selection: Ec2Selection, remoteUser: string): Promise<string> {
public async configureSshKeys(selection: Ec2Selection, remoteUser: string): Promise<SshKeyPair> {
const keyPath = path.join(globals.context.globalStorageUri.fsPath, `aws-ec2-key`)
const keyPair = await SshKeyPair.getSshKeyPair(keyPath)
const keyPair = await SshKeyPair.getSshKeyPair(keyPath, 30000)
await this.sendSshKeyToInstance(selection, keyPair, remoteUser)
return keyPath
return keyPair
}

public async sendSshKeyToInstance(
Expand Down
49 changes: 43 additions & 6 deletions packages/core/src/awsService/ec2/sshKeyPair.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,32 @@
import * as fs from 'fs-extra'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's not much trouble, it would be helpful to remove this and use our fs.ts module instead. We want to eliminate 'fs-extra'

Copy link
Contributor Author

@Hweinstock Hweinstock Sep 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't figure out how to set file permission (fs.chmod equivalent) through fs.ts. While ssh-keygen seems to do this for me locally, it fails in CI without this. Should I keep fs-extra for this until a chmod equivalent exists in fs.ts?

Copy link
Contributor

@justinmk3 justinmk3 Sep 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how to set file permission (fs.chmod equivalent) through fs.ts

would you mind adding a stub for chmod in fs.ts? it can just do nothing for if (isWeb()). and for non-web, it can use node's chmod (from 'fs', instead of 'fs-extra)

this could be a followup PR. for now in this PR, if it's easy, try using 'fs' instead of 'fs-extra'.

import { ToolkitError } from '../../shared/errors'
import { ChildProcess } from '../../shared/utilities/childProcess'
import { Timeout } from '../../shared/utilities/timeoutUtils'

export class SshKeyPair {
private publicKeyPath: string
private constructor(keyPath: string) {
private lifeTimeout: Timeout
private deleted: boolean = false

private constructor(
private keyPath: string,
lifetime: number
) {
this.publicKeyPath = `${keyPath}.pub`
this.lifeTimeout = new Timeout(lifetime)

this.lifeTimeout.onCompletion(async () => {
await this.delete()
justinmk3 marked this conversation as resolved.
Show resolved Hide resolved
})
}

public static async getSshKeyPair(keyPath: string) {
const keyExists = await fs.pathExists(keyPath)
if (!keyExists) {
await SshKeyPair.generateSshKeyPair(keyPath)
public static async getSshKeyPair(keyPath: string, lifetime: number) {
// Overwrite key if already exists
if (await fs.pathExists(keyPath)) {
await fs.remove(keyPath)
}
return new SshKeyPair(keyPath)
await SshKeyPair.generateSshKeyPair(keyPath)
return new SshKeyPair(keyPath, lifetime)
}

public static async generateSshKeyPair(keyPath: string): Promise<void> {
Expand All @@ -26,14 +39,38 @@ export class SshKeyPair {
if (result.exitCode !== 0) {
throw new ToolkitError('ec2: Failed to generate ssh key', { details: { stdout: result.stdout } })
}
await fs.chmod(keyPath, 0o600)
}

public getPublicKeyPath(): string {
return this.publicKeyPath
}

public getPrivateKeyPath(): string {
return this.keyPath
}

public async getPublicKey(): Promise<string> {
const contents = await fs.readFile(this.publicKeyPath, 'utf-8')
return contents
}

public async delete(): Promise<void> {
await fs.remove(this.publicKeyPath)
await fs.remove(this.keyPath)

if (!this.lifeTimeout.completed) {
this.lifeTimeout.cancel()
}

this.deleted = true
}

public isDeleted(): boolean {
return this.deleted
Copy link
Contributor

@justinmk3 justinmk3 Sep 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this extra state be avoided? usually better to check "reality", i.e. check the filesystem. after all, we're already storing the filepath.

intermediate state can drift, and leads to "coherency" bugs. even very simple state.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So in that case, replace the isDeleted() method with one that checks the actual filesystem?

}

public timeAlive(): number {
return this.lifeTimeout.elapsedTime
}
}
2 changes: 1 addition & 1 deletion packages/core/src/test/awsService/ec2/model.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ describe('Ec2ConnectClient', function () {
instanceId: 'test-id',
region: 'test-region',
}
const mockKeys = await SshKeyPair.getSshKeyPair('')
const mockKeys = await SshKeyPair.getSshKeyPair('', 0)
await client.sendSshKeyToInstance(testSelection, mockKeys, '')
sinon.assert.calledWith(sendCommandStub, testSelection.instanceId, 'AWS-RunShellScript')
sinon.restore()
Expand Down
80 changes: 65 additions & 15 deletions packages/core/src/test/awsService/ec2/sshKeyPair.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,51 @@
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

import * as vscode from 'vscode'
import assert from 'assert'
import * as fs from 'fs-extra'
import * as sinon from 'sinon'
import { makeTemporaryToolkitFolder, tryRemoveFolder } from '../../../shared/filesystemUtilities'
import * as path from 'path'
import { SshKeyPair } from '../../../awsService/ec2/sshKeyPair'
import { createTestWorkspaceFolder, installFakeClock } from '../../testUtil'
import { InstalledClock } from '@sinonjs/fake-timers'

describe('SshKeyUtility', async function () {
let temporaryDirectory: string
let keyPath: string
let keyPair: SshKeyPair
let clock: InstalledClock

before(async function () {
temporaryDirectory = await makeTemporaryToolkitFolder()
keyPath = `${temporaryDirectory}/test-key`
keyPair = await SshKeyPair.getSshKeyPair(keyPath)
temporaryDirectory = (await createTestWorkspaceFolder()).uri.fsPath
keyPath = path.join(temporaryDirectory, 'testKeyPair')
clock = installFakeClock()
})

beforeEach(async function () {
keyPair = await SshKeyPair.getSshKeyPair(keyPath, 30000)
})

afterEach(async function () {
await keyPair.delete()
})

after(async function () {
await tryRemoveFolder(temporaryDirectory)
await keyPair.delete()
clock.uninstall()
sinon.restore()
})

it('generates key in target file', async function () {
const contents = await vscode.workspace.fs.readFile(vscode.Uri.file(keyPath))
assert.notStrictEqual(contents.length, 0)
})

describe('generateSshKeys', async function () {
it('generates key in target file', async function () {
const contents = await fs.readFile(keyPath, 'utf-8')
assert.notStrictEqual(contents.length, 0)
})
it('sets key permission to read/write by owner', async function () {
const fileMode = (await fs.stat(keyPath)).mode
// Mode is in decimal, so convert to decimal with bitmask
// source: https://github.com/nodejs/node-v0.x-archive/issues/3045
assert.strictEqual(fileMode & 0o777, 0o600)
})

it('properly names the public key', function () {
Expand All @@ -40,10 +58,42 @@ describe('SshKeyUtility', async function () {
assert.notStrictEqual(key.length, 0)
})

it('does not overwrite existing keys', async function () {
const generateStub = sinon.stub(SshKeyPair, 'generateSshKeyPair')
await SshKeyPair.getSshKeyPair(keyPath)
sinon.assert.notCalled(generateStub)
it('does overwrite existing keys on get call', async function () {
const generateStub = sinon.spy(SshKeyPair, 'generateSshKeyPair')
const keyBefore = await vscode.workspace.fs.readFile(vscode.Uri.file(keyPath))
keyPair = await SshKeyPair.getSshKeyPair(keyPath, 30000)

const keyAfter = await vscode.workspace.fs.readFile(vscode.Uri.file(keyPath))
sinon.assert.calledOnce(generateStub)

assert.notStrictEqual(keyBefore, keyAfter)
sinon.restore()
})

it('deletes key on delete', async function () {
const pubKeyExistsBefore = await fs.pathExists(keyPair.getPublicKeyPath())
const privateKeyExistsBefore = await fs.pathExists(keyPair.getPrivateKeyPath())

await keyPair.delete()

const pubKeyExistsAfter = await fs.pathExists(keyPair.getPublicKeyPath())
const privateKeyExistsAfter = await fs.pathExists(keyPair.getPrivateKeyPath())

assert.strictEqual(pubKeyExistsBefore && privateKeyExistsBefore, true)
assert.strictEqual(pubKeyExistsAfter && privateKeyExistsAfter, false)
assert(keyPair.isDeleted())
})

it('deletes keys after timeout', async function () {
// Stub methods interacting with file system to avoid flaky test.
sinon.stub(SshKeyPair, 'generateSshKeyPair')
const deleteStub = sinon.stub(SshKeyPair.prototype, 'delete')

keyPair = await SshKeyPair.getSshKeyPair(keyPath, 50)
await clock.tickAsync(10)
sinon.assert.notCalled(deleteStub)
await clock.tickAsync(100)
sinon.assert.calledOnce(deleteStub)
sinon.restore()
})
})
Loading