mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-16 05:17:41 +02:00
Merge final immutable-image qualification guard
Co-Authored-By: Paperclip <noreply@paperclip.ing> * codex/runner-native-platform-prerequisites: fix(environments): bind platform facts to the effective immutable image
This commit is contained in:
commit
f340d92b39
4 files changed
+26
-8
No files matched your search
@@ -69,7 +69,7 @@ describe("probeEnvironment", () => {
|
||||
expect(await readSandboxPlatformQualification({} as any, environment, "foreign-company")).toBeNull();
|
||||
expect(await readSandboxPlatformQualification({} as any, { ...environment, config: { ...environment.config, image: `registry.example.test/runtime@sha256:${"b".repeat(64)}` } }, "company")).toBeNull();
|
||||
vi.mocked(configRuntime.resolveEnvironmentDriverConfigForRuntime).mockImplementation(async (_db, _company, _env, options) => {
|
||||
options?.onResolvedCustomImageTemplate?.({ id: "changed-template", capturedAt: null });
|
||||
options?.onResolvedCustomImageTemplate?.({ id: "changed-template", capturedAt: null, templateKind: "image", bindingField: "image" });
|
||||
return { driver: "sandbox", config: environment.config };
|
||||
});
|
||||
expect(await readSandboxPlatformQualification({} as any, environment, "company")).toBeNull();
|
||||
@@ -117,9 +117,24 @@ describe("probeEnvironment", () => {
|
||||
const identity = vi.fn();
|
||||
const result = await resolveActiveEnvironmentCustomImageTemplateForRuntime(db as never, { environmentId: "env", baseConfig: { provider: "daytona", reuseLease: false }, runtimeConfig: { provider: "daytona", reuseLease: false }, readOnly: true, onResolvedTemplate: identity });
|
||||
expect(result).toMatchObject({ image: row.templateRef });
|
||||
expect(identity).toHaveBeenCalledWith({ id: "template", capturedAt: capturedAt.toISOString() });
|
||||
expect(identity).toHaveBeenCalledWith({ id: "template", capturedAt: capturedAt.toISOString(), templateKind: "image", bindingField: "image" });
|
||||
expect(update).not.toHaveBeenCalled();
|
||||
});
|
||||
it.each(["provider_template", "unknown"])("does not qualify a named active %s boot binding over a pinned base image", async templateKind => {
|
||||
const image = `registry.example.test/runtime@sha256:${"a".repeat(64)}`;
|
||||
const environment = { id: "env", driver: "sandbox", status: "active", config: { provider: "daytona", image }, metadata: null } as any;
|
||||
vi.spyOn(configRuntime, "resolveEnvironmentDriverConfigForRuntime").mockImplementation(async (_db, _company, _env, options) => {
|
||||
const bindingField = templateKind === "provider_template" ? "template" : "templateRef";
|
||||
options?.onResolvedCustomImageTemplate?.({ id: "active-template", capturedAt: null, templateKind, bindingField });
|
||||
return { driver: "sandbox", config: { ...environment.config, [bindingField]: "mutable-template-name" } };
|
||||
});
|
||||
mockRuntimeAcquireRunLease.mockResolvedValue({ lease: { id: "lease", provider: "daytona", metadata: { image } } });
|
||||
const result = await probeEnvironment({} as any, environment, { companyId: "company", acquireSandboxRuntimeLease: true });
|
||||
expect(result.ok).toBe(true);
|
||||
expect(result.details?.platformQualificationRecorded).toBe(false);
|
||||
expect(mockRuntimeExecute).not.toHaveBeenCalled();
|
||||
expect(mockRecordPlatformProbe).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
it("returns false when an atomic probe record loses its config comparison", async () => {
|
||||
const { environmentService } = await vi.importActual<typeof import("../services/environments.js")>("../services/environments.js");
|
||||
const returning = vi.fn().mockResolvedValue([]);
|
||||
|
||||
@@ -661,7 +661,7 @@ export async function resolveEnvironmentDriverConfigForRuntime(
|
||||
// configuration and tooling so the test reflects what real agent runs use.
|
||||
applyCustomImageTemplate?: boolean;
|
||||
readOnlyCustomImageTemplate?: boolean;
|
||||
onResolvedCustomImageTemplate?: (identity: { id: string; capturedAt: string | null }) => void;
|
||||
onResolvedCustomImageTemplate?: (identity: { id: string; capturedAt: string | null; templateKind: string; bindingField: string }) => void;
|
||||
},
|
||||
): Promise<ParsedEnvironmentConfig> {
|
||||
const parsed = parseEnvironmentDriverConfig(environment);
|
||||
|
||||
@@ -560,7 +560,7 @@ export async function resolveActiveEnvironmentCustomImageTemplateForRuntime(
|
||||
secretRefExcludePaths?: Iterable<string>;
|
||||
now?: Date;
|
||||
readOnly?: boolean;
|
||||
onResolvedTemplate?: (identity: { id: string; capturedAt: string | null }) => void;
|
||||
onResolvedTemplate?: (identity: { id: string; capturedAt: string | null; templateKind: string; bindingField: string }) => void;
|
||||
},
|
||||
): Promise<SandboxEnvironmentConfig> {
|
||||
const row = await db
|
||||
@@ -591,7 +591,8 @@ export async function resolveActiveEnvironmentCustomImageTemplateForRuntime(
|
||||
// match. Runtime-only knobs such as lease reuse, timeouts, and resource hints
|
||||
// are excluded from the fingerprint so those edits do not discard the capture.
|
||||
const now = input.now ?? new Date();
|
||||
input.onResolvedTemplate?.({ id: row.id, capturedAt: row.capturedAt?.toISOString() ?? null });
|
||||
input.onResolvedTemplate?.({ id: row.id, capturedAt: row.capturedAt?.toISOString() ?? null,
|
||||
templateKind: row.templateKind ?? "unknown", bindingField: resolveEnvironmentCustomImageRuntimeConfigBinding(row).field });
|
||||
if (!input.readOnly) await db
|
||||
.update(environmentCustomImageTemplates)
|
||||
.set({ lastUsedAt: now, updatedAt: now })
|
||||
|
||||
@@ -34,15 +34,17 @@ export function parseSandboxPlatformFacts(output: string): PlatformFacts {
|
||||
...(libc === "glibc" ? { glibcVersion: version?.trim() ?? "unknown" } : {}) };
|
||||
}
|
||||
async function sandboxBinding(db: Db, environment: Environment, companyId: string) {
|
||||
let template: { id: string; capturedAt: string | null } | null = null;
|
||||
const selectedTemplate: { value: { id: string; capturedAt: string | null; templateKind: string; bindingField: string } | null } = { value: null };
|
||||
const parsed = await resolveEnvironmentDriverConfigForRuntime(db, companyId, environment, {
|
||||
applyCustomImageTemplate: true, readOnlyCustomImageTemplate: true,
|
||||
onResolvedCustomImageTemplate: identity => { template = identity; },
|
||||
onResolvedCustomImageTemplate: identity => { selectedTemplate.value = identity; },
|
||||
});
|
||||
if (parsed.driver !== "sandbox") return null;
|
||||
const image = parsed.config.image;
|
||||
// Names, mutable tags, provider defaults and snapshot names are not immutable identities.
|
||||
if (!isImmutableImage(image) || record(parsed.config).snapshot) return null;
|
||||
const template = selectedTemplate.value;
|
||||
if (!isImmutableImage(image) || ["snapshot", "template", "templateRef"].some(key => record(parsed.config)[key])
|
||||
|| (template && (template.templateKind !== "image" || template.bindingField !== "image"))) return null;
|
||||
return { baseConfigFingerprint: fingerprint(environment.config), runtimeConfigFingerprint: fingerprint(parsed.config as unknown as Record<string, unknown>), image, template };
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user