Skip to content

Commit c0e27d9

Browse files
committed
wip
1 parent 99b4850 commit c0e27d9

10 files changed

+99
-4
lines changed

agent/box.json.dist

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
2+
"$schema": "https://raw.githubusercontent.com/box-project/box/refs/heads/main/res/schema.json",
23
"main": "src/agent.php",
34
"output": "build/agent.phar",
45
"alias": "nightwatch-agent.phar",

agent/build/agent.phar

18.2 KB
Binary file not shown.

agent/build/signature.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
EA7F6BABFE0D3F3B53FEE7E9C5F2E657FAF8CA6156CC92AF7ADC0F750930A98963BC7BB69056C010F4A5B77B4B53C064618A53DE62B1B482A2D75EAEDA38B682
1+
C0978882D751DC34E15523FF52762FD07A38E47F79482287AFAEABF1CB797FBF38747687A11CE7C65BA97F7F682A8683E58EDA5AD509E84F981354F675858369

agent/src/Ingest.php

+1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ public function write(string $payload): void
5454
$this->ingest($records);
5555
} elseif ($this->buffer->isNotEmpty()) {
5656
$this->flushBufferAfterDelayTimer ??= Loop::addTimer($this->maxBufferDurationInSeconds, function (): void {
57+
// TODO Must not throw an exception
5758
$records = $this->buffer->flush();
5859

5960
$this->flushBufferAfterDelayTimer = null;

agent/src/IngestDetailsRepository.php

+1
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ private function scheduleRefreshIn(int $seconds): void
114114
$seconds = max($this->minRefreshDurationInSeconds, $seconds);
115115

116116
Loop::addTimer($seconds, function (): void {
117+
// TODO must not throw an exception
117118
$this->refresh()->then(function (?IngestDetails $ingestDetails): void {
118119
$this->ingestDetails = resolve($ingestDetails);
119120
});

agent/src/Signature.php

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
namespace Laravel\NightwatchAgent;
4+
5+
use Closure;
6+
use React\EventLoop\Loop;
7+
use RuntimeException;
8+
9+
use function call_user_func;
10+
use function file_get_contents;
11+
use function trim;
12+
13+
class Signature
14+
{
15+
private string $signature;
16+
17+
/**
18+
* @param (Closure(string $before, string $after): mixed) $onChange
19+
*/
20+
public function __construct(
21+
private string $path,
22+
private int $verificationIntervalInSeconds,
23+
private Closure $onChange,
24+
) {
25+
//
26+
}
27+
28+
public function capture(): void
29+
{
30+
$signature = file_get_contents($this->path);
31+
32+
if ($signature === false) {
33+
throw new RuntimeException('Unable to read the signature file');
34+
}
35+
36+
if (trim($signature) === '') {
37+
throw new RuntimeException('Signature file is empty');
38+
}
39+
40+
$this->signature = $signature;
41+
42+
Loop::addPeriodicTimer($this->verificationIntervalInSeconds, function () {
43+
// TODO must not throw an exception
44+
$this->verify(...);
45+
});
46+
}
47+
48+
private function verify(): void
49+
{
50+
$newSignature = file_get_contents($this->path);
51+
52+
if ($newSignature === false) {
53+
throw new RuntimeException('Unable to verify the signature file');
54+
}
55+
56+
if ($this->signature !== $newSignature) {
57+
call_user_func($this->onChange, $this->signature, $newSignature);
58+
}
59+
}
60+
}

agent/src/agent.php

+15-3
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@
7575
minRefreshDurationInSeconds: 60,
7676
server: $server,
7777
packageVersion: $packageVersion,
78-
onAuthenticationSuccess: static fn (IngestDetails $ingestDetails, float $duration) => $info('Authentication successful ['.round($duration, 3).'s]'),
79-
onAuthenticationError: static fn (Throwable $e, float $duration) => $info('Authentication failed ['.round($duration, 3).'s]: '.$e->getMessage()),
78+
onAuthenticationSuccess: static fn (IngestDetails $ingestDetails, float $duration) => $info('Authentication successful against ['.$baseUrl.'] ['.round($duration, 3).'s]'),
79+
onAuthenticationError: static fn (Throwable $e, float $duration) => $info('Authentication failed against ['.$baseUrl.'] ['.round($duration, 3).'s]: '.$e->getMessage()),
8080
);
8181

8282
$ingest = (new IngestFactory)(
@@ -95,16 +95,28 @@
9595

9696
$server = (new ServerFactory)(
9797
listenOn: $listenOn,
98-
onServerStarted: static fn () => $info("Nightwatch agent initiated: Listening on [{$listenOn}]."),
98+
onServerStarted: static fn () => $info("Nightwatch agent initiated: Listening on [{$listenOn}]"),
9999
onServerError: static fn (Throwable $e) => $error("Server error: {$e->getMessage()}"),
100100
onConnectionError: static fn (Throwable $e) => $error("Connection error: {$e->getMessage()}"),
101101
onPayloadReceived: $ingest->write(...),
102102
);
103103

104+
$signature = new Signature(
105+
path: $basePath.'/signature.txt',
106+
verificationIntervalInSeconds: 5,
107+
onChange: static function (string $before, string $after) use ($info) {
108+
$info("Restarting the agent as the signature has changed [{$before}] [{$after}]");
109+
110+
Loop::stop();
111+
},
112+
);
113+
104114
/*
105115
* Get things rolling...
106116
*/
107117

118+
$signature->capture();
119+
108120
$server->start();
109121

110122
$ingestDetails->hydrate();

agent/tests/Unit/SignatureTest.php

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
use Laravel\NightwatchAgent\Signature;
4+
5+
it('throws an exception if the signature file does not exist', function () {
6+
$signature = new Signature(
7+
path: __DIR__.'/file-that-does-not-exist',
8+
verificationIntervalInSeconds: 1,
9+
onChange: fn () => null,
10+
);
11+
12+
$signature->capture();
13+
})->throws(RuntimeException::class);

client/box.json.dist

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
2+
"$schema": "https://raw.githubusercontent.com/box-project/box/refs/heads/main/res/schema.json",
23
"main": "src/client.php",
34
"output": "build/client.phar",
45
"alias": "nightwatch-client.phar",

restart.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Restart
2+
3+
- There is not really any need to restart on every deployment. It would be detrimental to data collection process to do so unless:
4+
- The agent has changed.
5+
- The agent has received data it no longer understands, e.g, a payload version change.
6+
- Environment variables / configuration variables that the agent requires has changed. Currently this is only the `NIGHTWATCH_TOKEN` which is unlikely to change enough to make restarting on every deploy worth it.

0 commit comments

Comments
 (0)