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

main
Tsu-ba-me 1 year ago
parent e9f89baba3
commit abd2780d02
  1. 350
      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,221 +40,248 @@ export const buildManifest = async (
stdout('Begin building install manifest.'); stdout('Begin building install manifest.');
const dns = sanitize(rawDns, 'string'); const dns = sanitize(rawDns, 'string');
assert(REP_IPV4_CSV.test(dns), `DNS must be an IPv4 CSV; got [${dns}]`);
const domain = sanitize(rawDomain, 'string'); const domain = sanitize(rawDomain, 'string');
assert(
REP_PEACEFUL_STRING.test(domain),
`Domain must be a peaceful string; got [${domain}]`,
);
const manifestUuid = sanitize(rawManifestUuid, 'string'); const manifestUuid = sanitize(rawManifestUuid, 'string');
assert(
manifestUuid === 'new' || REP_UUID.test(manifestUuid),
`Manifest UUID must be a UUIDv4; got [${manifestUuid}]`,
);
const mtu = sanitize(rawMtu, 'number'); const mtu = sanitize(rawMtu, 'number');
assert(REP_INTEGER.test(String(mtu)), `MTU must be an integer; got [${mtu}]`);
const ntp = sanitize(rawNtp, 'string'); const ntp = sanitize(rawNtp, 'string');
const prefix = sanitize(rawPrefix, 'string');
const sequence = sanitize(rawSequence, 'number');
if (ntp) { try {
assert(REP_IPV4_CSV.test(ntp), `NTP must be an IPv4 CSV; got [${ntp}]`); assert(REP_IPV4_CSV.test(dns), `DNS must be an IPv4 CSV; got [${dns}]`);
}
const prefix = sanitize(rawPrefix, 'string'); assert(
assert( REP_PEACEFUL_STRING.test(domain),
REP_PEACEFUL_STRING.test(prefix), `Domain must be a peaceful string; got [${domain}]`,
`Prefix must be a peaceful string; got [${prefix}]`, );
);
const sequence = sanitize(rawSequence, 'number'); assert(
assert( manifestUuid === 'new' || REP_UUID.test(manifestUuid),
REP_INTEGER.test(String(sequence)), `Manifest UUID must be a UUIDv4; got [${manifestUuid}]`,
`Sequence must be an integer; got [${sequence}]`, );
);
const { counts: networkCountContainer, networks: networkContainer } =
Object.values(networkList).reduce<{
counts: Record<string, number>;
networks: Record<string, string>;
}>(
(
previous,
{
networkGateway: rawGateway,
networkMinIp: rawMinIp,
networkNumber: rawNetworkNumber,
networkSubnetMask: rawSubnetMask,
networkType: rawNetworkType,
},
) => {
const networkType = sanitize(rawNetworkType, 'string');
assert(
REP_PEACEFUL_STRING.test(networkType),
`Network type must be a peaceful string; got [${networkType}]`,
);
const networkNumber = sanitize(rawNetworkNumber, 'number'); assert(Number.isSafeInteger(mtu), `MTU must be an integer; got [${mtu}]`);
assert(
REP_INTEGER.test(String(networkNumber)),
`Network number must be an integer; got [${networkNumber}]`,
);
const networkId = `${networkType}${networkNumber}`; if (ntp) {
assert(REP_IPV4_CSV.test(ntp), `NTP must be an IPv4 CSV; got [${ntp}]`);
}
const gateway = sanitize(rawGateway, 'string'); assert(
REP_PEACEFUL_STRING.test(prefix),
`Prefix must be a peaceful string; got [${prefix}]`,
);
if (networkType === 'ifn') { assert(
assert( Number.isSafeInteger(sequence),
REP_IPV4.test(gateway), `Sequence must be an integer; got [${sequence}]`,
`Gateway of ${networkId} must be an IPv4; got [${gateway}]`, );
); } catch (error) {
} throw new Error(`Failed to assert build manifest input; CAUSE: ${error}`);
}
const minIp = sanitize(rawMinIp, 'string'); const netCounts: Record<string, number> = {};
assert( const netConfigs: Record<string, string> = {};
REP_IPV4.test(minIp),
`Minimum IP of ${networkId} must be an IPv4; got [${minIp}]`, try {
); Object.values(networkList).forEach((network) => {
const {
networkGateway: rawGateway,
networkMinIp: rawMinIp,
networkNumber: rawNetworkNumber,
networkSubnetMask: rawSubnetMask,
networkType: rawNetworkType,
} = network;
const gateway = sanitize(rawGateway, 'string');
const minIp = sanitize(rawMinIp, 'string');
const networkNumber = sanitize(rawNetworkNumber, 'number');
const networkType = sanitize(rawNetworkType, 'string');
const subnetMask = sanitize(rawSubnetMask, 'string');
const networkId = `${networkType}${networkNumber}`;
const subnetMask = sanitize(rawSubnetMask, 'string'); assert(
REP_PEACEFUL_STRING.test(networkType),
`Network type must be a peaceful string; got [${networkType}]`,
);
assert(
Number.isSafeInteger(networkNumber),
`Network number must be an integer; got [${networkNumber}]`,
);
assert(
REP_IPV4.test(minIp),
`Minimum IP of ${networkId} must be an IPv4; got [${minIp}]`,
);
assert(
REP_IPV4.test(subnetMask),
`Subnet mask of ${networkId} must be an IPv4; got [${subnetMask}]`,
);
if (networkType === 'ifn') {
assert( assert(
REP_IPV4.test(subnetMask), REP_IPV4.test(gateway),
`Subnet mask of ${networkId} must be an IPv4; got [${subnetMask}]`, `Gateway of ${networkId} must be an IPv4; got [${gateway}]`,
); );
}
const { counts: countContainer, networks: networkContainer } = previous; const countKey = `${networkType}_count`;
const countValue = netCounts[countKey] ?? 0;
const countKey = `${networkType}_count`;
const countValue = countContainer[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;
Object.values(networks).forEach(
({
networkIp: rawIp,
networkNumber: rawNetworkNumber,
networkType: rawNetworkType,
}) => {
const networkType = sanitize(rawNetworkType, 'string');
assert(
REP_PEACEFUL_STRING.test(networkType),
`Network type must be a peaceful string; got [${networkType}]`,
);
const networkNumber = sanitize(rawNetworkNumber, 'number'); try {
assert( Object.values(networks).forEach(
REP_INTEGER.test(String(networkNumber)), ({
`Network number must be an integer; got [${networkNumber}]`, networkIp: rawIp,
); networkNumber: rawNetworkNumber,
networkType: rawNetworkType,
}) => {
const ip = sanitize(rawIp, 'string');
const networkNumber = sanitize(rawNetworkNumber, 'number');
const networkType = sanitize(rawNetworkType, 'string');
const networkId = `${networkType}${networkNumber}`; const networkId = `${networkType}${networkNumber}`;
const ip = sanitize(rawIp, 'string'); assert(
assert( REP_PEACEFUL_STRING.test(networkType),
REP_IPV4.test(ip), `Network type must be a peaceful string; got [${networkType}]`,
`IP of host network ${networkId} must be an IPv4; got [${ip}]`, );
);
const networkIpKey = `${hostId}_${networkId}_ip`; assert(
Number.isSafeInteger(networkNumber),
`Network number must be an integer; got [${networkNumber}]`,
);
previous[networkIpKey] = ip; assert(
}, REP_IPV4.test(ip),
); `IP of host network ${networkId} must be an IPv4; got [${ip}]`,
);
Object.values(fences).forEach( const networkIpKey = `${hostId}_${networkId}_ip`;
({ fenceName: rawFenceName, fencePort: rawPort }) => {
const fenceName = sanitize(rawFenceName, 'string');
assert(
REP_PEACEFUL_STRING.test(fenceName),
`Fence name must be a peaceful string; got [${fenceName}]`,
);
const fenceKey = `${hostId}_fence_${fenceName}`; hosts[networkIpKey] = ip;
},
);
} catch (error) {
throw new Error(
`Failed to build [${hostId}] networks for manifest; CAUSE: ${error}`,
);
}
const port = sanitize(rawPort, 'string'); try {
assert( if (fences) {
REP_PEACEFUL_STRING.test(port), Object.values(fences).forEach(
`Port of ${fenceName} must be a peaceful string; got [${port}]`, ({ fenceName: rawFenceName, fencePort: rawPort }) => {
); const fenceName = sanitize(rawFenceName, 'string');
const port = sanitize(rawPort, 'string');
previous[fenceKey] = port; assert(
}, REP_PEACEFUL_STRING.test(fenceName),
); `Fence name must be a peaceful string; got [${fenceName}]`,
);
assert(
REP_PEACEFUL_STRING.test(port),
`Port of ${fenceName} must be a peaceful string; got [${port}]`,
);
Object.values(upses).forEach( const fenceKey = `${hostId}_fence_${fenceName}`;
({ isUsed: rawIsUsed, upsName: rawUpsName }) => {
const upsName = sanitize(rawUpsName, 'string'); hosts[fenceKey] = port;
assert( },
REP_PEACEFUL_STRING.test(upsName),
`UPS name must be a peaceful string; got [${upsName}]`,
); );
}
} catch (error) {
throw new Error(
`Failed to build [${hostId}] fences for manifest; CAUSE: ${error}`,
);
}
const upsKey = `${hostId}_ups_${upsName}`; try {
if (upses) {
Object.values(upses).forEach(
({ isUsed: rawIsUsed, upsName: rawUpsName }) => {
const upsName = sanitize(rawUpsName, 'string');
const isUsed = sanitize(rawIsUsed, 'boolean'); assert(
REP_PEACEFUL_STRING.test(upsName),
`UPS name must be a peaceful string; got [${upsName}]`,
);
if (isUsed) { const upsKey = `${hostId}_ups_${upsName}`;
previous[upsKey] = 'checked';
} const isUsed = sanitize(rawIsUsed, 'boolean');
},
);
return previous; if (isUsed) {
}, hosts[upsKey] = 'checked';
{}, }
); },
);
}
} 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