Merge pull request #7674 from nextcloud/enh/7664/start-watchtower

Dockercontroller: refactor streaming responses into dedicated functions and use them  in StartWatchtowerContainer method
This commit is contained in:
Simon L.
2026-03-06 10:13:34 +01:00
committed by GitHub
2 changed files with 49 additions and 25 deletions

View File

@@ -202,23 +202,9 @@ readonly class DockerController {
error_log('WARNING: Not pulling container images. Instead, using local ones.');
}
$nonbufResp = $response
->withBody(new NonBufferedBody())
->withHeader('Content-Type', 'text/html; charset=utf-8')
->withHeader('X-Accel-Buffering', 'no')
->withHeader('Cache-Control', 'no-cache');
// Text written into this body is immediately sent to the client, without waiting for later content.
$streamingResponseBody = $nonbufResp->getBody();
$streamingResponseBody->write($this->getStreamingResponseHtmlStart());
// Create a closure to pass around to the code, which should to the logging (because it e.g. decides
// if it'll actually pull an image), but which should not need to know anything about the
// wanted markup or formatting.
$addToStreamingResponseBody = function (Container $container, string $message) use ($streamingResponseBody) : void {
$streamingResponseBody->write("<div>{$container->displayName}: {$message}</div>");
};
// Get streaming response start and closure
$nonbufResp = $this->startStreamingResponse($response);
$addToStreamingResponseBody = $this->getAddToStreamingResponseBody($nonbufResp);
// Start container
$this->startTopContainer($pullImage, $addToStreamingResponseBody);
@@ -227,7 +213,8 @@ readonly class DockerController {
// Temporarily disabled as it leads much faster to docker rate limits
// apcu_clear_cache();
$streamingResponseBody->write($this->getStreamingResponseHtmlEnd());
// End streaming response
$this->finalizeStreamingResponse($nonbufResp);
return $nonbufResp;
}
@@ -243,14 +230,21 @@ readonly class DockerController {
}
public function StartWatchtowerContainer(Request $request, Response $response, array $args) : Response {
$this->startWatchtower();
return $response->withStatus(201)->withHeader('Location', '.');
// Get streaming response start and closure
$nonbufResp = $this->startStreamingResponse($response);
$addToStreamingResponseBody = $this->getAddToStreamingResponseBody($nonbufResp);
$this->startWatchtower($addToStreamingResponseBody);
// End streaming response
$this->finalizeStreamingResponse($nonbufResp);
return $nonbufResp;
}
public function startWatchtower() : void {
public function startWatchtower(?\Closure $addToStreamingResponseBody = null) : void {
$id = 'nextcloud-aio-watchtower';
$this->PerformRecursiveContainerStart($id);
$this->PerformRecursiveContainerStart($id, true, $addToStreamingResponseBody);
}
private function PerformRecursiveContainerStop(string $id, bool $forceStopNextcloud = false) : void
@@ -354,7 +348,37 @@ readonly class DockerController {
END;
}
private function startStreamingResponse(Response $response) : Response {
$nonbufResp = $response
->withBody(new NonBufferedBody())
->withHeader('Content-Type', 'text/html; charset=utf-8')
->withHeader('X-Accel-Buffering', 'no')
->withHeader('Cache-Control', 'no-cache');
// Text written into this body is immediately sent to the client, without waiting for later content.
$streamingResponseBody = $nonbufResp->getBody();
$streamingResponseBody->write($this->getStreamingResponseHtmlStart());
return $nonbufResp;
}
private function getAddToStreamingResponseBody(Response $nonbufResp) : ?\Closure {
// Create a closure to pass around to the code, which should to the logging (because it e.g. decides
// if it'll actually pull an image), but which should not need to know anything about the
// wanted markup or formatting.
$addToStreamingResponseBody = function (Container $container, string $message) use ($nonbufResp) : void {
$nonbufResp->getBody()->write("<div>{$container->displayName}: {$message}</div>");
};
return $addToStreamingResponseBody;
}
private function finalizeStreamingResponse(Response $nonbufResp) : void {
$nonbufResp->getBody()->write($this->getStreamingResponseHtmlEnd());
}
private function getStreamingResponseHtmlEnd() : string {
return "\n </body>\n</html>";
}

View File

@@ -90,7 +90,7 @@
{% elseif is_mastercontainer_update_available == true %}
<h2>Mastercontainer update</h2>
<p>⚠️ A mastercontainer update is available. Please click on the button below to update it. Afterwards, you will be able to proceed with the setup.</p>
<form method="POST" action="api/docker/watchtower" class="xhr">
<form method="POST" action="api/docker/watchtower" target="overlay-log">
<input type="hidden" name="{{csrf.keys.name}}" value="{{csrf.name}}">
<input type="hidden" name="{{csrf.keys.value}}" value="{{csrf.value}}">
<input type="submit" value="Update mastercontainer" />
@@ -332,7 +332,7 @@
{% endif %}
{% if is_mastercontainer_update_available == true %}
<p>⚠️ A mastercontainer update is available. Please click on the button below to update it.</p>
<form method="POST" action="api/docker/watchtower" class="xhr">
<form method="POST" action="api/docker/watchtower" target="overlay-log">
<input type="hidden" name="{{csrf.keys.name}}" value="{{csrf.name}}">
<input type="hidden" name="{{csrf.keys.value}}" value="{{csrf.value}}">
<input type="submit" value="Update mastercontainer" />