@mesh-tech/mesh-cli
Version:
CLI for Mesh platform development utilities
214 lines (213 loc) • 9.13 kB
JavaScript
import { logInfo, logSuccess } from "../../utils/log.js";
import { MeshCliError } from "../../utils/errors.js";
export const LOCAL_TENANT = "local";
export const LOCAL_ENV = "dev";
export const LOCAL_AWS_ENDPOINT = "http://localhost:4566";
export const LOCAL_AWS_REGION = "us-east-2";
export const ARTIFACTS_BUCKET = "mesh-local-artifacts";
export const DATA_BUCKET = "mesh-local-data";
export const APP_TENANTS_PARAM = `/mesh-platform/${LOCAL_TENANT}/${LOCAL_ENV}/app-tenants`;
const DATA_BUCKET_SEEDS = [
{
key: "samples/pipes/fragment-0001.json",
body: JSON.stringify({
id: "sample-0001",
kind: "pipes-fragment",
account: "0001",
asOf: "2026-01-01",
lines: [{ amount: "125.00", currency: "USD", description: "Sample credit" }],
}, null, 2),
},
];
export const LOCAL_AWS_CONFIG = {
endpoint: LOCAL_AWS_ENDPOINT,
region: LOCAL_AWS_REGION,
credentials: { accessKeyId: "test", secretAccessKey: "test" },
};
const AWS_CONFIG = LOCAL_AWS_CONFIG;
export function localAwsEnv() {
return {
AWS_ENDPOINT_URL: LOCAL_AWS_ENDPOINT,
AWS_REGION: LOCAL_AWS_REGION,
AWS_ACCESS_KEY_ID: "test",
AWS_SECRET_ACCESS_KEY: "test",
};
}
export function buildAppTenantsSeed() {
const tenantExport = {
albGroupName: LOCAL_TENANT,
certificateArn: "",
baseDomain: "localhost",
deployerRoleArn: "arn:aws:iam::000000000000:role/local-dev-deployer",
artifactsBucket: ARTIFACTS_BUCKET,
spicedb: {
endpoint: "localhost:50051",
presharedKeySecret: "local-dev-key",
},
zitadel: {
orgId: "local",
domain: "localhost:8080",
issuer: "http://localhost:8080",
},
};
return {
hubTenant: LOCAL_TENANT,
primaryTenant: tenantExport,
sharedTenants: {},
};
}
export const FABRIC_CHECK_PATH = `/mesh-platform/${LOCAL_TENANT}/${LOCAL_ENV}/.fabric-check`;
export const TEMPORAL_NAMESPACE = `${LOCAL_TENANT}-${LOCAL_ENV}`;
export const TEMPORAL_ADDRESS = "localhost:7233";
export async function seedTemporalNamespace() {
await ensureTemporalNamespace(TEMPORAL_NAMESPACE, `Mesh local tenant '${LOCAL_TENANT}' env '${LOCAL_ENV}' (seeded by mesh start)`);
}
export async function ensureTemporalNamespace(namespace, description) {
const { Connection } = await import("@temporalio/client");
const connection = await Connection.connect({ address: TEMPORAL_ADDRESS });
try {
await connection.workflowService.registerNamespace({
namespace,
description: description ?? `Mesh local namespace '${namespace}'`,
workflowExecutionRetentionPeriod: { seconds: (3 * 24 * 60 * 60) },
});
logInfo(`Registered Temporal namespace '${namespace}' — waiting for it to become active…`);
const deadline = Date.now() + 20_000;
while (Date.now() < deadline) {
try {
await connection.workflowService.describeTaskQueue({
namespace,
taskQueue: { name: "namespace-propagation-probe" },
taskQueueType: 1,
});
break;
}
catch {
await new Promise((resolve) => setTimeout(resolve, 1_000));
}
}
logSuccess(`Temporal namespace '${namespace}' is active`);
}
catch (err) {
if (err?.name === "NamespaceAlreadyExistsError" || /already exists/i.test(err?.message ?? "")) {
logInfo(`Temporal namespace '${namespace}' already exists`);
}
else {
throw err;
}
}
finally {
await connection.close();
}
}
export function buildFabricCheckPayload(targetBytes = 8_000) {
const base = { probe: "advanced-tier-round-trip", tenant: LOCAL_TENANT, env: LOCAL_ENV, pad: "" };
const overhead = JSON.stringify(base).length;
return JSON.stringify({ ...base, pad: "x".repeat(Math.max(0, targetBytes - overhead)) });
}
export async function verifyFabric() {
const { SSMClient, PutParameterCommand, GetParameterCommand, GetParametersByPathCommand, DeleteParametersCommand } = await import("@aws-sdk/client-ssm");
const ssm = new SSMClient(AWS_CONFIG);
const payload = buildFabricCheckPayload();
const mainParam = FABRIC_CHECK_PATH;
const childParams = [`${FABRIC_CHECK_PATH}/vpc`, `${FABRIC_CHECK_PATH}/eks`];
try {
await ssm.send(new PutParameterCommand({
Name: mainParam,
Type: "String",
Tier: "Advanced",
Overwrite: true,
Value: payload,
}));
for (const name of childParams) {
await ssm.send(new PutParameterCommand({ Name: name, Type: "String", Overwrite: true, Value: `{"probe":"${name}"}` }));
}
const roundTrip = await ssm.send(new GetParameterCommand({ Name: mainParam }));
if (roundTrip.Parameter?.Value !== payload) {
throw new MeshCliError(`Local AWS fabric failed the ${payload.length}-byte Advanced-tier SSM round-trip (got ${roundTrip.Parameter?.Value?.length ?? 0} bytes back).`, {
remediation: {
docs: "libs/mesh-cli/stack/docker-compose.yml — swap the ministack image for motoserver/moto (design §2.5 fallback)",
},
});
}
const byPath = await ssm.send(new GetParametersByPathCommand({ Path: FABRIC_CHECK_PATH, Recursive: true }));
const returned = new Set((byPath.Parameters ?? []).map((p) => p.Name));
const missing = childParams.filter((name) => !returned.has(name));
if (missing.length > 0) {
throw new MeshCliError(`Local AWS fabric's GetParametersByPath missed child parameter(s): ${missing.join(", ")} (splitKeys exports would not resolve).`, {
remediation: {
docs: "libs/mesh-cli/stack/docker-compose.yml — swap the ministack image for motoserver/moto (design §2.5 fallback)",
},
});
}
logSuccess(`Fabric check passed: ${payload.length}-byte Advanced-tier round-trip + GetParametersByPath`);
}
finally {
await ssm
.send(new DeleteParametersCommand({ Names: [mainParam, ...childParams] }))
.catch(() => { });
}
}
export async function registerTenantEnv(tenant, opts = {}) {
const { SSMClient, PutParameterCommand } = await import("@aws-sdk/client-ssm");
const ssm = new SSMClient(AWS_CONFIG);
await ssm.send(new PutParameterCommand({
Name: `/mesh-platform/${tenant}`,
Type: "String",
Overwrite: true,
Value: JSON.stringify({ name: tenant, tier: opts.tier ?? "shared", subdomain: tenant }),
Description: `Tenant registration (seeded by mesh CLI, local platform)`,
}));
await ssm.send(new PutParameterCommand({
Name: `/mesh-platform/${tenant}/${LOCAL_ENV}`,
Type: "String",
Overwrite: true,
Value: JSON.stringify({
env: LOCAL_ENV,
region: LOCAL_AWS_REGION,
baseDomain: "localhost",
services: { temporal: true, zitadel: true, monitoring: true },
}),
Description: `Environment registration (seeded by mesh CLI, local platform)`,
}));
}
export async function seedLocalPlatform() {
const { SSMClient, PutParameterCommand } = await import("@aws-sdk/client-ssm");
const ssm = new SSMClient(AWS_CONFIG);
await ssm.send(new PutParameterCommand({
Name: APP_TENANTS_PARAM,
Type: "String",
Overwrite: true,
Value: JSON.stringify(buildAppTenantsSeed()),
Description: `TenantEnvironments exports for ${LOCAL_TENANT}-${LOCAL_ENV} (seeded by mesh start)`,
}));
await registerTenantEnv(LOCAL_TENANT, { tier: "hub" });
logSuccess(`Registered tenant '${LOCAL_TENANT}' env '${LOCAL_ENV}' → ${APP_TENANTS_PARAM}`);
const { S3Client, CreateBucketCommand, PutObjectCommand } = await import("@aws-sdk/client-s3");
const s3 = new S3Client({ ...AWS_CONFIG, forcePathStyle: true });
for (const bucket of [ARTIFACTS_BUCKET, DATA_BUCKET]) {
try {
await s3.send(new CreateBucketCommand({ Bucket: bucket }));
logSuccess(`Created bucket s3://${bucket}`);
}
catch (err) {
if (err?.name === "BucketAlreadyOwnedByYou" || err?.name === "BucketAlreadyExists") {
logInfo(`Bucket s3://${bucket} already exists`);
}
else {
throw err;
}
}
}
for (const seed of DATA_BUCKET_SEEDS) {
await s3.send(new PutObjectCommand({
Bucket: DATA_BUCKET,
Key: seed.key,
Body: seed.body,
ContentType: "application/json",
}));
}
logSuccess(`Seeded s3://${DATA_BUCKET} with ${DATA_BUCKET_SEEDS.length} sample object(s)`);
await seedTemporalNamespace();
return { parameter: APP_TENANTS_PARAM, bucket: ARTIFACTS_BUCKET };
}