Tag Komodo deployments with game + canonical map name
New deployments get two tags attached (e.g. "t5" and "zombie_pentagon") so operators can filter the Komodo deployment list by game and by map. Tags are ensured via read/ListTags + write/CreateTag fallback, then attached with write/UpdateTagsOnResource against the deployment name. Tag application runs fire-and-forget after CreateDeployment: any failure logs a warning and the server creation proceeds. Tags are cosmetic organization inside Komodo, not a correctness requirement.
This commit is contained in:
@@ -519,6 +519,91 @@ export async function komodoDeleteDeployment(deployment: string): Promise<Komodo
|
||||
return postAndClassify('write/DeleteDeployment', { id: deployment, name: deployment });
|
||||
}
|
||||
|
||||
// ---------- Tags ----------
|
||||
|
||||
export type KomodoTagResult =
|
||||
| { ok: true; id: string }
|
||||
| { ok: false; message: string };
|
||||
|
||||
/**
|
||||
* Resolve a tag by name, creating it if it doesn't exist. Returns the tag's
|
||||
* Komodo id (Mongo ObjectId string) for use in UpdateTagsOnResource.
|
||||
*
|
||||
* Best-effort: callers should treat failure as "skip tagging", not "roll back
|
||||
* the whole operation". Tags are cosmetic organization inside Komodo.
|
||||
*/
|
||||
export async function komodoEnsureTag(name: string): Promise<KomodoTagResult> {
|
||||
if (!komodoConfigured()) return { ok: false, message: 'komodo not configured' };
|
||||
|
||||
// 1. Look for an existing tag by name.
|
||||
try {
|
||||
const listRes = await komodoCall('read/ListTags', {});
|
||||
if (listRes.ok) {
|
||||
const tags = await listRes.json().catch(() => null);
|
||||
if (Array.isArray(tags)) {
|
||||
const match = tags.find(
|
||||
(t: unknown): t is { _id: string; name: string } =>
|
||||
!!t && typeof t === 'object' &&
|
||||
typeof (t as { name?: unknown }).name === 'string' &&
|
||||
(t as { name: string }).name === name &&
|
||||
typeof (t as { _id?: unknown })._id === 'string'
|
||||
);
|
||||
if (match) return { ok: true, id: match._id };
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
// Fall through to create — list failure isn't fatal, we can still try
|
||||
// to create; if the name is taken, CreateTag will surface a clean error.
|
||||
console.warn(`[komodoEnsureTag] ListTags failed for "${name}":`, (err as Error).message);
|
||||
}
|
||||
|
||||
// 2. Create the tag. Response shape: `{ _id, name, ... }` on success.
|
||||
let createRes: Response;
|
||||
try {
|
||||
createRes = await komodoCall('write/CreateTag', { name });
|
||||
} catch (err) {
|
||||
return { ok: false, message: `CreateTag ${name}: ${(err as Error).message}` };
|
||||
}
|
||||
if (!createRes.ok) {
|
||||
const snippet = await createRes.text().catch(() => '');
|
||||
return {
|
||||
ok: false,
|
||||
message: `CreateTag ${name}: HTTP ${createRes.status}${snippet ? ` — ${truncate(snippet)}` : ''}`
|
||||
};
|
||||
}
|
||||
try {
|
||||
const body = await createRes.json();
|
||||
if (body && typeof body === 'object' && typeof (body as { _id?: unknown })._id === 'string') {
|
||||
return { ok: true, id: (body as { _id: string })._id };
|
||||
}
|
||||
return { ok: false, message: `CreateTag ${name}: unexpected response shape` };
|
||||
} catch (err) {
|
||||
return { ok: false, message: `CreateTag ${name}: ${(err as Error).message}` };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach tags to a Komodo resource. `tagIds` is the array of tag Mongo IDs
|
||||
* from `komodoEnsureTag`. Replaces the resource's existing tag set — pass the
|
||||
* full desired list, not a delta.
|
||||
*
|
||||
* Komodo's target.id accepts either the true id or the resource name for
|
||||
* Deployments, so we pass the name here to save a lookup.
|
||||
*/
|
||||
export async function komodoUpdateTagsOnResource(
|
||||
targetType: 'Deployment' | 'Stack' | 'Server' | 'Repo' | 'Build' | 'Procedure',
|
||||
targetName: string,
|
||||
tagIds: string[]
|
||||
): Promise<KomodoWriteResult> {
|
||||
if (!komodoConfigured()) {
|
||||
return { ok: false, kind: 'unconfigured', message: 'komodo not configured' };
|
||||
}
|
||||
return postAndClassify('write/UpdateTagsOnResource', {
|
||||
target: { type: targetType, id: targetName },
|
||||
tags: tagIds
|
||||
});
|
||||
}
|
||||
|
||||
// ---------- Extractor ----------
|
||||
|
||||
// Property names Komodo/Docker use for the collection of port mappings.
|
||||
|
||||
Reference in New Issue
Block a user