Skip to content

Commit 2646980

Browse files
committed
Merge branch 'Namoshek-add-new-ping-check'
2 parents 5892324 + e9dbd57 commit 2646980

File tree

9 files changed

+279
-0
lines changed

9 files changed

+279
-0
lines changed

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,14 @@ return [
146146
'connections' => [],
147147
],
148148
\BeyondCode\SelfDiagnosis\Checks\RoutesAreCached::class,
149+
\BeyondCode\SelfDiagnosis\Checks\ServersArePingable::class => [
150+
'servers' => [
151+
'www.google.com',
152+
['host' => 'www.google.com', 'port' => 8080],
153+
'8.8.8.8',
154+
['host' => '8.8.8.8', 'port' => 8080, 'timeout' => 5],
155+
],
156+
],
149157
\BeyondCode\SelfDiagnosis\Checks\SupervisorProgramsAreRunning::class => [
150158
'programs' => [
151159
'horizon',
@@ -184,6 +192,8 @@ The following options are available for the individual checks:
184192
- [`BeyondCode\SelfDiagnosis\Checks\SupervisorProgramsAreRunning`](src/Checks/SupervisorProgramsAreRunning.php)
185193
- **programs** *(array, list of programs like `['horizon', 'another-program']`, default: `[]`)*: programs that are required to be running
186194
- **restarted_within** *(integer, max allowed seconds since last restart of programs (`0` = disabled), default: `0`)*: verifies that programs have been restarted recently to grab code updates
195+
- [`BeyondCode\SelfDiagnosis\Checks\ServersArePingable`](src/Checks/ServersArePingable.php)
196+
- **servers** *(array, list of servers and parameters like `['google.com', ['host' => 'google.com', 'port' => 8080]]`)*: servers to ping
187197

188198
### Custom Checks
189199

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
],
1818
"require": {
1919
"php": "^7.1",
20+
"geerlingguy/ping": "^1.1",
2021
"illuminate/support": "5.2.*|5.3.*|5.4.*|5.5.*|5.6.*",
2122
"vlucas/phpdotenv": "~2.5"
2223
},

config/config.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,14 @@
7575
],
7676
],
7777
\BeyondCode\SelfDiagnosis\Checks\RoutesAreCached::class,
78+
//\BeyondCode\SelfDiagnosis\Checks\ServersArePingable::class => [
79+
// 'servers' => [
80+
// 'www.google.com',
81+
// ['host' => 'www.google.com', 'port' => 8080],
82+
// '8.8.8.8',
83+
// ['host' => '8.8.8.8', 'port' => 8080, 'timeout' => 5],
84+
// ],
85+
//],
7886
//\BeyondCode\SelfDiagnosis\Checks\SupervisorProgramsAreRunning::class => [
7987
// 'programs' => [
8088
// 'horizon',

