Skip to content

Commit 17cca84

Browse files
authored
Merge pull request #36 from launchdarkly/eb/ch28432/consul
add Consul integration
2 parents 088aef2 + a49b3c1 commit 17cca84

File tree

6 files changed

+153
-2
lines changed

6 files changed

+153
-2
lines changed

.circleci/config.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,21 +42,25 @@ jobs:
4242
docker:
4343
- image: circleci/php:5.6.34-cli-jessie
4444
- image: amazon/dynamodb-local
45+
- image: consul
4546
test-7.0:
4647
<<: *php-docker-template
4748
docker:
4849
- image: circleci/php:7.0.28-cli-jessie
4950
- image: amazon/dynamodb-local
51+
- image: consul
5052
test-7.1:
5153
<<: *php-docker-template
5254
docker:
5355
- image: circleci/php:7.1.15-cli-jessie
5456
- image: amazon/dynamodb-local
57+
- image: consul
5558
test-7.2:
5659
<<: *php-docker-template
5760
docker:
5861
- image: circleci/php:7.2.3-cli-stretch
5962
- image: amazon/dynamodb-local
63+
- image: consul
6064

6165
test-5.5: # CircleCI doesn't provide a Docker image for 5.5
6266
machine:
@@ -90,6 +94,16 @@ jobs:
9094
name: start DynamoDB
9195
command: docker run -p 8000:8000 amazon/dynamodb-local
9296
background: true
97+
- run:
98+
name: download Consul
99+
command: wget https://releases.hashicorp.com/consul/0.8.0/consul_0.8.0_linux_amd64.zip
100+
- run:
101+
name: extract Consul
102+
command: unzip consul_0.8.0_linux_amd64.zip
103+
- run:
104+
name: start Consul
105+
command: ./consul agent -dev
106+
background: true
93107
- run:
94108
name: run tests
95109
command: vendor/bin/phpunit --log-junit ~/phpunit/junit.xml --coverage-text tests

README.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ With Guzzle, you could persist your cache somewhere other than the default in-me
6464
Using LD-Relay
6565
==============
6666

