scalr/fatmouse-phpclient

此软件包已被弃用且不再维护。没有建议的替代软件包。

Fatmouse PHP 客户端

dev-master / 1.0.x-dev 2016-09-19 18:35 UTC

README

FatMouse 为 Scalr 提供了一个 PHP 客户端,用于集成。

composer install

测试

测试在 devbox 中执行。

配置 devbox

sudo -E fab -f provision/linux/fabfile.py pull retag phpclient

启动 Fatmouse

  • 本地使用 docker-compose up
  • 或远程设置 FAM_PHPCLIENT_BROKER_URL

运行套件

cd phpclient
./vendor/bin/phpunit -v tests/large

示例

初始化 PHP 客户端

<?php
$broker = "amqp://fatmouse-api.scalr-labs.com";
$fatmouse = new Fatmouse\Fatmouse($broker);

调用服务器任务。通用

<?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();
}

调用服务器任务。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());
}

在一个进程中调用服务器任务,在其他进程中获取结果

进程 1

<?php
$asyncResult = $fatmouse->callAsync($taskName, $params);
$taskId = $asyncResult->getTaskId();
// Then pass $taskId to Process 2

进程 2

<?php
$asyncResult = $fatmouse->newAsyncResult($taskId, $taskName);
$task = $asyncResult->wait();
try {
    $task->getResult();
}
finally {
    $asyncResult->ack();  
}

调用 Scalarizr 任务。通用

<?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);

调用 Scalarizr 任务。Sugar

<?php
$fatmouse->agent($serverId)->sys->setHostname("my.example.com");

// control timeout
$fatmouse->agent($serverId, ['timeout' => 30])->sys->setHostname("my.example.com");

消费事件

<?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());
    }  
}

在 FatMouse 中注册 Scalr 管理服务器

Fatmouse 应该知道 Scalr 启动和终止的每个服务器。

当 Scalr 即将启动新服务器时,它应该调用 registerServer()

然后,处理结果数据

  • $reg->agentConfig 导出为 JSON 并将其注入到云服务器中的 /etc/scalr/agent.json
  • $reg->agentConfig['CELERY']['BROKER_URL'] 中编辑主机名到公 DNS 名称

要运行 Scalr 管理服务器,需要在它上安装代理。这可以通过 CloudInit 轻松委托。取 user-data.tpl.sh,将其中的 %AGENT_JSON% 替换为 JSON 数据,并将结果脚本作为用户数据拉取到云提供商的创建服务器 API 调用中。您使用的镜像应该支持 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);

当 Scalr 终止服务器时,它应该调用 deregisterServer()。任务没有结果值

<?php
// When Scalr terminates server
$fatmouse->deregisterServer();

执行工作流

如果您不熟悉工作流,请 阅读简介

为了初始化服务器,Scalr 应该运行 init 工作流。

<?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
}

调用代理

可以直接调用代理任务而无需工作流。这对于从 Scalr 以 RPC 方式直接获取各种 OS 信息、日志、度量很有用。

此示例将获取服务器 CPU 统计信息

<?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)
}
*/

接收事件

FatMouse 触发各种事件,应该被监听和处理。

处理服务器重启

<?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);
}

处理编排工作流进度

<?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);
}

处理工作流完成事件

<?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);
}

完整的服务器初始化过程(HostInit -> HostUp)

1. 注册新服务器。

<?php
$reg = $fatmouse->registerServer($serverId)->wait()->getResult();

2. 在云中启动服务器

在云中启动服务器,并在用户数据脚本中将 $reg->agentConfig 传递给服务器代理。 更多关于服务器注册信息

3. 等待 AuthAgent 任务完成

authenticate_server 任务接收 task_finished 事件。 了解如何接收事件

<?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. 编排 HostInit

创建 HostInit 事件对象并执行 orchestration 工作流。

<?php
// TODO: TBD $orc format
$ar = $fatmouse->workflows()->orchestration($orc);
$result = $ar->wait()->getResult();

了解如何设置回调以异步处理[工作流结果](#Handle Workflow Finished Event)。同样适用于后续步骤。

5. 执行 "init" 工作流

执行 "init" 工作流以初始化服务器。(HostInitResponse 和 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$result 包含类似于 HostInitResponse / HostUp 消息的数据

6. 编排 BeforeHostUp

创建 BeforeHostUp 事件对象并执行 orchestration 工作流。

服务器正在运行!Scalr 将服务器添加到 DNS,并执行内部 HostUp 程序

7. 编排 HostUp

创建 HostUp 事件对象并执行 orchestration 工作流。