@@ -177,7 +177,7 @@ ContainerClient::ContainerClient(capnp::ByteStreamFactory& byteStreamFactory,
177177 kj::String dockerPath,
178178 kj::String containerName,
179179 kj::String imageName,
180- kj::String containerEgressInterceptorImage,
180+ kj::Maybe<kj:: String> containerEgressInterceptorImage,
181181 kj::TaskSet& waitUntilTasks,
182182 kj::Function<void ()> cleanupCallback,
183183 ChannelTokenHandler& channelTokenHandler)
@@ -314,8 +314,7 @@ class EgressHttpService final: public kj::HttpService {
314314 auto innerServer =
315315 kj::heap<kj::HttpServer>(containerClient.timer , headerTable, *innerService);
316316
317- co_await innerServer->listenHttpCleanDrain (connection)
318- .attach (kj::mv (innerServer), kj::mv (innerService));
317+ co_await innerServer->listenHttpCleanDrain (connection);
319318
320319 co_return ;
321320 }
@@ -351,7 +350,6 @@ constexpr kj::StringPtr WORKERD_NETWORK_NAME = "workerd-network"_kj;
351350
352351kj::Promise<ContainerClient::IPAMConfigResult> ContainerClient::getDockerBridgeIPAMConfig () {
353352 // First, try to find or create the workerd-network
354- // Docker API: GET /networks/workerd-network
355353 auto response = co_await dockerApiRequest (network, kj::str (dockerPath), kj::HttpMethod::GET,
356354 kj::str (" /networks/" , WORKERD_NETWORK_NAME));
357355
@@ -383,7 +381,6 @@ kj::Promise<ContainerClient::IPAMConfigResult> ContainerClient::getDockerBridgeI
383381}
384382
385383kj::Promise<void > ContainerClient::createWorkerdNetwork () {
386- // Docker API: POST /networks/create
387384 // Equivalent to: docker network create -d bridge --ipv6 workerd-network
388385 capnp::JsonCodec codec;
389386 codec.handleByAnnotation <docker_api::Docker::NetworkCreateRequest>();
@@ -466,7 +463,6 @@ kj::Promise<ContainerClient::Response> ContainerClient::dockerApiRequest(kj::Net
466463}
467464
468465kj::Promise<ContainerClient::InspectResponse> ContainerClient::inspectContainer () {
469- // Docker API: GET /containers/{id}/json
470466 auto endpoint = kj::str (" /containers/" , containerName, " /json" );
471467
472468 auto response = co_await dockerApiRequest (
@@ -526,7 +522,6 @@ kj::Promise<void> ContainerClient::createContainer(
526522 kj::Maybe<capnp::List<capnp::Text>::Reader> entrypoint,
527523 kj::Maybe<capnp::List<capnp::Text>::Reader> environment,
528524 rpc::Container::StartParams::Reader params) {
529- // Docker API: POST /containers/create
530525 capnp::JsonCodec codec;
531526 codec.handleByAnnotation <docker_api::Docker::ContainerCreateRequest>();
532527 capnp::MallocMessageBuilder message;
@@ -601,7 +596,6 @@ kj::Promise<void> ContainerClient::createContainer(
601596}
602597
603598kj::Promise<void > ContainerClient::startContainer () {
604- // Docker API: POST /containers/{id}/start
605599 auto endpoint = kj::str (" /containers/" , containerName, " /start" );
606600 // We have to send an empty body since docker API will throw an error if we don't.
607601 auto response = co_await dockerApiRequest (
@@ -613,7 +607,6 @@ kj::Promise<void> ContainerClient::startContainer() {
613607}
614608
615609kj::Promise<void > ContainerClient::stopContainer () {
616- // Docker API: POST /containers/{id}/stop
617610 auto endpoint = kj::str (" /containers/" , containerName, " /stop" );
618611 auto response = co_await dockerApiRequest (
619612 network, kj::str (dockerPath), kj::HttpMethod::POST, kj::mv (endpoint));
@@ -625,7 +618,6 @@ kj::Promise<void> ContainerClient::stopContainer() {
625618}
626619
627620kj::Promise<void > ContainerClient::killContainer (uint32_t signal) {
628- // Docker API: POST /containers/{id}/kill
629621 auto endpoint = kj::str (" /containers/" , containerName, " /kill?signal=" , signalToString (signal));
630622 auto response = co_await dockerApiRequest (
631623 network, kj::str (dockerPath), kj::HttpMethod::POST, kj::mv (endpoint));
@@ -669,7 +661,10 @@ kj::Promise<void> ContainerClient::createSidecarContainer(
669661 codec.handleByAnnotation <docker_api::Docker::ContainerCreateRequest>();
670662 capnp::MallocMessageBuilder message;
671663 auto jsonRoot = message.initRoot <docker_api::Docker::ContainerCreateRequest>();
672- jsonRoot.setImage (containerEgressInterceptorImage);
664+ auto & image = KJ_ASSERT_NONNULL (containerEgressInterceptorImage,
665+ " containerEgressInterceptorImage must be configured to use egress interception. "
666+ " Set it in the localDocker configuration." );
667+ jsonRoot.setImage (image);
673668
674669 auto cmd = jsonRoot.initCmd (4 );
675670 cmd.set (0 , " --http-egress-port" );
@@ -695,8 +690,7 @@ kj::Promise<void> ContainerClient::createSidecarContainer(
695690 }
696691
697692 if (response.statusCode != 201 ) {
698- JSG_REQUIRE (response.statusCode != 404 , Error, " No such image available named " ,
699- containerEgressInterceptorImage,
693+ JSG_REQUIRE (response.statusCode != 404 , Error, " No such image available named " , image,
700694 " . Please ensure the container egress interceptor image is built and available." );
701695 JSG_FAIL_REQUIRE (Error, " Failed to create the networking sidecar [" , response.statusCode , " ] " ,
702696 response.body );
@@ -744,14 +738,17 @@ kj::Promise<void> ContainerClient::start(StartContext context) {
744738 internetEnabled = params.getEnableInternet ();
745739
746740 co_await createContainer (entrypoint, environment, params);
741+ co_await startContainer ();
747742
748743 // Opt in to the proxy sidecar container only if the user has configured egressMappings
749744 // for now. In the future, it will always run when a user container is running
750745 if (!egressMappings.empty ()) {
746+ // The user container will be blocked on network connectivity until this finishes.
747+ // When workerd-network is more battle-tested and goes out of experimental so it's non-optional,
748+ // we should make the sidecar start first and _then_ make the user container join the sidecar network.
751749 co_await ensureSidecarStarted ();
752750 }
753751
754- co_await startContainer ();
755752 containerStarted.store (true , std::memory_order_release);
756753}
757754
@@ -861,7 +858,6 @@ kj::Promise<void> ContainerClient::setEgressHttp(SetEgressHttpContext context) {
861858 auto params = context.getParams ();
862859 auto hostPortStr = kj::str (params.getHostPort ());
863860 auto tokenBytes = params.getChannelToken ();
864- JSG_REQUIRE (containerEgressInterceptorImage != " " , Error, " should be set for setEgressHttp" );
865861
866862 auto parsed = parseHostPort (hostPortStr);
867863 uint16_t port = parsed.port .orDefault (80 );
0 commit comments