-
Notifications
You must be signed in to change notification settings - Fork 1
MILAB-5933: e2e test-side fixes for Hetzner k3s runner migration #1740
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
946a752
f495158
350cb9a
a209633
9251bd9
b4283e2
073d561
81f0e5d
f54a312
ef477fe
3dc2d47
7e298f8
f3b1070
7b03070
8025b92
c3995df
5f473c4
a042478
8ab4463
d550a0c
c738dbd
0b6a9c5
9a86778
37a6886
613f058
c230508
f20b189
9b41589
9aacd7c
6150096
677907c
e07c7f2
b368acf
0deda81
49739bb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@platforma-sdk/workflow-tengo": patch | ||
| --- | ||
|
|
||
| fix: xsv converter can be launched in k8s installations. |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,25 +1,25 @@ | ||||||||||||||||||||||||||||||||||||||||||||
| import { getTestClient, getTestClientConf } from "../test/test_config"; | ||||||||||||||||||||||||||||||||||||||||||||
| import { getTestAdminClient, getTestAdminClientConf } from "../test/test_config"; | ||||||||||||||||||||||||||||||||||||||||||||
| import { PlClient } from "./client"; | ||||||||||||||||||||||||||||||||||||||||||||
| import { PlDriver, PlDriverDefinition } from "./driver"; | ||||||||||||||||||||||||||||||||||||||||||||
| import { Dispatcher, request } from "undici"; | ||||||||||||||||||||||||||||||||||||||||||||
| import { GrpcClientProviderFactory } from "./grpc"; | ||||||||||||||||||||||||||||||||||||||||||||
| import { test, expect } from "vitest"; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| test("test client init", async () => { | ||||||||||||||||||||||||||||||||||||||||||||
| await getTestClient(undefined); | ||||||||||||||||||||||||||||||||||||||||||||
| await getTestAdminClient(undefined); | ||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| test("test client alternative root init", async () => { | ||||||||||||||||||||||||||||||||||||||||||||
| const aRootName = "test_root"; | ||||||||||||||||||||||||||||||||||||||||||||
| const { conf, auth } = await getTestClientConf(); | ||||||||||||||||||||||||||||||||||||||||||||
| const { conf, auth } = await getTestAdminClientConf(); | ||||||||||||||||||||||||||||||||||||||||||||
| await PlClient.init({ ...conf, alternativeRoot: aRootName }, auth); | ||||||||||||||||||||||||||||||||||||||||||||
| const clientB = await PlClient.init(conf, auth); | ||||||||||||||||||||||||||||||||||||||||||||
| const result = await clientB.deleteAlternativeRoot(aRootName); | ||||||||||||||||||||||||||||||||||||||||||||
| expect(result).toBe(true); | ||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
12
to
19
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Both
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| test("test client init 2", async () => { | ||||||||||||||||||||||||||||||||||||||||||||
| await getTestClient(); | ||||||||||||||||||||||||||||||||||||||||||||
| await getTestAdminClient(); | ||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
21
to
23
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The initialized
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| interface SimpleDriver extends PlDriver { | ||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -46,7 +46,7 @@ const SimpleDriverDefinition: PlDriverDefinition<SimpleDriver> = { | |||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| test("test driver", async () => { | ||||||||||||||||||||||||||||||||||||||||||||
| const client = await getTestClient(); | ||||||||||||||||||||||||||||||||||||||||||||
| const client = await getTestAdminClient(); | ||||||||||||||||||||||||||||||||||||||||||||
| const drv = client.getDriver(SimpleDriverDefinition); | ||||||||||||||||||||||||||||||||||||||||||||
| expect(await drv.ping()).toEqual("pong"); | ||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
48
to
52
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The initialized
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,8 @@ export interface TestConfig { | |
| test_proxy?: string; | ||
| test_user?: string; | ||
| test_password?: string; | ||
| test_admin_user?: string; | ||
| test_admin_password?: string; | ||
| } | ||
|
|
||
| const CONFIG_FILE = "test_config.json"; | ||
|
|
@@ -44,6 +46,11 @@ export function getTestConfig(): TestConfig { | |
|
|
||
| if (process.env.PL_TEST_PROXY !== undefined) conf.test_proxy = process.env.PL_TEST_PROXY; | ||
|
|
||
| if (process.env.PL_TEST_ADMIN_USER !== undefined) conf.test_admin_user = process.env.PL_TEST_ADMIN_USER; | ||
|
|
||
| if (process.env.PL_TEST_ADMIN_PASSWORD !== undefined) | ||
| conf.test_admin_password = process.env.PL_TEST_ADMIN_PASSWORD; | ||
|
|
||
| if (conf.address === undefined) | ||
| throw new Error( | ||
| `can't resolve platform address (checked ${CONFIG_FILE} file and PL_ADDRESS environment var)`, | ||
|
|
@@ -157,6 +164,70 @@ export async function getTestLLClient(confOverrides: Partial<PlClientConfig> = { | |
| return await LLPlClient.build({ ...conf, ...confOverrides }, { auth }); | ||
| } | ||
|
|
||
| export async function getTestAdminClientConf(): Promise<{ conf: PlClientConfig; auth: AuthOps }> { | ||
| const tConf = getTestConfig(); | ||
|
|
||
| if (tConf.test_admin_user === undefined || tConf.test_admin_password === undefined) | ||
| throw new Error( | ||
| `No admin auth found in config (${CONFIG_FILE}) or env vars: PL_TEST_ADMIN_USER, PL_TEST_ADMIN_PASSWORD`, | ||
| ); | ||
|
|
||
| const plConf = plAddressToTestConfig(tConf.address); | ||
| const uClient = await UnauthenticatedPlClient.build(plConf); | ||
| const authInformation = await uClient.login(tConf.test_admin_user, tConf.test_admin_password); | ||
|
|
||
| return { | ||
| conf: plConf, | ||
| auth: { | ||
| authInformation, | ||
| onUpdate: () => {}, | ||
| onAuthError: () => {}, | ||
| onUpdateError: () => {}, | ||
| }, | ||
| }; | ||
| } | ||
|
Comment on lines
+167
to
+188
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Prompt To Fix With AIThis is a comment left during a code review.
Path: lib/node/pl-client/src/test/test_config.ts
Line: 167-188
Comment:
**`requireAuth()` guard absent from `getTestAdminClientConf`**
`getTestClientConf` calls `await uClient.requireAuth()` and emits clear errors when credentials are supplied to an auth-disabled server (or omitted on an auth-enabled one). `getTestAdminClientConf` skips that check entirely and goes straight to `uClient.login()`. On a dev server that has auth disabled but where `PL_TEST_ADMIN_USER`/`PASSWORD` happen to be set (e.g. copied from a CI env file), `login()` will throw a server-side error rather than the familiar "server requires no auth" message, making the failure harder to diagnose. Worth adding the same guard even if the "strict security mode" assumption makes it an unlikely path in practice.
How can I resolve this? If you propose a fix, please make it concise. |
||
|
|
||
| export async function getTestAdminLLClient(confOverrides: Partial<PlClientConfig> = {}) { | ||
| const { conf, auth } = await getTestAdminClientConf(); | ||
| return await LLPlClient.build({ ...conf, ...confOverrides }, { auth }); | ||
| } | ||
|
|
||
| export async function getTestAdminClient( | ||
| alternativeRoot?: string, | ||
| confOverrides: Partial<PlClientConfig> = {}, | ||
| ) { | ||
| const { conf, auth } = await getTestAdminClientConf(); | ||
| if (alternativeRoot !== undefined && conf.alternativeRoot !== undefined) | ||
| throw new Error("test pl address configured with alternative root"); | ||
| return await PlClient.init({ ...conf, ...confOverrides, alternativeRoot }, auth); | ||
| } | ||
|
|
||
| export async function withAdminTempRoot<T>(body: (pl: PlClient) => Promise<T>): Promise<T | void> { | ||
| const alternativeRoot = `test_${Date.now()}_${randomUUID()}`; | ||
| let altRootId: OptionalResourceId = NullResourceId; | ||
| try { | ||
| const client = await getTestAdminClient(alternativeRoot); | ||
| altRootId = client.clientRoot; | ||
| try { | ||
| const value = await body(client); | ||
| const rawClient = await getTestAdminClient(); | ||
| try { | ||
| await rawClient.deleteAlternativeRoot(alternativeRoot); | ||
| } catch (cleanupErr: any) { | ||
| console.warn(`Failed to clean up alternative root ${alternativeRoot}:`, cleanupErr.message); | ||
| } finally { | ||
| await rawClient.close(); | ||
| } | ||
| return value; | ||
| } finally { | ||
| await client.close(); | ||
| } | ||
| } catch (err: any) { | ||
| console.log(`ALTERNATIVE ROOT: ${alternativeRoot} (${resourceIdToString(altRootId)})`); | ||
| throw err; | ||
| } | ||
| } | ||
|
Comment on lines
+205
to
+229
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In We should restructure the export async function withAdminTempRoot<T>(body: (pl: PlClient) => Promise<T>): Promise<T> {
const alternativeRoot = `test_${Date.now()}_${randomUUID()}`;
let altRootId: OptionalResourceId = NullResourceId;
const client = await getTestAdminClient(alternativeRoot);
altRootId = client.clientRoot;
try {
return await body(client);
} catch (err: any) {
console.log(`ALTERNATIVE ROOT: ${alternativeRoot} (${resourceIdToString(altRootId)})`);
throw err;
} finally {
try {
const rawClient = await getTestAdminClient();
try {
await rawClient.deleteAlternativeRoot(alternativeRoot);
} catch (cleanupErr: any) {
console.warn(`Failed to clean up alternative root ${alternativeRoot}:`, cleanupErr.message);
} finally {
await rawClient.close();
}
} finally {
await client.close();
}
}
}
Comment on lines
+205
to
+229
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When Prompt To Fix With AIThis is a comment left during a code review.
Path: lib/node/pl-client/src/test/test_config.ts
Line: 205-229
Comment:
**Alternative root not cleaned up on `body` failure**
When `body(client)` throws, execution jumps straight to the outer `catch` (via `client.close()` in the inner `finally`) and the alternative root is logged but never deleted. The same pattern exists in `withTempRoot`, so this is consistent, but `withAdminTempRoot` does not have the TCP-proxy path that can explicitly `return` early — every non-error exit goes through `rawClient.deleteAlternativeRoot`. If tests are repeatedly flaky under load the leaked roots accumulate. A best-effort cleanup attempt in the outer catch (mirroring the `console.warn` pattern used for `cleanupErr`) would make the admin variant more robust than `withTempRoot`.
How can I resolve this? If you propose a fix, please make it concise. |
||
|
|
||
| export async function getTestClient( | ||
| alternativeRoot?: string, | ||
| confOverrides: Partial<PlClientConfig> = {}, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,6 @@ | ||
| import { TestHelpers } from "@milaboratories/pl-client"; | ||
| import { ConsoleLoggerAdapter, HmacSha256Signer } from "@milaboratories/ts-helpers"; | ||
| import { HmacSha256Signer } from "@milaboratories/ts-helpers"; | ||
| import type { MiLogger } from "@milaboratories/ts-helpers"; | ||
| import * as os from "node:os"; | ||
| import { text } from "node:stream/consumers"; | ||
| import { Readable } from "node:stream"; | ||
|
|
@@ -10,9 +11,13 @@ import { DownloadUrlDriver } from "./driver"; | |
| import { test, expect } from "vitest"; | ||
| import { TestTags } from "@milaboratories/build-configs"; | ||
|
|
||
| // ponytail: noop logger prevents background TaskProcessor workers from triggering | ||
| // vitest's onUserConsoleLog RPC after test ends, which causes EnvironmentTeardownError | ||
| const noopLogger: MiLogger = { info: () => {}, warn: () => {}, error: () => {} }; | ||
|
Comment on lines
+14
to
+16
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using a completely silent The root cause of the |
||
|
|
||
| test("should download a tar archive and extracts its content and then deleted", async () => { | ||
| await TestHelpers.withTempRoot(async (client) => { | ||
| const logger = new ConsoleLoggerAdapter(); | ||
| const logger = noopLogger; | ||
| const dir = await fsp.mkdtemp(path.join(os.tmpdir(), "test1-")); | ||
| const driver = new DownloadUrlDriver(logger, client.httpDispatcher, dir, genSigner()); | ||
|
|
||
|
|
@@ -41,6 +46,7 @@ test("should download a tar archive and extracts its content and then deleted", | |
| expect(indexJsCode).toContain("use strict"); | ||
|
|
||
| c.resetState(); | ||
| await driver.releaseAll(); | ||
| await c.awaitChange(); | ||
| }); | ||
| }, 45000); | ||
|
|
@@ -50,7 +56,7 @@ test( | |
| { tag: [TestTags.Flaky] }, | ||
| async () => { | ||
| await TestHelpers.withTempRoot(async (client) => { | ||
| const logger = new ConsoleLoggerAdapter(); | ||
| const logger = noopLogger; | ||
| const dir = await fsp.mkdtemp(path.join(os.tmpdir(), "test1-")); | ||
| const driver = new DownloadUrlDriver(logger, client.httpDispatcher, dir, genSigner()); | ||
|
|
||
|
|
@@ -69,14 +75,15 @@ test( | |
| expect(url2).not.toBeUndefined(); | ||
| expect(url2?.error).not.toBeUndefined(); | ||
| expect(url2?.url).toBeUndefined(); | ||
| await driver.releaseAll(); | ||
| }); | ||
| }, | ||
| 60000, | ||
| ); | ||
|
|
||
| test("should abort a downloading process when we reset a state of a computable", async () => { | ||
| await TestHelpers.withTempRoot(async (client) => { | ||
| const logger = new ConsoleLoggerAdapter(); | ||
| const logger = noopLogger; | ||
| const dir = await fsp.mkdtemp(path.join(os.tmpdir(), "test2-")); | ||
| const driver = new DownloadUrlDriver(logger, client.httpDispatcher, dir, genSigner()); | ||
|
|
||
|
|
@@ -94,6 +101,7 @@ test("should abort a downloading process when we reset a state of a computable", | |
|
|
||
| const url2 = await c.getValue(); | ||
| expect(url2).toBeUndefined(); | ||
| await driver.releaseAll(); | ||
| }); | ||
| }); | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The initialized
PlClientreturned bygetTestAdminClientis never closed, which leaks resources (such as connections or background workers) after the test completes. We should ensure the client is closed.