fix(striker-ui-api): allow create manifest with no fences or UPSes

main
Tsu-ba-me 1 year ago
parent e9f89baba3
commit abd2780d02
  1. 180
      striker-ui-api/src/lib/request_handlers/manifest/buildManifest.ts
  2. 6
      striker-ui-api/src/lib/request_handlers/manifest/createManifest.ts
  3. 6
      striker-ui-api/src/lib/request_handlers/manifest/updateManifest.ts
  4. 10
      striker-ui-api/src/lib/shell.ts

@ -2,7 +2,6 @@ import assert from 'assert';
import { RequestHandler } from 'express'; import { RequestHandler } from 'express';
import { import {
REP_INTEGER,
REP_IPV4, REP_IPV4,
REP_IPV4_CSV, REP_IPV4_CSV,
REP_PEACEFUL_STRING, REP_PEACEFUL_STRING,
@ -41,168 +40,173 @@ export const buildManifest = async (
stdout('Begin building install manifest.'); stdout('Begin building install manifest.');
const dns = sanitize(rawDns, 'string'); const dns = sanitize(rawDns, 'string');
const domain = sanitize(rawDomain, 'string');
const manifestUuid = sanitize(rawManifestUuid, 'string');
const mtu = sanitize(rawMtu, 'number');
const ntp = sanitize(rawNtp, 'string');
const prefix = sanitize(rawPrefix, 'string');
const sequence = sanitize(rawSequence, 'number');
try {
assert(REP_IPV4_CSV.test(dns), `DNS must be an IPv4 CSV; got [${dns}]`); assert(REP_IPV4_CSV.test(dns), `DNS must be an IPv4 CSV; got [${dns}]`);
const domain = sanitize(rawDomain, 'string');
assert( assert(
REP_PEACEFUL_STRING.test(domain), REP_PEACEFUL_STRING.test(domain),
`Domain must be a peaceful string; got [${domain}]`, `Domain must be a peaceful string; got [${domain}]`,
); );
const manifestUuid = sanitize(rawManifestUuid, 'string');
assert( assert(
manifestUuid === 'new' || REP_UUID.test(manifestUuid), manifestUuid === 'new' || REP_UUID.test(manifestUuid),
`Manifest UUID must be a UUIDv4; got [${manifestUuid}]`, `Manifest UUID must be a UUIDv4; got [${manifestUuid}]`,
); );
const mtu = sanitize(rawMtu, 'number'); assert(Number.isSafeInteger(mtu), `MTU must be an integer; got [${mtu}]`);
assert(REP_INTEGER.test(String(mtu)), `MTU must be an integer; got [${mtu}]`);
const ntp = sanitize(rawNtp, 'string');
if (ntp) { if (ntp) {
assert(REP_IPV4_CSV.test(ntp), `NTP must be an IPv4 CSV; got [${ntp}]`); assert(REP_IPV4_CSV.test(ntp), `NTP must be an IPv4 CSV; got [${ntp}]`);
} }
const prefix = sanitize(rawPrefix, 'string');
assert( assert(
REP_PEACEFUL_STRING.test(prefix), REP_PEACEFUL_STRING.test(prefix),
`Prefix must be a peaceful string; got [${prefix}]`, `Prefix must be a peaceful string; got [${prefix}]`,
); );
const sequence = sanitize(rawSequence, 'number');
assert( assert(
REP_INTEGER.test(String(sequence)), Number.isSafeInteger(sequence),
`Sequence must be an integer; got [${sequence}]`, `Sequence must be an integer; got [${sequence}]`,
); );
} catch (error) {
throw new Error(`Failed to assert build manifest input; CAUSE: ${error}`);
}
const { counts: networkCountContainer, networks: networkContainer } = const netCounts: Record<string, number> = {};
Object.values(networkList).reduce<{ const netConfigs: Record<string, string> = {};
counts: Record<string, number>;
networks: Record<string, string>; try {
}>( Object.values(networkList).forEach((network) => {
( const {
previous,
{
networkGateway: rawGateway, networkGateway: rawGateway,
networkMinIp: rawMinIp, networkMinIp: rawMinIp,
networkNumber: rawNetworkNumber, networkNumber: rawNetworkNumber,
networkSubnetMask: rawSubnetMask, networkSubnetMask: rawSubnetMask,
networkType: rawNetworkType, networkType: rawNetworkType,
}, } = network;
) => {
const gateway = sanitize(rawGateway, 'string');
const minIp = sanitize(rawMinIp, 'string');
const networkNumber = sanitize(rawNetworkNumber, 'number');
const networkType = sanitize(rawNetworkType, 'string'); const networkType = sanitize(rawNetworkType, 'string');
const subnetMask = sanitize(rawSubnetMask, 'string');
const networkId = `${networkType}${networkNumber}`;
assert( assert(
REP_PEACEFUL_STRING.test(networkType), REP_PEACEFUL_STRING.test(networkType),
`Network type must be a peaceful string; got [${networkType}]`, `Network type must be a peaceful string; got [${networkType}]`,
); );
const networkNumber = sanitize(rawNetworkNumber, 'number');
assert( assert(
REP_INTEGER.test(String(networkNumber)), Number.isSafeInteger(networkNumber),
`Network number must be an integer; got [${networkNumber}]`, `Network number must be an integer; got [${networkNumber}]`,
); );
const networkId = `${networkType}${networkNumber}`;
const gateway = sanitize(rawGateway, 'string');
if (networkType === 'ifn') {
assert(
REP_IPV4.test(gateway),
`Gateway of ${networkId} must be an IPv4; got [${gateway}]`,
);
}
const minIp = sanitize(rawMinIp, 'string');
assert( assert(
REP_IPV4.test(minIp), REP_IPV4.test(minIp),
`Minimum IP of ${networkId} must be an IPv4; got [${minIp}]`, `Minimum IP of ${networkId} must be an IPv4; got [${minIp}]`,
); );
const subnetMask = sanitize(rawSubnetMask, 'string');
assert( assert(
REP_IPV4.test(subnetMask), REP_IPV4.test(subnetMask),
`Subnet mask of ${networkId} must be an IPv4; got [${subnetMask}]`, `Subnet mask of ${networkId} must be an IPv4; got [${subnetMask}]`,
); );
const { counts: countContainer, networks: networkContainer } = previous; if (networkType === 'ifn') {
assert(
REP_IPV4.test(gateway),
`Gateway of ${networkId} must be an IPv4; got [${gateway}]`,
);
}
const countKey = `${networkType}_count`; const countKey = `${networkType}_count`;
const countValue = countContainer[countKey] ?? 0; const countValue = netCounts[countKey] ?? 0;
countContainer[countKey] = countValue + 1; netCounts[countKey] = countValue + 1;
const gatewayKey = `${networkId}_gateway`; const gatewayKey = `${networkId}_gateway`;
const minIpKey = `${networkId}_network`; const minIpKey = `${networkId}_network`;
const subnetMaskKey = `${networkId}_subnet`; const subnetMaskKey = `${networkId}_subnet`;
networkContainer[gatewayKey] = gateway; netConfigs[gatewayKey] = gateway;
networkContainer[minIpKey] = minIp; netConfigs[minIpKey] = minIp;
networkContainer[subnetMaskKey] = subnetMask; netConfigs[subnetMaskKey] = subnetMask;
});
} catch (error) {
throw new Error(`Failed to build networks for manifest; CAUSE: ${error}`);
}
return previous; const hosts: Record<string, string> = {};
},
{ counts: {}, networks: {} },
);
const hostContainer = Object.values(hostList).reduce<Record<string, string>>( try {
( Object.values(hostList).forEach((host) => {
previous, const {
{
fences, fences,
hostNumber: rawHostNumber, hostNumber: rawHostNumber,
hostType: rawHostType, hostType: rawHostType,
ipmiIp: rawIpmiIp, ipmiIp: rawIpmiIp,
networks, networks,
upses, upses,
}, } = host;
) => {
const hostNumber = sanitize(rawHostNumber, 'number');
const hostType = sanitize(rawHostType, 'string'); const hostType = sanitize(rawHostType, 'string');
const ipmiIp = sanitize(rawIpmiIp, 'string');
const hostId = `${hostType}${hostNumber}`;
assert( assert(
REP_PEACEFUL_STRING.test(hostType), REP_PEACEFUL_STRING.test(hostType),
`Host type must be a peaceful string; got [${hostType}]`, `Host type must be a peaceful string; got [${hostType}]`,
); );
const hostNumber = sanitize(rawHostNumber, 'number');
assert( assert(
REP_INTEGER.test(String(hostNumber)), Number.isSafeInteger(hostNumber),
`Host number must be an integer; got [${hostNumber}]`, `Host number must be an integer; got [${hostNumber}]`,
); );
const hostId = `${hostType}${hostNumber}`;
const ipmiIp = sanitize(rawIpmiIp, 'string');
assert( assert(
REP_IPV4.test(ipmiIp), REP_IPV4.test(ipmiIp),
`IPMI IP of ${hostId} must be an IPv4; got [${ipmiIp}]`, `IPMI IP of ${hostId} must be an IPv4; got [${ipmiIp}]`,
); );
assert.ok(networks, `Host networks is required`);
const ipmiIpKey = `${hostId}_ipmi_ip`; const ipmiIpKey = `${hostId}_ipmi_ip`;
previous[ipmiIpKey] = ipmiIp; hosts[ipmiIpKey] = ipmiIp;
try {
Object.values(networks).forEach( Object.values(networks).forEach(
({ ({
networkIp: rawIp, networkIp: rawIp,
networkNumber: rawNetworkNumber, networkNumber: rawNetworkNumber,
networkType: rawNetworkType, networkType: rawNetworkType,
}) => { }) => {
const ip = sanitize(rawIp, 'string');
const networkNumber = sanitize(rawNetworkNumber, 'number');
const networkType = sanitize(rawNetworkType, 'string'); const networkType = sanitize(rawNetworkType, 'string');
const networkId = `${networkType}${networkNumber}`;
assert( assert(
REP_PEACEFUL_STRING.test(networkType), REP_PEACEFUL_STRING.test(networkType),
`Network type must be a peaceful string; got [${networkType}]`, `Network type must be a peaceful string; got [${networkType}]`,
); );
const networkNumber = sanitize(rawNetworkNumber, 'number');
assert( assert(
REP_INTEGER.test(String(networkNumber)), Number.isSafeInteger(networkNumber),
`Network number must be an integer; got [${networkNumber}]`, `Network number must be an integer; got [${networkNumber}]`,
); );
const networkId = `${networkType}${networkNumber}`;
const ip = sanitize(rawIp, 'string');
assert( assert(
REP_IPV4.test(ip), REP_IPV4.test(ip),
`IP of host network ${networkId} must be an IPv4; got [${ip}]`, `IP of host network ${networkId} must be an IPv4; got [${ip}]`,
@ -210,33 +214,50 @@ export const buildManifest = async (
const networkIpKey = `${hostId}_${networkId}_ip`; const networkIpKey = `${hostId}_${networkId}_ip`;
previous[networkIpKey] = ip; hosts[networkIpKey] = ip;
}, },
); );
} catch (error) {
throw new Error(
`Failed to build [${hostId}] networks for manifest; CAUSE: ${error}`,
);
}
try {
if (fences) {
Object.values(fences).forEach( Object.values(fences).forEach(
({ fenceName: rawFenceName, fencePort: rawPort }) => { ({ fenceName: rawFenceName, fencePort: rawPort }) => {
const fenceName = sanitize(rawFenceName, 'string'); const fenceName = sanitize(rawFenceName, 'string');
const port = sanitize(rawPort, 'string');
assert( assert(
REP_PEACEFUL_STRING.test(fenceName), REP_PEACEFUL_STRING.test(fenceName),
`Fence name must be a peaceful string; got [${fenceName}]`, `Fence name must be a peaceful string; got [${fenceName}]`,
); );
const fenceKey = `${hostId}_fence_${fenceName}`;
const port = sanitize(rawPort, 'string');
assert( assert(
REP_PEACEFUL_STRING.test(port), REP_PEACEFUL_STRING.test(port),
`Port of ${fenceName} must be a peaceful string; got [${port}]`, `Port of ${fenceName} must be a peaceful string; got [${port}]`,
); );
previous[fenceKey] = port; const fenceKey = `${hostId}_fence_${fenceName}`;
hosts[fenceKey] = port;
}, },
); );
}
} catch (error) {
throw new Error(
`Failed to build [${hostId}] fences for manifest; CAUSE: ${error}`,
);
}
try {
if (upses) {
Object.values(upses).forEach( Object.values(upses).forEach(
({ isUsed: rawIsUsed, upsName: rawUpsName }) => { ({ isUsed: rawIsUsed, upsName: rawUpsName }) => {
const upsName = sanitize(rawUpsName, 'string'); const upsName = sanitize(rawUpsName, 'string');
assert( assert(
REP_PEACEFUL_STRING.test(upsName), REP_PEACEFUL_STRING.test(upsName),
`UPS name must be a peaceful string; got [${upsName}]`, `UPS name must be a peaceful string; got [${upsName}]`,
@ -247,15 +268,20 @@ export const buildManifest = async (
const isUsed = sanitize(rawIsUsed, 'boolean'); const isUsed = sanitize(rawIsUsed, 'boolean');
if (isUsed) { if (isUsed) {
previous[upsKey] = 'checked'; hosts[upsKey] = 'checked';
} }
}, },
); );
}
return previous; } catch (error) {
}, throw new Error(
{}, `Failed to build ${hostId} UPSes for manifest; CAUSE: ${error}`,
); );
}
});
} catch (error) {
throw new Error(`Failed to build hosts for manifest; CAUSE: ${error}`);
}
let result: { name: string; uuid: string }; let result: { name: string; uuid: string };
@ -272,9 +298,9 @@ export const buildManifest = async (
ntp, ntp,
prefix, prefix,
sequence, sequence,
...networkCountContainer, ...netCounts,
...networkContainer, ...netConfigs,
...hostContainer, ...hosts,
}, },
], ],
pre: ['Striker'], pre: ['Striker'],
@ -282,8 +308,8 @@ export const buildManifest = async (
); );
result = { name, uuid }; result = { name, uuid };
} catch (subError) { } catch (error) {
throw new Error(`Failed to generate manifest; CAUSE: ${subError}`); throw new Error(`Failed to generate manifest; CAUSE: ${error}`);
} }
return result; return result;

@ -11,12 +11,12 @@ export const createManifest: RequestHandler = async (...handlerArgs) => {
try { try {
result = await buildManifest(...handlerArgs); result = await buildManifest(...handlerArgs);
} catch (buildError) { } catch (error) {
stderr(`Failed to create new install manifest; CAUSE ${buildError}`); stderr(`Failed to create new install manifest; CAUSE ${error}`);
let code = 500; let code = 500;
if (buildError instanceof AssertionError) { if (error instanceof AssertionError) {
code = 400; code = 400;
} }

@ -14,14 +14,14 @@ export const updateManifest: RequestHandler = async (...args) => {
try { try {
result = await buildManifest(...args); result = await buildManifest(...args);
} catch (buildError) { } catch (error) {
stderr( stderr(
`Failed to update install manifest ${manifestUuid}; CAUSE: ${buildError}`, `Failed to update install manifest ${manifestUuid}; CAUSE: ${error}`,
); );
let code = 500; let code = 500;
if (buildError instanceof AssertionError) { if (error instanceof AssertionError) {
code = 400; code = 400;
} }

@ -54,7 +54,15 @@ export const resolveGid = (id: number | string) => resolveId(id, 'group');
export const resolveUid = (id: number | string) => resolveId(id, 'passwd'); export const resolveUid = (id: number | string) => resolveId(id, 'passwd');
export const stderr = (message: string) => print(message, { stream: 'stderr' }); export const stderr = (message: string, error?: unknown) => {
let msg = message;
if (error instanceof Error) {
msg += `\n${error.stack}`;
}
print(msg, { stream: 'stderr' });
};
export const stdout = (message: string) => print(message); export const stdout = (message: string) => print(message);

Loading…
Cancel
Save