test enhancements

This commit is contained in:
Raven Scott
2025-11-24 09:06:29 -05:00
parent 46d359f18a
commit 074d8df3cc
15 changed files with 2354 additions and 165 deletions
+297
View File
@@ -0,0 +1,297 @@
/**
* Container configuration builder utilities
* Extracted from deployContainer to improve modularity
*/
import * as validation from './validation.js';
/**
* Build basic container configuration
* @param {Object} args - Deployment arguments
* @returns {Object} - Container configuration
*/
export function buildBasicConfig(args) {
const containerConfig = {
name: args.containerName,
Image: args.image,
};
// Basic settings
if (args.command) {
containerConfig.Cmd = args.command.split(' ');
}
if (args.entrypoint) {
containerConfig.Entrypoint = args.entrypoint.split(' ');
}
if (args.workingDir) {
containerConfig.WorkingDir = args.workingDir;
}
return containerConfig;
}
/**
* Build environment variables configuration
* @param {Array} envVars - Environment variables array
* @returns {Array} - Formatted environment variables
*/
export function buildEnvConfig(envVars) {
if (!envVars || !Array.isArray(envVars)) {
return [];
}
return envVars
.filter(e => e.name && e.value !== undefined)
.map(e => {
const name = validation.sanitizeEnvVarName(e.name);
const value = validation.sanitizeEnvVarValue(e.value);
return name && value !== null ? `${name}=${value}` : null;
})
.filter(e => e !== null);
}
/**
* Build labels configuration
* @param {Object} labels - Labels object
* @returns {Object} - Sanitized labels
*/
export function buildLabelsConfig(labels) {
if (!labels || typeof labels !== 'object') {
return {};
}
const sanitizedLabels = {};
for (const [key, value] of Object.entries(labels)) {
const sanitizedKey = validation.sanitizeLabelKey(key);
const sanitizedValue = validation.sanitizeLabelValue(value);
if (sanitizedKey && sanitizedValue !== null) {
sanitizedLabels[sanitizedKey] = sanitizedValue;
}
}
return sanitizedLabels;
}
/**
* Build networking configuration
* @param {Object} args - Deployment arguments
* @returns {Object} - HostConfig networking settings
*/
export function buildNetworkingConfig(args) {
const hostConfig = {
NetworkMode: args.networkMode || 'bridge',
};
// Port bindings
if (args.ports && Array.isArray(args.ports)) {
hostConfig.PortBindings = {};
args.ports.forEach((portStr) => {
const sanitizedPort = validation.sanitizeString(portStr, 50);
if (validation.isValidPortMapping(sanitizedPort)) {
if (sanitizedPort.includes(':')) {
const [hostPort, rest] = sanitizedPort.split(':');
const [containerPort, protocol] = rest.split('/');
hostConfig.PortBindings[`${containerPort}/${protocol || 'tcp'}`] = [{ HostPort: hostPort }];
} else {
const [containerPort, protocol] = sanitizedPort.split('/');
hostConfig.PortBindings[`${containerPort}/${protocol || 'tcp'}`] = [{ HostPort: containerPort }];
}
}
});
}
// DNS
if (args.dns && Array.isArray(args.dns)) {
hostConfig.Dns = args.dns
.map(dns => validation.sanitizeString(dns, 50))
.filter(dns => validation.isValidDnsServer(dns));
}
// Extra hosts
if (args.extraHosts && Array.isArray(args.extraHosts)) {
hostConfig.ExtraHosts = args.extraHosts
.map(host => validation.sanitizeString(host, 200))
.filter(host => host.includes(':'));
}
return hostConfig;
}
/**
* Build volumes configuration
* @param {Object} args - Deployment arguments
* @returns {Object} - HostConfig volumes settings
*/
export function buildVolumesConfig(args) {
const hostConfig = {};
// Volumes
if (args.volumes && Array.isArray(args.volumes)) {
hostConfig.Binds = args.volumes
.map(v => validation.sanitizeString(v, 500))
.filter(v => v && validation.isValidVolumeMount(v));
}
// Tmpfs
if (args.tmpfs && Array.isArray(args.tmpfs)) {
hostConfig.Tmpfs = {};
args.tmpfs.forEach(tmpfsStr => {
const sanitized = validation.sanitizeString(tmpfsStr, 100);
const [path, ...opts] = sanitized.split(':');
if (path) {
hostConfig.Tmpfs[path] = opts.join(':') || '';
}
});
}
return hostConfig;
}
/**
* Build resources configuration
* @param {Object} args - Deployment arguments
* @returns {Object} - HostConfig resources settings
*/
export function buildResourcesConfig(args) {
const hostConfig = {};
// CPU limits
if (args.cpuLimit) {
hostConfig.NanoCpus = args.cpuLimit * 1000000000; // Convert to nanoseconds
}
if (args.cpuReservation) {
hostConfig.CpuQuota = args.cpuReservation * 1000000000;
}
if (args.cpuShares) {
hostConfig.CpuShares = args.cpuShares;
}
// Memory limits
if (args.memoryLimit) {
hostConfig.Memory = args.memoryLimit * 1024 * 1024; // Convert MB to bytes
}
if (args.memoryReservation) {
hostConfig.MemoryReservation = args.memoryReservation * 1024 * 1024;
}
if (args.memorySwap !== undefined && args.memorySwap !== null) {
hostConfig.MemorySwap = args.memorySwap === -1 ? -1 : args.memorySwap * 1024 * 1024;
}
// Devices
if (args.devices && Array.isArray(args.devices)) {
hostConfig.Devices = args.devices
.map(deviceStr => validation.sanitizeString(deviceStr, 200))
.map(deviceStr => {
const parts = deviceStr.split(':');
return {
PathOnHost: parts[0],
PathInContainer: parts[1] || parts[0],
CgroupPermissions: parts[2] || 'rwm'
};
});
}
return hostConfig;
}
/**
* Build security configuration
* @param {Object} args - Deployment arguments
* @returns {Object} - HostConfig security settings
*/
export function buildSecurityConfig(args) {
const hostConfig = {};
// Privileged
if (args.privileged === true) {
hostConfig.Privileged = true;
}
// Capabilities
if (args.capabilities && Array.isArray(args.capabilities)) {
hostConfig.CapAdd = args.capabilities
.map(cap => validation.sanitizeString(cap, 50).toUpperCase())
.filter(cap => /^[A-Z_]+$/.test(cap));
}
// Security options
if (args.securityOpts && Array.isArray(args.securityOpts)) {
hostConfig.SecurityOpt = args.securityOpts
.map(opt => validation.sanitizeString(opt, 200));
}
return hostConfig;
}
/**
* Build runtime configuration
* @param {Object} args - Deployment arguments
* @returns {Object} - HostConfig runtime settings
*/
export function buildRuntimeConfig(args) {
const hostConfig = {};
// Restart policy
if (args.restartPolicy) {
hostConfig.RestartPolicy = {
Name: args.restartPolicy,
MaximumRetryCount: args.restartMaxRetries || 0
};
}
// Auto remove
if (args.autoRemove === true) {
hostConfig.AutoRemove = true;
}
// Init process
if (args.init === true) {
hostConfig.Init = true;
}
// Sysctls
if (args.sysctls && typeof args.sysctls === 'object') {
hostConfig.Sysctls = {};
for (const [key, value] of Object.entries(args.sysctls)) {
const sanitizedKey = validation.sanitizeString(key, 100);
const sanitizedValue = validation.sanitizeString(String(value), 100);
if (sanitizedKey && sanitizedValue) {
hostConfig.Sysctls[sanitizedKey] = sanitizedValue;
}
}
}
// Ulimits
if (args.ulimits && Array.isArray(args.ulimits)) {
hostConfig.Ulimits = args.ulimits
.map(ulimit => {
if (typeof ulimit === 'object' && ulimit.Name) {
return {
Name: validation.sanitizeString(ulimit.Name, 50),
Soft: validation.validateNumber(ulimit.Soft, 0, Infinity),
Hard: validation.validateNumber(ulimit.Hard, 0, Infinity)
};
}
return null;
})
.filter(ulimit => ulimit !== null);
}
// OOM kill disable
if (args.oomKillDisable === true) {
hostConfig.OomKillDisable = true;
}
// PIDs limit
if (args.pidsLimit !== undefined && args.pidsLimit !== null) {
hostConfig.PidsLimit = args.pidsLimit === -1 ? 0 : args.pidsLimit;
}
// Shared memory size
if (args.shmSize) {
hostConfig.ShmSize = args.shmSize * 1024 * 1024; // Convert MB to bytes
}
return hostConfig;
}