feat: add ssh connection retry

This commit is contained in:
Justin Lin committed 2024-10-14 17:31:35 +08:00
1 parent 251139c159
commit 31301419ad
4 files changed
+63 -43

No files matched your search

+1 -1
View File
@@ -1,11 +1,11 @@
HOST=localhost
PORT=3000
BASE_URL=http://localhost:5173
BACKEND_URL=http://localhost:3000
MONGO_URL=mongodb://localhost:27017/odyssey-test
SECRET_KEY=sample-secret
# Optional
# BACKEND_URL=http://host.docker.internal:3000
# CONTAINER_EXPIRY=60 # 60 minutes
# SESSION_MAX_AGE=168 # 7 days
# TRUSTED_PROXIES=1.2.3.4
+2 -2
View File
@@ -35,6 +35,7 @@ function createConfig() {
const host = get('HOST', '0.0.0.0')
const port = Number(get('PORT', 3000))
const baseUrl = get('BASE_URL', `http://${host}:${port}`)
const backendUrl = get('BACKEND_URL', `http://host.docker.internal:${port}`)
const url = new URL(baseUrl)
const projectRoot = getProjectRoot()
const keypairPath = get(
@@ -49,7 +50,7 @@ function createConfig() {
baseUrl,
domain: url.hostname,
protocol: url.protocol,
backendUrl: get('BACKEND_URL', baseUrl),
backendUrl,
db: get('MONGO_URL', 'mongodb://localhost:27017/odyssey-test'),
secret: get('SECRET_KEY', ''),
isProduction: process.env.NODE_ENV === 'production',
@@ -97,7 +98,6 @@ function getProjectRoot(): string {
}
const config = createConfig()
console.log(config.docker.keypair)
export function getQuestImage(id: string): string {
return `${config.docker.imagePrefix}${id}`
+12 -40
View File
@@ -1,9 +1,9 @@
import fs from 'fs/promises'
import { Duplex } from 'stream'
import { Client } from 'ssh2'
import Docker from 'dockerode'
import config, { getQuestImage } from '../config.js'
import logger from '../utils/logger.js'
import { connectToSSH } from './ssh.js'
const engine = new Docker()
@@ -70,9 +70,6 @@ export async function getAndStartContainer(
if (!(await container.inspect()).State.Running) {
await container.start()
}
await new Promise((resolve) => {
setTimeout(resolve, 2000)
})
return container
}
@@ -83,45 +80,20 @@ export async function attachContainer(
const containerIp = (await container.inspect()).NetworkSettings.Networks[
config.docker.network
].IPAddress
const conn = new Client()
console.log(config.docker.keypair.privateKey)
return new Promise((resolve, reject) => {
conn
.on('ready', () => {
console.log('SSH connection ready')
conn.shell(
{
env: {
TOKEN: token,
API_ENDPOINT: 'http://host.docker.internal:3000',
ZDOTDIR: '/etc/zsh',
NODE_PATH: '/usr/src/node_modules',
},
},
(err, stream) => {
if (err) {
reject(err)
for (let i = 0; i < 10; i++) {
try {
// eslint-disable-next-line no-await-in-loop
const stream = await connectToSSH(containerIp, { token })
return stream
} catch {
// eslint-disable-next-line no-await-in-loop
await new Promise((resolve) => {
setTimeout(resolve, 1000)
})
}
stream.on('close', () => {
console.log('Stream :: close')
conn.end()
})
resolve(stream)
}
)
})
.on('error', (err) => {
reject(err)
})
.connect({
host: containerIp,
port: 22,
username: 'commander',
privateKey: config.docker.keypair.privateKey,
debug: console.log,
})
})
throw new Error('Failed to connect to SSH')
}
export async function deleteContainer(id: string) {
+48
View File
@@ -0,0 +1,48 @@
import { Client } from 'ssh2'
import { Duplex } from 'stream'
import config from '../config.js'
export async function connectToSSH(
containerIp: string,
{ token }: { token: string }
): Promise<Duplex> {
const conn = new Client()
return new Promise((resolve, reject) => {
conn
.on('ready', () => {
console.log('SSH connection ready')
conn.shell(
{
env: {
TOKEN: token,
API_ENDPOINT: config.backendUrl,
ZDOTDIR: '/etc/zsh',
NODE_PATH: '/usr/src/node_modules',
},
},
(err, stream) => {
if (err) {
reject(err)
}
stream.on('close', () => {
console.log('Stream :: close')
conn.end()
})
resolve(stream)
}
)
})
.on('error', (err) => {
reject(err)
})
.connect({
host: containerIp,
port: 22,
username: 'commander',
privateKey: config.docker.keypair.privateKey,
debug: console.log,
timeout: 5000,
})
})
}