-
-
Notifications
You must be signed in to change notification settings - Fork 266
Reuse insecure HTTP wait agent across retries #1382
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
Changes from 1 commit
d4f2190
b325235
311ae0f
baf84a7
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 |
|---|---|---|
|
|
@@ -17,6 +17,7 @@ export class HttpWaitStrategy extends AbstractWaitStrategy { | |
| private readonly predicates: Array<(response: Response) => Promise<boolean>> = []; | ||
| private _allowInsecure = false; | ||
| private readTimeoutMs = 1000; | ||
| private insecureAgent?: Agent; | ||
|
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 a single Useful? React with 👍 / 👎. |
||
|
|
||
| constructor( | ||
| private readonly path: string, | ||
|
|
@@ -57,8 +58,8 @@ export class HttpWaitStrategy extends AbstractWaitStrategy { | |
| return this; | ||
| } | ||
|
|
||
| public withReadTimeout(startupTimeoutMs: number): this { | ||
| this.readTimeoutMs = startupTimeoutMs; | ||
| public withReadTimeout(readTimeoutMs: number): this { | ||
| this.readTimeoutMs = readTimeoutMs; | ||
| return this; | ||
| } | ||
|
|
||
|
|
@@ -80,60 +81,64 @@ export class HttpWaitStrategy extends AbstractWaitStrategy { | |
| const client = await getContainerRuntimeClient(); | ||
| const { abortOnContainerExit } = this.options; | ||
|
|
||
| await new IntervalRetry<Response | undefined, Error>(this.readTimeoutMs).retryUntil( | ||
| async () => { | ||
| try { | ||
| const url = `${this.protocol}://${client.info.containerRuntime.host}:${boundPorts.getBinding(this.port)}${ | ||
| this.path | ||
| }`; | ||
|
|
||
| if (abortOnContainerExit) { | ||
| const containerStatus = (await client.container.inspect(container)).State.Status; | ||
|
|
||
| if (containerStatus === exitStatus) { | ||
| containerExited = true; | ||
| return; | ||
| try { | ||
| await new IntervalRetry<Response | undefined, Error>(this.readTimeoutMs).retryUntil( | ||
| async () => { | ||
| try { | ||
| const url = `${this.protocol}://${client.info.containerRuntime.host}:${boundPorts.getBinding(this.port)}${ | ||
| this.path | ||
| }`; | ||
|
|
||
| if (abortOnContainerExit) { | ||
| const containerStatus = (await client.container.inspect(container)).State.Status; | ||
|
|
||
| if (containerStatus === exitStatus) { | ||
| containerExited = true; | ||
| return; | ||
| } | ||
| } | ||
|
|
||
| return undiciResponseToFetchResponse( | ||
| await request(url, { | ||
| method: this.method, | ||
| signal: AbortSignal.timeout(this.readTimeoutMs), | ||
| headers: this.headers, | ||
| dispatcher: this.getAgent(), | ||
| }) | ||
| ); | ||
| } catch { | ||
| return undefined; | ||
| } | ||
| }, | ||
| async (response) => { | ||
| if (abortOnContainerExit && containerExited) { | ||
| return true; | ||
| } | ||
|
|
||
| return undiciResponseToFetchResponse( | ||
| await request(url, { | ||
| method: this.method, | ||
| signal: AbortSignal.timeout(this.readTimeoutMs), | ||
| headers: this.headers, | ||
| dispatcher: this.getAgent(), | ||
| }) | ||
| ); | ||
| } catch { | ||
| return undefined; | ||
| } | ||
| }, | ||
| async (response) => { | ||
| if (abortOnContainerExit && containerExited) { | ||
| return true; | ||
| } | ||
|
|
||
| if (response === undefined) { | ||
| return false; | ||
| } else if (!this.predicates.length) { | ||
| return response.ok; | ||
| } else { | ||
| for (const predicate of this.predicates) { | ||
| const result = await predicate(response); | ||
| if (!result) { | ||
| return false; | ||
| if (response === undefined) { | ||
| return false; | ||
| } else if (!this.predicates.length) { | ||
| return response.ok; | ||
| } else { | ||
| for (const predicate of this.predicates) { | ||
| const result = await predicate(response); | ||
| if (!result) { | ||
| return false; | ||
| } | ||
| } | ||
| return true; | ||
| } | ||
| return true; | ||
| } | ||
| }, | ||
| () => { | ||
| const message = `URL ${this.path} not accessible after ${this.startupTimeoutMs}ms`; | ||
| log.error(message, { containerId: container.id }); | ||
| throw new Error(message); | ||
| }, | ||
| this.startupTimeoutMs | ||
| ); | ||
| }, | ||
| () => { | ||
| const message = `URL ${this.path} not accessible after ${this.startupTimeoutMs}ms`; | ||
| log.error(message, { containerId: container.id }); | ||
| throw new Error(message); | ||
| }, | ||
| this.startupTimeoutMs | ||
| ); | ||
| } finally { | ||
| await this.closeAgent(); | ||
| } | ||
|
|
||
| if (abortOnContainerExit && containerExited) { | ||
| return this.handleContainerExit(container); | ||
|
|
@@ -166,12 +171,26 @@ export class HttpWaitStrategy extends AbstractWaitStrategy { | |
| } | ||
|
|
||
| private getAgent(): Agent | undefined { | ||
| if (this._allowInsecure) { | ||
| return new Agent({ | ||
| if (!this._allowInsecure) { | ||
| return undefined; | ||
| } | ||
|
|
||
| if (!this.insecureAgent) { | ||
| this.insecureAgent = new Agent({ | ||
| connect: { | ||
| rejectUnauthorized: false, | ||
| }, | ||
| }); | ||
| } | ||
|
|
||
| return this.insecureAgent; | ||
| } | ||
|
|
||
| private async closeAgent(): Promise<void> { | ||
| if (this.insecureAgent) { | ||
| const agent = this.insecureAgent; | ||
| this.insecureAgent = undefined; | ||
| await agent.close(); | ||
|
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 Useful? React with 👍 / 👎. |
||
| } | ||
| } | ||
| } | ||
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.
This
vi.doMockis registered after the file's staticWaitimport has already loadedHttpWaitStrategythrough./wait, andvi.doMockonly affects subsequent imports rather than already-cached modules. As a result, these supposedly Docker-free lifecycle tests still use the realgetContainerRuntimeClientand will try to initialize a real container runtime in environments without Docker; reset modules/remove the eager import before dynamically importing the strategy under the mock.Useful? React with 👍 / 👎.