scalr / fatmouse-phpclient
Fatmouse PHP Client
Installs: 2 389
Dependents: 0
Suggesters: 0
Security: 0
Stars: 1
Watchers: 31
Forks: 3
Open Issues: 1
Requires
- php-amqplib/php-amqplib: 2.6.3
Requires (Dev)
- phpunit/phpunit: 5.0.10
- squizlabs/php_codesniffer: 2.5.0
This package is not auto-updated.
Last update: 2017-10-05 04:36:37 UTC
README
FatMouse provides a PHP client for integration with Scalr.
composer install
Tests
Tests are executed in devbox.
Provision devbox
sudo -E fab -f provision/linux/fabfile.py pull retag phpclient
Start Fatmouse
- localy with
docker-compose up
- or remotely and set
FAM_PHPCLIENT_BROKER_URL
Run suite
cd phpclient
./vendor/bin/phpunit -v tests/large
Examples
Initialize PHP Client
<?php $broker = "amqp://fatmouse-api.scalr-labs.com"; $fatmouse = new Fatmouse\Fatmouse($broker);
Call Server Task. Generic
<?php use Fatmouse\Errors; $timeout = 30 $taskName = "register_server"; $params = [ "server_id" => "cf3a320b-7ac6-4b88-810a-c760a94e1875", "env_id" => "7743d825-9792-46bc-827f-285882f58fb2" ]; $asyncResult = $fatmouse->callAsync($taskName, $params); try { $task = $asyncResult->wait(); var_dump($task->getResult()); } finally { // Acknowledge result to remove it from queue $asyncResult->ack(); }
Call Server Task. Sugar
<?php use Fatmouse\Errors; try { $asyncResult = $fatmouse->registerServer($serverId, $envId); try { $reg = $asyncResult->wait()->getResult(); print("Server registered!") var_dump($reg->agentConfig); } finally { $asyncResult->ack(); } } catch (\Exception $e) { printf("Server registration failed %s: %s", get_class($e), $e->getMessage()); }
Call Server Task In One Process, Get Result In Other
Process 1:
<?php $asyncResult = $fatmouse->callAsync($taskName, $params); $taskId = $asyncResult->getTaskId(); // Then pass $taskId to Process 2
Process 2:
<?php $asyncResult = $fatmouse->newAsyncResult($taskId, $taskName); $task = $asyncResult->wait(); try { $task->getResult(); } finally { $asyncResult->ack(); }
Call Scalarizr Task. Generic
<?php $serverId = "cf3a320b-7ac6-4b88-810a-c760a94e1875"; $taskName = "sys.set_hostname"; $params = ["hostname" => "my.example.com"]; $timeout = 5; $result = $fatmouse->callAgentSync($serverId, $taskName, $params, $timeout);
Call Scalarizr Task. Sugar
<?php $fatmouse->agent($serverId)->sys->setHostname("my.example.com"); // control timeout $fatmouse->agent($serverId, ['timeout' => 30])->sys->setHostname("my.example.com");
Consume Events
<?php $callback = function ($event) { printf( "Received event %s (ID: %s) with payload: %s", $event->name, $event->eventId, var_export($event->getPayload(), true) ); // After result was handled, it should be acknowledged to prevent repeating handling $event->ack(); } $fatmouse->initEventConsumer($callback); $timeout = 1; while (true) { try { // poll for event until timeout $fatmouse->consumeEvent($timeout); } catch (\Exception $e) { print("Event loop error: %s" . $e->getMessage()); } }
Register Scalr Managed Server In FatMouse
Fatmouse should know about each server started and terminated by Scalr.
When Scalr is going to launch new server, it should call registerServer()
.
Then, process result data:
- Dump
$reg->agentConfig
into JSON and inject into cloud server as/etc/scalr/agent.json
- Edit hostname in
$reg->agentConfig['CELERY']['BROKER_URL']
to a public DNS name
To run a Scalr managed server, it's required to install agent on it. It's easy to delegate this to CloudInit. Take the user-data.tpl.sh, replace there %AGENT_JSON%
with the JSON data, and pull down the result script as user-data to cloud provider's create server API call. Image you're using should support cloud-init (Ubuntu, Amazon Linux)
<?php // When Scalr has launched new server $asyncResult = $fatmouse->registerServer($serverId, $envId); $reg = $asyncResult->wait()->getResult(); // agent.json data var_dump($reg->agentConfig);
When Scalr has terminated server, it should call deregisterServer()
. Task has no result value
<?php // When Scalr terminates server $fatmouse->deregisterServer();
Execute Workflows
If you're unfamiliar with workflows, read an introduction
To initialize server Scalr should run init
workflow.
<?php use \Fatmouse\Errors; $asyncResult = $fatmouse->workflows()->init($params); $init = $asyncResult->wait(); try { var_dump($init->getResult()); // output workflow result if ($init->isHalfCompleted()) { foreach ($init->failedTasks as $task) { printf('Task "%s" %s(%s) error: %s', $task->title, $task->name, $task->taskId, $task->getException()->getMessage() ); } } catch (Errors\ServerException $e) { // handle error printf('Workflow %s(%s) error: %s', $init->getName(), $init->getTaskId(), $e->getMessage() ); } finally { $asyncResult->ack(); // Acknowlenge result and remove it from queue }
Call Agent
It's possible to call agent tasks directly without workflows. It's useful for getting various OS information, logs, metrics directly from Scalr in a RPC fashion.
This example will get server CPU statistic
<?php use Fatmouse\Agent\Fact; $serverId = 'd20dc26c-6220-4df8-996a-92f09d710524'; $stat = $fatmouse->agent($serverId)->sys->getFact(Fact::STAT_CPU); var_dump($stat); /* Output array(4) { ["nice"]=> int(0) ["user"]=> int(8416) ["system"]=> int(6754) ["idle"]=> int(147309) } */
Receive Events
FatMouse fires various events, that should be listened and handled.
Handle Server Reboot
<?php use Fatmouse\Events; $callback = function ($event) { $pay = $event->getPayload(); if ($pay instanceof Events\ServerRebooted)) { // Server was rebooted printf( 'Server %s was rebooted at %s, and new boot_id is: %s', $pay->serverId, $event->firedAt, $pay->bootId ); } else { // default event handler } // acknowledge event, so it'll be never received by other workers // all received events should be acknowledged, or they will come again. $event->ack(); } $timeout = 1; $fatmouse->initEventConsumer($callback); while (true) { $fatmouse->consumeEvent($timeout); }
Handle Orchestration Workflow Progress
<?php use Fatmouse\Agent; use Fatmouse\Events; $asyncResult = $fatmouse->workflows()->orchestration(...); $workflowId = $asyncResult->getTaskId(); $callback = function ($event) { $pay = $ev->getPayload(); // track scripts and chef tasks status if ($pay instanceof Events\TaskFinished && $pay->workflow && $pay->workflow->taskId == $workflowId && in_array($pay->task->name, [ Agent\TaskName::EXECUTE, Agent\TaskName::CHEF_SOLO, Agent\TaskName::CHEF_CLIENT ]) ) { // Push notification to js // // Example output: // [orchestration] task chef.solo(deploy application) on 83888...4f9368a COMPLETED // [orchestration] task execute(reload crontab) on d9f82a12...740a28 COMPLETED // [orchestration] task execute(danger staff) on d9f82a12...740a28 FAILED // E: Command exited with non-zero code 2 $js->pushStatus(sprintf( '[%s] task %(%s) on server %s %s', $pay->task->workflow->name, $pay->task->name, $pay->task->title, $pay->serverId, strtoupper($pay->task->status) )); if ($pay->task->isFailed()) { $js->pushStatus(sprintf( 'error: %s', $pay->task->getException()->getMessage() )); } $event->ack(); // acknowledge event! } } $timeout = 1; $fatmouse->initEventConsumer($callback); while (true) { $fatmouse->consumeEvent($timeout); }
Handle Workflow Finished Event
<?php use Fatmouse\Events; use Fatmouse\Errors; use Fatmouse\Workflows; $callback = function ($event) { $pay = $event->getPayload(); if ($pay instanceof Events\WorkflowFinished) { try { $pay->workflow->throwForState(); if ($pay->workflow instanceof Workflows\AgentUpdate) { // agent updated. proceed to orchestration(HostInit) } elseif ($pay->workflow instanceof Workflows\Init) { // initialization completed $pay->workflow->getResult(); // HostUp like data } elseif ($pay->workflow instanceof Workflows\Orchestration) { // orchestration completed } elseif ($pay->workflow instanceof Workflows\Volume) { // storage volume created, attached and configured } } catch (Errors\ServerException $e) { // handle workflow error printf('Workflow %s(%s) error: %s', $pay->workflow->name, $pay->workflow->taskId, $e->getMessage() ); } $event->ack(); // acknowledge event! } } $timeout = 1; $fatmouse->initEventConsumer($callback); while (true) { $fatmouse->consumeEvent($timeout); }
The Complete Server Initialization Process (HostInit -> HostUp)
1. Register New Server.
<?php $reg = $fatmouse->registerServer($serverId)->wait()->getResult();
2. Launch Server In a Cloud
Launch server in a cloud and pass $reg->agentConfig
to server agent in a user-data script. More about server registration
3. Wait For AuthAgent Task Completion
Receive task_finished
event from authenticate_server
task. Read how to receive events
<?php use Fatmouse\Events; use Fatmouse\Errors; use Fatmouse\Tasks; $callback = function ($event) { $pay = $event->getPayload(); if ($pay instanceof Events\TaskFinished && $pay->task instanceof Tasks\AuthenticateServer ) { try { $pay->task->throwForState(); printf( 'Server %s successfully authenticated', $pay->task->serverId ); } catch (Errors\ServerException $e) { printf('Server %s authentication error: %s', $pay->task->serverId, $e->getMessage()) } $event->ack(); // acknowledge event! } }
4. Orchestrate HostInit
Create HostInit
event object and execute orchestration
workflow.
<?php // TODO: TBD $orc format $ar = $fatmouse->workflows()->orchestration($orc); $result = $ar->wait()->getResult();
See how to setup callback to [handle workflow result](#Handle Workflow Finished Event) asynchronously. The same is valid for next steps.
5. Execute "init" workflow
Execute "init" workflow to initialize server. (Phase between HostInitResponse and BeforeHostUp)
<?php use \Fatmouse\Types; $init = new Types\InitializationParams() ->withHostName($hostname) ->andVolume($ephemeral_D) ->andVolume($ebs_E) ->andChefAction(new Types\Orchestration\ChefSoloAction() ->withJsonAttributes($jsonAttributes) ->andCookbooks(new Types\Orchestration\CookbookUrlSource() ->withUrl($httpsCookbooksTarGz))); $ar = $fatmouse->workflows()->init($init) $result = $ar->wait()->getResult();
$init
and $result
contain data similar to HostInitResponse
/ HostUp
messages
6. Orchestrate BeforeHostUp
Create BeforeHostUp
event object and execute orchestration
workflow.
Server is running! Scalr adds server to DNS, and execute internal HostUp routines
7. Orchestrate HostUp
Create HostUp
event object and execute orchestration
workflow.
orchestration
workflow.