67-
The LaunchDarkly Relay Proxy ([ld-relay](https://github.com/launchdarkly/ld-relay)) consumes the LaunchDarkly streaming API and can update a database cache operating in your production environment. The ld-relay offers many benefits such as performance and feature flag consistency. With PHP applications, we strongly recommend setting up ld-relay with a database store. The database can be Redis or DynamoDB. (For more about using LaunchDarkly with Redis or DynamoDB, see the [SDK reference guide](https://docs.launchdarkly.com/v2.0/docs/using-a-persistent-feature-store).)
67+
The LaunchDarkly Relay Proxy ([ld-relay](https://github.com/launchdarkly/ld-relay)) consumes the LaunchDarkly streaming API and can update a database cache operating in your production environment. The ld-relay offers many benefits such as performance and feature flag consistency. With PHP applications, we strongly recommend setting up ld-relay with a database store. The database can be Redis, Consul, or DynamoDB. (For more about using LaunchDarkly with databases, see the [SDK reference guide](https://docs.launchdarkly.com/v2.0/docs/using-a-persistent-feature-store).)
6868

6969
1. Set up ld-relay in [daemon-mode](https://github.com/launchdarkly/ld-relay#redis-storage-and-daemon-mode) with Redis
7070

@@ -74,6 +74,10 @@ The LaunchDarkly Relay Proxy ([ld-relay](https://github.com/launchdarkly/ld-rela
7474

7575
php composer.phar require "predis/predis:1.0.*"
7676

77+
For Consul:
78+
79+
php composer.phar require "sensiolabs/consul-php-sdk:2.*"
80+
7781
For DynamoDB:
7882

7983
php composer.phar require "aws/aws-sdk-php:3.*"
@@ -91,6 +95,16 @@ The LaunchDarkly Relay Proxy ([ld-relay](https://github.com/launchdarkly/ld-rela
9195
'predis_client' => $myClient // use this if you have already configured a Predis client instance
9296
]);
9397

98+
For Consul:
99+
100+
$client = new LaunchDarkly\LDClient("your_sdk_key", [
101+
'feature_requester' => 'LaunchDarkly\ConsulFeatureRequester',
102+
'consul_uri' => 'http://localhost:8500', // this is the default
103+
'consul_prefix' => 'env1', // corresponds to the prefix setting in ld-relay
104+
'consul_options' => array(), // you may pass any options supported by the Guzzle client
105+
'apc_expiration' => 30 // expiration time for local caching, if you have apcu installed
106+
]);
107+
94108
For DynamoDB:
95109

96110
$client = new LaunchDarkly\LDClient("your_sdk_key", [

composer.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,15 @@
2626
"phpdocumentor/phpdocumentor": "^2.0",
2727
"phpunit/phpunit": ">=4.8.26 <5.4",
2828
"predis/predis": "^1.0",
29+
"sensiolabs/consul-php-sdk": ">=2.1 <3.0",
2930
"zendframework/zend-serializer": "^2.7"
3031
},
3132
"suggest": {
3233
"guzzlehttp/guzzle": "(^6.2.1) Required when using GuzzleEventPublisher or the default FeatureRequester",
3334
"kevinrob/guzzle-cache-middleware": "(^1.4.1) Recommended for performance when using the default FeatureRequester",
3435
"predis/predis": "(^1.0) Required when using LDDFeatureRequester",
35-
"aws/aws-sdk-php": "(^3.86) Required when using DynamoDbFeatureRequester"
36+
"aws/aws-sdk-php": "(^3.86) Required when using DynamoDbFeatureRequester",
37+
"sensiolabs/consul-php-sdk": "(>=2.1 <3.0) Required when using ConsulFeatureRequester"
3638
},
3739
"autoload": {
3840
"psr-4": {
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
namespace LaunchDarkly;
3+
4+
use SensioLabs\Consul\Exception\ClientException;
5+
use SensioLabs\Consul\ServiceFactory;
6+
7+
class ConsulFeatureRequester extends FeatureRequesterBase
8+
{
9+
/** @var string */
10+
protected $_prefix;
11+
/** @var \SensioLabs\Consul\Services\KV */
12+
protected $_kvClient;
13+
14+
public function __construct($baseUri, $sdkKey, $options)
15+
{
16+
parent::__construct($baseUri, $sdkKey, $options);
17+
18+
$consulOpts = isset($options['consul_options']) ? $options['consul_options'] : array();
19+
if (isset($options['consul_uri'])) {
20+
$consulOpts['base_uri'] = $options['consul_uri'];
21+
}
22+
$sf = new ServiceFactory($consulOpts);
23+
$this->_kvClient = $sf->get('kv');
24+
25+
$prefix = isset($options['consul_prefix']) ? $options['consul_prefix'] : 'launchdarkly';
26+
$this->_prefix = $prefix . '/';
27+
}
28+
29+
protected function readItemString($namespace, $key)
30+
{
31+
try {
32+
$resp = $this->_kvClient->get($this->makeKey($namespace, $key));
33+
} catch (ClientException $e) {
34+
if ($e->getCode() === 404) {
35+
return null;
36+
}
37+
throw $e;
38+
}
39+
$results = $resp->json();
40+
if (count($results) != 1) {
41+
return null;
42+
}
43+
return base64_decode($results[0]['Value']);
44+
}
45+
46+
protected function readItemStringList($namespace)
47+
{
48+
try {
49+
$resp = $this->_kvClient->get($this->makeKey($namespace, ''), array('recurse' => true));
50+
} catch (ClientException $e) {
51+
if ($e->getCode() === 404) {
52+
return array();
53+
}
54+
throw $e;
55+
}
56+
$results = $resp->json();
57+
$ret = array();
58+
foreach ($results as $result) {
59+
$ret[] = base64_decode($result['Value']);
60+
}
61+
return $ret;
62+
}
63+
64+
private function makeKey($namespace, $key)
65+
{
66+
return $this->_prefix . $namespace . '/' . $key;
67+
}
68+
}

src/LaunchDarkly/FeatureRequesterBase.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,9 @@ protected function getJsonItemList($namespace)
167167
$values = json_decode($raw, true);
168168
} else {
169169
$values = $this->readItemStringList($namespace);
170+
if (!$values) {
171+
$values = array();
172+
}
170173
$this->putCachedString($namespace, self::ALL_ITEMS_KEY, json_encode($values));
171174
}
172175
foreach ($values as $i => $s) {
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
3+
namespace LaunchDarkly\Tests;
4+
5+
use LaunchDarkly\ConsulFeatureRequester;
6+
use SensioLabs\Consul\Exception\ClientException;
7+
use SensioLabs\Consul\ServiceFactory;
8+
9+
class ConsulFeatureRequesterTest extends FeatureRequesterTestBase
10+
{
11+
const TABLE_NAME = 'test-table';
12+
const PREFIX = 'test';
13+
14+
private static $kvClient;
15+
16+
public static function setUpBeforeClass()
17+
{
18+
$sf = new ServiceFactory();
19+
self::$kvClient = $sf->get('kv');
20+
}
21+
22+
protected function makeRequester()
23+
{
24+
$options = array(
25+
'consul_prefix' => self::PREFIX
26+
);
27+
return new ConsulFeatureRequester('', '', $options);
28+
}
29+
30+
protected function putItem($namespace, $key, $version, $json)
31+
{
32+
self::$kvClient->put(self::PREFIX . '/' . $namespace . '/' . $key, $json);
33+
}
34+
35+
protected function deleteExistingData()
36+
{
37+
try {
38+
$resp = self::$kvClient->get(self::PREFIX . '/', array('keys' => true, 'recurse' => true));
39+
} catch (ClientException $e) {
40+
if ($e->getCode() === 404) {
41+
return;
42+
}
43+
throw $e;
44+
}
45+
$results = $resp->json();
46+
foreach ($results as $key) {
47+
self::$kvClient->delete($key);
48+
}
49+
}
50+
}

0 commit comments

Comments
 (0)