Skip to content
This repository has been archived by the owner on Apr 16, 2024. It is now read-only.

Commit

Permalink
Merge 3ea06c2 into 2973698
Browse files Browse the repository at this point in the history
  • Loading branch information
reyostallenberg committed Apr 20, 2017
2 parents 2973698 + 3ea06c2 commit 890b3f7
Show file tree
Hide file tree
Showing 4 changed files with 329 additions and 0 deletions.
Binary file added docs/images/event-flows/LinkTask.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
37 changes: 37 additions & 0 deletions docs/tasks/LinkTask.md
@@ -0,0 +1,37 @@
# LinkTask

Creates symlinks to the configured directories.

## Configuration options

| Name | Type | Default value | Description |
|------|------|---------------|-------------|
| links | array | | The links to create. |

### Configure the permissions for directories
Example configuration:
```json
{
"class": "Accompli\\Task\\LinkTask",
"links": {
"location/within/release": "../../../data/%stage%/target/to/point/to",
"another/location/within/release": "%data%/another/target/to/point/to"
}
}
```

Make sure that the targets configured exist.
The option otherDirectories in [CreateWorkspace](CreateWorkspaceTask.md) task can help with that.

### Variables
There are some predefined variables that are replaced before targeting the link.
These are:

| Variable | Description |
|----------|-------------|
| %data% | The data directory |
| %stage% | The stage currently being installed to |
| %version% | The version currently being installed |

# Event flow
![Flowchart with highlighted events the LinkTask is listening to](../images/event-flows/LinkTask.png)
132 changes: 132 additions & 0 deletions src/Task/LinkTask.php
@@ -0,0 +1,132 @@
<?php

namespace Accompli\Task;

use Accompli\AccompliEvents;
use Accompli\Deployment\Release;
use Accompli\EventDispatcher\Event\InstallReleaseEvent;
use Accompli\EventDispatcher\Event\LogEvent;
use Accompli\EventDispatcher\EventDispatcherInterface;
use Accompli\Exception\TaskRuntimeException;
use Psr\Log\LogLevel;

/**
* LinkTask.
*
* @author Reyo Stallenberg <reyo@connectholland.nl>
*/
class LinkTask extends AbstractConnectedTask
{
/**
* The array with paths to link.
*
* @var array
*/
private $links;

/**
* The array with environment variables to use in the paths.
*
* @var array
*/
private $environmentVariables;

/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return array(
AccompliEvents::INSTALL_RELEASE => array(
array('onInstallReleaseCreateLinks', 0),
),
);
}

/**
* Constructs a new LinkTask.
*
* @param array $links
*/
public function __construct(array $links)
{
$this->links = $links;
}

/**
* Creates the configured links.
*
* @param InstallReleaseEvent $event
* @param string $eventName
* @param EventDispatcherInterface $eventDispatcher
*
* @throws TaskRuntimeException
*/
public function onInstallReleaseCreateLinks(InstallReleaseEvent $event, $eventName, EventDispatcherInterface $eventDispatcher)
{
$release = $event->getRelease();
$host = $release->getWorkspace()->getHost();
$connection = $this->ensureConnection($host);

$eventDispatcher->dispatch(AccompliEvents::LOG, new LogEvent(LogLevel::NOTICE, 'Linking paths...', $eventName, $this, array('event.task.action' => TaskInterface::ACTION_IN_PROGRESS)));

$this->gatherEnvironmentVariables($release);

$releasePath = $release->getPath();
foreach ($this->links as $target => $source) {
$source = strtr($source, $this->environmentVariables);

$targetPath = $releasePath.'/'.$target;
$sourcePath = $releasePath.'/'.$source;

$context = array('target' => $target, 'source' => $source, 'event.task.action' => TaskInterface::ACTION_IN_PROGRESS);

$eventDispatcher->dispatch(AccompliEvents::LOG, new LogEvent(LogLevel::INFO, 'Creating link in "{target}" to "{source}".', $eventName, $this, $context));

if ($connection->isFile($sourcePath) === false && $connection->isDirectory($sourcePath) === false && $connection->isLink($sourcePath) === false) {
$context['event.task.action'] = TaskInterface::ACTION_FAILED;
$context['output.resetLine'] = true;

$eventDispatcher->dispatch(AccompliEvents::LOG, new LogEvent(LogLevel::WARNING, 'Failed creating link for "{target}" because "{source}" does not exist.', $eventName, $this, $context));

throw new TaskRuntimeException('Failed linking paths.', $this);
}

if ($connection->isLink($targetPath) === false || $connection->readLink($targetPath) !== $sourcePath) {
$connection->delete($targetPath);

if ($connection->link($sourcePath, $targetPath)) {
$context['event.task.action'] = TaskInterface::ACTION_COMPLETED;
$context['output.resetLine'] = true;

$eventDispatcher->dispatch(AccompliEvents::LOG, new LogEvent(LogLevel::INFO, 'Created link in "{target}" to "{source}".', $eventName, $this, $context));

continue;
} else {
$context['event.task.action'] = TaskInterface::ACTION_FAILED;
$context['output.resetLine'] = true;

$eventDispatcher->dispatch(AccompliEvents::LOG, new LogEvent(LogLevel::WARNING, 'Failed creating link for "{target}".', $eventName, $this, $context));
}

throw new TaskRuntimeException('Failed linking paths.', $this);
}
}

$eventDispatcher->dispatch(AccompliEvents::LOG, new LogEvent(LogLevel::NOTICE, 'Linked paths.', $eventName, $this, array('event.task.action' => TaskInterface::ACTION_COMPLETED, 'output.resetLine' => true)));
}

