diff --git a/docs/features/containers.md b/docs/features/containers.md index 704a65187..7b44e36d2 100644 --- a/docs/features/containers.md +++ b/docs/features/containers.md @@ -21,10 +21,26 @@ const container = await new GenericContainer("alpine:3.10").start(); Testcontainers will automatically pull an image if it doesn't exist. This is configurable: ```javascript -const { GenericContainer, AlwaysPullPolicy } = require("testcontainers"); +const { GenericContainer, PullPolicy } = require("testcontainers"); const container = await new GenericContainer("alpine") - .withPullPolicy(new AlwaysPullPolicy()) + .withPullPolicy(PullPolicy.alwaysPull()) + .start(); +``` + +#### Custom pull policy + +```typescript +const { GenericContainer, ImagePullPolicy } = require("testcontainers"); + +class CustomPullPolicy implements ImagePullPolicy { + public shouldPull(): boolean { + return true; + } +} + +const container = await new GenericContainer("alpine") + .withPullPolicy(new CustomPullPolicy()) .start(); ``` diff --git a/docs/features/images.md b/docs/features/images.md index fb9d8311a..ba8793609 100644 --- a/docs/features/images.md +++ b/docs/features/images.md @@ -14,16 +14,33 @@ const container = await GenericContainer const startedContainer = await container.start(); ``` -### With pull policy +### With a pull policy Testcontainers will automatically pull an image if it doesn't exist. This is configurable: ```javascript -const { GenericContainer, AlwaysPullPolicy } = require("testcontainers"); +const { GenericContainer, PullPolicy } = require("testcontainers"); const container = await GenericContainer .fromDockerfile("/path/to/build-context") - .withPullPolicy(new AlwaysPullPolicy()) + .withPullPolicy(PullPolicy.alwaysPull()) + .build(); +``` + +#### Custom pull policy + +```typescript +const { GenericContainer, ImagePullPolicy } = require("testcontainers"); + +class CustomPullPolicy implements ImagePullPolicy { + public shouldPull(): boolean { + return true; + } +} + +const container = await GenericContainer + .fromDockerfile("/path/to/build-context") + .withPullPolicy(new CustomPullPolicy()) .build(); ``` diff --git a/src/docker-compose-environment/docker-compose-environment.test.ts b/src/docker-compose-environment/docker-compose-environment.test.ts index df82a96d9..06a43636c 100644 --- a/src/docker-compose-environment/docker-compose-environment.test.ts +++ b/src/docker-compose-environment/docker-compose-environment.test.ts @@ -2,6 +2,7 @@ import fetch from "node-fetch"; import path from "path"; import { DockerComposeEnvironment } from "./docker-compose-environment"; import { Wait } from "../wait-strategy/wait"; +import { PullPolicy } from "../pull-policy"; import { checkEnvironmentContainerIsHealthy, composeContainerName, @@ -9,7 +10,6 @@ import { getVolumeNames, waitForDockerEvent, } from "../test-helper"; -import { AlwaysPullPolicy } from "../pull-policy"; describe("DockerComposeEnvironment", () => { jest.setTimeout(180_000); @@ -45,7 +45,7 @@ describe("DockerComposeEnvironment", () => { const startedEnv1 = await env.up(); const dockerPullEventPromise = waitForDockerEvent("pull", 2); - const startedEnv2 = await env.withPullPolicy(new AlwaysPullPolicy()).up(); + const startedEnv2 = await env.withPullPolicy(PullPolicy.alwaysPull()).up(); await dockerPullEventPromise; await startedEnv1.stop(); @@ -57,7 +57,7 @@ describe("DockerComposeEnvironment", () => { const startedEnv1 = await env.up(["service_2"]); const dockerPullEventPromise = waitForDockerEvent("pull"); - const startedEnv2 = await env.withPullPolicy(new AlwaysPullPolicy()).up(["service_2"]); + const startedEnv2 = await env.withPullPolicy(PullPolicy.alwaysPull()).up(["service_2"]); await dockerPullEventPromise; await startedEnv1.stop(); diff --git a/src/docker-compose-environment/docker-compose-environment.ts b/src/docker-compose-environment/docker-compose-environment.ts index a4cd99970..4fa0b8293 100644 --- a/src/docker-compose-environment/docker-compose-environment.ts +++ b/src/docker-compose-environment/docker-compose-environment.ts @@ -17,7 +17,7 @@ import { dockerComposeDown } from "../docker-compose/functions/docker-compose-do import { dockerComposeUp } from "../docker-compose/functions/docker-compose-up"; import { waitForContainer } from "../wait-for-container"; import { defaultWaitStrategy } from "../wait-strategy/default-wait-strategy"; -import { DefaultPullPolicy, PullPolicy } from "../pull-policy"; +import { ImagePullPolicy, PullPolicy } from "../pull-policy"; import { dockerComposePull } from "../docker-compose/functions/docker-compose-pull"; export class DockerComposeEnvironment { @@ -30,7 +30,7 @@ export class DockerComposeEnvironment { private environmentFile = ""; private profiles: string[] = []; private environment: Environment = {}; - private pullPolicy: PullPolicy = new DefaultPullPolicy(); + private pullPolicy: ImagePullPolicy = PullPolicy.defaultPolicy(); private waitStrategy: { [containerName: string]: WaitStrategy } = {}; private startupTimeout = 60_000; @@ -66,7 +66,7 @@ export class DockerComposeEnvironment { return this; } - public withPullPolicy(pullPolicy: PullPolicy): this { + public withPullPolicy(pullPolicy: ImagePullPolicy): this { this.pullPolicy = pullPolicy; return this; } diff --git a/src/docker/functions/image/build-image.ts b/src/docker/functions/image/build-image.ts index ff1d1e7a5..59d3d620f 100644 --- a/src/docker/functions/image/build-image.ts +++ b/src/docker/functions/image/build-image.ts @@ -1,5 +1,5 @@ import { DockerImageName } from "../../../docker-image-name"; -import { PullPolicy } from "../../../pull-policy"; +import { ImagePullPolicy } from "../../../pull-policy"; import { log } from "../../../logger"; import tar from "tar-fs"; import byline from "byline"; @@ -16,7 +16,7 @@ export type BuildImageOptions = { context: string; dockerfileName: string; buildArgs: BuildArgs; - pullPolicy: PullPolicy; + pullPolicy: ImagePullPolicy; registryConfig: RegistryConfig; cache: boolean; }; diff --git a/src/generic-container/generic-container-builder.ts b/src/generic-container/generic-container-builder.ts index 3e7911d37..95c0fc246 100644 --- a/src/generic-container/generic-container-builder.ts +++ b/src/generic-container/generic-container-builder.ts @@ -1,5 +1,5 @@ import { AuthConfig, BuildArgs, RegistryConfig } from "../docker/types"; -import { DefaultPullPolicy, PullPolicy } from "../pull-policy"; +import { ImagePullPolicy, PullPolicy } from "../pull-policy"; import { RandomUuid, Uuid } from "../uuid"; import { ReaperInstance } from "../reaper"; import { DockerImageName } from "../docker-image-name"; @@ -14,7 +14,7 @@ import { imageExists } from "../docker/functions/image/image-exists"; export class GenericContainerBuilder { private buildArgs: BuildArgs = {}; - private pullPolicy: PullPolicy = new DefaultPullPolicy(); + private pullPolicy: ImagePullPolicy = PullPolicy.defaultPolicy(); private cache = true; constructor( @@ -28,7 +28,7 @@ export class GenericContainerBuilder { return this; } - public withPullPolicy(pullPolicy: PullPolicy): this { + public withPullPolicy(pullPolicy: ImagePullPolicy): this { this.pullPolicy = pullPolicy; return this; } diff --git a/src/generic-container/generic-container-dockerfile.test.ts b/src/generic-container/generic-container-dockerfile.test.ts index 935603c63..fcd9c46c8 100644 --- a/src/generic-container/generic-container-dockerfile.test.ts +++ b/src/generic-container/generic-container-dockerfile.test.ts @@ -1,6 +1,6 @@ import path from "path"; import { GenericContainer } from "./generic-container"; -import { AlwaysPullPolicy } from "../pull-policy"; +import { PullPolicy } from "../pull-policy"; import { Wait } from "../wait-strategy/wait"; import { checkContainerIsHealthy, waitForDockerEvent } from "../test-helper"; @@ -23,7 +23,7 @@ describe("GenericContainer Dockerfile", () => { if (!process.env["CI_PODMAN"]) { it("should use pull policy", async () => { const dockerfile = path.resolve(fixtures, "docker"); - const containerSpec = GenericContainer.fromDockerfile(dockerfile).withPullPolicy(new AlwaysPullPolicy()); + const containerSpec = GenericContainer.fromDockerfile(dockerfile).withPullPolicy(PullPolicy.alwaysPull()); await containerSpec.build(); const dockerPullEventPromise = waitForDockerEvent("pull"); diff --git a/src/generic-container/generic-container.test.ts b/src/generic-container/generic-container.test.ts index 49462b6a7..4c836d596 100644 --- a/src/generic-container/generic-container.test.ts +++ b/src/generic-container/generic-container.test.ts @@ -2,7 +2,7 @@ import fetch from "node-fetch"; import path from "path"; import getPort from "get-port"; import { GenericContainer } from "./generic-container"; -import { AlwaysPullPolicy } from "../pull-policy"; +import { PullPolicy } from "../pull-policy"; import { checkContainerIsHealthy, waitForDockerEvent } from "../test-helper"; import { getContainerById } from "../docker/functions/container/get-container"; @@ -224,7 +224,7 @@ describe("GenericContainer", () => { const startedContainer1 = await container.start(); const dockerPullEventPromise = waitForDockerEvent("pull"); - const startedContainer2 = await container.withPullPolicy(new AlwaysPullPolicy()).start(); + const startedContainer2 = await container.withPullPolicy(PullPolicy.alwaysPull()).start(); await dockerPullEventPromise; await startedContainer1.stop(); diff --git a/src/generic-container/generic-container.ts b/src/generic-container/generic-container.ts index e4b6ff001..a25ca36f2 100644 --- a/src/generic-container/generic-container.ts +++ b/src/generic-container/generic-container.ts @@ -3,7 +3,7 @@ import AsyncLock from "async-lock"; import { BoundPorts } from "../bound-ports"; import { containerLog, log } from "../logger"; import { PortWithOptionalBinding } from "../port"; -import { DefaultPullPolicy, PullPolicy } from "../pull-policy"; +import { ImagePullPolicy, PullPolicy } from "../pull-policy"; import { ReaperInstance } from "../reaper"; import { DockerImageName } from "../docker-image-name"; import { StartedTestContainer, TestContainer } from "../test-container"; @@ -53,7 +53,7 @@ export class GenericContainer implements TestContainer { protected tarToCopy?: archiver.Archiver; protected networkMode?: string; protected networkAliases: string[] = []; - protected pullPolicy: PullPolicy = new DefaultPullPolicy(); + protected pullPolicy: ImagePullPolicy = PullPolicy.defaultPolicy(); constructor(readonly image: string) { const imageName = DockerImageName.fromString(image); @@ -362,7 +362,7 @@ export class GenericContainer implements TestContainer { return this; } - public withPullPolicy(pullPolicy: PullPolicy): this { + public withPullPolicy(pullPolicy: ImagePullPolicy): this { this.pullPolicy = pullPolicy; return this; } diff --git a/src/index.ts b/src/index.ts index f1a6bcf74..fd0395d61 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,7 +12,7 @@ export { Network, StartedNetwork, StoppedNetwork } from "./network"; export { Wait } from "./wait-strategy/wait"; export { StartupCheckStrategy, StartupStatus } from "./wait-strategy/startup-check-strategy"; -export { PullPolicy, DefaultPullPolicy, AlwaysPullPolicy } from "./pull-policy"; +export { PullPolicy, ImagePullPolicy } from "./pull-policy"; export { InspectResult } from "./docker/functions/container/inspect-container"; export { AbstractStartedContainer } from "./modules/abstract-started-container"; diff --git a/src/pull-policy.test.ts b/src/pull-policy.test.ts new file mode 100644 index 000000000..d7d397166 --- /dev/null +++ b/src/pull-policy.test.ts @@ -0,0 +1,19 @@ +import { PullPolicy, ImagePullPolicy } from "./index"; + +test("default pull policy should return false", () => { + expect(PullPolicy.defaultPolicy().shouldPull()).toBe(false); +}); + +test("always pull policy should return true", () => { + expect(PullPolicy.alwaysPull().shouldPull()).toBe(true); +}); + +test("should be able to create a custom pull policy", () => { + class CustomPullPolicy implements ImagePullPolicy { + public shouldPull(): boolean { + return true; + } + } + + expect(new CustomPullPolicy().shouldPull()).toBe(true); +}); diff --git a/src/pull-policy.ts b/src/pull-policy.ts index de7f67c53..8e8fd4fb7 100644 --- a/src/pull-policy.ts +++ b/src/pull-policy.ts @@ -1,15 +1,25 @@ -export interface PullPolicy { +export interface ImagePullPolicy { shouldPull(): boolean; } -export class DefaultPullPolicy implements PullPolicy { +class DefaultPullPolicy implements ImagePullPolicy { public shouldPull(): boolean { return false; } } -export class AlwaysPullPolicy implements PullPolicy { +class AlwaysPullPolicy implements ImagePullPolicy { public shouldPull(): boolean { return true; } } + +export class PullPolicy { + public static defaultPolicy(): ImagePullPolicy { + return new DefaultPullPolicy(); + } + + public static alwaysPull(): ImagePullPolicy { + return new AlwaysPullPolicy(); + } +} diff --git a/src/test-container.ts b/src/test-container.ts index 6e629b641..d4a0dd119 100644 --- a/src/test-container.ts +++ b/src/test-container.ts @@ -1,5 +1,5 @@ import { PortWithOptionalBinding } from "./port"; -import { PullPolicy } from "./pull-policy"; +import { ImagePullPolicy } from "./pull-policy"; import { WaitStrategy } from "./wait-strategy/wait-strategy"; import { Readable } from "stream"; import { @@ -53,7 +53,7 @@ export interface TestContainer { withUser(user: string): this; - withPullPolicy(pullPolicy: PullPolicy): this; + withPullPolicy(pullPolicy: ImagePullPolicy): this; withReuse(): this;