X-Git-Url: https://git.heureux-cyclage.org/?p=lhc%2Fweb%2Fwiklou.git;a=blobdiff_plain;f=includes%2Fconfig%2FEtcdConfig.php;h=3811da325c4c5a2155f46208fb9d5ca437790f2c;hp=6605c38d5a5924ec921d06c6cf7150d7f9c2d923;hb=79de8fd02f20c8f8a9e22c537906151044f52979;hpb=4427b84407e03275bdb62ca58a0fde14f3dc7be6 diff --git a/includes/config/EtcdConfig.php b/includes/config/EtcdConfig.php index 6605c38d5a..3811da325c 100644 --- a/includes/config/EtcdConfig.php +++ b/includes/config/EtcdConfig.php @@ -20,6 +20,7 @@ use Psr\Log\LoggerAwareInterface; use Psr\Log\LoggerInterface; +use Wikimedia\ObjectFactory; use Wikimedia\WaitConditionLoop; /** @@ -45,11 +46,11 @@ class EtcdConfig implements Config, LoggerAwareInterface { private $directory; /** @var string */ private $encoding; - /** @var integer */ + /** @var int */ private $baseCacheTTL; - /** @var integer */ + /** @var int */ private $skewCacheTTL; - /** @var integer */ + /** @var int */ private $timeout; /** @@ -228,7 +229,7 @@ class EtcdConfig implements Config, LoggerAwareInterface { // Retrieve all the values under the MediaWiki config directory list( $rcode, $rdesc, /* $rhdrs */, $rbody, $rerr ) = $this->http->run( [ 'method' => 'GET', - 'url' => "{$this->protocol}://{$address}/v2/keys/{$this->directory}/", + 'url' => "{$this->protocol}://{$address}/v2/keys/{$this->directory}/?recursive=true", 'headers' => [ 'content-type' => 'application/json' ] ] ); @@ -240,28 +241,65 @@ class EtcdConfig implements Config, LoggerAwareInterface { empty( $terminalCodes[$rcode] ) ]; } + try { + return [ $this->parseResponse( $rbody ), null, false ]; + } catch ( EtcdConfigParseError $e ) { + return [ null, $e->getMessage(), false ]; + } + } + /** + * Parse a response body, throwing EtcdConfigParseError if there is a validation error + * + * @param string $rbody + * @return array + */ + protected function parseResponse( $rbody ) { $info = json_decode( $rbody, true ); - if ( $info === null || !isset( $info['node']['nodes'] ) ) { - return [ null, "Unexpected JSON response; missing 'nodes' list.", false ]; + if ( $info === null ) { + throw new EtcdConfigParseError( "Error unserializing JSON response." ); + } + if ( !isset( $info['node'] ) || !is_array( $info['node'] ) ) { + throw new EtcdConfigParseError( + "Unexpected JSON response: Missing or invalid node at top level." ); } - $config = []; - foreach ( $info['node']['nodes'] as $node ) { + $this->parseDirectory( '', $info['node'], $config ); + return $config; + } + + /** + * Recursively parse a directory node and populate the array passed by + * reference, throwing EtcdConfigParseError if there is a validation error + * + * @param string $dirName The relative directory name + * @param array $dirNode The decoded directory node + * @param array &$config The output array + */ + protected function parseDirectory( $dirName, $dirNode, &$config ) { + if ( !isset( $dirNode['nodes'] ) ) { + throw new EtcdConfigParseError( + "Unexpected JSON response in dir '$dirName'; missing 'nodes' list." ); + } + if ( !is_array( $dirNode['nodes'] ) ) { + throw new EtcdConfigParseError( + "Unexpected JSON response in dir '$dirName'; 'nodes' is not an array." ); + } + + foreach ( $dirNode['nodes'] as $node ) { + $baseName = basename( $node['key'] ); + $fullName = $dirName === '' ? $baseName : "$dirName/$baseName"; if ( !empty( $node['dir'] ) ) { - continue; // skip directories - } + $this->parseDirectory( $fullName, $node, $config ); + } else { + $value = $this->unserialize( $node['value'] ); + if ( !is_array( $value ) || !array_key_exists( 'val', $value ) ) { + throw new EtcdConfigParseError( "Failed to parse value for '$fullName'." ); + } - $name = basename( $node['key'] ); - $value = $this->unserialize( $node['value'] ); - if ( !is_array( $value ) || !array_key_exists( 'val', $value ) ) { - return [ null, "Failed to parse value for '$name'.", false ]; + $config[$fullName] = $value['val']; } - - $config[$name] = $value['val']; } - - return [ $config, null, false ]; } /**