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

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

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

Loading…
Cancel
Save