src/Checks/ServersArePingable.php

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
<?php
2+
3+
namespace BeyondCode\SelfDiagnosis\Checks;
4+
5+
use BeyondCode\SelfDiagnosis\Exceptions\InvalidConfigurationException;
6+
use BeyondCode\SelfDiagnosis\Server;
7+
use Illuminate\Support\Collection;
8+
use JJG\Ping;
9+
10+
class ServersArePingable implements Check
11+
{
12+
protected const DEFAULT_TIMEOUT = 5;
13+
14+
/** @var Collection */
15+
protected $notReachableServers;
16+
17+
/**
18+
* The name of the check.
19+
*
20+
* @param array $config
21+
* @return string
22+
*/
23+
public function name(array $config): string
24+
{
25+
return trans('self-diagnosis::checks.servers_are_pingable.name');
26+
}
27+
28+
/**
29+
* Perform the actual verification of this check.
30+
*
31+
* @param array $config
32+
* @return bool
33+
* @throws InvalidConfigurationException
34+
*/
35+
public function check(array $config): bool
36+
{
37+
$this->notReachableServers = $this->parseConfiguredServers(array_get($config, 'servers', []));
38+
if ($this->notReachableServers->isEmpty()) {
39+
return true;
40+
}
41+
42+
$this->notReachableServers = $this->notReachableServers->reject(function (Server $server) {
43+
$ping = new Ping($server->getHost());
44+
$ping->setPort($server->getPort());
45+
$ping->setTimeout($server->getTimeout());
46+
47+
if ($ping->getPort() === null) {
48+
$latency = $ping->ping('exec');
49+
} else {
50+
$latency = $ping->ping('fsockopen');
51+
}
52+
53+
return $latency !== false;
54+
});
55+
56+
return $this->notReachableServers->isEmpty();
57+
}
58+
59+
/**
60+
* The error message to display in case the check does not pass.
61+
*
62+
* @param array $config
63+
* @return string
64+
*/
65+
public function message(array $config): string
66+
{
67+
return $this->notReachableServers->map(function (Server $server) {
68+
return trans('self-diagnosis::checks.servers_are_pingable.message', [
69+
'host' => $server->getHost(),
70+
'port' => $server->getPort() ?? 'n/a',
71+
'timeout' => $server->getTimeout(),
72+
]);
73+
})->implode(PHP_EOL);
74+
}
75+
76+
/**
77+
* Parses an array of servers which can be given in different formats.
78+
* Unifies the format for the resulting collection.
79+
*
80+
* @param array $servers
81+
* @return Collection
82+
* @throws InvalidConfigurationException
83+
*/
84+
private function parseConfiguredServers(array $servers): Collection
85+
{
86+
$result = new Collection();
87+
88+
foreach ($servers as $server) {
89+
if (is_array($server)) {
90+
if (!empty(array_except($server, ['host', 'port', 'timeout']))) {
91+
throw new InvalidConfigurationException('Servers in array notation may only contain a host, port and timeout parameter.');
92+
}
93+
if (!array_has($server, 'host')) {
94+
throw new InvalidConfigurationException('For servers in array notation, the host parameter is required.');
95+
}
96+
97+
$host = array_get($server, 'host');
98+
$port = array_get($server, 'port');
99+
$timeout = array_get($server, 'timeout', self::DEFAULT_TIMEOUT);
100+
101+
$result->push(new Server($host, $port, $timeout));
102+
} else if (is_string($server)) {
103+
$result->push(new Server($server, null, self::DEFAULT_TIMEOUT));
104+
} else {
105+
throw new InvalidConfigurationException('The server configuration may only contain arrays or strings.');
106+
}
107+
}
108+
109+
return $result;
110+
}
111+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
namespace BeyondCode\SelfDiagnosis\Exceptions;
4+
5+
/**
6+
* Exception to be used when an invalid check configuration is encountered.
7+
*
8+
* @package BeyondCode\SelfDiagnosis\Exceptions
9+
*/
10+
class InvalidConfigurationException extends \Exception
11+
{
12+
}

src/Server.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
namespace BeyondCode\SelfDiagnosis;
4+
5+
/**
6+
* DTO class for a server used by the ping check.
7+
*
8+
* @package BeyondCode\SelfDiagnosis
9+
*/
10+
class Server
11+
{
12+
/** @var string */
13+
protected $host;
14+
15+
/** @var int|null */
16+
protected $port;
17+
18+
/** @var int */
19+
protected $timeout;
20+
21+
public function __construct(string $host, ?int $port, int $timeout)
22+
{
23+
$this->host = $host;
24+
$this->port = $port;
25+
$this->timeout = $timeout;
26+
}
27+
28+
public function getHost(): string
29+
{
30+
return $this->host;
31+
}
32+
33+
public function getPort(): ?int
34+
{
35+
return $this->port;
36+
}
37+
38+
public function getTimeout(): int
39+
{
40+
return $this->timeout;
41+
}
42+
}

tests/ServersArePingableTest.php

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<?php
2+
3+
namespace BeyondCode\SelfDiagnosis\Tests;
4+
5+
use BeyondCode\SelfDiagnosis\Checks\ServersArePingable;
6+
use BeyondCode\SelfDiagnosis\Exceptions\InvalidConfigurationException;
7+
use BeyondCode\SelfDiagnosis\SelfDiagnosisServiceProvider;
8+
use Orchestra\Testbench\TestCase;
9+
10+
class ServersArePingableTest extends TestCase
11+
{
12+
public function getPackageProviders($app)
13+
{
14+
return [
15+
SelfDiagnosisServiceProvider::class,
16+
];
17+
}
18+
19+
/** @test */
20+
public function it_succeeds_when_no_servers_are_given()
21+
{
22+
$check = new ServersArePingable();
23+
$this->assertTrue($check->check([]));
24+
}
25+
26+
/** @test */
27+
public function it_fails_when_numeric_server_host_is_given()
28+
{
29+
$config = ['servers' => [1234567890]];
30+
31+
$check = new ServersArePingable();
32+
33+
$this->expectException(InvalidConfigurationException::class);
34+
$check->check($config);
35+
}
36+
37+
/** @test */
38+
public function it_fails_when_no_host_key_is_given_for_server_in_array_form()
39+
{
40+
$config = ['servers' => [['port' => 80]]];
41+
42+
$check = new ServersArePingable();
43+
44+
$this->expectException(InvalidConfigurationException::class);
45+
$check->check($config);
46+
}
47+
48+
/** @test */
49+
public function it_fails_when_not_supported_keys_are_given_for_server_in_array_form()
50+
{
51+
$config = ['servers' => [['host' => 'localhost', 'unsupported_param' => true]]];
52+
53+
$check = new ServersArePingable();
54+
55+
$this->expectException(InvalidConfigurationException::class);
56+
$check->check($config);
57+
}
58+
59+
/** @test */
60+
public function it_fails_when_the_server_ip_cannot_be_resolved()
61+
{
62+
$config = ['servers' => [['host' => 'somethingthatdoesnotexist.local', 'timeout' => 1]]];
63+
64+
$check = new ServersArePingable();
65+
$this->assertFalse($check->check($config));
66+
$this->assertSame("The server 'somethingthatdoesnotexist.local' (port: n/a) is not reachable (timeout after 1 seconds).", $check->message($config));
67+
}
68+
69+
/** @test */
70+
public function it_fails_when_the_server_ip_cannot_be_reached()
71+
{
72+
$config = ['servers' => [['host' => '254.254.254.254', 'timeout' => 1]]];
73+
74+
$check = new ServersArePingable();
75+
$this->assertFalse($check->check($config));
76+
$this->assertSame("The server '254.254.254.254' (port: n/a) is not reachable (timeout after 1 seconds).", $check->message($config));
77+
}
78+
79+
/** @test */
80+
public function it_succeeds_when_the_server_ip_can_be_reached()
81+
{
82+
$config = ['servers' => [['host' => 'www.google.com', 'timeout' => 2]]];
83+
84+
$check = new ServersArePingable();
85+
$this->assertTrue($check->check($config));
86+
}
87+
}

translations/de/checks.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@
8585
'message' => 'Die Routen sollten während der Entwicklung nicht gecached sein. Nutze "php artisan route:clear", um den Routen-Cache zu leeren.',
8686
'name' => 'Routen sind nicht gecached',
8787
],
88+
'servers_are_pingable' => [
89+
'message' => "Der Server ':host' (Port: :port) ist nicht erreichbar (Timeout nach :timeout Sekunden).",
90+
'name' => 'Benötigte Server sind pingbar',
91+
],
8892
'storage_directory_is_linked' => [
8993
'message' => 'Das Speicherverzeichnis ist nicht verlinkt. Nutze "php artisan storage:link", um eine symbolische Verknüpfung zu erstellen.',
9094
'name' => 'Das Speicherverzeichnis ist verlinkt',

translations/en/checks.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@
8585
'message' => 'Your routes should not be cached during development. Call "php artisan route:clear" to clear the route cache.',
8686
'name' => 'Routes are not cached',
8787
],
88+
'servers_are_pingable' => [
89+
'message' => "The server ':host' (port: :port) is not reachable (timeout after :timeout seconds).",
90+
'name' => 'Required servers are pingable',
91+
],
8892
'storage_directory_is_linked' => [
8993
'message' => 'The storage directory is not linked. Use "php artisan storage:link" to create a symbolic link.',
9094
'name' => 'The storage directory is linked',

0 commit comments

Comments
 (0)