diff --git a/src/HPCloud/Services/DBaaS.php b/src/HPCloud/Services/DBaaS.php index 35ca8a5..93e435c 100644 --- a/src/HPCloud/Services/DBaaS.php +++ b/src/HPCloud/Services/DBaaS.php @@ -49,7 +49,7 @@ class DBaaS { public static function newFromIdentity($identity) { - $endpoint = 'https://region-a.geo-1.dbaas-mysql.hpcloudsvc.com:443/v1.0/' . $identity->tenantId(); + $endpoint = 'https://db-aw2az2-api0001.uswest.hpcloud.net:8779/v1.0/' . $identity->tenantId(); $dbaas = new DBaaS($identity->token(), $endpoint, $identity->tenantName()); return $dbaas; diff --git a/src/HPCloud/Services/DBaaS/Instance.php b/src/HPCloud/Services/DBaaS/Instance.php index 5e3f6f0..46a3c67 100644 --- a/src/HPCloud/Services/DBaaS/Instance.php +++ b/src/HPCloud/Services/DBaaS/Instance.php @@ -27,31 +27,107 @@ SOFTWARE. namespace HPCloud\Services\DBaaS; +use \HPCloud\Transport; + class Instance { protected $token; protected $projectId; protected $url; + protected $client; public function __construct($token, $projectId, $endpoint) { $this->token = $token; $this->projectId = $projectId; $this->url = $endpoint; + $this->client = Transport::instance(); } public function describe($instanceId) { + $url = sprintf('%s/instances/%s', $this->url, $instanceId); + $res = $this->client->doRequest($url, 'GET', $this->headers()); + + $json = json_decode($res->content(), TRUE); + return InstanceDetails::newFromJSON($json); } public function listInstances() { + $url = $this->url . '/instances'; + $res = $this->client->doRequest($url, 'GET', $this->headers()); + $json = json_decode($res->content(), TRUE); + + $list = array(); + foreach ($json['instances'] as $instance) { + $list[] = InstanceDetails::newFromJSON($instance); + } + return $list; } + /** + * Create a new database. + * + * This creates a database tuned according to the $flavor settings. The + * return data will include login/password and connection information. + * + * @attention + * This is the only time that the login and password info will be returned. + * + * @param string $name + * The name of the database. + * @param string $flavor + * The string flavor name. Known values are: + *- small + *- medium + * @param int $port + * If this is not specified, the default is used. + * @param array $typeSpec + * A typespec array. + * @retval object HPCloud::Services::DBaaS::InstanceDetails + * The details of creation, including login and password info. + * @see http://api-docs.hpcloud.com/hpcloud-dbaas/1.0/content/instance-create.html + */ public function create($name, $flavor = 'medium', $port = NULL, $typeSpec = NULL) { + // Set type spec. As of the initial release of DBaaS, the only support + // type is mysql 5.5. + if (empty($typeSpec)) { + $typeSpec = array( + 'name' => 'mysql', + 'version' => '5.5', + ); + } + $json = array( + 'instance' => array( + 'name' => $name, + 'flavorRef' => $flavor, + 'dbtype' => $typeSpec, + ), + ); + if (isset($port)) { + $json['instance']['port'] = $port; + } + + $url = $this->url . '/instances'; + $postData = json_encode($json); + $length = strlen($postData); + $headers = $this->headers(array('Accept' => 'application/json', 'Content-length' => $length)); + $res = $this->client->doRequest($url, 'POST', $headers, $postData); + + $results = json_decode($res->content(), TRUE); + + return InstanceDetails::newFromJSON($results); } public function delete($instanceId) { + $url = sprintf('%s/instances/%s', $this->url, $instanceId); + $this->client->doRequest($url, 'DELETE', $this->headers()); + return TRUE; } public function restart($instanceId) { + $url = sprintf('%s/instances/%s/restart', $this->url, $instanceId); + $headers = $this->headers(array('Content-Length' => '0')); + $this->client->doRequest($url, 'POST', $headers); + return TRUE; } /** @@ -61,7 +137,18 @@ class Instance { * The new (autogenerated) password. */ public function resetPassword($instanceId) { + $url = sprintf('%s/instances/%s/resetpassword', $this->url, $instanceId); + $headers = $this->headers(array('Content-Length' => '0')); + $res = $this->client->doRequest($url, 'POST', $headers); + $json = json_decode($res); + + return $json->password; } - + protected function headers($add = array()) { + return $add + array( + 'X-Auth-Token' => $this->token, + 'X-Auth-Project-Id' => $this->projectId, + ); + } } diff --git a/src/HPCloud/Services/DBaaS/InstanceDetails.php b/src/HPCloud/Services/DBaaS/InstanceDetails.php index b36e682..3361c8d 100644 --- a/src/HPCloud/Services/DBaaS/InstanceDetails.php +++ b/src/HPCloud/Services/DBaaS/InstanceDetails.php @@ -28,4 +28,100 @@ SOFTWARE. namespace HPCloud\Services\DBaaS; class InstanceDetails { + + protected $name; + protected $id; + protected $links; + protected $created; + protected $status; + protected $hostname; + protected $port; + + protected $username; + protected $password; + + public function newFromJSON($json) { + + $o = new InstanceDetails($json['name'], $json['id']); + $o->links = $json['links']; + $o->created = $json['created']; + $o->status = $json['status']; + $o->hostname = $json['hostname']; + $o->port= !empty($json['port']) ? $json['port'] : '3306'; + + if (!empty($json['credential']['username'])) { + $o->username = $json['credential']['username']; + } + if (!empty($json['credential']['password'])) { + $o->username = $json['credential']['pasword']; + } + + + + + } + + public function __construct($name, $id) { + } + + public function createdOn() { + return $this->created; + } + + public function status() { + return $this->status; + } + + public function hostname() { + return $this->hostname; + } + + public function port() { + return $this->port; + } + + public function username() { + return $this->username; + } + public function password() { + return $this->password; + } + public function links() { + return $this->links; + } + + /** + * Get the DSN to connect to the database instance. + * + * A convenience function for PDO. + * + * @see http://us3.php.net/manual/en/ref.pdo-mysql.connection.php + * + * @param string $dbName + * The name of the database to connect to. If none is specified, + * this will be left off of the DSN. + * @param string $charset + * This will attempt to set the character set. Not all versions + * of PHP use this. + * + * @retval string + * The DSN, including driver, host, port, and database name. + * @todo + * At this time, 'mysql' is hard-coded as the driver name. Does this + * need to change? + */ + public function dsn($dbName = NULL, $charset = NULL) { + $dsn = sprintf('mysql:host=%s;port=%s', $this->hostname(), $this->port()); + if (!empty($dbName)) { + $dsn .= ';dbname=' . $dbName; + } + if (!empty($charset)) { + $dsn .= ';charset=' . $charset; + } + + return $dsn; + + } + + } diff --git a/test/Tests/DBaaSInstanceTest.php b/test/Tests/DBaaSInstanceTest.php index 58e2249..46b4595 100644 --- a/test/Tests/DBaaSInstanceTest.php +++ b/test/Tests/DBaaSInstanceTest.php @@ -29,29 +29,201 @@ namespace HPCloud\Tests\Services\DBaaS; require_once 'src/HPCloud/Bootstrap.php'; require_once 'test/TestCase.php'; +use \HPCloud\Services\DBaaS; use \HPCloud\Services\DBaaS\Instance; +/** + * @group dbaas + */ class DBaaSInstanceTest extends \HPCloud\Tests\TestCase { + public function inst() { + $ident = $this->identity(); + $dbaas = DBaaS::newFromIdentity($ident); + return $dbaas->instance(); + } + + public function destroyDatabase() { + $inst = $this->inst(); + $list = $inst->listInstances(); + + fwrite(STDOUT, print_r($list, TRUE)); + if (!empty($list['instances'])) { + $dbName = self::conf('hpcloud.dbaas.database'); + foreach ($list['instances'] as $item) { + if ($item['name'] == $dbName) { + fprintf(STDOUT, "Deleting %s (%s)\n", $item['name'], $item['id']); + $inst->delete($item['id']); + } + } + } + + } + public function testConstruct() { + $ident = $this->identity(); + $dbaas = DBaaS::newFromIdentity($ident); + + $endpoint = self::conf('hpcloud.dbaas.endpoint') . '/' . $ident->tenantId(); + + // Test #1: Build from scratch. + $inst = new Instance($ident->token(), $ident->tenantName(), $endpoint); + $this->assertInstanceOf('\HPCloud\Services\DBaaS\Instance', $inst); + + // Test #2: Build from DBaaS. + $inst = $dbaas->instance(); + $this->assertInstanceOf('\HPCloud\Services\DBaaS\Instance', $inst); } public function testCreate() { + // Make sure there aren't old fixtures hanging around from a + // failed run. + $this->destroyDatabase(); + + $dbName = self::conf('hpcloud.dbaas.database'); + + $details = $this->inst()->create($dbName, 'small', '3307'); + + $this->assertInstanceOf('\HPCloud\Services\DBaaS\InstanceDetails', $details); + + $this->assertNotEmpty($details->username()); + $this->assertNotEmpty($details->password()); + $this->assertNotEmpty($details->id()); + $this->assertNotEmpty($details->hostname()); + $this->assertNotEmpty($details->port()); + $this->assertNotEmpty($details->createdOn()); + $this->assertEquals($dbName, $details->name()); + + $dsn = sprint('mysql:host=%s;port=3307;dbname=foo;charset=utf-8', $details->hostname()); + + $this->assertEquals($dsn, $details->dsn('foo', 'utf-8')); + + $this->credentials = array( + 'name' => $details->username(), + 'pass' => $details->password(), + ); + $this->dbId = $details->id(); + $this->created = $details->createdOn(); } + /** + * @depends testCreate + */ public function testDescribe() { + $dbName = self::conf('hpcloud.dbaas.database'); + + // Canary. + $this->assertNotEmpty($this->dbId); + + $details = $this->inst()->describe($this->dbId); + $this->assertInstanceOf('\HPCloud\Services\DBaaS\InstanceDetails', $details); + + $this->assertEmpty($details->username()); + $this->assertEmpty($details->password()); + + $this->assertNotEmpty($details->hostname()); + $this->assertNotEmpty($details->createdOn()); + + $this->assertEquals($this->dbId, $details->id()); + $this->assertEquals($dbName, $details->name()); + } + /** + * @depends testCreate + */ public function testRestart() { + // Canary. + $this->assertNotEmpty($this->dbId); + + $this->inst()->restart($this->dbId); + sleep(5); + $details = $this->inst()->details($this->dbId); + + $this->assertEquals($this->created, $details->createdOn()); } + /** + * @depends testCreate + */ public function testResetPassword() { + $pass = $this->credentials['pass']; + $this->assertNotEmpty($pass); + + $newPass = $this->inst()->resetPassword($this->dbId); + + $this->assertNotEmpty($newPass); + $this->assertNotEquals($pass, $newPass); } + /** + * @depends testCreate + */ public function testListInstances() { + + $instances = $this->inst()->listInstances(); + + $this->assertNotEmpty($instances); + $this->assertGreaterThan(0, count($instances['instances'])); + + $match = 0; + $dbName = self::conf('hpcloud.dbaas.database'); + + foreach ($instances['instances'] as $server) { + $this->assertInstanceOf('\HPCloud\Services\DBaaS\InstanceDetails', $server); + $this->assertNotEmpty($server->id()); + if ($server->name() == $dbName) { + ++$match; + } + } + $this->assertEquals(1, $match); } + /** + * @depends testListInstances + */ + public function testIsItAllWorthIt() { + $inst = $this->inst(); + + $maxAttempts = 5; + for($i = 0; $i < $maxAttempts; ++$i) { + $details = $inst->describe($this->dbId); + + if ($details->status == 'ready') { + $dsn = $details->dsn(); + break; + } + + } + + $this->assertNotEmpty($dsn); + + $conn = new PDO($dsn, $this->credentials['user'], $this->credentials['pass']); + + $affected = $conn->execute('SELECT 1'); + + $this->assertEquals(0, $affected); + + unset($conn); + } + + /** + * @depends testCreate + */ public function testDelete() { + $match = 0; + $dbName = self::conf('hpcloud.dbaas.database'); + + $inst = $this->inst(); + + $inst->delete($this->dbId); + + foreach ($instances['instances'] as $server) { + if ($server->name() == $dbName) { + ++$match; + } + } + $this->assertEquals(0, $match); } } diff --git a/test/Tests/DBaaSSnapshotTest.php b/test/Tests/DBaaSSnapshotTest.php index cb94531..4c544ec 100644 --- a/test/Tests/DBaaSSnapshotTest.php +++ b/test/Tests/DBaaSSnapshotTest.php @@ -31,7 +31,10 @@ require_once 'test/TestCase.php'; use \HPCloud\Services\DBaaS\Snapshot; -class DBaaSSnapshotextends \HPCloud\Tests\TestCase { +/** + * @group dbaas + */ +class DBaaSSnapshot extends \HPCloud\Tests\TestCase { public function testConstruct() { } diff --git a/test/Tests/DBaaSTest.php b/test/Tests/DBaaSTest.php index 85fbbf2..fb3f52d 100644 --- a/test/Tests/DBaaSTest.php +++ b/test/Tests/DBaaSTest.php @@ -31,6 +31,9 @@ require_once 'test/TestCase.php'; use \HPCloud\Services\DBaaS; +/** + * @group dbaas + */ class DBaaSTest extends \HPCloud\Tests\TestCase { protected function dbaas() { @@ -70,7 +73,7 @@ class DBaaSTest extends \HPCloud\Tests\TestCase { public function testConstructor() { $ident = $this->identity(); - $dbaas = new DBaaS($ident->token(), self::$settings['hpcloud.dbaas.endpoint'], $ident->tenantName()); + $dbaas = new DBaaS($ident->token(), self::conf('hpcloud.dbaas.endpoint'), $ident->tenantName()); $this->assertInstanceOf("\HPCloud\Services\DBaaS", $dbaas); $this->assertEquals($ident->tenantName(), $dbaas->projectId());