diff --git a/src/workerd/api/container.c++ b/src/workerd/api/container.c++ index 9838373e059..21335b1d42d 100644 --- a/src/workerd/api/container.c++ +++ b/src/workerd/api/container.c++ @@ -5,6 +5,7 @@ #include "container.h" #include +#include #include namespace workerd::api { @@ -17,6 +18,7 @@ Container::Container(rpc::Container::Client rpcClient, bool running) running(running) {} void Container::start(jsg::Lock& js, jsg::Optional maybeOptions) { + auto flags = FeatureFlags::get(js); JSG_REQUIRE(!running, Error, "start() cannot be called on a container that is already running."); StartupOptions options = kj::mv(maybeOptions).orDefault({}); @@ -50,6 +52,13 @@ void Container::start(jsg::Lock& js, jsg::Optional maybeOptions) IoContext::current().addTask(req.sendIgnoringResult()); running = true; + + if (flags.getWorkerdExperimental()) { + KJ_IF_SOME(hardTimeoutMs, options.hardTimeout) { + JSG_REQUIRE(hardTimeoutMs > 0, TypeError, "Hard timeout must be greater than 0"); + req.setHardTimeoutMs(hardTimeoutMs); + } + } } jsg::Promise Container::setInactivityTimeout(jsg::Lock& js, int64_t durationMs) { diff --git a/src/workerd/api/container.h b/src/workerd/api/container.h index 4158f3ba105..7494080e6b4 100644 --- a/src/workerd/api/container.h +++ b/src/workerd/api/container.h @@ -28,10 +28,27 @@ class Container: public jsg::Object { jsg::Optional> entrypoint; bool enableInternet = false; jsg::Optional> env; + jsg::Optional hardTimeout; // TODO(containers): Allow intercepting stdin/stdout/stderr by specifying streams here. - JSG_STRUCT(entrypoint, enableInternet, env); + JSG_STRUCT(entrypoint, enableInternet, env, hardTimeout); + JSG_STRUCT_TS_OVERRIDE_DYNAMIC(CompatibilityFlags::Reader flags) { + if (flags.getWorkerdExperimental()) { + JSG_TS_OVERRIDE(ContainerStartupOptions { + entrypoint?: string[]; + enableInternet: boolean; + env?: Record; + hardTimeout?: number; + }); + } else { + JSG_TS_OVERRIDE(ContainerStartupOptions { + entrypoint?: string[]; + enableInternet: boolean; + env?: Record; + }); + } + } }; bool getRunning() { diff --git a/src/workerd/io/container.capnp b/src/workerd/io/container.capnp index a68d09a57b6..f79e85defb2 100644 --- a/src/workerd/io/container.capnp +++ b/src/workerd/io/container.capnp @@ -34,6 +34,12 @@ interface Container @0x9aaceefc06523bca { # If null, the container will start with the environment variables defined in its image. # The format is defined as a list of `NAME=VALUE`. # The container runtime should validate the environment variables input. + + hardTimeoutMs @3 :Int64; + # Configures an absolute timeout that starts when the container starts and never resets. + # The container will be forcefully terminated when this timeout expires, regardless of activity. + # Unlike inactivity timeout, this is a hard deadline from container startup. + # If 0 (default), no hard timeout is applied. } monitor @2 () -> (exitCode: Int32); diff --git a/types/generated-snapshot/experimental/index.d.ts b/types/generated-snapshot/experimental/index.d.ts index b52eae3f10b..4af44b9dac1 100755 --- a/types/generated-snapshot/experimental/index.d.ts +++ b/types/generated-snapshot/experimental/index.d.ts @@ -3840,6 +3840,7 @@ interface ContainerStartupOptions { entrypoint?: string[]; enableInternet: boolean; env?: Record; + hardTimeout?: number; } /** * The **`FileSystemHandle`** interface of the File System API is an object which represents a file or directory entry. diff --git a/types/generated-snapshot/experimental/index.ts b/types/generated-snapshot/experimental/index.ts index 22e313aec87..3d052ca7167 100755 --- a/types/generated-snapshot/experimental/index.ts +++ b/types/generated-snapshot/experimental/index.ts @@ -3851,6 +3851,7 @@ export interface ContainerStartupOptions { entrypoint?: string[]; enableInternet: boolean; env?: Record; + hardTimeout?: number; } /** * The **`FileSystemHandle`** interface of the File System API is an object which represents a file or directory entry. diff --git a/types/generated-snapshot/latest/index.d.ts b/types/generated-snapshot/latest/index.d.ts index db9c6f368b3..2239c3aadd3 100755 --- a/types/generated-snapshot/latest/index.d.ts +++ b/types/generated-snapshot/latest/index.d.ts @@ -3734,6 +3734,7 @@ interface ContainerStartupOptions { entrypoint?: string[]; enableInternet: boolean; env?: Record; + hardTimeout?: number | bigint; } /** * The **`MessagePort`** interface of the Channel Messaging API represents one of the two ports of a MessageChannel, allowing messages to be sent from one port and listening out for them arriving at the other. diff --git a/types/generated-snapshot/latest/index.ts b/types/generated-snapshot/latest/index.ts index 20316383a3b..2cebba66e85 100755 --- a/types/generated-snapshot/latest/index.ts +++ b/types/generated-snapshot/latest/index.ts @@ -3745,6 +3745,7 @@ export interface ContainerStartupOptions { entrypoint?: string[]; enableInternet: boolean; env?: Record; + hardTimeout?: number | bigint; } /** * The **`MessagePort`** interface of the Channel Messaging API represents one of the two ports of a MessageChannel, allowing messages to be sent from one port and listening out for them arriving at the other.