fix(striker-ui-api): add command to run manifest

main
Tsu-ba-me 2 years ago
parent b7b4e13028
commit a50321eb9f
  1. 1
      striker-ui-api/src/lib/consts/SERVER_PATHS.ts
  2. 1
      striker-ui-api/src/lib/request_handlers/command/index.ts
  3. 193
      striker-ui-api/src/lib/request_handlers/command/runManifest.ts
  4. 2
      striker-ui-api/src/routes/command.ts
  5. 23
      striker-ui-api/src/types/APIManifest.d.ts
  6. 22
      striker-ui-api/src/types/GetAnvilDataFunction.d.ts

@ -19,6 +19,7 @@ const EMPTY_SERVER_PATHS: ServerPath = {
'anvil-access-module': {}, 'anvil-access-module': {},
'anvil-configure-host': {}, 'anvil-configure-host': {},
'anvil-get-server-screenshot': {}, 'anvil-get-server-screenshot': {},
'anvil-join-anvil': {},
'anvil-manage-keys': {}, 'anvil-manage-keys': {},
'anvil-manage-power': {}, 'anvil-manage-power': {},
'anvil-provision-server': {}, 'anvil-provision-server': {},

@ -1,4 +1,5 @@
export * from './getHostSSH'; export * from './getHostSSH';
export * from './poweroffHost'; export * from './poweroffHost';
export * from './rebootHost'; export * from './rebootHost';
export * from './runManifest';
export * from './updateSystem'; export * from './updateSystem';

@ -0,0 +1,193 @@
import assert from 'assert';
import { RequestHandler } from 'express';
import { REP_PEACEFUL_STRING, REP_UUID } from '../../consts/REG_EXP_PATTERNS';
import SERVER_PATHS from '../../consts/SERVER_PATHS';
import { getAnvilData, job, sub } from '../../accessModule';
import { sanitize } from '../../sanitize';
import { stderr } from '../../shell';
export const runManifest: RequestHandler<
{ manifestUuid: string },
undefined,
RunManifestRequestBody
> = (request, response) => {
const {
params: { manifestUuid },
body: {
debug = 2,
description: rawDescription,
hosts: rawHostList = {},
password: rawPassword,
} = {},
} = request;
const description = sanitize(rawDescription, 'string');
const password = sanitize(rawPassword, 'string');
const hostList: ManifestExecutionHostList = {};
const handleAssertError = (assertError: unknown) => {
stderr(
`Failed to assert value when trying to run manifest ${manifestUuid}; CAUSE: ${assertError}`,
);
response.status(400).send();
};
try {
assert(
REP_PEACEFUL_STRING.test(description),
`Description must be a peaceful string; got: [${description}]`,
);
assert(
REP_PEACEFUL_STRING.test(password),
`Password must be a peaceful string; got [${password}]`,
);
const uniqueList: Record<string, boolean | undefined> = {};
const isHostListUnique = !Object.values(rawHostList).some(
({ hostNumber, hostType, hostUuid }) => {
const hostId = `${hostType}${hostNumber}`;
assert(
/^node[12]$/.test(hostId),
`Host ID must be "node" followed by 1 or 2; got [${hostId}]`,
);
assert(
REP_UUID.test(hostUuid),
`Host UUID assigned to ${hostId} must be a UUIDv4; got [${hostUuid}]`,
);
const isIdDuplicate = Boolean(uniqueList[hostId]);
const isUuidDuplicate = Boolean(uniqueList[hostUuid]);
uniqueList[hostId] = true;
uniqueList[hostUuid] = true;
hostList[hostId] = { hostNumber, hostType, hostUuid, hostId };
return isIdDuplicate || isUuidDuplicate;
},
);
assert(isHostListUnique, `Each entry in hosts must be unique`);
} catch (assertError) {
handleAssertError(assertError);
return;
}
let rawHostListData: AnvilDataHostListHash | undefined;
let rawManifestListData: AnvilDataManifestListHash | undefined;
let rawSysData: AnvilDataSysHash | undefined;
try {
({ manifests: rawManifestListData, sys: rawSysData } = getAnvilData<{
hosts?: AnvilDataHostListHash;
manifests?: AnvilDataManifestListHash;
sys?: AnvilDataSysHash;
}>(
{ hosts: true, manifests: true, sys: true },
{
predata: [
['Database->get_hosts'],
[
'Striker->load_manifest',
{
debug,
manifest_uuid: manifestUuid,
},
],
],
},
));
} catch (subError) {
stderr(
`Failed to get install manifest ${manifestUuid}; CAUSE: ${subError}`,
);
response.status(500).send();
return;
}
if (!rawHostListData || !rawManifestListData || !rawSysData) {
response.status(404).send();
return;
}
const { host_uuid: hostUuidMapToData } = rawHostListData;
const {
manifest_uuid: {
[manifestUuid]: {
parsed: { name: manifestName },
},
},
} = rawManifestListData;
const { hosts: { by_uuid: mapToHostNameData = {} } = {} } = rawSysData;
const joinAnJobs: DBJobParams[] = [];
let anParams: Record<string, string> | undefined;
try {
anParams = Object.values(hostList).reduce<Record<string, string>>(
(previous, { hostId = '', hostUuid }) => {
const hostName = mapToHostNameData[hostUuid];
const { anvil_name: anName } = hostUuidMapToData[hostUuid];
assert(
anName && anName !== manifestName,
`Host ${hostName} cannot be used for ${manifestName} because it belongs to ${anName}`,
);
joinAnJobs.push({
debug,
file: __filename,
job_command: SERVER_PATHS.usr.sbin['anvil-join-anvil'].self,
job_data: `as_machine=${hostId},manifest_uuid=${manifestUuid},anvil_uuid=`,
job_description: 'job_0073',
job_host_uuid: hostUuid,
job_name: `join_anvil::${hostId}`,
job_progress: 0,
job_title: 'job_0072',
});
previous[`anvil_${hostId}_host_uuid`] = hostUuid;
return previous;
},
{
anvil_description: description,
anvil_name: manifestName,
anvil_password: password,
},
);
} catch (assertError) {
handleAssertError(assertError);
return;
}
try {
const [newAnUuid] = sub('insert_or_update_anvils', { subParams: anParams })
.stdout as [string];
joinAnJobs.forEach((jobParams) => {
jobParams.job_data += newAnUuid;
job(jobParams);
});
} catch (subError) {
stderr(`Failed to record new anvil node entry; CAUSE: ${subError}`);
response.status(500).send();
return;
}
response.status(204).send();
};

@ -4,6 +4,7 @@ import {
getHostSSH, getHostSSH,
poweroffHost, poweroffHost,
rebootHost, rebootHost,
runManifest,
updateSystem, updateSystem,
} from '../lib/request_handlers/command'; } from '../lib/request_handlers/command';
@ -13,6 +14,7 @@ router
.put('/inquire-host', getHostSSH) .put('/inquire-host', getHostSSH)
.put('/poweroff-host', poweroffHost) .put('/poweroff-host', poweroffHost)
.put('/reboot-host', rebootHost) .put('/reboot-host', rebootHost)
.put('/run-manifest/:manifestUuid', runManifest)
.put('/update-system', updateSystem); .put('/update-system', updateSystem);
export default router; export default router;

