Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Daniel Aharon
committed
Aug 22, 2011
0 parents
commit accd68a
Showing
2 changed files
with
389 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,389 @@ | ||
<?php | ||
|
||
/** | ||
* Defines the Chef class | ||
* | ||
* A wrapper for the Chef HTTP API. | ||
* | ||
* PHP version 5 | ||
* | ||
* @package framework.php | ||
* @author Daniel Aharon <daharon@sazze.com> | ||
* @copyright 2011 Sazze, Inc. | ||
*/ | ||
|
||
namespace chef { | ||
|
||
class Chef { | ||
|
||
private $host; | ||
private $port; | ||
private $privateKey; | ||
private $userId; | ||
private $version; | ||
|
||
/** | ||
* Chef::__construct() | ||
* | ||
* Set the variables used for every request. | ||
* | ||
* @param string $hostname FQDN or IP address of the Chef server. | ||
* @param integer $port The port that the Chef server is listening on. | ||
* @param string $userId The client name associated with the private key. | ||
* @param string $privateKey Either the key itself, or the filename containing the key. | ||
* @param string $chefVersion The Chef server version. | ||
*/ | ||
public function __construct($host, $port, $userId, $privateKey, $chefVersion = '0.9.12') { | ||
|
||
$this->host = $host; | ||
$this->port = $port; | ||
$this->userId = $userId; | ||
$this->version = $chefVersion; | ||
|
||
if (file_exists($privateKey)) { | ||
$this->privateKey = file_get_contents($privateKey); | ||
} else { | ||
$this->privateKey = $privateKey; | ||
} | ||
} | ||
|
||
/** | ||
* Chef::factory() | ||
* | ||
* Return a Chef instance. | ||
* | ||
* @param string $hostname FQDN or IP address of the Chef server. | ||
* @param integer $port The port that the Chef server is listening on. | ||
* @param string $privateKey Either the key itself, or the filename containing the key. | ||
* @param string $userId The client name associated with the private key. | ||
* @param string $chefVersion The Chef server version. | ||
* | ||
* @return Chef An instance of the Chef class. | ||
*/ | ||
public static function factory($hostname, $port, $privateKey, $userId, $chefVersion = '0.9.12') { | ||
return new static($hostname, $port, $privateKey, $userId, $chefVersion); | ||
} | ||
|
||
/** | ||
* Chef::searchIndexes() | ||
* | ||
* @return array A list of the indexes available for search on the Chef server. | ||
*/ | ||
public function searchIndexes() { | ||
$uri = '/search'; | ||
return $this->httpGet($uri); | ||
} | ||
|
||
/** | ||
* Chef::search() | ||
* | ||
* Search a Chef server index. | ||
* | ||
* @param string $indexName The index to search. | ||
* @param string $searchString A valid search string. eg: 'recipes:"php::fpm"' | ||
* @param string $sort Sort the results by the specified attribute. | ||
* @param integer $start The result number to start from. | ||
* @param integer $rows How many rows to return. | ||
* | ||
* @return array The result set. | ||
*/ | ||
public function search($indexName, $searchString, $sort = '', $start = 0, $rows = 9999) { | ||
|
||
$uri = '/search/' . $indexName; | ||
$queryString = array( | ||
'q=' . urlencode($searchString), | ||
'rows=' . $rows | ||
); | ||
|
||
if ($start > 0) { | ||
$queryString[] = 'start=' . $start; | ||
} | ||
|
||
// Perform the search. | ||
$result = $this->httpGet($uri, join('&', $queryString)); | ||
|
||
// Sort the results. | ||
if (!empty($sort)) { | ||
static::sortSearchResult($result, $sort); | ||
} | ||
|
||
return $result; | ||
} | ||
|
||
/** | ||
* Chef::getNodes() | ||
* | ||
* Retrieve the list of nodes serviced by the Chef server. | ||
* | ||
* @return array The list of nodes known to the Chef server. | ||
*/ | ||
public function getNodes() { | ||
$uri = '/nodes'; | ||
return $this->httpGet($uri); | ||
} | ||
|
||
/** | ||
* Chef::getNode() | ||
* | ||
* Retrieve the information associated with a specific node. | ||
* | ||
* @param string $nodeName The name of the node. | ||
* | ||
* @return array An associative array containing all known info about the node. | ||
*/ | ||
public function getNode($nodeName) { | ||
$uri = '/nodes/' . $nodeName; | ||
return $this->httpGet($uri); | ||
} | ||
|
||
/** | ||
* Chef::getNodeRunList() | ||
* | ||
* Retrieve the run list of a specific node. | ||
* | ||
* @param string $nodeName The name of the node. | ||
* | ||
* @return array A list of the roles and recipes in the node's run list. | ||
*/ | ||
public function getNodeRunList($nodeName) { | ||
$uri = '/nodes/' . $nodeName . '/cookbooks'; | ||
return $this->httpGet($uri); | ||
} | ||
|
||
/** | ||
* Chef::getRoles() | ||
* | ||
* Retrieve the list of roles. | ||
* | ||
* @return array The list of roles. | ||
*/ | ||
public function getRoles() { | ||
$uri = '/roles'; | ||
return $this->httpGet($uri); | ||
} | ||
|
||
/** | ||
* Chef::getRole() | ||
* | ||
* Retrieve the specified role information. | ||
* | ||
* @param string $roleName The name of the role. | ||
* | ||
* @return array The roles and recipes contained in the specified role. | ||
*/ | ||
public function getRole($roleName) { | ||
$uri = '/roles/' . $roleName; | ||
return $this->httpGet($uri); | ||
} | ||
|
||
/** | ||
* Chef::getCookbooks() | ||
* | ||
* @return array A list of cookbooks. | ||
*/ | ||
public function getCookbooks() { | ||
$uri = '/cookbooks'; | ||
return $this->httpGet($uri); | ||
} | ||
|
||
/** | ||
* Chef::getCookbook() | ||
* | ||
* Get the specified cookbook's data. | ||
* | ||
* @param string $cookbookName The name of the cookbook. | ||
* | ||
* @return array An array containing all the cookbook data. | ||
*/ | ||
public function getCookbook($cookbookName) { | ||
$uri = '/cookbooks/' . $cookbookName; | ||
return $this->httpGet($uri); | ||
} | ||
|
||
/** | ||
* Chef::getDataBags() | ||
* | ||
* Get a list of the data bags on the Chef server. | ||
* | ||
* @return array A list of the data bags on the Chef Server. | ||
*/ | ||
public function getDataBags() { | ||
$uri = '/data'; | ||
return $this->httpGet($uri); | ||
} | ||
|
||
/** | ||
* Chef::getDataBagItems() | ||
* | ||
* Retrieve a list of the items in a data bag. | ||
* | ||
* @param string $dataBagName The name of the data bag. | ||
* | ||
* @return array A list of the items stored in the specified data bag. | ||
*/ | ||
public function getDataBagItems($dataBagName) { | ||
$uri = '/data/' . $dataBagName; | ||
return $this->httpGet($uri); | ||
} | ||
|
||
/** | ||
* Chef::getDataBagItem() | ||
* | ||
* Get the contents of a data bag item. | ||
* | ||
* @param string $dataBagName The name of the data bag containing the item. | ||
* @param string $itemName The name of the data bag item. | ||
* | ||
* @return array The contents of the data bag item. | ||
*/ | ||
public function getDataBagItem($dataBagName, $itemName) { | ||
$uri = '/data/' . $dataBagName . '/' . $itemName; | ||
return $this->httpGet($uri); | ||
} | ||
|
||
/** | ||
* Chef::httpGet() | ||
* | ||
* Perform a GET request to the Chef server. | ||
* | ||
* @param string $uri The request URI. | ||
* @param string $queryString The query string (for searching). | ||
* | ||
* @return array An associative array generated from the JSON response. | ||
*/ | ||
private function httpGet($uri, $queryString = '') { | ||
|
||
$headers = $this->requestHeaders($uri, 'GET', '', $this->userId, $this->privateKey); | ||
$headers['X-Chef-Version'] = $this->version; | ||
$headers['Accept'] = 'application/json'; | ||
|
||
if (!empty($queryString)) { | ||
$uri .= '?' . $queryString; | ||
} | ||
|
||
$request = new \HttpRequest( | ||
'http://' . $this->host . ':' . $this->port . $uri, | ||
HTTP_METH_GET, | ||
array( | ||
'headers' => $headers | ||
) | ||
); | ||
|
||
$response = $request->send(); | ||
return json_decode($response->getBody(), true); | ||
} | ||
|
||
/** | ||
* Chef::requestHeaders() | ||
* | ||
* Generate the encrypted header entries for the Chef request. | ||
* | ||
* @param string $uri The request URI. | ||
* @param string $httpMethod The HTTP request method GET, or PUT. PUT not implemented yet. | ||
* @param string $body The body of the request. | ||
* @param string $userId The Chef server client name. | ||
* @param string $privateKey The RSA private key for the client. | ||
* | ||
* @return array The headers specific to Chef server requests. | ||
*/ | ||
private static function requestHeaders($uri, $httpMethod, $body, $userId, $privateKey) { | ||
|
||
$timestamp = new \DateTime(null, new \DateTimeZone('UTC')); | ||
$timestamp = substr($timestamp->format(\DateTime::ISO8601), 0, -5) . 'Z'; | ||
$hashedBody = base64_encode(sha1($body, true)); | ||
|
||
$headers = array( | ||
'X-Ops-Sign' => 'version=1.0', | ||
'X-Ops-Userid' => $userId, | ||
'X-Ops-TimeStamp' => $timestamp, | ||
'X-Ops-Content-Hash' => $hashedBody | ||
); | ||
|
||
$reqSig = static::requestSignature($uri, $httpMethod, $hashedBody, $timestamp, $userId, $privateKey); | ||
|
||
for ($index = 0; $index < strlen($reqSig); $index += 60) { | ||
$key = 'X-Ops-Authorization-' . (string) (($index / 60) + 1); | ||
$headers[$key] = substr($reqSig, $index, 60); | ||
} | ||
|
||
return $headers; | ||
} | ||
|
||
/** | ||
* Chef::requestSignature() | ||
* | ||
* @param string $uri The request URI. | ||
* @param string $httpMethod The HTTP request method GET, or PUT. PUT not implemented yet. | ||
* @param string $hashedBody The hashed (base64 encoded SHA1) body of the request. | ||
* @param string $timestamp ISO 8601 format using T as the separator. The timezone must be UTC, using Z as the indicator. eg: 2010-12-04T15:47:49Z | ||
* @param string $userId The Chef server client name. | ||
* @param string $privateKey The RSA private key for the client. | ||
*/ | ||
private static function requestSignature($uri, $httpMethod, $hashedBody, $timestamp, $userId, $privateKey) { | ||
|
||
$httpMethod = strtoupper($httpMethod); | ||
$hashedUri = base64_encode(sha1($uri, true)); | ||
|
||
$reqSig = | ||
"Method:$httpMethod\n" . | ||
"Hashed Path:$hashedUri\n" . | ||
"X-Ops-Content-Hash:$hashedBody\n" . | ||
"X-Ops-Timestamp:$timestamp\n" . | ||
"X-Ops-UserId:$userId"; | ||
|
||
openssl_private_encrypt($reqSig, $crypted, $privateKey); | ||
return base64_encode($crypted); | ||
} | ||
|
||
/** | ||
* Chef::arrayFlatten() | ||
* | ||
* Create a flattened copy of an array. | ||
* | ||
* @param array $arr The array to flatten. Duplicate keys will be overwritten. | ||
* | ||
* @return array A copy of the array that has been flattened. | ||
*/ | ||
private static function arrayFlatten(array $arr) { | ||
$output = array(); | ||
|
||
array_walk_recursive( | ||
$arr, | ||
function($value, $key) use (&$output) { | ||
$output[$key] = $value; | ||
} | ||
); | ||
|
||
return $output; | ||
} | ||
|
||
private static function arrayKeySearch($key, $arr) { | ||
|
||
$arrIt = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($arr)); | ||
|
||
foreach ($arrIt as $sub) { | ||
$subArray = $arrIt->getSubIterator(); | ||
|
||
if (array_key_exists($key, $subArray)) { | ||
return $subArray[$key]; | ||
} | ||
} | ||
|
||
return null; | ||
} | ||
|
||
private static function sortSearchResult(&$arr, $sortBy) { | ||
|
||
$values = array(); | ||
|
||
foreach ($arr['rows'] as $element) { | ||
$values[] = static::arrayKeySearch($sortBy, $element); | ||
} | ||
|
||
array_multisort($values, $arr['rows']); | ||
} | ||
|
||
} | ||
} | ||
|
||
?> |
Empty file.