This repository has been archived by the owner on Apr 16, 2024. It is now read-only.
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
Showing
4 changed files
with
329 additions
and
0 deletions.
There are no files selected for viewing
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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,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) |
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,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(), | ||
); | ||
} | ||
} |
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,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); | ||
} | ||
} |