parent
e20a3a4af1
commit
099b2c9470
10 changed files with 264 additions and 15 deletions
@ -0,0 +1,190 @@ |
||||
import { RequestHandler } from 'express'; |
||||
|
||||
import SERVER_PATHS from '../../consts/SERVER_PATHS'; |
||||
|
||||
import { |
||||
getAnvilData, |
||||
getLocalHostUUID, |
||||
getPeerData, |
||||
job, |
||||
sub, |
||||
} from '../../accessModule'; |
||||
import { sanitize } from '../../sanitize'; |
||||
import { rm, stderr, stdoutVar } from '../../shell'; |
||||
|
||||
export const createHostConnection: RequestHandler< |
||||
unknown, |
||||
undefined, |
||||
CreateHostConnectionRequestBody |
||||
> = (request, response) => { |
||||
const { |
||||
body: { |
||||
dbName = 'anvil', |
||||
ipAddress, |
||||
isPing = false, |
||||
password, |
||||
port = 5432, |
||||
sshPort = 22, |
||||
user = 'admin', |
||||
}, |
||||
} = request; |
||||
|
||||
const commonDBName = sanitize(dbName, 'string'); |
||||
const commonIsPing = sanitize(isPing, 'boolean'); |
||||
const commonPassword = sanitize(password, 'string'); |
||||
const commonDBPort = sanitize(port, 'number'); |
||||
const commonDBUser = sanitize(user, 'string'); |
||||
const peerIPAddress = sanitize(ipAddress, 'string'); |
||||
const peerSSHPort = sanitize(sshPort, 'number'); |
||||
|
||||
const commonPing = commonIsPing ? 1 : 0; |
||||
|
||||
let localDBPort: number; |
||||
let localIPAddress: string; |
||||
let isPeerReachable = false; |
||||
let isPeerDBReachable = false; |
||||
let peerHostUUID: string; |
||||
|
||||
try { |
||||
({ hostUUID: peerHostUUID, isConnected: isPeerReachable } = getPeerData( |
||||
peerIPAddress, |
||||
{ password: commonPassword, port: peerSSHPort }, |
||||
)); |
||||
} catch (subError) { |
||||
stderr(`Failed to get peer data; CAUSE: ${subError}`); |
||||
|
||||
response.status(500).send(); |
||||
|
||||
return; |
||||
} |
||||
|
||||
stdoutVar({ peerHostUUID, isPeerReachable }); |
||||
|
||||
if (!isPeerReachable) { |
||||
stderr( |
||||
`Cannot connect to peer; please verify credentials and SSH keys validity.`, |
||||
); |
||||
|
||||
response.status(400).send(); |
||||
|
||||
return; |
||||
} |
||||
|
||||
try { |
||||
localIPAddress = sub('find_matching_ip', { |
||||
subModuleName: 'System', |
||||
subParams: { host: peerIPAddress }, |
||||
}).stdout; |
||||
} catch (subError) { |
||||
stderr(`Failed to get matching IP address; CAUSE: ${subError}`); |
||||
|
||||
response.status(500).send(); |
||||
|
||||
return; |
||||
} |
||||
|
||||
stdoutVar({ localIPAddress }); |
||||
|
||||
const pgpassFilePath = '/tmp/.pgpass'; |
||||
const pgpassFileBody = `${peerIPAddress}:${commonDBPort}:${commonDBName}:${commonDBUser}:${commonPassword.replace( |
||||
/:/g, |
||||
'\\:', |
||||
)}`;
|
||||
|
||||
stdoutVar({ pgpassFilePath, pgpassFileBody }); |
||||
|
||||
try { |
||||
sub('write_file', { |
||||
subModuleName: 'Storage', |
||||
subParams: { |
||||
body: pgpassFileBody, |
||||
file: pgpassFilePath, |
||||
mode: '0600', |
||||
overwrite: 1, |
||||
secure: 1, |
||||
}, |
||||
}); |
||||
} catch (subError) { |
||||
stderr(`Failed to write ${pgpassFilePath}; CAUSE: ${subError}`); |
||||
|
||||
response.status(500).send(); |
||||
|
||||
return; |
||||
} |
||||
|
||||
try { |
||||
const [rawIsPeerDBReachable] = sub('call', { |
||||
subModuleName: 'System', |
||||
subParams: { |
||||
shell_call: `PGPASSFILE="${pgpassFilePath}" ${SERVER_PATHS.usr.bin.psql.self} --host ${peerIPAddress} --port ${commonDBPort} --dbname ${commonDBName} --username ${commonDBUser} --no-password --tuples-only --no-align --command "SELECT 1"`, |
||||
}, |
||||
}).stdout as [output: string, returnCode: number]; |
||||
|
||||
isPeerDBReachable = rawIsPeerDBReachable === '1'; |
||||
} catch (subError) { |
||||
stderr(`Failed to test connection to peer database; CAUSE: ${subError}`); |
||||
} |
||||
|
||||
try { |
||||
rm(pgpassFilePath); |
||||
} catch (fsError) { |
||||
stderr(`Failed to remove ${pgpassFilePath}; CAUSE: ${fsError}`); |
||||
|
||||
response.status(500).send(); |
||||
|
||||
return; |
||||
} |
||||
|
||||
stdoutVar({ isPeerDBReachable }); |
||||
|
||||
if (!isPeerDBReachable) { |
||||
stderr( |
||||
`Cannot connect to peer database; please verify database credentials.`, |
||||
); |
||||
|
||||
response.status(400).send(); |
||||
|
||||
return; |
||||
} |
||||
|
||||
const localHostUUID = getLocalHostUUID(); |
||||
|
||||
try { |
||||
const { |
||||
database: { |
||||
[localHostUUID]: { port: rawLocalDBPort }, |
||||
}, |
||||
} = getAnvilData({ database: true }) as { database: DatabaseHash }; |
||||
|
||||
localDBPort = sanitize(rawLocalDBPort, 'number'); |
||||
} catch (subError) { |
||||
stderr(`Failed to get local database data from hash; CAUSE: ${subError}`); |
||||
|
||||
response.status(500).send(); |
||||
|
||||
return; |
||||
} |
||||
|
||||
const jobCommand = `${SERVER_PATHS.usr.sbin['striker-manage-peers'].self} --add --host-uuid ${peerHostUUID} --host ${peerIPAddress} --port ${commonDBPort} --ping ${commonPing}`; |
||||
const peerJobCommand = `${SERVER_PATHS.usr.sbin['striker-manage-peers'].self} --add --host-uuid ${localHostUUID} --host ${localIPAddress} --port ${localDBPort} --ping ${commonPing}`; |
||||
|
||||
try { |
||||
job({ |
||||
file: __filename, |
||||
job_command: jobCommand, |
||||
job_data: `password=${commonPassword} |
||||
peer_job_command=${peerJobCommand}`,
|
||||
job_description: 'job_0012', |
||||
job_name: 'striker-peer::add', |
||||
job_title: 'job_0011', |
||||
}); |
||||
} catch (subError) { |
||||
stderr(`Failed to add peer ${peerHostUUID}; CAUSE: ${subError}`); |
||||
|
||||
response.status(500).send(); |
||||
|
||||
return; |
||||
} |
||||
|
||||
response.status(201).send(); |
||||
}; |
@ -0,0 +1,42 @@ |
||||
import { RequestHandler } from 'express'; |
||||
|
||||
import SERVER_PATHS from '../../consts/SERVER_PATHS'; |
||||
|
||||
import { job } from '../../accessModule'; |
||||
import { toHostUUID } from '../../convertHostUUID'; |
||||
import { stderr } from '../../shell'; |
||||
|
||||
export const deleteHostConnection: RequestHandler< |
||||
unknown, |
||||
undefined, |
||||
DeleteHostConnectionRequestBody |
||||
> = (request, response) => { |
||||
const { body } = request; |
||||
const hostUUIDs = Object.keys(body); |
||||
|
||||
hostUUIDs.forEach((key) => { |
||||
const hostUUID = toHostUUID(key); |
||||
const peerHostUUIDs = body[key]; |
||||
|
||||
peerHostUUIDs.forEach((peerHostUUID) => { |
||||
try { |
||||
job({ |
||||
file: __filename, |
||||
job_command: `${SERVER_PATHS.usr.sbin['striker-manage-peers'].self} --remove --host-uuid ${peerHostUUID}`, |
||||
job_description: 'job_0014', |
||||
job_host_uuid: hostUUID, |
||||
job_name: 'striker-peer::delete', |
||||
job_title: 'job_0013', |
||||
}); |
||||
} catch (subError) { |
||||
stderr(`Failed to delete peer ${peerHostUUID}; CAUSE: ${subError}`); |
||||
|
||||
response.status(500).send(); |
||||
|
||||
return; |
||||
} |
||||
}); |
||||
}); |
||||
|
||||
response.status(204).send(); |
||||
}; |
@ -0,0 +1,11 @@ |
||||
type CreateHostConnectionRequestBody = { |
||||
dbName?: string; |
||||
ipAddress: string; |
||||
isPing?: boolean; |
||||
// Host password; same as database password.
|
||||
password: string; |
||||
port?: number; |
||||
sshPort?: number; |
||||
// database user.
|
||||
user?: string; |
||||
}; |
@ -0,0 +1,3 @@ |
||||
type DeleteHostConnectionRequestBody = { |
||||
[hostUUID: string]: string[]; |
||||
}; |
Loading…
Reference in new issue