/**
* Gathers environment variables to use in the paths.
*
* @param Release $release
*/
private function gatherEnvironmentVariables(Release $release)
{
$this->environmentVariables = array(
'%data%' => $release->getWorkspace()->getDataDirectory(),
'%stage%' => $release->getWorkspace()->getHost()->getStage(),
'%version%' => $release->getVersion(),
);
}
}
160 changes: 160 additions & 0 deletions tests/Task/LinkTaskTest.php
@@ -0,0 +1,160 @@
<?php

namespace Accompli\Test\Task;

use Accompli\AccompliEvents;
use Accompli\Deployment\Connection\ConnectionAdapterInterface;
use Accompli\Deployment\Host;
use Accompli\Deployment\Release;
use Accompli\Deployment\Workspace;
use Accompli\EventDispatcher\Event\InstallReleaseEvent;
use Accompli\EventDispatcher\EventDispatcherInterface;
use Accompli\Exception\TaskRuntimeException;
use Accompli\Task\LinkTask;
use PHPUnit_Framework_TestCase;

/**
* LinkTaskTest.
*
* @author Reyo Stallenberg <reyo@connectholland.nl>
*/
class LinkTaskTest extends PHPUnit_Framework_TestCase
{
/**
* Tests if LinkTask::getSubscribedEvents returns an array with a AccompliEvents::INSTALL_RELEASE key.
*/
public function testGetSubscribedEvents()
{
$this->assertInternalType('array', LinkTask::getSubscribedEvents());
$this->assertArrayHasKey(AccompliEvents::INSTALL_RELEASE, LinkTask::getSubscribedEvents());
}

/**
* Tests if constructing a new LinkTask sets the instance property.
*/
public function testOnConstruct()
{
$links = array('var/log', 'log');
$task = new LinkTask($links);

$this->assertAttributeSame($links, 'links', $task);
}

/**
* Tests if LinkTask::onPrepareReleaseCreateLinks throws an exception if the result is false.
*/
public function testOnInstallReleaseCreateLinksThrowsRuntimeException()
{
$eventDispatcherMock = $this->getMockBuilder(EventDispatcherInterface::class)
->getMock();

$connectionAdapterMock = $this->getMockBuilder(ConnectionAdapterInterface::class)
->getMock();

$hostMock = $this->getMockBuilder(Host::class)
->disableOriginalConstructor()
->getMock();
$hostMock->expects($this->once())
->method('hasConnection')
->willReturn(true);
$hostMock->expects($this->once())
->method('getConnection')
->willReturn($connectionAdapterMock);
$hostMock->expects($this->once())
->method('getStage')
->willReturn('test');

$workspaceMock = $this->getMockBuilder(Workspace::class)
->disableOriginalConstructor()
->getMock();
$workspaceMock->expects($this->exactly(2))
->method('getHost')
->willReturn($hostMock);
$workspaceMock->expects($this->once())
->method('getDataDirectory')
->willReturn('/data/directory');

$releaseMock = $this->getMockBuilder(Release::class)
->disableOriginalConstructor()
->getMock();
$releaseMock->expects($this->exactly(1))
->method('getPath')
->willReturn('{workspace}/0.1.0');
$releaseMock->expects($this->exactly(1))
->method('getVersion')
->willReturn('0.1.0');
$releaseMock->expects($this->exactly(3))
->method('getWorkspace')
->willReturn($workspaceMock);

$event = new InstallReleaseEvent($releaseMock);

$this->setExpectedException(TaskRuntimeException::class, 'Failed linking paths.');

$links = array('invalid' => false);

$task = new LinkTask($links);
$task->onInstallReleaseCreateLinks($event, AccompliEvents::INSTALL_RELEASE, $eventDispatcherMock);
}

/**
* Tests if LinkTask::onPrepareReleaseCreateLinks works if the correct settings are applied.
*/
public function testOnPrepareReleaseCreateLinks()
{
$eventDispatcherMock = $this->getMockBuilder(EventDispatcherInterface::class)
->getMock();

$connectionAdapterMock = $this->getMockBuilder(ConnectionAdapterInterface::class)
->getMock();
$connectionAdapterMock->expects($this->exactly(2))
->method('isLink')
->willReturn(true);
$connectionAdapterMock->expects($this->exactly(2))
->method('link')
->willReturn(true);

$hostMock = $this->getMockBuilder(Host::class)
->disableOriginalConstructor()
->getMock();
$hostMock->expects($this->once())
->method('hasConnection')
->willReturn(true);
$hostMock->expects($this->once())
->method('getConnection')
->willReturn($connectionAdapterMock);
$hostMock->expects($this->once())
->method('getStage')
->willReturn('test');

$workspaceMock = $this->getMockBuilder(Workspace::class)
->disableOriginalConstructor()
->getMock();
$workspaceMock->expects($this->exactly(2))
->method('getHost')
->willReturn($hostMock);
$workspaceMock->expects($this->once())
->method('getDataDirectory')
->willReturn('/data/directory');

$releaseMock = $this->getMockBuilder(Release::class)
->disableOriginalConstructor()
->getMock();
$releaseMock->expects($this->exactly(1))
->method('getPath')
->willReturn('{workspace}/0.1.0');
$releaseMock->expects($this->exactly(1))
->method('getVersion')
->willReturn('0.1.0');
$releaseMock->expects($this->exactly(3))
->method('getWorkspace')
->willReturn($workspaceMock);

$event = new InstallReleaseEvent($releaseMock);

$links = array('var/log', 'log');

$task = new LinkTask($links);
$task->onInstallReleaseCreateLinks($event, AccompliEvents::INSTALL_RELEASE, $eventDispatcherMock);
}
}

0 comments on commit 890b3f7

Please sign in to comment.