mirror of
https://github.com/linux-odyssey/linux-odyssey.git
synced 2026-11-05 19:20:12 +08:00
feat: add ssh connection retry
This commit is contained in:
1 parent
251139c159
commit
31301419ad
4 files changed
+64
-44
No files matched your search
+1
-1
@@ -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
|
||||
|
||||
@@ -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}`
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
stream.on('close', () => {
|
||||
console.log('Stream :: close')
|
||||
conn.end()
|
||||
})
|
||||
resolve(stream)
|
||||
}
|
||||
)
|
||||
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)
|
||||
})
|
||||
.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) {
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user