Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion console/src/fleetToml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
// regex-based (not a full TOML parser) so it's unit-testable and only ever
// touches the one array/block it means to.

function quote(s: string): string {
// Exported so fleetsK8sToml.ts (fleets-k8s.toml's client-side edits, same
// `[fleet.<name>]` shape) can reuse it instead of duplicating a one-line
// helper.
export function quote(s: string): string {
return JSON.stringify(s);
}

Expand Down
57 changes: 57 additions & 0 deletions console/src/fleetsK8sToml.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { describe, it, expect } from "vitest";
import { appendMember, appendK8sFleetBlock } from "./fleetsK8sToml";

describe("appendMember (reused from fleetToml.ts)", () => {
it("works against fleets-k8s.toml's [fleet.<name>] shape too", () => {
const text = `[fleet.orbstack-dev]
context = "orbstack"
namespace = "dev"
members = ["scratch-agent"]
`;
const out = appendMember(text, "orbstack-dev", "scratch-agent-2");
expect(out).toContain('members = ["scratch-agent", "scratch-agent-2"]');
expect(out).toContain('context = "orbstack"');
expect(out).toContain('namespace = "dev"');
});
});

describe("appendK8sFleetBlock", () => {
it("appends a new [fleet.<name>] block with context, namespace, members, expected_principal", () => {
const out = appendK8sFleetBlock('default_cluster = "oab"\n', {
name: "orbstack-dev",
member: "oab-dev-scratch-agent",
context: "orbstack",
namespace: "dev",
expectedPrincipal: "system:serviceaccount:dev:oab-agent",
});
expect(out).toContain("[fleet.orbstack-dev]");
expect(out).toContain('context = "orbstack"');
expect(out).toContain('namespace = "dev"');
expect(out).toContain('members = ["oab-dev-scratch-agent"]');
expect(out).toContain('expected_principal = "system:serviceaccount:dev:oab-agent"');
});

it("omits context and expected_principal when not provided, but always writes namespace", () => {
const out = appendK8sFleetBlock("", {
name: "orca-k8s",
member: "oab-prod-orca",
context: null,
namespace: "prod",
expectedPrincipal: null,
});
expect(out).not.toContain("context =");
expect(out).not.toContain("expected_principal =");
expect(out).toContain('namespace = "prod"');
});

it("separates the new block from existing content with exactly one blank line", () => {
const out = appendK8sFleetBlock('default_cluster = "oab"\n', {
name: "x",
member: "m",
context: null,
namespace: "ns",
expectedPrincipal: null,
});
expect(out).toBe('default_cluster = "oab"\n\n[fleet.x]\nnamespace = "ns"\nmembers = ["m"]\n');
});
});
39 changes: 39 additions & 0 deletions console/src/fleetsK8sToml.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Pure text-level edits to fleets-k8s.toml's `[fleet.<name>]` blocks — the
// k8s counterpart to fleetToml.ts, same rationale (studio#104: k8s_fleet_
// config_write has no partial/append primitive, so the client computes the
// new/edited TOML and calls it with the full updated text).
//
// `[fleet.<name>]` block lookup/append-member is identical between fleets.
// toml and fleets-k8s.toml (same table shape, same `members = [...]` array —
// neither `findFleetBlock` nor `appendMember` reference any AWS-specific
// field), so this module reuses fleetToml.ts's `appendMember` rather than
// duplicating it. Only "create a brand-new fleet block" differs, since the
// two files' required/optional fields differ (context+namespace vs
// region+profile).

import { quote, appendMember } from "./fleetToml";

export { appendMember };

export interface NewK8sFleetEntry {
name: string;
member: string;
context: string | null;
namespace: string;
expectedPrincipal: string | null;
}

// Append a brand-new `[fleet.<name>]` block to the end of the file, with the
// one member — the first instance just deployed. `context` and
// `expected_principal` are optional fields, omitted rather than written as
// empty strings (mirrors fleetToml.ts's appendFleetBlock).
export function appendK8sFleetBlock(text: string, entry: NewK8sFleetEntry): string {
const lines = [`[fleet.${entry.name}]`];
if (entry.context) lines.push(`context = ${quote(entry.context)}`);
lines.push(`namespace = ${quote(entry.namespace)}`);
lines.push(`members = [${quote(entry.member)}]`);
if (entry.expectedPrincipal) lines.push(`expected_principal = ${quote(entry.expectedPrincipal)}`);
const block = `${lines.join("\n")}\n`;
const trimmed = text.replace(/\s*$/, "");
return trimmed.length ? `${trimmed}\n\n${block}` : block;
}
Loading