@hashgraph/solo
Version:
An opinionated CLI tool to deploy and manage private Hedera Networks.
82 lines (70 loc) • 3.06 kB
text/typescript
// SPDX-License-Identifier: Apache-2.0
import fs from 'node:fs';
import path from 'node:path';
import * as constants from '../../core/constants.js';
import {PathEx} from '../../business/utils/path-ex.js';
import {type SoloLogger} from '../../core/logging/solo-logger.js';
import {type K8Factory} from '../../integration/kube/k8-factory.js';
import {type K8} from '../../integration/kube/k8.js';
import {type Contexts} from '../../integration/kube/resources/context/contexts.js';
import {type ConfigMap} from '../../integration/kube/resources/config-map/config-map.js';
export class RemoteConfigCollector {
public constructor(
private readonly k8Factory: K8Factory,
private readonly logger: SoloLogger,
) {}
/**
* Sanitize a string for safe use as a filename on all platforms.
* Replaces characters invalid on Windows with underscores.
*/
private sanitizeFilename(input: string): string {
return input.replaceAll(/[^A-Za-z0-9._-]/g, '_');
}
public async collect(customOutputDirectory: string = ''): Promise<string> {
const outputDirectory: string = customOutputDirectory
? path.resolve(customOutputDirectory, 'remote-config')
: PathEx.join(constants.SOLO_LOGS_DIR, 'remote-config');
fs.mkdirSync(outputDirectory, {recursive: true});
const contexts: Contexts = this.k8Factory.default().contexts();
for (const context of contexts.list()) {
const k8: K8 = this.k8Factory.getK8(context);
try {
const configMaps: ConfigMap[] = await k8
.configMaps()
.listForAllNamespaces([constants.SOLO_REMOTE_CONFIGMAP_LABEL_SELECTOR]);
for (const configMap of configMaps) {
const namespace: string = configMap.namespace.name;
const outputFileName: string = `${this.sanitizeFilename(context)}-${this.sanitizeFilename(namespace)}-${this.sanitizeFilename(configMap.name)}.json`;
const outputFilePath: string = PathEx.join(outputDirectory, outputFileName);
fs.writeFileSync(
outputFilePath,
JSON.stringify(this.toSerializableConfigMap(configMap), undefined, 2),
'utf8',
);
this.logger.info(`Saved solo-remote-config for ${context}/${namespace} to ${outputFilePath}`);
}
} catch (error) {
this.logger.warn(`Failed to get solo-remote-config in context ${context}: ${(error as Error).message}`);
}
}
return outputDirectory;
}
private toSerializableConfigMap(configMap: ConfigMap): Record<string, unknown> {
const output: Record<string, unknown> = {
name: configMap.name,
namespace: configMap.namespace.name,
labels: configMap.labels ?? {},
data: {} as Record<string, unknown>,
};
if (configMap.data) {
for (const [key, value] of Object.entries(configMap.data)) {
try {
(output.data as Record<string, unknown>)[key] = JSON.parse(value);
} catch {
(output.data as Record<string, unknown>)[key] = value;
}
}
}
return output;
}
}