Skip to content

Commit 642afd1

Browse files
authored
feat(ec2): connect to EC2 instance #3671
* add new command for ec2 instance remote-connect * automatic key setup, ssh config, and connection * split up verify ssh host into pieces
1 parent e469351 commit 642afd1

File tree

18 files changed

+1076
-532
lines changed

18 files changed

+1076
-532
lines changed

package.json

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1120,10 +1120,6 @@
11201120
"command": "aws.ec2.openTerminal",
11211121
"when": "aws.isDevMode"
11221122
},
1123-
{
1124-
"command": "aws.ec2.openTerminal",
1125-
"when": "aws.isDevMode"
1126-
},
11271123
{
11281124
"command": "aws.ec2.startInstance",
11291125
"when": "aws.isDevMode"

resources/ec2_connect

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#!/usr/bin/env bash
2+
3+
# Usage:
4+
# When connecting to a dev environment
5+
# AWS_REGION=… AWS_SSM_CLI=… STREAM_URL=… TOKEN=… LOG_FILE_LOCATION==… ./ec2_connect
6+
7+
set -e
8+
set -u
9+
10+
_DATE_CMD=true
11+
12+
if command > /dev/null 2>&1 -v date; then
13+
_DATE_CMD=date
14+
elif command > /dev/null 2>&1 -v /bin/date; then
15+
_DATE_CMD=/bin/date
16+
fi
17+
18+
_log() {
19+
echo "$("$_DATE_CMD" '+%Y/%m/%d %H:%M:%S')" "$@" >> "${LOG_FILE_LOCATION}" 2>&1
20+
}
21+
22+
_require_nolog() {
23+
if [ -z "${1:-}" ] || [ -z "${2:-}" ]; then
24+
_log "error: missing required arg: $1"
25+
exit 1
26+
fi
27+
}
28+
29+
_require() {
30+
_require_nolog "$@"
31+
_log "$1=$2"
32+
}
33+
34+
_ec2() {
35+
# Function inputs
36+
local AWS_SSM_CLI=$1
37+
local AWS_REGION=$2
38+
local STREAM_URL=$3
39+
local TOKEN=$4
40+
local SESSION_ID=$4
41+
42+
exec "$AWS_SSM_CLI" "{\"streamUrl\":\"$STREAM_URL\",\"tokenValue\":\"$TOKEN\",\"sessionId\":\"$SESSION_ID\"}" "$AWS_REGION" "StartSession"
43+
}
44+
45+
_main() {
46+
_log "=============================================================================="
47+
48+
_require AWS_SSM_CLI "${AWS_SSM_CLI:-}"
49+
_require AWS_REGION "${AWS_REGION:-}"
50+
_require STREAM_URL "${STREAM_URL:-}"
51+
_require TOKEN "${TOKEN:-}"
52+
_require SESSION_ID "${SESSION_ID:-}"
53+
_require LOG_FILE_LOCATION "${LOG_FILE_LOCATION:-}"
54+
55+
_ec2 "$AWS_SSM_CLI" "$AWS_REGION" "$STREAM_URL" "$TOKEN" "$SESSION_ID"
56+
}
57+
58+
_main

src/codecatalyst/model.ts

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import { getCodeCatalystSpaceName, getCodeCatalystProjectName, getCodeCatalystDe
2121
import { writeFile } from 'fs-extra'
2222
import { sshAgentSocketVariable, startSshAgent, startVscodeRemote } from '../shared/extensions/ssh'
2323
import { ChildProcess } from '../shared/utilities/childProcess'
24-
import { ensureDependencies, hostNamePrefix } from './tools'
2524
import { isDevenvVscode } from './utils'
2625
import { Timeout } from '../shared/utilities/timeoutUtils'
2726
import { Commands } from '../shared/vscode/commands2'
@@ -30,8 +29,11 @@ import { fileExists } from '../shared/filesystemUtilities'
3029
import { CodeCatalystAuthenticationProvider } from './auth'
3130
import { ToolkitError } from '../shared/errors'
3231
import { Result } from '../shared/utilities/result'
32+
import { VscodeRemoteConnection, ensureDependencies } from '../shared/remoteSession'
33+
import { SshConfig, sshLogFileLocation } from '../shared/sshConfig'
3334

3435
export type DevEnvironmentId = Pick<DevEnvironment, 'id' | 'org' | 'project'>
36+
export const connectScriptPrefix = 'codecatalyst_connect'
3537

3638
export const docs = {
3739
vscode: {
@@ -86,7 +88,7 @@ export function getCodeCatalystSsmEnv(region: string, ssmPath: string, devenv: D
8688
AWS_SSM_CLI: ssmPath,
8789
CODECATALYST_ENDPOINT: getCodeCatalystConfig().endpoint,
8890
BEARER_TOKEN_LOCATION: bearerTokenCacheLocation(devenv.id),
89-
LOG_FILE_LOCATION: sshLogFileLocation(devenv.id),
91+
LOG_FILE_LOCATION: sshLogFileLocation('codecatalyst', devenv.id),
9092
SPACE_NAME: devenv.org.name,
9193
PROJECT_NAME: devenv.project.name,
9294
DEVENV_ID: devenv.id,
@@ -139,14 +141,6 @@ export function bearerTokenCacheLocation(devenvId: string): string {
139141
return path.join(globals.context.globalStorageUri.fsPath, `codecatalyst.${devenvId}.token`)
140142
}
141143

142-
export function sshLogFileLocation(devenvId: string): string {
143-
return path.join(globals.context.globalStorageUri.fsPath, `codecatalyst.${devenvId}.log`)
144-
}
145-
146-
export function getHostNameFromEnv(env: DevEnvironmentId): string {
147-
return `${hostNamePrefix}${env.id}`
148-
}
149-
150144
export interface ConnectedDevEnv {
151145
readonly summary: DevEnvironment
152146
readonly devenvClient: DevEnvClient
@@ -200,12 +194,7 @@ export async function getThisDevEnv(authProvider: CodeCatalystAuthenticationProv
200194
/**
201195
* Everything needed to connect to a dev environment via VS Code or `ssh`
202196
*/
203-
interface DevEnvConnection {
204-
readonly sshPath: string
205-
readonly vscPath: string
206-
readonly hostname: string
207-
readonly envProvider: EnvProvider
208-
readonly SessionProcess: typeof ChildProcess
197+
interface DevEnvConnection extends VscodeRemoteConnection {
209198
readonly devenv: DevEnvironment
210199
}
211200

@@ -215,13 +204,24 @@ export async function prepareDevEnvConnection(
215204
{ topic, timeout }: { topic?: string; timeout?: Timeout } = {}
216205
): Promise<DevEnvConnection> {
217206
const { ssm, vsc, ssh } = (await ensureDependencies()).unwrap()
207+
const hostNamePrefix = 'aws-devenv-'
208+
const sshConfig = new SshConfig(ssh, hostNamePrefix, connectScriptPrefix)
209+
const config = await sshConfig.ensureValid()
210+
211+
if (config.isErr()) {
212+
const err = config.err()
213+
getLogger().error(`codecatalyst: failed to add ssh config section: ${err.message}`)
214+
215+
throw err
216+
}
217+
218218
const runningDevEnv = await client.startDevEnvironmentWithProgress({
219219
id,
220220
spaceName: org.name,
221221
projectName: project.name,
222222
})
223223

224-
const hostname = getHostNameFromEnv({ id, org, project })
224+
const hostname = `${hostNamePrefix}${id}`
225225
const logPrefix = topic ? `codecatalyst ${topic} (${id})` : `codecatalyst (${id})`
226226
const logger = (data: string) => getLogger().verbose(`${logPrefix}: ${data}`)
227227
const envProvider = createCodeCatalystEnvProvider(client, ssm, runningDevEnv)
@@ -259,7 +259,7 @@ export async function openDevEnv(
259259
const repo = env.devenv.repositories.length == 1 ? env.devenv.repositories[0].repositoryName : undefined
260260
targetPath = repo ? `/projects/${repo}` : '/projects'
261261
}
262-
await startVscodeRemote(env.SessionProcess, getHostNameFromEnv(env.devenv), targetPath, env.vscPath)
262+
await startVscodeRemote(env.SessionProcess, env.hostname, targetPath, env.vscPath)
263263
}
264264

265265
// The "codecatalyst_connect" metric should really be splt into two parts:

0 commit comments

Comments
 (0)