forked from farhoodlabs/paperclip
fix(plugin): warn on missing kubernetes adapter env
This commit is contained in:
@@ -68,7 +68,7 @@ Common optional fields:
|
|||||||
| `imageAllowList` | `[]` | Glob patterns of allowed `target.imageOverride` values. Empty = no override permitted. |
|
| `imageAllowList` | `[]` | Glob patterns of allowed `target.imageOverride` values. Empty = no override permitted. |
|
||||||
| `imagePullSecrets` | `[]` | Names of pre-created Docker image pull secrets in the tenant namespace. |
|
| `imagePullSecrets` | `[]` | Names of pre-created Docker image pull secrets in the tenant namespace. |
|
||||||
| `egressAllowFqdns` | `[]` | Additional FQDNs (beyond adapter defaults like `api.anthropic.com`). |
|
| `egressAllowFqdns` | `[]` | Additional FQDNs (beyond adapter defaults like `api.anthropic.com`). |
|
||||||
| `egressAllowCidrs` | `[]` | Additional CIDRs to allow egress to. |
|
| `egressAllowCidrs` | `[]` | Additional CIDRs to allow HTTPS egress to. CIDR egress is restricted to TCP port 443. |
|
||||||
| `egressMode` | `"standard"` | `standard` (NetworkPolicy + CIDRs, plus public HTTPS fallback when adapter FQDNs are configured) or `cilium` (CiliumNetworkPolicy + exact FQDN allow-list). |
|
| `egressMode` | `"standard"` | `standard` (NetworkPolicy + CIDRs, plus public HTTPS fallback when adapter FQDNs are configured) or `cilium` (CiliumNetworkPolicy + exact FQDN allow-list). |
|
||||||
| `runtimeClassName` | (none) | e.g. `kata-fc` for Firecracker-backed microVMs. Cluster must have the RuntimeClass installed. |
|
| `runtimeClassName` | (none) | e.g. `kata-fc` for Firecracker-backed microVMs. Cluster must have the RuntimeClass installed. |
|
||||||
| `serviceAccountAnnotations` | `{}` | Annotations applied to per-tenant ServiceAccount (e.g. IRSA `eks.amazonaws.com/role-arn`). |
|
| `serviceAccountAnnotations` | `{}` | Annotations applied to per-tenant ServiceAccount (e.g. IRSA `eks.amazonaws.com/role-arn`). |
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ const manifest: PaperclipPluginManifestV1 = {
|
|||||||
egressAllowCidrs: {
|
egressAllowCidrs: {
|
||||||
type: "array",
|
type: "array",
|
||||||
items: { type: "string" },
|
items: { type: "string" },
|
||||||
description: "Additional CIDRs to allow egress to from agent pods.",
|
description: "Additional CIDRs to allow HTTPS egress to from agent pods. CIDR egress is restricted to TCP port 443.",
|
||||||
},
|
},
|
||||||
egressMode: {
|
egressMode: {
|
||||||
type: "string",
|
type: "string",
|
||||||
|
|||||||
@@ -72,11 +72,24 @@ function deriveTenantNamespace(config: KubernetesProviderConfig, companyId: stri
|
|||||||
* TODO: future milestones may thread per-run secrets differently (e.g. via
|
* TODO: future milestones may thread per-run secrets differently (e.g. via
|
||||||
* a secret store reference on the environment config).
|
* a secret store reference on the environment config).
|
||||||
*/
|
*/
|
||||||
function extractAdapterEnvFromProcess(envKeys: string[]): Record<string, string> {
|
export function extractAdapterEnvFromProcess(
|
||||||
|
envKeys: string[],
|
||||||
|
warn: (message: string) => void = console.warn,
|
||||||
|
): Record<string, string> {
|
||||||
const out: Record<string, string> = {};
|
const out: Record<string, string> = {};
|
||||||
|
const missing: string[] = [];
|
||||||
for (const k of envKeys) {
|
for (const k of envKeys) {
|
||||||
const v = process.env[k];
|
const v = process.env[k];
|
||||||
if (v) out[k] = v;
|
if (v) {
|
||||||
|
out[k] = v;
|
||||||
|
} else {
|
||||||
|
missing.push(k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (missing.length > 0) {
|
||||||
|
warn(
|
||||||
|
`[plugin-kubernetes] adapter environment variable(s) missing from plugin worker process: ${missing.join(", ")}. Agent pods may fail provider authentication unless these keys are optional for the selected adapter.`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import manifest from "../../src/manifest.js";
|
|||||||
|
|
||||||
describe("manifest", () => {
|
describe("manifest", () => {
|
||||||
const configSchema = manifest.environmentDrivers[0]?.configSchema as {
|
const configSchema = manifest.environmentDrivers[0]?.configSchema as {
|
||||||
properties: Record<string, { const?: unknown; maxLength?: number; pattern?: string }>;
|
properties: Record<string, { const?: unknown; description?: string; maxLength?: number; pattern?: string }>;
|
||||||
anyOf: Array<{
|
anyOf: Array<{
|
||||||
properties?: Record<string, { const?: unknown }>;
|
properties?: Record<string, { const?: unknown }>;
|
||||||
required?: string[];
|
required?: string[];
|
||||||
@@ -23,4 +23,8 @@ describe("manifest", () => {
|
|||||||
});
|
});
|
||||||
expect(configSchema.anyOf).toContainEqual({ required: ["kubeconfig"] });
|
expect(configSchema.anyOf).toContainEqual({ required: ["kubeconfig"] });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("documents that CIDR egress is HTTPS-only", () => {
|
||||||
|
expect(configSchema.properties.egressAllowCidrs.description).toContain("TCP port 443");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { describe, it, expect } from "vitest";
|
import { describe, it, expect } from "vitest";
|
||||||
import plugin from "../../src/plugin.js";
|
import plugin, { extractAdapterEnvFromProcess } from "../../src/plugin.js";
|
||||||
|
|
||||||
describe("plugin", () => {
|
describe("plugin", () => {
|
||||||
it("exports the kubernetes driver", () => {
|
it("exports the kubernetes driver", () => {
|
||||||
@@ -91,4 +91,33 @@ describe("plugin", () => {
|
|||||||
expect(result.ok).toBe(true);
|
expect(result.ok).toBe(true);
|
||||||
expect(result.warnings).toBeUndefined();
|
expect(result.warnings).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("warns when adapter env keys are missing from the worker process", () => {
|
||||||
|
const warnMessages: string[] = [];
|
||||||
|
const originalPresent = process.env.PAPERCLIP_TEST_PRESENT_KEY;
|
||||||
|
const originalMissing = process.env.PAPERCLIP_TEST_MISSING_KEY;
|
||||||
|
process.env.PAPERCLIP_TEST_PRESENT_KEY = "secret-value";
|
||||||
|
delete process.env.PAPERCLIP_TEST_MISSING_KEY;
|
||||||
|
try {
|
||||||
|
const result = extractAdapterEnvFromProcess(
|
||||||
|
["PAPERCLIP_TEST_PRESENT_KEY", "PAPERCLIP_TEST_MISSING_KEY"],
|
||||||
|
(message) => warnMessages.push(message),
|
||||||
|
);
|
||||||
|
expect(result).toEqual({ PAPERCLIP_TEST_PRESENT_KEY: "secret-value" });
|
||||||
|
expect(warnMessages).toHaveLength(1);
|
||||||
|
expect(warnMessages[0]).toContain("PAPERCLIP_TEST_MISSING_KEY");
|
||||||
|
expect(warnMessages[0]).not.toContain("secret-value");
|
||||||
|
} finally {
|
||||||
|
if (originalPresent === undefined) {
|
||||||
|
delete process.env.PAPERCLIP_TEST_PRESENT_KEY;
|
||||||
|
} else {
|
||||||
|
process.env.PAPERCLIP_TEST_PRESENT_KEY = originalPresent;
|
||||||
|
}
|
||||||
|
if (originalMissing === undefined) {
|
||||||
|
delete process.env.PAPERCLIP_TEST_MISSING_KEY;
|
||||||
|
} else {
|
||||||
|
process.env.PAPERCLIP_TEST_MISSING_KEY = originalMissing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user