mirror of
https://github.com/librenms/librenms.git
synced 2024-10-07 16:52:45 +00:00
Alert transport cleanup, no_proxy support and other proxy cleanups (#14763)
* Add no_proxy and other proxy related settings Set user agent on all http client requests Unify http client usage * Style fixes * Remove useless use statements * Correct variable, good job phpstan * Add tests fix https_proxy bug add tcp:// to the config settings format * style and lint fixes * Remove guzzle from the direct dependencies * Use built in Laravel testing functionality * update baseline
This commit is contained in:
@@ -27,12 +27,11 @@ namespace LibreNMS\Tests;
|
||||
|
||||
use RecursiveDirectoryIterator;
|
||||
use RecursiveIteratorIterator;
|
||||
use RecursiveRegexIterator;
|
||||
use RegexIterator;
|
||||
|
||||
class AlertingTest extends TestCase
|
||||
{
|
||||
public function testJsonAlertCollection()
|
||||
public function testJsonAlertCollection(): void
|
||||
{
|
||||
$rules = get_rules_from_json();
|
||||
$this->assertIsArray($rules);
|
||||
@@ -41,7 +40,7 @@ class AlertingTest extends TestCase
|
||||
}
|
||||
}
|
||||
|
||||
public function testTransports()
|
||||
public function testTransports(): void
|
||||
{
|
||||
foreach ($this->getTransportFiles() as $file => $_unused) {
|
||||
$parts = explode('/', $file);
|
||||
@@ -52,10 +51,10 @@ class AlertingTest extends TestCase
|
||||
}
|
||||
}
|
||||
|
||||
private function getTransportFiles()
|
||||
private function getTransportFiles(): RegexIterator
|
||||
{
|
||||
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator('LibreNMS/Alert/Transport'));
|
||||
|
||||
return new RegexIterator($iterator, '/^.+\.php$/i', RecursiveRegexIterator::GET_MATCH);
|
||||
return new RegexIterator($iterator, '/^.+\.php$/i', RegexIterator::GET_MATCH);
|
||||
}
|
||||
}
|
||||
|
@@ -22,21 +22,70 @@
|
||||
|
||||
namespace LibreNMS\Tests;
|
||||
|
||||
use LibreNMS\Util\Proxy;
|
||||
use LibreNMS\Config;
|
||||
use LibreNMS\Util\Http;
|
||||
use LibreNMS\Util\Version;
|
||||
|
||||
class ProxyTest extends TestCase
|
||||
{
|
||||
public function testShouldBeUsed(): void
|
||||
public function testClientAgentIsCorrect(): void
|
||||
{
|
||||
$this->assertTrue(Proxy::shouldBeUsed('http://example.com/foobar'));
|
||||
$this->assertTrue(Proxy::shouldBeUsed('foo/bar'));
|
||||
$this->assertTrue(Proxy::shouldBeUsed('192.168.0.1'));
|
||||
$this->assertTrue(Proxy::shouldBeUsed('2001:db8::8a2e:370:7334'));
|
||||
$this->assertEquals('LibreNMS/' . Version::VERSION, Http::client()->getOptions()['headers']['User-Agent']);
|
||||
}
|
||||
|
||||
$this->assertFalse(Proxy::shouldBeUsed('http://localhost/foobar'));
|
||||
$this->assertFalse(Proxy::shouldBeUsed('localhost/foobar'));
|
||||
$this->assertFalse(Proxy::shouldBeUsed('127.0.0.1'));
|
||||
$this->assertFalse(Proxy::shouldBeUsed('127.0.0.1:1337'));
|
||||
$this->assertFalse(Proxy::shouldBeUsed('::1'));
|
||||
public function testProxyIsNotSet(): void
|
||||
{
|
||||
Config::set('http_proxy', '');
|
||||
Config::set('https_proxy', '');
|
||||
Config::set('no_proxy', '');
|
||||
$client_options = Http::client()->getOptions();
|
||||
$this->assertEmpty($client_options['proxy']['http']);
|
||||
$this->assertEmpty($client_options['proxy']['https']);
|
||||
$this->assertEmpty($client_options['proxy']['no']);
|
||||
}
|
||||
|
||||
public function testProxyIsSet(): void
|
||||
{
|
||||
Config::set('http_proxy', 'http://proxy:5000');
|
||||
Config::set('https_proxy', 'tcp://proxy:5183');
|
||||
Config::set('no_proxy', 'localhost,127.0.0.1,::1,.domain.com');
|
||||
$client_options = Http::client()->getOptions();
|
||||
$this->assertEquals('http://proxy:5000', $client_options['proxy']['http']);
|
||||
$this->assertEquals('tcp://proxy:5183', $client_options['proxy']['https']);
|
||||
$this->assertEquals([
|
||||
'localhost',
|
||||
'127.0.0.1',
|
||||
'::1',
|
||||
'.domain.com',
|
||||
], $client_options['proxy']['no']);
|
||||
}
|
||||
|
||||
public function testProxyIsSetFromEnv(): void
|
||||
{
|
||||
Config::set('http_proxy', '');
|
||||
Config::set('https_proxy', '');
|
||||
Config::set('no_proxy', '');
|
||||
|
||||
putenv('HTTP_PROXY=someproxy:3182');
|
||||
putenv('HTTPS_PROXY=https://someproxy:3182');
|
||||
putenv('NO_PROXY=.there.com');
|
||||
|
||||
$client_options = Http::client()->getOptions();
|
||||
$this->assertEquals('someproxy:3182', $client_options['proxy']['http']);
|
||||
$this->assertEquals('https://someproxy:3182', $client_options['proxy']['https']);
|
||||
$this->assertEquals([
|
||||
'.there.com',
|
||||
], $client_options['proxy']['no']);
|
||||
|
||||
putenv('http_proxy=otherproxy:3182');
|
||||
putenv('https_proxy=otherproxy:3183');
|
||||
putenv('no_proxy=dontproxymebro');
|
||||
|
||||
$client_options = Http::client()->getOptions();
|
||||
$this->assertEquals('otherproxy:3182', $client_options['proxy']['http']);
|
||||
$this->assertEquals('otherproxy:3183', $client_options['proxy']['https']);
|
||||
$this->assertEquals([
|
||||
'dontproxymebro',
|
||||
], $client_options['proxy']['no']);
|
||||
}
|
||||
}
|
||||
|
@@ -1,80 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace LibreNMS\Tests\Traits;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Handler\MockHandler;
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Middleware;
|
||||
|
||||
/**
|
||||
* @mixin \LibreNMS\Tests\TestCase
|
||||
*/
|
||||
trait MockGuzzleClient
|
||||
{
|
||||
/**
|
||||
* @var MockHandler
|
||||
*/
|
||||
private $guzzleMockHandler;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $guzzleConfig;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $guzzleHistory = [];
|
||||
|
||||
/**
|
||||
* Create a Guzzle MockHandler and bind Client with the handler to the Laravel container
|
||||
*
|
||||
* @param array $queue Sequential Responses to give to the client.
|
||||
* @param array $config Guzzle config settings.
|
||||
*/
|
||||
public function mockGuzzleClient(array $queue, array $config = []): MockHandler
|
||||
{
|
||||
$this->guzzleConfig = $config;
|
||||
$this->guzzleMockHandler = new MockHandler($queue);
|
||||
|
||||
$this->app->bind(Client::class, function () {
|
||||
$handlerStack = HandlerStack::create($this->guzzleMockHandler);
|
||||
$handlerStack->push(Middleware::history($this->guzzleHistory));
|
||||
|
||||
return new Client(array_merge($this->guzzleConfig, ['handler' => $handlerStack]));
|
||||
});
|
||||
|
||||
return $this->guzzleMockHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the request and response history to inspect
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function guzzleHistory(): array
|
||||
{
|
||||
return $this->guzzleHistory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the request history to inspect
|
||||
*
|
||||
* @return \GuzzleHttp\Psr7\Request[]
|
||||
*/
|
||||
public function guzzleRequestHistory(): array
|
||||
{
|
||||
return array_column($this->guzzleHistory, 'request');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the response history to inspect
|
||||
*
|
||||
* @return \GuzzleHttp\Psr7\Response[]
|
||||
*/
|
||||
public function guzzleResponseHistory(): array
|
||||
{
|
||||
return array_column($this->guzzleHistory, 'response');
|
||||
}
|
||||
}
|
155
tests/Unit/Alert/Transports/SlackTest.php
Normal file
155
tests/Unit/Alert/Transports/SlackTest.php
Normal file
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
/**
|
||||
* SlackTest.php
|
||||
*
|
||||
* -Description-
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @link https://www.librenms.org
|
||||
*
|
||||
* @copyright 2022 Tony Murray
|
||||
* @author Tony Murray <murraytony@gmail.com>
|
||||
*/
|
||||
|
||||
namespace LibreNMS\Tests\Unit\Alert\Transports;
|
||||
|
||||
use App\Models\AlertTransport;
|
||||
use App\Models\Device;
|
||||
use Illuminate\Http\Client\Request;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use LibreNMS\Alert\AlertData;
|
||||
use LibreNMS\Alert\Transport;
|
||||
use LibreNMS\Tests\TestCase;
|
||||
|
||||
class SlackTest extends TestCase
|
||||
{
|
||||
public function testSlackNoConfigDelivery(): void
|
||||
{
|
||||
Http::fake();
|
||||
|
||||
$slack = new Transport\Slack(new AlertTransport);
|
||||
|
||||
/** @var Device $mock_device */
|
||||
$mock_device = Device::factory()->make();
|
||||
$slack->deliverAlert(AlertData::testData($mock_device));
|
||||
|
||||
Http::assertSent(function (Request $request) {
|
||||
return
|
||||
$request->url() == '' &&
|
||||
$request->method() == 'POST' &&
|
||||
$request->hasHeader('Content-Type', 'application/json') &&
|
||||
$request->data() == [
|
||||
'attachments' => [
|
||||
[
|
||||
'fallback' => 'This is a test alert',
|
||||
'color' => '#ff0000',
|
||||
'title' => 'Testing transport from LibreNMS',
|
||||
'text' => 'This is a test alert',
|
||||
'mrkdwn_in' => [
|
||||
'text',
|
||||
'fallback',
|
||||
],
|
||||
'author_name' => null,
|
||||
],
|
||||
],
|
||||
'channel' => null,
|
||||
'icon_emoji' => null,
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
public function testSlackLegacyDelivery(): void
|
||||
{
|
||||
Http::fake();
|
||||
|
||||
$slack = new Transport\Slack(new AlertTransport([
|
||||
'transport_config' => [
|
||||
'slack-url' => 'https://slack.com/some/webhook',
|
||||
'slack-options' => "icon_emoji=smile\nauthor=Me\nchannel=Alerts",
|
||||
],
|
||||
]));
|
||||
|
||||
/** @var Device $mock_device */
|
||||
$mock_device = Device::factory()->make();
|
||||
$slack->deliverAlert(AlertData::testData($mock_device));
|
||||
|
||||
Http::assertSent(function (Request $request) {
|
||||
return
|
||||
$request->url() == 'https://slack.com/some/webhook' &&
|
||||
$request->method() == 'POST' &&
|
||||
$request->hasHeader('Content-Type', 'application/json') &&
|
||||
$request->data() == [
|
||||
'attachments' => [
|
||||
[
|
||||
'fallback' => 'This is a test alert',
|
||||
'color' => '#ff0000',
|
||||
'title' => 'Testing transport from LibreNMS',
|
||||
'text' => 'This is a test alert',
|
||||
'mrkdwn_in' => [
|
||||
'text',
|
||||
'fallback',
|
||||
],
|
||||
'author_name' => 'Me',
|
||||
],
|
||||
],
|
||||
'channel' => 'Alerts',
|
||||
'icon_emoji' => ':smile:',
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
public function testSlackDelivery(): void
|
||||
{
|
||||
Http::fake();
|
||||
|
||||
$slack = new Transport\Slack(new AlertTransport([
|
||||
'transport_config' => [
|
||||
'slack-url' => 'https://slack.com/some/webhook',
|
||||
'slack-options' => "icon_emoji=smile\nauthor=Me\nchannel=Alerts",
|
||||
'slack-icon_emoji' => ':slight_smile:',
|
||||
'slack-author' => 'Other',
|
||||
'slack-channel' => 'Critical',
|
||||
],
|
||||
]));
|
||||
|
||||
/** @var Device $mock_device */
|
||||
$mock_device = Device::factory()->make();
|
||||
$slack->deliverAlert(AlertData::testData($mock_device));
|
||||
|
||||
Http::assertSent(function (Request $request) {
|
||||
return
|
||||
$request->url() == 'https://slack.com/some/webhook' &&
|
||||
$request->method() == 'POST' &&
|
||||
$request->hasHeader('Content-Type', 'application/json') &&
|
||||
$request->data() == [
|
||||
'attachments' => [
|
||||
[
|
||||
'fallback' => 'This is a test alert',
|
||||
'color' => '#ff0000',
|
||||
'title' => 'Testing transport from LibreNMS',
|
||||
'text' => 'This is a test alert',
|
||||
'mrkdwn_in' => [
|
||||
'text',
|
||||
'fallback',
|
||||
],
|
||||
'author_name' => 'Other',
|
||||
],
|
||||
],
|
||||
'channel' => 'Critical',
|
||||
'icon_emoji' => ':slight_smile:',
|
||||
];
|
||||
});
|
||||
}
|
||||
}
|
@@ -3,34 +3,31 @@
|
||||
namespace LibreNMS\Tests\Unit;
|
||||
|
||||
use App\Models\AlertTransport;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use LibreNMS\Config;
|
||||
use Illuminate\Http\Client\Request;
|
||||
use Illuminate\Support\Facades\Http as LaravelHttp;
|
||||
use LibreNMS\Tests\TestCase;
|
||||
use LibreNMS\Tests\Traits\MockGuzzleClient;
|
||||
|
||||
class ApiTransportTest extends TestCase
|
||||
{
|
||||
use MockGuzzleClient;
|
||||
|
||||
public function testGetMultilineVariables(): void
|
||||
{
|
||||
/** @var AlertTransport $transport */
|
||||
$transport = AlertTransport::factory()->api('text={{ $msg }}')->make();
|
||||
|
||||
$this->mockGuzzleClient([
|
||||
new Response(200),
|
||||
LaravelHttp::fake([
|
||||
'*' => LaravelHttp::response(),
|
||||
]);
|
||||
|
||||
$obj = ['msg' => "This is a multi-line\nalert."];
|
||||
$opts = Config::get('alert.transports.' . $transport->transport_type);
|
||||
$result = $transport->instance()->deliverAlert($obj, $opts);
|
||||
$result = $transport->instance()->deliverAlert($obj);
|
||||
|
||||
$this->assertTrue($result);
|
||||
|
||||
$history = $this->guzzleRequestHistory();
|
||||
$this->assertCount(1, $history);
|
||||
$this->assertEquals('GET', $history[0]->getMethod());
|
||||
$this->assertEquals('text=This%20is%20a%20multi-line%0Aalert.', $history[0]->getUri()->getQuery());
|
||||
LaravelHttp::assertSentCount(1);
|
||||
LaravelHttp::assertSent(function (Request $request) {
|
||||
return $request->method() == 'GET' &&
|
||||
$request->url() == 'https://librenms.org?text=This%20is%20a%20multi-line%0Aalert.';
|
||||
});
|
||||
}
|
||||
|
||||
public function testPostMultilineVariables(): void
|
||||
@@ -42,21 +39,20 @@ class ApiTransportTest extends TestCase
|
||||
'bodytext={{ $msg }}',
|
||||
)->make();
|
||||
|
||||
$this->mockGuzzleClient([
|
||||
new Response(200),
|
||||
LaravelHttp::fake([
|
||||
'*' => LaravelHttp::response(),
|
||||
]);
|
||||
|
||||
$obj = ['msg' => "This is a post multi-line\nalert."];
|
||||
$opts = Config::get('alert.transports.' . $transport->transport_type);
|
||||
$result = $transport->instance()->deliverAlert($obj, $opts);
|
||||
$result = $transport->instance()->deliverAlert($obj);
|
||||
|
||||
$this->assertTrue($result);
|
||||
|
||||
$history = $this->guzzleRequestHistory();
|
||||
$this->assertCount(1, $history);
|
||||
$this->assertEquals('POST', $history[0]->getMethod());
|
||||
// FUBAR
|
||||
$this->assertEquals('text=This%20is%20a%20post%20multi-line%0Aalert.', $history[0]->getUri()->getQuery());
|
||||
$this->assertEquals("bodytext=This is a post multi-line\nalert.", (string) $history[0]->getBody());
|
||||
LaravelHttp::assertSentCount(1);
|
||||
LaravelHttp::assertSent(function (Request $request) {
|
||||
return $request->method() == 'POST' &&
|
||||
$request->url() == 'https://librenms.org?text=This%20is%20a%20post%20multi-line%0Aalert.' &&
|
||||
$request->body() == "bodytext=This is a post multi-line\nalert.";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -25,21 +25,16 @@
|
||||
|
||||
namespace LibreNMS\Tests\Unit\Data;
|
||||
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use GuzzleHttp\Psr7\Request;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use Illuminate\Support\Facades\Http as LaravelHttp;
|
||||
use LibreNMS\Config;
|
||||
use LibreNMS\Data\Store\Prometheus;
|
||||
use LibreNMS\Tests\TestCase;
|
||||
use LibreNMS\Tests\Traits\MockGuzzleClient;
|
||||
|
||||
/**
|
||||
* @group datastores
|
||||
*/
|
||||
class PrometheusStoreTest extends TestCase
|
||||
{
|
||||
use MockGuzzleClient;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
@@ -48,26 +43,20 @@ class PrometheusStoreTest extends TestCase
|
||||
Config::set('prometheus.url', 'http://fake:9999');
|
||||
}
|
||||
|
||||
public function testFailWrite()
|
||||
public function testFailWrite(): void
|
||||
{
|
||||
$this->mockGuzzleClient([
|
||||
new Response(422, [], 'Bad response'),
|
||||
new RequestException('Exception thrown', new Request('POST', 'test')),
|
||||
]);
|
||||
|
||||
LaravelHttp::fakeSequence()->push('Bad response', 422);
|
||||
$prometheus = app(Prometheus::class);
|
||||
|
||||
\Log::shouldReceive('debug');
|
||||
\Log::shouldReceive('error')->once()->with("Prometheus Exception: Client error: `POST http://fake:9999/metrics/job/librenms/instance/test/measurement/none` resulted in a `422 Unprocessable Entity` response:\nBad response\n");
|
||||
\Log::shouldReceive('error')->once()->with('Prometheus Exception: Exception thrown');
|
||||
$prometheus->put(['hostname' => 'test'], 'none', [], ['one' => 1]);
|
||||
\Log::shouldReceive('error')->once()->with('Prometheus Error: Bad response');
|
||||
$prometheus->put(['hostname' => 'test'], 'none', [], ['one' => 1]);
|
||||
}
|
||||
|
||||
public function testSimpleWrite()
|
||||
public function testSimpleWrite(): void
|
||||
{
|
||||
$this->mockGuzzleClient([
|
||||
new Response(200),
|
||||
LaravelHttp::fake([
|
||||
'*' => LaravelHttp::response(),
|
||||
]);
|
||||
|
||||
$prometheus = app(Prometheus::class);
|
||||
@@ -82,12 +71,11 @@ class PrometheusStoreTest extends TestCase
|
||||
|
||||
$prometheus->put($device, $measurement, $tags, $fields);
|
||||
|
||||
$history = $this->guzzleRequestHistory();
|
||||
$this->assertCount(1, $history, 'Did not receive the expected number of requests');
|
||||
$this->assertEquals('POST', $history[0]->getMethod());
|
||||
$this->assertEquals('/metrics/job/librenms/instance/testhost/measurement/testmeasure/ifName/testifname/type/testtype', $history[0]->getUri()->getPath());
|
||||
$this->assertEquals('fake', $history[0]->getUri()->getHost());
|
||||
$this->assertEquals(9999, $history[0]->getUri()->getPort());
|
||||
$this->assertEquals("ifIn 234234\nifOut 53453\n", (string) $history[0]->getBody());
|
||||
LaravelHttp::assertSentCount(1);
|
||||
LaravelHttp::assertSent(function (\Illuminate\Http\Client\Request $request) {
|
||||
return $request->method() == 'POST' &&
|
||||
$request->url() == 'http://fake:9999/metrics/job/librenms/instance/testhost/measurement/testmeasure/ifName/testifname/type/testtype' &&
|
||||
$request->body() == "ifIn 234234\nifOut 53453\n";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user