@ -72,7 +72,19 @@ type ManifestDetail = {
sequence: number; sequence: number;
}; };
type BuildManifestRequestBody = Omit<ManifestDetail, 'name'>; type ManifestExecutionHost = {
anName?: string;
anUuid?: string;
hostId?: string;
hostName?: string;
hostNumber: number;
hostType: string;
hostUuid: string;
};
type ManifestExecutionHostList = {
[hostId: string]: ManifestExecutionHost;
};
type ManifestTemplate = { type ManifestTemplate = {
domain: string; domain: string;
@ -91,3 +103,12 @@ type ManifestTemplate = {
}; };
}; };
}; };
type BuildManifestRequestBody = Omit<ManifestDetail, 'name'>;
type RunManifestRequestBody = {
debug?: number;
description: string;
hosts: ManifestExecutionHostList;
password: string;
};

@ -13,6 +13,21 @@ type AnvilDataDatabaseHash = {
}; };
}; };
type AnvilDataHostListHash = {
host_uuid: {
[hostUuid: string]: {
anvil_name?: string;
anvil_uuid?: string;
host_ipmi: string;
host_key: string;
host_name: string;
host_status: string;
host_type: string;
short_host_name: string;
};
};
};
type AnvilDataManifestListHash = { type AnvilDataManifestListHash = {
manifest_uuid: { manifest_uuid: {
[manifestUUID: string]: { [manifestUUID: string]: {
@ -81,6 +96,13 @@ type AnvilDataManifestListHash = {
} }
>; >;
type AnvilDataSysHash = {
hosts?: {
by_uuid: { [hostUuid: string]: string };
by_name: { [hostName: string]: string };
};
};
type AnvilDataUPSHash = { type AnvilDataUPSHash = {
[upsName: string]: { [upsName: string]: {
agent: string; agent: string;

Loading…
Cancel
Save