Add 'lib/influxdb-php-sdk/' from commit '1928d59eb90bfcba40a957074987c5311afd4e3b'

git-subtree-dir: lib/influxdb-php-sdk
git-subtree-mainline: 15a338061d
git-subtree-split: 1928d59eb9
This commit is contained in:
laf
2015-08-09 15:52:58 +00:00
31 changed files with 2368 additions and 0 deletions

3
lib/influxdb-php-sdk/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
vendor
tags
composer.lock

View File

@@ -0,0 +1,3 @@
tools:
external_code_coverage:
timeout: 600

View File

@@ -0,0 +1,13 @@
# Contributing to InfluxDB PHP SDK
## Pull requests are always welcome
Not sure if that typo is worth a pull request? Found a bug and know how to fix
it? Do it! We will appreciate it. Any significant improvement should be
documented as a GitHub issue before anybody starts working on it.
We are always thrilled to receive pull requests. We do our best to process them
quickly. If your pull request is not accepted on the first try, don't get
discouraged!
Just branch on master and prepare your PR.

View File

@@ -0,0 +1,19 @@
Copyright (c) 2015 Corley S.r.l.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,347 @@
# InfluxDB PHP SDK
[![Circle CI](https://circleci.com/gh/corley/influxdb-php-sdk/tree/master.svg?style=svg)](https://circleci.com/gh/corley/influxdb-php-sdk/tree/master)
[![Code Coverage](https://scrutinizer-ci.com/g/corley/influxdb-php-sdk/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/corley/influxdb-php-sdk/?branch=master)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/corley/influxdb-php-sdk/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/corley/influxdb-php-sdk/?branch=master)
[![Latest Stable Version](https://poser.pugx.org/corley/influxdb-sdk/v/stable)](https://packagist.org/packages/corley/influxdb-sdk)
[![License](https://poser.pugx.org/corley/influxdb-sdk/license)](https://packagist.org/packages/corley/influxdb-sdk)
Send metrics to InfluxDB and query for any data.
This project support InfluxDB API `>= 0.9` - **For InfluxDB v0.8 checkout branch 0.3**
Supported adapters:
* HTTP
* UDP/IP
## Install it
Just use composer
```json
{
"require": {
// ...
"corley/influxdb-sdk": ">=0.4"
}
}
```
## Use it
Add new points:
```php
$client->mark("app-search", [
"key" => "this is my search"
]);
```
Or use InfluxDB direct messages
```php
$client->mark([
"tags" => [
"dc" => "eu-west-1",
],
"points" => [
[
"measurement" => "instance",
"fields" => [
"cpu" => 18.12,
"free" => 712423,
],
],
]
]);
```
Retrieve existing points:
```php
$results = $client->query('select * from "app-search"');
```
## InfluxDB client adapters
Actually we supports two network adapters
* UDP/IP - in order to send data via UDP/IP (datagram)
* HTTP JSON - in order to send/retrieve using HTTP messages (connection oriented)
### Using UDP/IP Adapter
In order to use the UDP/IP adapter your must have PHP compiled with the `sockets` extension.
**Usage**
```php
$options = new Options();
$adapter = new UdpAdapter($options);
$client = new Client($adapter);
```
### Using HTTP Adapters
Actually Guzzle is used as HTTP client library
```php
<?php
$http = new \GuzzleHttp\Client();
$options = new Options();
$adapter = new GuzzleAdapter($http, $options);
$client = new Client($adapter);
```
## Create your client with the factory method
Effectively the client creation is not so simple, for that
reason you can you the factory method provided with the library.
```php
$options = [
"adapter" => [
"name" => "InfluxDB\\Adapter\\GuzzleAdapter",
"options" => [
// guzzle options
],
],
"options" => [
"host" => "my.influx.domain.tld",
"db" => "mydb",
"retention_policy" => "myPolicy",
"tags" => [
"env" => "prod",
"app" => "myApp",
],
]
];
$client = \InfluxDB\ClientFactory::create($options);
```
Of course you can always use a DiC (eg `symfony/dependency-injection`) or your service manager in order to create
a valid client instance.
### Query InfluxDB
You can query the time series database using the query method.
```php
$influx->query('select * from "mine"');
```
You can query the database only if the adapter is queryable (implements
`QueryableInterface`), actually `GuzzleAdapter`.
The adapter returns the json decoded body of the InfluxDB response, something
like:
```
array(1) {
'results' =>
array(1) {
[0] =>
array(1) {
'series' =>
array(1) {
...
}
}
}
}
```
## UDP/IP support
As you know InfluxDB support UDP/IP with a "line protocol", that is a string
line, like:
```
cpu,region=us-west,env=prod,zone=1c cpu=18.12,free=712432 1257894000
```
In order to simplify the SDK usage, you will use a single method signature
for both adapters, UDP/IP and HTTP:
**Concise Format**
```php
$client->mark("serie-name", [
"power" => 124.21,
"voltage" => 12.4,
]);
```
**Extended Format**
```php
$client->mark([
"tags" => [
"region" => "us-west",
"host" => "serverA",
"env" => "prod",
"target" => "servers",
"zone" => "1c",
],
"time" => "2009-11-10T23:00:00Z",
"points" => [
[
"measurement" => "cpu",
"fields" => [
"cpu" => 18.12,
"free" => 712432,
],
],
],
]);
```
If you want to use the inline protocol directly you have to use the UDP/IP adapter directly
```
$udp = new UdpAdapter($options);
$udp->write("cpu,region=us-west,host=serverA,env=prod,target=servers,zone=1c cpu=18.12,free=712432 1257894000");
```
## Database operations
You can create, list or destroy databases using dedicated methods
```php
$client->getDatabases(); // list all databases
$client->createDatabase("my.name"); // create a new database with name "my.name"
$client->deleteDatabase("my.name"); // delete an existing database with name "my.name"
```
Actually only queryable adapters can handle databases (implements the
`QueryableInterface`)
## Global tags and retention policy
You can set a set of default tags, that the SDK will add to your metrics:
```php
$options = new Options();
$options->setTags([
"env" => "prod",
"region" => "eu-west-1",
]);
```
The SDK mark all point adding those tags.
You can set a default retentionPolicy using
```
$options->setRetentionPolicy("myPolicy");
```
In that way the SDK use that policy instead of `default` policy.
## Proxies and InfluxDB
If you proxy your InfluxDB typically you have a prefix in your endpoints.
```
$option->setHost("proxy.influxdb.tld");
$option->setPort(80);
$option->setPrefix("/influxdb"); // your prefix is: /influxdb
// final url will be: http://proxy.influxdb.tld:80/influxdb/write
$client->mark("serie", ["data" => "my-data"]);
```
## Benchmarks
Simple benchmarks executed on a Sony Vaio T13 (SVT1311C5E)
### Adapters
The impact using UDP/IP or HTTP adapters
```
Corley\Benchmarks\InfluxDB\AdapterEvent
Method Name Iterations Average Time Ops/second
------------------------ ------------ -------------- -------------
sendDataUsingHttpAdapter: [1,000 ] [0.0167509446144] [59.69813]
sendDataUsingUdpAdapter : [1,000 ] [0.0000905156136] [11,047.81773]
```
### Message to inline protocol conversion
As you know the SDK will provide a single interface in order to send data to
InfluxDB (concise or expanded).
The impact of message to inline protocol conversion is:
```
Corley\Benchmarks\InfluxDB\MessageToInlineProtocolEvent
Method Name Iterations Average Time Ops/second
---------------------------------------------------- ------------ -------------- -------------
convertMessageToInlineProtocolWithNoTags : [10,000 ] [0.0000343696594] [29,095.42942]
convertMessageToInlineProtocolWithGlobalTags : [10,000 ] [0.0000437165260] [22,874.64469]
convertMessageToInlineProtocolWithDifferentTagLevels: [10,000 ] [0.0000493728638] [20,254.04086]
```
### Query Builder
Interested in a Query Builder?
https://github.com/corley/dbal-influxdb
Thanks to Doctrine DBAL (Abstract Layer) you can use the query builder
```
$qb = $conn->createQueryBuilder();
$qb->select("*")
->from("cpu_load_short")
->where("time = ?")
->setParameter(0, 1434055562000000000);
$data = $qb->execute();
foreach ($data->fetchAll() as $element) {
// Use your element
}
```
```php
$config = new \Doctrine\DBAL\Configuration();
//..
$connectionParams = array(
'dbname' => 'mydb',
'user' => 'root',
'password' => 'root',
'host' => 'localhost',
'port' => 8086,
"driverClass" => "Corley\\DBAL\\Driver\\InfluxDB",
);
$conn = \Doctrine\DBAL\DriverManager::getConnection($connectionParams, $config);
```
## FAQ
### Add sockets support to your PHP
To verify if you have the `sockets` extension just issue a:
```bash
php -m | grep sockets
```
If you don't have the `sockets` extension, you can proceed in two ways:
- Recompile your PHP whith the `--enable-sockets` flag
- Or just compile the `sockets` extension extracting it from the PHP source.
1. Download the source relative to the PHP version that you on from [here](https://github.com/php/php-src/releases)
2. Enter in the `ext/sockets` directory
3. Issue a `phpize && ./configure && make -j && sudo make install`
4. Add `extension=sockets.so` to your php.ini

View File

@@ -0,0 +1 @@
0.5.0

View File

@@ -0,0 +1,54 @@
<?php
namespace Corley\Benchmarks\InfluxDB;
use InfluxDB\Client;
use InfluxDB\Adapter\GuzzleAdapter;
use InfluxDB\Adapter\UdpAdapter;
use Athletic\AthleticEvent;
use GuzzleHttp\Client as HttpClient;
use InfluxDB\Options;
class AdapterEvent extends AthleticEvent
{
private $httpClient;
private $udpClient;
public function setUp()
{
$options = new Options();
$options->setHost("localhost");
$options->setPort(8086);
$options->setUsername("root");
$options->setPassword("root");
$options->setDatabase("tcp.test");
$client = new Client(new GuzzleAdapter(new HttpClient(), $options));
$client->createDatabase("tcp.test");
$client->createDatabase("udp.test");
$this->httpClient = $client;
$opts = new Options();
$opts->setPort(4444);
$client = new Client(new UdpAdapter($opts));
$this->udpClient = $client;
}
/**
* @iterations 1000
*/
public function sendDataUsingHttpAdapter()
{
$this->httpClient->mark("metric.name", ["key" => "value"]);
}
/**
* @iterations 1000
*/
public function sendDataUsingUdpAdapter()
{
$this->udpClient->mark("metric.name", ["key" => "value"]);
}
}

View File

@@ -0,0 +1,76 @@
<?php
namespace Corley\Benchmarks\InfluxDB;
use Athletic\AthleticEvent;
class MessageToInlineProtocolEvent extends AthleticEvent
{
/**
* @iterations 10000
*/
public function convertMessageToInlineProtocolWithNoTags()
{
\InfluxDB\Adapter\message_to_inline_protocol(
[
"points" => [
[
"measurement" => "vm-serie",
"fields" => [
"cpu" => 18.12,
"free" => 712423,
],
],
]
]
);
}
/**
* @iterations 10000
*/
public function convertMessageToInlineProtocolWithGlobalTags()
{
\InfluxDB\Adapter\message_to_inline_protocol(
[
"tags" => [
"dc" => "eu-west-1",
],
"points" => [
[
"measurement" => "vm-serie",
"fields" => [
"cpu" => 18.12,
"free" => 712423,
],
],
]
]
);
}
/**
* @iterations 10000
*/
public function convertMessageToInlineProtocolWithDifferentTagLevels()
{
\InfluxDB\Adapter\message_to_inline_protocol(
[
"tags" => [
"dc" => "eu-west-1",
],
"points" => [
[
"measurement" => "vm-serie",
"tags" => [
"server" => "tc12",
],
"fields" => [
"cpu" => 18.12,
"free" => 712423,
],
],
]
]
);
}
}

View File

@@ -0,0 +1,51 @@
machine:
timezone:
Europe/Rome
php:
version: 5.6.5
dependencies:
pre:
- sudo apt-get update
- sudo apt-get install nginx
- wget https://s3.amazonaws.com/influxdb/influxdb_latest_amd64.deb
- sudo useradd influxdb
- sudo dpkg -i influxdb_latest_amd64.deb
- sudo cp ./scripts/influxdb_conf.toml /etc/opt/influxdb/influxdb.conf
- sudo /opt/influxdb/influxd -config /etc/opt/influxdb/influxdb.conf: {background: true}
- sudo cp scripts/nginx_proxy.conf /etc/nginx/conf.d/proxy.conf
- sudo service nginx stop
- sleep 2
- sudo service nginx start
- sed -i 's/^;//' ~/.phpenv/versions/$(phpenv global)/etc/conf.d/xdebug.ini
override:
- phpenv global 5.6.5
- composer install --prefer-source --no-interaction
- phpenv global 5.5.21
- composer install --prefer-source --no-interaction
test:
override:
- phpenv global 5.6.5
- composer require guzzlehttp/guzzle:~6 --prefer-source --no-interaction --update-with-dependencies
- composer show -i
- vendor/bin/phpunit --coverage-clover clover.xml
- composer require guzzlehttp/guzzle:~5 --prefer-source --no-interaction --update-with-dependencies
- composer show -i
- vendor/bin/phpunit
- composer require guzzlehttp/guzzle:~4 --prefer-source --no-interaction --update-with-dependencies
- composer show -i
- vendor/bin/phpunit
- phpenv global 5.5.21
- composer require guzzlehttp/guzzle:~6 --prefer-source --no-interaction --update-with-dependencies
- composer show -i
- vendor/bin/phpunit
- composer require guzzlehttp/guzzle:~5 --prefer-source --no-interaction --update-with-dependencies
- composer show -i
- vendor/bin/phpunit
- composer require guzzlehttp/guzzle:~4 --prefer-source --no-interaction --update-with-dependencies
- composer show -i
- vendor/bin/phpunit
post:
- wget https://scrutinizer-ci.com/ocular.phar
- php ocular.phar code-coverage:upload --format=php-clover clover.xml

View File

@@ -0,0 +1,53 @@
{
"name": "corley/influxdb-sdk",
"license": "MIT",
"description": "Send your app metrics to InfluxDB",
"require": {
"php": ">=5.4",
"guzzlehttp/guzzle": "~4|~5|~6",
"zendframework/zend-stdlib": "~2",
"zendframework/zend-filter": "~2",
"zendframework/zend-servicemanager": "~2"
},
"require-dev": {
"codeclimate/php-test-reporter": "dev-master",
"phpunit/phpunit": "4.*",
"athletic/athletic": "~0.1",
"ext-sockets": "*"
},
"homepage": "http://www.corley.it/",
"keywords": ["influxdb", "udp", "sdk"],
"authors": [
{
"name": "Gianluca Arbezzano",
"email": "gianluca.arbezzano@corley.it"
},
{
"name": "Walter Dal Mut",
"email": "walter.dalmut@corley.it"
},
{
"name": "Gabriele Mittica",
"email": "gabriele.mittica@corley.it"
}
],
"autoload": {
"psr-4": {
"InfluxDB\\": ["./src/"],
"Corley\\": ["./benchmarks/"]
},
"files": ["src/Adapter/helpers.php"]
},
"autoload-dev": {
"psr-4": {
"InfluxDB\\": ["./tests/unit"],
"InfluxDB\\Integration\\": ["./tests/integration"]
}
},
"suggest": {
"corley/dbal-influxdb": "This library help you to build query",
"fabpot/pimple": "Allows to prepare the client dependencies in order to require an initialized instance",
"zendframework/zend-servicemanager": "Use a service locator in order to prepare a valid instance",
"symfony/dependency-injection": "Prepare a valid instance via the symfony DiC"
}
}

View File

@@ -0,0 +1,22 @@
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/3.7/phpunit.xsd"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
verbose="true">
<testsuites>
<testsuite name="InfluxDB SDK Integration Tests">
<directory>tests/integration</directory>
</testsuite>
<testsuite name="InfluxDB SDK Unit Tests">
<directory>tests/unit</directory>
</testsuite>
</testsuites>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">src</directory>
</whitelist>
</filter>
</phpunit>

View File

@@ -0,0 +1,74 @@
[meta]
dir = "/var/opt/influxdb/meta"
hostname = "localhost"
bind-address = ":8088"
retention-autocreate = true
election-timeout = "1s"
heartbeat-timeout = "1s"
leader-lease-timeout = "500ms"
commit-timeout = "50ms"
[data]
dir = "/var/opt/influxdb/data"
retention-auto-create = true
retention-check-enabled = true
retention-check-period = "10m0s"
retention-create-period = "45m0s"
[cluster]
shard-writer-timeout = "5s"
[retention]
enabled = true
check-interval = "10m0s"
[admin]
enabled = true
bind-address = ":8083"
[http]
enabled = true
bind-address = ":8086"
auth-enabled = false
log-enabled = true
write-tracing = false
pprof-enabled = false
[collectd]
enabled = false
bind-address = ""
database = ""
typesdb = ""
[opentsdb]
enabled = false
bind-address = ""
database = ""
retention-policy = ""
[udp]
enabled = true
bind-address = ":4444"
database = "udp.test"
batch-size = 0
batch-timeout = "10ns"
[monitoring]
enabled = false
write-interval = "1m0s"
[continuous_queries]
enabled = true
recompute-previous-n = 2
recompute-no-older-than = "10m0s"
compute-runs-per-interval = 10
compute-no-more-than = "2m0s"
[hinted-handoff]
enabled = true
dir = "/var/opt/influxdb/hh"
max-size = 1073741824
max-age = "168h0m0s"
retry-rate-limit = 0
retry-interval = "1s"

View File

@@ -0,0 +1,25 @@
server {
listen 9000 default_server;
listen [::]:9000 default_server ipv6only=on;
root /usr/share/nginx/html;
index index.html index.htm;
server_name localhost;
location / {
try_files $uri $uri/ =404;
}
location /influxdb {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
rewrite ^/influxdb/?(.*) /$1 break;
proxy_pass http://localhost:8086;
proxy_redirect off;
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace InfluxDB\Adapter;
use InfluxDB\Options;
use InfluxDB\Adapter\WritableInterface;
abstract class AdapterAbstract implements WritableInterface
{
private $options;
public function __construct(Options $options)
{
$this->options = $options;
}
public function getOptions()
{
return $this->options;
}
protected function getMessageDefaults()
{
return [
"database" => $this->getOptions()->getDatabase(),
"retentionPolicy" => $this->getOptions()->getRetentionPolicy(),
"tags" => $this->getOptions()->getTags(),
];
}
abstract public function send(array $message);
}

View File

@@ -0,0 +1,81 @@
<?php
namespace InfluxDB\Adapter;
use GuzzleHttp\Client;
use InfluxDB\Options;
class GuzzleAdapter extends AdapterAbstract implements QueryableInterface
{
private $httpClient;
public function __construct(Client $httpClient, Options $options)
{
parent::__construct($options);
$this->httpClient = $httpClient;
}
public function send(array $message)
{
$message = array_replace_recursive($this->getMessageDefaults(), $message);
if (!count($message["tags"])) {
unset($message["tags"]);
}
$httpMessage = [
"auth" => [$this->getOptions()->getUsername(), $this->getOptions()->getPassword()],
'query' => [
"db" => $message["database"],
"retentionPolicy" => $message["retentionPolicy"],
],
"body" => message_to_inline_protocol($message)
];
$endpoint = $this->getHttpSeriesEndpoint();
return $this->httpClient->post($endpoint, $httpMessage);
}
public function query($query)
{
$options = [
"auth" => [$this->getOptions()->getUsername(), $this->getOptions()->getPassword()],
'query' => [
"q" => $query,
"db" => $this->getOptions()->getDatabase(),
]
];
return $this->get($options);
}
private function get(array $httpMessage)
{
$endpoint = $this->getHttpQueryEndpoint();
return json_decode($this->httpClient->get($endpoint, $httpMessage)->getBody(), true);
}
protected function getHttpSeriesEndpoint()
{
return $this->getHttpEndpoint("write");
}
protected function getHttpQueryEndpoint()
{
return $this->getHttpEndpoint("query");
}
private function getHttpEndpoint($operation)
{
$url = sprintf(
"%s://%s:%d%s/%s",
$this->getOptions()->getProtocol(),
$this->getOptions()->getHost(),
$this->getOptions()->getPort(),
$this->getOptions()->getPrefix(),
$operation
);
return $url;
}
}

View File

@@ -0,0 +1,7 @@
<?php
namespace InfluxDB\Adapter;
interface QueryableInterface
{
public function query($query);
}

View File

@@ -0,0 +1,36 @@
<?php
namespace InfluxDB\Adapter;
use DateTime;
class UdpAdapter extends AdapterAbstract
{
public function send(array $message)
{
$message = array_replace_recursive($this->getMessageDefaults(), $message);
if (array_key_exists("tags", $message)) {
$message["tags"] = array_replace_recursive($this->getOptions()->getTags(), $message["tags"]);
}
$message = message_to_inline_protocol($message);
$this->write($message);
}
public function write($message)
{
// Create a handler in order to handle the 'Host is down' message
set_error_handler(function() {
// Suppress the error, this is the UDP adapter and if we can't send
// it then we shouldn't inturrupt their application.
});
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
socket_sendto($socket, $message, strlen($message), 0, $this->getOptions()->getHost(), $this->getOptions()->getPort());
socket_close($socket);
// Remove our error handler.
restore_error_handler();
}
}

View File

@@ -0,0 +1,7 @@
<?php
namespace InfluxDB\Adapter;
interface WritableInterface
{
public function send(array $message);
}

View File

@@ -0,0 +1,56 @@
<?php
namespace InfluxDB\Adapter;
use DateTime;
function message_to_inline_protocol(array $message)
{
if (!array_key_exists("points", $message)) {
return;
}
$unixepoch = (int)(microtime(true) * 1e9);
if (array_key_exists("time", $message)) {
$dt = new DateTime($message["time"]);
$unixepoch = (int)($dt->format("U") * 1e9);
}
$lines = [];
foreach ($message["points"] as $point) {
$tags = array_key_exists("tags", $message) ? $message["tags"] : [];
if (array_key_exists("tags", $point)) {
$tags = array_replace_recursive($tags, $point["tags"]);
}
if (!$tags) {
$lines[] = trim(
sprintf(
"%s %s %d",
$point["measurement"], list_to_string($point["fields"], true), $unixepoch
)
);
} else {
$lines[] = trim(
sprintf(
"%s,%s %s %d",
$point["measurement"], list_to_string($tags), list_to_string($point["fields"], true), $unixepoch
)
);
}
}
return implode("\n", $lines);
}
function list_to_string(array $elements, $escape = false)
{
array_walk($elements, function(&$value, $key) use ($escape) {
if ($escape && is_string($value)) {
$value = "\"{$value}\"";
}
$value = "{$key}={$value}";
});
return implode(",", $elements);
}

View File

@@ -0,0 +1,67 @@
<?php
namespace InfluxDB;
use InfluxDB\Adapter\WritableInterface;
use InfluxDb\Adapter\QueryableInterface;
/**
* Client to manage request at InfluxDB
*/
class Client
{
private $adapter;
public function __construct($adapter)
{
$this->adapter = $adapter;
}
public function getAdapter()
{
return $this->adapter;
}
public function mark($name, array $values = [])
{
if (!($this->getAdapter() instanceOf WritableInterface)) {
throw new \BadMethodCallException("You can write data to database only if the adapter supports it!");
}
$data = $name;
if (!is_array($name)) {
$data =[];
$data['points'][0]['measurement'] = $name;
$data['points'][0]['fields'] = $values;
}
return $this->getAdapter()->send($data);
}
public function query($query)
{
if (!($this->getAdapter() instanceOf QueryableInterface)) {
throw new \BadMethodCallException("You can query the database only if the adapter supports it!");
}
$return = $this->getAdapter()->query($query);
return $return;
}
public function getDatabases()
{
return $this->getAdapter()->query("show databases");
}
public function createDatabase($name)
{
return $this->getAdapter()->query("create database \"{$name}\"");
}
public function deleteDatabase($name)
{
return $this->getAdapter()->query("drop database \"{$name}\"");
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace InfluxDB;
use Zend\Stdlib\Hydrator\ClassMethods;
use GuzzleHttp\Client as GuzzleClient;
/**
* Create your static client
*/
abstract class ClientFactory
{
/**
* Create new client
* @param array $options
* @return Client
* @throws InvalidArgumentException If not exist adapter name
*/
public static function create(array $options)
{
$defaultOptions = [
"adapter" => [
"name" => false,
"options" => [],
],
"options" => [],
];
$options = array_replace_recursive($defaultOptions, $options);
$adapterOptions = new Options();
$hydrator = new ClassMethods();
$hydrator->hydrate($options["options"], $adapterOptions);
$adapter = null;
$adapterName = $options["adapter"]["name"];
switch ($adapterName) {
case 'InfluxDB\\Adapter\\UdpAdapter':
$adapter = new $adapterName($adapterOptions);
break;
case 'InfluxDB\\Adapter\\GuzzleAdapter':
$adapter = new $adapterName(new GuzzleClient($options["adapter"]["options"]), $adapterOptions);
break;
default:
throw new \InvalidArgumentException("Missing adapter {$adapter}");
}
$client = new Client($adapter);
return $client;
}
}

View File

@@ -0,0 +1,142 @@
<?php
namespace InfluxDB;
/**
* Manage in the best way InfluxDB Client Configuration
*/
class Options
{
private $host;
private $port;
private $username;
private $password;
private $protocol;
private $database;
private $retentionPolicy;
private $tags;
private $prefix;
public function __construct()
{
$this->setHost("localhost");
$this->setPort(8086);
$this->setUsername("root");
$this->setPassword("root");
$this->setProtocol("http");
$this->setPrefix("");
$this->setRetentionPolicy("default");
$this->setTags([]);
}
public function getPrefix()
{
return $this->prefix;
}
public function setPrefix($prefix)
{
$this->prefix = $prefix;
return $this;
}
public function getTags()
{
return $this->tags;
}
public function setTags($tags)
{
$this->tags = $tags;
return $this;
}
public function getRetentionPolicy()
{
return $this->retentionPolicy;
}
public function setRetentionPolicy($retentionPolicy)
{
$this->retentionPolicy = $retentionPolicy;
return $this;
}
/**
* @return string
*/
public function getProtocol()
{
return $this->protocol;
}
public function setProtocol($protocol)
{
$this->protocol = $protocol;
return $this;
}
public function getHost()
{
return $this->host;
}
public function setHost($host)
{
$this->host = $host;
return $this;
}
public function getPort()
{
return $this->port;
}
public function setPort($port)
{
$this->port = $port;
return $this;
}
public function getUsername()
{
return $this->username;
}
public function setUsername($username)
{
$this->username = $username;
return $this;
}
public function getPassword()
{
return $this->password;
}
public function setPassword($password)
{
$this->password = $password;
return $this;
}
public function getDatabase()
{
return $this->database;
}
public function setDatabase($database)
{
$this->database = $database;
return $this;
}
}

View File

@@ -0,0 +1,72 @@
<?php
namespace InfluxDB\Integration\Adapter;
use DateTime;
use DateTimeZone;
use InfluxDB\Options;
use InfluxDB\Client;
use InfluxDB\Adapter\GuzzleAdapter;
use GuzzleHttp\Client as GuzzleHttpClient;
use InfluxDB\Integration\Framework\TestCase as InfluxDBTestCase;
class GuzzleAdapterTest extends InfluxDBTestCase
{
public function testAdapterWriteDataCorrectly()
{
$this->getClient()->createDatabase("tcp.test");
$options = new Options();
$options->setPort(8086);
$options->setDatabase("tcp.test");
$http = new GuzzleHttpClient();
$adapter = new GuzzleAdapter($http, $options);
$adapter->send([
"points" => [
[
"measurement" => "vm-serie",
"fields" => [
"cpu" => 18.12,
"free" => 712423,
],
],
]
]);
$this->assertSerieExists("tcp.test", "vm-serie");
$this->assertSerieCount("tcp.test", "vm-serie", 1);
$this->assertValueExistsInSerie("tcp.test", "vm-serie", "cpu", 18.12);
$this->assertValueExistsInSerie("tcp.test", "vm-serie", "free", 712423);
}
public function testWorksWithProxies()
{
$this->getClient()->createDatabase("proxy.test");
$options = new Options();
$options->setPort(9000);
$options->setDatabase("proxy.test");
$options->setPrefix("/influxdb");
$http = new GuzzleHttpClient();
$adapter = new GuzzleAdapter($http, $options);
$adapter->send([
"points" => [
[
"measurement" => "vm-serie",
"fields" => [
"cpu" => 18.12,
"free" => 712423,
],
],
]
]);
$this->assertSerieExists("proxy.test", "vm-serie");
$this->assertSerieCount("proxy.test", "vm-serie", 1);
$this->assertValueExistsInSerie("proxy.test", "vm-serie", "cpu", 18.12);
$this->assertValueExistsInSerie("proxy.test", "vm-serie", "free", 712423);
}
}

View File

@@ -0,0 +1,55 @@
<?php
namespace InfluxDB\Integration\Adapter;
use InfluxDB\Integration\Framework\TestCase as InfluxDBTestCase;
use InfluxDB\Adapter\UdpAdapter;
use InfluxDB\Options;
class UdpAdapterTest extends InfluxDBTestCase
{
public function testWriteSimplePointsUsingDirectWrite()
{
$options = (new Options())
->setPort(4444);
$adapter = new UdpAdapter($options);
$this->getClient()->createDatabase("udp.test");
$adapter->write("cpu value=12.33 " . (int)(microtime(true)*1e9));
sleep(2);
$this->assertSerieExists("udp.test", "cpu");
$this->assertSerieCount("udp.test", "cpu", 1);
$this->assertValueExistsInSerie("udp.test", "cpu", "value", 12.33);
}
public function testWriteSimplePointsUsingSendMethod()
{
$options = (new Options())
->setPort(4444);
$adapter = new UdpAdapter($options);
$this->getClient()->createDatabase("udp.test");
$adapter->send([
"retentionPolicy" => "default",
"points" => [
[
"measurement" => "mem",
"fields" => [
"value" => 1233,
"with_string" => "this is a string",
],
],
],
]);
sleep(2);
$this->assertSerieExists("udp.test", "mem");
$this->assertSerieCount("udp.test", "mem", 1);
$this->assertValueExistsInSerie("udp.test", "mem", "value", 1233);
$this->assertValueExistsInSerie("udp.test", "mem", "with_string", "this is a string");
}
}

View File

@@ -0,0 +1,166 @@
<?php
namespace InfluxDB\Integration;
use DateTime;
use DateTimeZone;
use InfluxDB\Options;
use InfluxDB\Adapter\UdpAdapter;
use InfluxDB\Adapter\GuzzleAdapter as InfluxHttpAdapter;
use GuzzleHttp\Client as GuzzleHttpClient;
use InfluxDB\Client;
use InfluxDB\Integration\Framework\TestCase;
class ClientTest extends TestCase
{
public function setUp()
{
parent::setUp();
$this->getClient()->createDatabase("tcp.test");
$this->getClient()->createDatabase("udp.test");
}
public function testSimpleMarkPublicSignature()
{
$options = new Options();
$options->setDatabase("tcp.test");
$guzzleHttp = new GuzzleHttpClient();
$adapter = new InfluxHttpAdapter($guzzleHttp, $options);
$client = new Client($adapter);
$client->mark("vm", ["mark" => "element"]);
$this->assertSerieExists("tcp.test", "vm");
$this->assertSerieCount("tcp.test", "vm", 1);
$this->assertValueExistsInSerie("tcp.test", "vm", "mark", "element");
}
public function testDirectMessagesMarkPublicSignature()
{
$options = new Options();
$options->setDatabase("tcp.test");
$guzzleHttp = new GuzzleHttpClient();
$adapter = new InfluxHttpAdapter($guzzleHttp, $options);
$client = new Client($adapter);
$client->mark([
"database" => "tcp.test",
"retentionPolicy" => "default",
"points" => [
[
"measurement" => "tt",
"fields" => [
"cpu" => 1,
"mem" => 2,
],
]
],
]);
$this->assertSerieExists("tcp.test", "tt");
$this->assertSerieCount("tcp.test", "tt", 1);
$this->assertValueExistsInSerie("tcp.test", "tt", "cpu", 1);
$this->assertValueExistsInSerie("tcp.test", "tt", "mem", 2);
}
public function testListActiveDatabases()
{
$options = new Options();
$guzzleHttp = new GuzzleHttpClient();
$adapter = new InfluxHttpAdapter($guzzleHttp, $options);
$client = new Client($adapter);
$databases = $client->getDatabases();
$this->assertCount(2, $databases["results"][0]["series"][0]["values"]);
}
public function testCreateANewDatabase()
{
$options = new Options();
$guzzleHttp = new GuzzleHttpClient();
$adapter = new InfluxHttpAdapter($guzzleHttp, $options);
$client = new Client($adapter);
$client->createDatabase("walter");
$databases = $client->getDatabases();
$this->assertCount(3, $databases["results"][0]["series"][0]["values"]);
}
public function testDropExistingDatabase()
{
$options = new Options();
$guzzleHttp = new GuzzleHttpClient();
$adapter = new InfluxHttpAdapter($guzzleHttp, $options);
$client = new Client($adapter);
$client->createDatabase("walter");
$this->assertDatabasesCount(3);
$client->deleteDatabase("walter");
$this->assertDatabasesCount(2);
}
/**
* Test that we handle socket problems correctly in the UDP
* adapter, and that they don't inturrupt the user's application.
*
* @group udp
*/
public function testReplicateIssue27()
{
$options = new \InfluxDB\Options();
// Configure options
$options->setHost('172.16.1.182');
$options->setPort(4444);
$options->setDatabase('...');
$options->setUsername('root');
$options->setPassword('root');
$httpAdapter = new \InfluxDB\Adapter\UdpAdapter($options);
$client = new \InfluxDB\Client($httpAdapter);
$client->mark("udp.test", ["mark" => "element"]);
}
/**
* @group udp
*/
public function testWriteUDPPackagesToNoOne()
{
$options = new Options();
$options->setHost("127.0.0.1");
$options->setUsername("nothing");
$options->setPassword("nothing");
$options->setPort(64071); //This is a wrong port
$adapter = new UdpAdapter($options);
$object = new Client($adapter);
$object->mark("udp.test", ["mark" => "element"]);
}
/**
* @group udp
*/
public function testWriteUDPPackagesToInvalidHostname()
{
$options = new Options();
$options->setHost("www.test-invalid.this-is-not-a-tld");
$options->setUsername("nothing");
$options->setPassword("nothing");
$options->setPort(15984);
$adapter = new UdpAdapter($options);
$object = new Client($adapter);
$object->mark("udp.test", ["mark" => "element"]);
}
}

View File

@@ -0,0 +1,91 @@
<?php
namespace InfluxDB\Integration\Framework;
use InfluxDB\Options;
use InfluxDB\Adapter\GuzzleAdapter as InfluxHttpAdapter;
use GuzzleHttp\Client as GuzzleHttpClient;
use InfluxDB\Client;
class TestCase extends \PHPUnit_Framework_TestCase
{
private $client;
private $options;
public function setUp()
{
$options = $this->options = new Options();
$guzzleHttp = new GuzzleHttpClient();
$adapter = new InfluxHttpAdapter($guzzleHttp, $options);
$client = $this->client = new Client($adapter);
$this->dropAll();
}
public function tearDown()
{
$this->dropAll();
}
private function dropAll()
{
$databases = $this->getClient()->getDatabases();
if (array_key_exists("values", $databases["results"][0]["series"][0])) {
foreach ($databases["results"][0]["series"][0]["values"] as $database) {
$this->getClient()->deleteDatabase($database[0]);
}
}
}
public function assertValueExistsInSerie($database, $serieName, $column, $value)
{
$this->getOptions()->setDatabase($database);
$body = $this->getClient()->query("select {$column} from \"{$serieName}\"");
foreach ($body["results"][0]["series"][0]["values"] as $result) {
if ($result[1] == $value) {
return $this->assertTrue(true);
}
}
return $this->fail("Missing value '{$value}'");
}
public function assertSerieCount($database, $serieName, $count)
{
$this->getOptions()->setDatabase($database);
$body = $this->getClient()->query("select * from \"{$serieName}\"");
$this->assertCount(1, $body["results"][0]["series"][0]["values"]);
}
public function assertSerieExists($database, $serieName)
{
$this->getOptions()->setDatabase($database);
$body = $this->getClient()->query("show measurements");
foreach ($body["results"][0]["series"][0]["values"] as $result) {
if ($result[0] == $serieName) {
return $this->assertTrue(true);
}
}
return $this->fail("Missing serie with name '{$serieName}' in database '{$database}'");
}
public function assertDatabasesCount($count)
{
$databases = $this->client->getDatabases();
$this->assertCount($count, $databases["results"][0]["series"][0]["values"]);
}
public function getOptions()
{
return $this->options;
}
public function getClient()
{
return $this->client;
}
}

View File

@@ -0,0 +1,225 @@
<?php
namespace InfluxDB\Adater;
use DateTime;
use DateTimeZone;
use InfluxDB\Options;
use GuzzleHttp\Client as GuzzleHttpClient;
use InfluxDB\Adapter\GuzzleAdapter as InfluxHttpAdapter;
use InfluxDB\Client;
use Prophecy\Argument;
class GuzzleAdapterTest extends \PHPUnit_Framework_TestCase
{
/**
* @group tcp
* @group proxy
* @dataProvider getWriteEndpoints
*/
public function testWriteEndpointGeneration($final, $options)
{
$guzzleHttp = new GuzzleHttpClient();
$adapter = new InfluxHttpAdapter($guzzleHttp, $options);
$reflection = new \ReflectionClass(get_class($adapter));
$method = $reflection->getMethod("getHttpSeriesEndpoint");
$method->setAccessible(true);
$endpoint = $method->invokeArgs($adapter, []);
$this->assertEquals($final, $endpoint);
}
public function getWriteEndpoints()
{
return [
["http://localhost:9000/write", (new Options())->setHost("localhost")->setPort(9000)],
["https://localhost:9000/write", (new Options())->setHost("localhost")->setPort(9000)->setProtocol("https")],
["http://localhost:9000/influxdb/write", (new Options())->setHost("localhost")->setPort(9000)->setPrefix("/influxdb")],
];
}
/**
* @group tcp
* @group proxy
* @dataProvider getQueryEndpoints
*/
public function testQueryEndpointGeneration($final, $options)
{
$guzzleHttp = new GuzzleHttpClient();
$adapter = new InfluxHttpAdapter($guzzleHttp, $options);
$reflection = new \ReflectionClass(get_class($adapter));
$method = $reflection->getMethod("getHttpQueryEndpoint");
$method->setAccessible(true);
$endpoint = $method->invokeArgs($adapter, []);
$this->assertEquals($final, $endpoint);
}
public function getQueryEndpoints()
{
return [
["http://localhost:9000/query", (new Options())->setHost("localhost")->setPort(9000)],
["https://localhost:9000/query", (new Options())->setHost("localhost")->setPort(9000)->setProtocol("https")],
["http://localhost:9000/influxdb/query", (new Options())->setHost("localhost")->setPort(9000)->setPrefix("/influxdb")],
];
}
public function testMergeWithDefaultOptions()
{
$options = new Options();
$options->setDatabase("db");
$httpClient = $this->prophesize("GuzzleHttp\\Client");
$httpClient->post(Argument::Any(), [
"auth" => ["root", "root"],
"query" => [
"db" => "db",
"retentionPolicy" => "default",
],
"body" => null,
])->shouldBeCalledTimes(1);
$adapter = new InfluxHttpAdapter($httpClient->reveal(), $options);
$adapter->send([]);
}
public function testAdapterPrepareJsonDataCorrectly()
{
$guzzleHttp = $this->prophesize("GuzzleHttp\Client");
$guzzleHttp->post("http://localhost:8086/write", [
"auth" => ["root", "root"],
"query" => [
"db" => "db",
"retentionPolicy" => "default",
],
"body" => 'tcp.test mark="element" 1257894000000000000',
])->shouldBeCalledTimes(1);
$options = (new Options())->setDatabase("db");
$adapter = new InfluxHttpAdapter($guzzleHttp->reveal(), $options);
$adapter->send([
"time" => "2009-11-10T23:00:00Z",
"points" => [
[
"measurement" => "tcp.test",
"fields" => [
"mark" => "element"
]
]
]
]);
}
public function testDefaultOptionOverwrite()
{
$options = new Options();
$options->setDatabase("db");
$httpClient = $this->prophesize("GuzzleHttp\\Client");
$httpClient->post(Argument::Any(), [
"auth" => ["root", "root"],
"query" => [
"db" => "mydb",
"retentionPolicy" => "myPolicy",
],
"body" => null,
])->shouldBeCalledTimes(1);
$adapter = new InfluxHttpAdapter($httpClient->reveal(), $options);
$adapter->send([
"database" => "mydb",
"retentionPolicy" => "myPolicy"
]);
}
public function testEmptyTagsFieldIsRemoved()
{
$options = new Options();
$options->setDatabase("db");
$httpClient = $this->prophesize("GuzzleHttp\\Client");
$httpClient->post(Argument::Any(), [
"auth" => ["root", "root"],
"query" => [
"db" => "db",
"retentionPolicy" => "default",
],
"body" => 'tcp.test mark="element" 1257894000000000000',
])->shouldBeCalledTimes(1);
$adapter = new InfluxHttpAdapter($httpClient->reveal(), $options);
$adapter->send([
"time" => "2009-11-10T23:00:00Z",
"points" => [
[
"measurement" => "tcp.test",
"fields" => [
"mark" => "element"
]
]
]
]);
}
public function testGlobalTagsAreInPlace()
{
$options = new Options();
$options->setDatabase("db");
$options->setTags([
"dc" => "us-west",
]);
$httpClient = $this->prophesize("GuzzleHttp\\Client");
$httpClient->post(Argument::Any(), [
"auth" => ["root", "root"],
"query" => [
"db" => "db",
"retentionPolicy" => "default",
],
"body" => 'tcp.test,dc=us-west mark="element" 1257894000000000000',
])->shouldBeCalledTimes(1);
$adapter = new InfluxHttpAdapter($httpClient->reveal(), $options);
$adapter->send([
"time" => "2009-11-10T23:00:00Z",
"points" => [
[
"measurement" => "tcp.test",
"fields" => [
"mark" => "element"
]
]
]
]);
}
public function testTagsFieldIsMergedWithGlobalTags()
{
$options = new Options();
$options->setDatabase("db");
$options->setTags([
"dc" => "us-west",
]);
$httpClient = $this->prophesize("GuzzleHttp\\Client");
$httpClient->post(Argument::Any(), [
"auth" => ["root", "root"],
"query" => [
"db" => "db",
"retentionPolicy" => "default",
],
"body" => 'tcp.test,dc=us-west,region=us mark="element" 1257894000000000000',
])->shouldBeCalledTimes(1);
$adapter = new InfluxHttpAdapter($httpClient->reveal(), $options);
$adapter->send([
"time" => "2009-11-10T23:00:00Z",
"tags" => ["region" => "us"],
"points" => [
[
"measurement" => "tcp.test",
"fields" => [
"mark" => "element"
]
]
]
]);
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace InfluxDB\Adapter;
class HelpersTest extends \PHPUnit_Framework_TestCase
{
/**
* @dataProvider getElements
*/
public function testListToInlineValues($message, $result, $escape)
{
$this->assertEquals($result, list_to_string($message, $escape));
}
public function getElements()
{
return [
[["one" => "two"], "one=two", false],
[["one" => "two"], "one=\"two\"", true],
[["one" => "two", "three" => "four"], "one=two,three=four", false],
[["one" => "two", "three" => "four"], "one=\"two\",three=\"four\"", true],
];
}
}

View File

@@ -0,0 +1,299 @@
<?php
namespace InfluxDB\Adapter;
use DateTime;
use DateTimeZone;
use InfluxDB\Options;
use GuzzleHttp\Client as GuzzleHttpClient;
use InfluxDB\Adapter\GuzzleAdapter as InfluxHttpAdapter;
use InfluxDB\Client;
use Prophecy\Argument;
class UdpAdapterTest extends \PHPUnit_Framework_TestCase
{
/**
* @dataProvider getMessages
*/
public function testRewriteMessages($input, $response)
{
$object = new UdpAdapter(new Options());
$object = $this->getMockBuilder("InfluxDB\Adapter\UdpAdapter")
->setConstructorArgs([new Options()])
->setMethods(["write"])
->getMock();
$object->expects($this->once())
->method("write")
->with($response);
$object->send($input);
}
public function getMessages()
{
return [
[
[
"time" => "2009-11-10T23:00:00Z",
"points" => [
[
"measurement" => "cpu",
"fields" => [
"value" => 1,
],
],
],
],
"cpu value=1 1257894000000000000"
],
[
[
"time" => "2009-11-10T23:00:00Z",
"points" => [
[
"measurement" => "cpu",
"fields" => [
"value" => 1,
"string" => "escape",
],
],
],
],
"cpu value=1,string=\"escape\" 1257894000000000000"
],
[
[
"tags" => [
"region" => "us-west",
"host" => "serverA",
"env" => "prod",
"target" => "servers",
"zone" => "1c",
],
"time" => "2009-11-10T23:00:00Z",
"points" => [
[
"measurement" => "cpu",
"fields" => [
"cpu" => 18.12,
"free" => 712432,
],
],
],
],
"cpu,region=us-west,host=serverA,env=prod,target=servers,zone=1c cpu=18.12,free=712432 1257894000000000000"
],
[
[
"tags" => [
"region" => "us-west",
"host" => "serverA",
"env" => "prod",
"target" => "servers",
"zone" => "1c",
],
"time" => "2009-11-10T23:00:00Z",
"points" => [
[
"measurement" => "cpu",
"fields" => [
"cpu" => 18.12,
],
],
[
"measurement" => "mem",
"fields" => [
"free" => 712432,
],
],
],
],
<<<EOF
cpu,region=us-west,host=serverA,env=prod,target=servers,zone=1c cpu=18.12 1257894000000000000
mem,region=us-west,host=serverA,env=prod,target=servers,zone=1c free=712432 1257894000000000000
EOF
],
];
}
/**
* @group udp
*/
public function testUdpIpWriteDataWillBeConvertedAsLineProtocol()
{
$options = (new Options())->setDatabase("test");
$adapter = $this->getMockBuilder("InfluxDB\\Adapter\\UdpAdapter")
->setConstructorArgs([$options])
->setMethods(["write", "generateTimeInNanoSeconds"])
->getMock();
$adapter->expects($this->any())
->method("generateTimeInNanoSeconds")
->will($this->returnValue(1245));
$adapter->expects($this->once())
->method("write")
->with($this->matchesRegularExpression("/udp.test mark=\"element\" \d+/i"));
$adapter->send([
"points" => [
[
"measurement" => "udp.test",
"fields" => [
"mark" => "element"
]
]
]
]);
}
/**
* @group udp
*/
public function testSendMultipleMeasurementWithUdpIp()
{
$options = (new Options())->setDatabase("test");
$adapter = $this->getMockBuilder("InfluxDB\\Adapter\\UdpAdapter")
->setConstructorArgs([$options])
->setMethods(["write", "generateTimeInNanoSeconds"])
->getMock();
$adapter->expects($this->any())
->method("generateTimeInNanoSeconds")
->will($this->onConsecutiveCalls(1245, 1246));
$adapter->expects($this->once())
->method("write")
->with($this->matchesRegularExpression(<<<EOF
/mem free=712423 \d+
cpu cpu=18.12 \d+/i
EOF
));
$adapter->send([
"points" => [
[
"measurement" => "mem",
"fields" => [
"free" => 712423,
],
],
[
"measurement" => "cpu",
"fields" => [
"cpu" => 18.12,
],
],
]
]);
}
/**
* @group udp
*/
public function testFillWithGlobalTags()
{
$options = (new Options())
->setDatabase("test")
->setTags(["dc" => "eu-west"]);
$adapter = $this->getMockBuilder("InfluxDB\\Adapter\\UdpAdapter")
->setConstructorArgs([$options])
->setMethods(["write"])
->getMock();
$adapter->expects($this->once())
->method("write")
->with($this->matchesRegularExpression("/mem,dc=eu-west free=712423 \d+/i"));
$adapter->send([
"points" => [
[
"measurement" => "mem",
"fields" => [
"free" => 712423,
],
],
]
]);
}
/**
* @group udp
*/
public function testMergeGlobalTags()
{
$options = (new Options())
->setDatabase("test")
->setTags(["dc" => "eu-west"]);
$adapter = $this->getMockBuilder("InfluxDB\\Adapter\\UdpAdapter")
->setConstructorArgs([$options])
->setMethods(["write", "generateTimeInNanoSeconds"])
->getMock();
$adapter->expects($this->any())
->method("generateTimeInNanoSeconds")
->will($this->returnValue(1245));
$adapter->expects($this->once())
->method("write")
->with($this->matchesRegularExpression(<<<EOF
/mem,dc=eu-west,region=eu-west-1 free=712423 \d+/i
EOF
));
$adapter->send([
"tags" => [
"region" => "eu-west-1",
],
"points" => [
[
"measurement" => "mem",
"fields" => [
"free" => 712423,
],
],
]
]);
}
/**
* @group udp
*/
public function testMergeFullTagsPositions()
{
$options = (new Options())
->setDatabase("test")
->setTags(["dc" => "eu-west"]);
$adapter = $this->getMockBuilder("InfluxDB\\Adapter\\UdpAdapter")
->setConstructorArgs([$options])
->setMethods(["write", "generateTimeInNanoSeconds"])
->getMock();
$adapter->expects($this->any())
->method("generateTimeInNanoSeconds")
->will($this->returnValue(1245));
$adapter->expects($this->once())
->method("write")
->with($this->matchesRegularExpression(<<<EOF
/mem,dc=eu-west,region=eu-west-1,location=ireland free=712423 \d+/i
EOF
));
$adapter->send([
"tags" => [
"region" => "eu-west-1",
],
"points" => [
[
"measurement" => "mem",
"tags" => [
"location" => "ireland",
],
"fields" => [
"free" => 712423,
],
],
]
]);
}
}

View File

@@ -0,0 +1,135 @@
<?php
namespace InfluxDB;
class ClientFactoryTest extends \PHPUnit_Framework_TestCase
{
/**
* @group factory
*
* @expectedException InvalidArgumentException
*/
public function testEmptyOptions()
{
$client = ClientFactory::create([]);
}
/**
* @group factory
* @expectedException InvalidArgumentException
*/
public function testInvalidAdapter()
{
$client = ClientFactory::create(["adapter" => ["name" => "UdpAdapter"]]);
}
/**
* @group factory
* @group udp
*/
public function testCreateUdpClient()
{
$options = [
"adapter" => [
"name" => "InfluxDB\\Adapter\\UdpAdapter",
],
"options" => [
"host" => "127.0.0.1",
"username" => "user",
"password" => "pass",
],
];
$client = ClientFactory::create($options);
$this->assertInstanceOf("InfluxDB\\Client", $client);
$this->assertInstanceOf("InfluxDB\\Adapter\\UdpAdapter", $client->getAdapter());
$this->assertEquals("127.0.0.1", $client->getAdapter()->getOptions()->getHost());
$this->assertEquals("user", $client->getAdapter()->getOptions()->getUsername());
$this->assertEquals("pass", $client->getAdapter()->getOptions()->getPassword());
}
/**
* @group factory
* @group tcp
* @dataProvider getHttpAdapters
*/
public function testCreateTcpClient($adapter)
{
$options = [
"adapter" => [
"name" => $adapter,
],
"options" => [
"host" => "127.0.0.1",
"username" => "user",
"password" => "pass",
],
];
$client = ClientFactory::create($options);
$this->assertInstanceOf("InfluxDB\\Client", $client);
$this->assertInstanceOf($adapter, $client->getAdapter());
$this->assertEquals("127.0.0.1", $client->getAdapter()->getOptions()->getHost());
$this->assertEquals("user", $client->getAdapter()->getOptions()->getUsername());
$this->assertEquals("pass", $client->getAdapter()->getOptions()->getPassword());
}
/**
* @group factory
* @dataProvider getHttpAdapters
*/
public function testCreateTcpClientWithAllOptions($adapter)
{
$options = [
"adapter" => [
"name" => $adapter,
],
"options" => [
"host" => "127.0.0.1",
"username" => "user",
"password" => "pass",
"retention_policy" => "too_many_data",
"tags" => [
"region" => "eu",
"env" => "prod",
],
],
];
$client = ClientFactory::create($options);
$this->assertInstanceOf("InfluxDB\\Client", $client);
$this->assertInstanceOf($adapter, $client->getAdapter());
$this->assertEquals("127.0.0.1", $client->getAdapter()->getOptions()->getHost());
$this->assertEquals("user", $client->getAdapter()->getOptions()->getUsername());
$this->assertEquals("pass", $client->getAdapter()->getOptions()->getPassword());
$this->assertEquals(["region" => "eu", "env" => "prod"], $client->getAdapter()->getOptions()->getTags());
$this->assertEquals("too_many_data", $client->getAdapter()->getOptions()->getRetentionPolicy());
}
public function getHttpAdapters()
{
return [
["InfluxDB\\Adapter\\GuzzleAdapter"],
];
}
/**
* @expectedException InvalidArgumentException
* @dataProvider getInvalidClasses
*/
public function testInvalidProviderThrowsException($class)
{
$client = ClientFactory::create([
"adapter" => [
"name" => $class,
],
]);
}
public function getInvalidClasses()
{
return [["InvalidClass"],["stdClass"]];
}
}

View File

@@ -0,0 +1,83 @@
<?php
namespace InfluxDB;
use DateTime;
use DateTimeZone;
use InfluxDB\Adapter\GuzzleAdapter as InfluxHttpAdapter;
use InfluxDB\Options;
use InfluxDB\Adapter\UdpAdapter;
use GuzzleHttp\Client as GuzzleHttpClient;
class ClientTest extends \PHPUnit_Framework_TestCase
{
public function testMarkNewMeasurementWithShortSyntax()
{
$mock = $this->prophesize("InfluxDB\\Adapter\\WritableInterface");
$mock->send([
"points" => [
[
"measurement" => "tcp.test",
"fields" => [
"mark" => "element"
]
]
]
])->shouldBeCalledTimes(1);
$object = new Client($mock->reveal());
$object->mark("tcp.test", ["mark" => "element"]);
}
public function testWriteDirectMessages()
{
$mock = $this->prophesize("InfluxDB\\Adapter\\WritableInterface");
$mock->send([
"tags" => [
"dc" => "eu-west-1",
],
"points" => [
[
"measurement" => "vm-serie",
"fields" => [
"cpu" => 18.12,
"free" => 712423,
]
]
]
])->shouldBeCalledTimes(1);
$object = new Client($mock->reveal());
$object->mark([
"tags" => [
"dc" => "eu-west-1",
],
"points" => [
[
"measurement" => "vm-serie",
"fields" => [
"cpu" => 18.12,
"free" => 712423,
],
],
]
]);
}
/**
* @expectedException BadMethodCallException
*/
public function testNeedWritableInterfaceDuringMark()
{
$client = new Client(new \stdClass());
$client->mark("OK", []);
}
/**
* @expectedException BadMethodCallException
*/
public function testNeedQueryableInterfaceDuringQuery()
{
$client = new Client(new \stdClass());
$client->query("OK", []);
}
}