Page MenuHomePhabricator

D7582.id17445.diff
No OneTemporary

D7582.id17445.diff

Index: src/__phutil_library_map__.php
===================================================================
--- src/__phutil_library_map__.php
+++ src/__phutil_library_map__.php
@@ -642,6 +642,7 @@
'DrydockConstants' => 'applications/drydock/constants/DrydockConstants.php',
'DrydockController' => 'applications/drydock/controller/DrydockController.php',
'DrydockDAO' => 'applications/drydock/storage/DrydockDAO.php',
+ 'DrydockFilesystemInterface' => 'applications/drydock/interface/filesystem/DrydockFilesystemInterface.php',
'DrydockInterface' => 'applications/drydock/interface/DrydockInterface.php',
'DrydockLease' => 'applications/drydock/storage/DrydockLease.php',
'DrydockLeaseListController' => 'applications/drydock/controller/DrydockLeaseListController.php',
@@ -668,6 +669,7 @@
'DrydockResourceQuery' => 'applications/drydock/query/DrydockResourceQuery.php',
'DrydockResourceStatus' => 'applications/drydock/constants/DrydockResourceStatus.php',
'DrydockResourceViewController' => 'applications/drydock/controller/DrydockResourceViewController.php',
+ 'DrydockSFTPFilesystemInterface' => 'applications/drydock/interface/filesystem/DrydockSFTPFilesystemInterface.php',
'DrydockSSHCommandInterface' => 'applications/drydock/interface/command/DrydockSSHCommandInterface.php',
'DrydockWebrootInterface' => 'applications/drydock/interface/webroot/DrydockWebrootInterface.php',
'DrydockWorkingCopyBlueprintImplementation' => 'applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php',
@@ -2332,6 +2334,7 @@
'SleepBuildStepImplementation' => 'applications/harbormaster/step/SleepBuildStepImplementation.php',
'SlowvoteEmbedView' => 'applications/slowvote/view/SlowvoteEmbedView.php',
'SlowvoteRemarkupRule' => 'applications/slowvote/remarkup/SlowvoteRemarkupRule.php',
+ 'UploadArtifactBuildStepImplementation' => 'applications/harbormaster/step/UploadArtifactBuildStepImplementation.php',
'VariableBuildStepImplementation' => 'applications/harbormaster/step/VariableBuildStepImplementation.php',
),
'function' =>
@@ -2987,6 +2990,7 @@
'DrydockCommandInterface' => 'DrydockInterface',
'DrydockController' => 'PhabricatorController',
'DrydockDAO' => 'PhabricatorLiskDAO',
+ 'DrydockFilesystemInterface' => 'DrydockInterface',
'DrydockLease' => 'DrydockDAO',
'DrydockLeaseListController' => 'DrydockController',
'DrydockLeaseQuery' => 'PhabricatorOffsetPagedQuery',
@@ -3016,6 +3020,7 @@
'DrydockResourceQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'DrydockResourceStatus' => 'DrydockConstants',
'DrydockResourceViewController' => 'DrydockController',
+ 'DrydockSFTPFilesystemInterface' => 'DrydockFilesystemInterface',
'DrydockSSHCommandInterface' => 'DrydockCommandInterface',
'DrydockWebrootInterface' => 'DrydockInterface',
'DrydockWorkingCopyBlueprintImplementation' => 'DrydockBlueprintImplementation',
@@ -4973,6 +4978,7 @@
'SleepBuildStepImplementation' => 'BuildStepImplementation',
'SlowvoteEmbedView' => 'AphrontView',
'SlowvoteRemarkupRule' => 'PhabricatorRemarkupRuleObject',
+ 'UploadArtifactBuildStepImplementation' => 'VariableBuildStepImplementation',
'VariableBuildStepImplementation' => 'BuildStepImplementation',
),
));
Index: src/applications/drydock/blueprint/DrydockPreallocatedHostBlueprintImplementation.php
===================================================================
--- src/applications/drydock/blueprint/DrydockPreallocatedHostBlueprintImplementation.php
+++ src/applications/drydock/blueprint/DrydockPreallocatedHostBlueprintImplementation.php
@@ -105,6 +105,12 @@
'port' => $resource->getAttribute('port'),
'credential' => $resource->getAttribute('credential'),
'platform' => $resource->getAttribute('platform')));
+ case 'filesystem':
+ return id(new DrydockSFTPFilesystemInterface())
+ ->setConfiguration(array(
+ 'host' => $resource->getAttribute('host'),
+ 'port' => $resource->getAttribute('port'),
+ 'credential' => $resource->getAttribute('credential')));
}
throw new Exception("No interface of type '{$type}'.");
Index: src/applications/drydock/interface/filesystem/DrydockFilesystemInterface.php
===================================================================
--- /dev/null
+++ src/applications/drydock/interface/filesystem/DrydockFilesystemInterface.php
@@ -0,0 +1,24 @@
+<?php
+
+abstract class DrydockFilesystemInterface extends DrydockInterface {
+
+ final public function getInterfaceType() {
+ return 'filesystem';
+ }
+
+ /**
+ * Reads a file on the Drydock resource and returns the contents of the file.
+ */
+ abstract public function readFile($path);
+
+ /**
+ * Reads a file on the Drydock resource and saves it as a PhabricatorFile.
+ */
+ abstract public function saveFile($path, $name);
+
+ /**
+ * Writes a file to the Drydock resource.
+ */
+ abstract public function writeFile($path, $data);
+
+}
Index: src/applications/drydock/interface/filesystem/DrydockSFTPFilesystemInterface.php
===================================================================
--- /dev/null
+++ src/applications/drydock/interface/filesystem/DrydockSFTPFilesystemInterface.php
@@ -0,0 +1,63 @@
+<?php
+
+final class DrydockSFTPFilesystemInterface extends DrydockFilesystemInterface {
+
+ private $passphraseSSHKey;
+
+ private function openCredentialsIfNotOpen() {
+ if ($this->passphraseSSHKey !== null) {
+ return;
+ }
+
+ $credential = id(new PassphraseCredentialQuery())
+ ->setViewer(PhabricatorUser::getOmnipotentUser())
+ ->withIDs(array($this->getConfig('credential')))
+ ->needSecrets(true)
+ ->executeOne();
+
+ if ($credential->getProvidesType() !==
+ PassphraseCredentialTypeSSHPrivateKey::PROVIDES_TYPE) {
+ throw new Exception("Only private key credentials are supported.");
+ }
+
+ $this->passphraseSSHKey = PassphraseSSHKey::loadFromPHID(
+ $credential->getPHID(),
+ PhabricatorUser::getOmnipotentUser());
+ }
+
+ private function getExecFuture($path) {
+ $this->openCredentialsIfNotOpen();
+
+ return new ExecFuture(
+ 'sftp -o "StrictHostKeyChecking no" -P %s -i %P %P@%s',
+ $this->getConfig('port'),
+ $this->passphraseSSHKey->getKeyfileEnvelope(),
+ $this->passphraseSSHKey->getUsernameEnvelope(),
+ $this->getConfig('host'));
+ }
+
+ public function readFile($path) {
+ $target = new TempFile();
+ $future = $this->getExecFuture($path);
+ $future->write(csprintf("get %s %s", $path, $target));
+ $future->resolvex();
+ return Filesystem::readFile($target);
+ }
+
+ public function saveFile($path, $name) {
+ $data = $this->readFile($path);
+ $file = PhabricatorFile::newFromFileData($data);
+ $file->setName($name);
+ $file->save();
+ return $file;
+ }
+
+ public function writeFile($path, $data) {
+ $source = new TempFile();
+ Filesystem::writeFile($source, $data);
+ $future = $this->getExecFuture($path);
+ $future->write(csprintf("put %s %s", $source, $path));
+ $future->resolvex();
+ }
+
+}
Index: src/applications/drydock/query/DrydockBlueprintQuery.php
===================================================================
--- src/applications/drydock/query/DrydockBlueprintQuery.php
+++ src/applications/drydock/query/DrydockBlueprintQuery.php
@@ -56,7 +56,7 @@
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
- 'phid IN (%Ld)',
+ 'phid IN (%Ls)',
$this->phids);
}
Index: src/applications/harbormaster/step/CommandBuildStepImplementation.php
===================================================================
--- src/applications/harbormaster/step/CommandBuildStepImplementation.php
+++ src/applications/harbormaster/step/CommandBuildStepImplementation.php
@@ -32,30 +32,9 @@
$settings['command'],
$variables);
- $artifact = id(new HarbormasterBuildArtifactQuery())
- ->setViewer(PhabricatorUser::getOmnipotentUser())
- ->withArtifactKeys(
- $build->getPHID(),
- array($settings['hostartifact']))
- ->executeOne();
- if ($artifact === null) {
- throw new Exception("Associated Drydock host artifact not found!");
- }
-
- $data = $artifact->getArtifactData();
+ $artifact = $build->loadArtifact($settings['hostartifact']);
- // FIXME: Is there a better way of doing this?
- $lease = id(new DrydockLease())->load(
- $data['drydock-lease']);
- if ($lease === null) {
- throw new Exception("Associated Drydock lease not found!");
- }
- $resource = id(new DrydockResource())->load(
- $lease->getResourceID());
- if ($resource === null) {
- throw new Exception("Associated Drydock resource not found!");
- }
- $lease->attachResource($resource);
+ $lease = $artifact->loadDrydockLease();
$interface = $lease->getInterface('command');
Index: src/applications/harbormaster/step/UploadArtifactBuildStepImplementation.php
===================================================================
--- /dev/null
+++ src/applications/harbormaster/step/UploadArtifactBuildStepImplementation.php
@@ -0,0 +1,97 @@
+<?php
+
+final class UploadArtifactBuildStepImplementation
+ extends VariableBuildStepImplementation {
+
+ public function getName() {
+ return pht('Upload Artifact');
+ }
+
+ public function getGenericDescription() {
+ return pht('Upload an artifact from a Drydock host to Phabricator.');
+ }
+
+ public function getDescription() {
+ $settings = $this->getSettings();
+
+ return pht(
+ 'Upload artifact located at \'%s\' on \'%s\'.',
+ $settings['path'],
+ $settings['hostartifact']);
+ }
+
+ public function execute(
+ HarbormasterBuild $build,
+ HarbormasterBuildTarget $build_target) {
+
+ $settings = $this->getSettings();
+ $variables = $build_target->getVariables();
+
+ $path = $this->mergeVariables(
+ 'vsprintf',
+ $settings['path'],
+ $variables);
+
+ $artifact = $build->loadArtifact($settings['hostartifact']);
+
+ $lease = $artifact->loadDrydockLease();
+
+ $interface = $lease->getInterface('filesystem');
+
+ // TODO: Handle exceptions.
+ $file = $interface->saveFile($path, $settings['name']);
+
+ // Insert the artifact record.
+ $artifact = $build->createArtifact(
+ $build_target,
+ $settings['name'],
+ HarbormasterBuildArtifact::TYPE_FILE);
+ $artifact->setArtifactData(array(
+ 'filePHID' => $file->getPHID()));
+ $artifact->save();
+ }
+
+ public function validateSettings() {
+ $settings = $this->getSettings();
+
+ if ($settings['path'] === null || !is_string($settings['path'])) {
+ return false;
+ }
+ if ($settings['name'] === null || !is_string($settings['name'])) {
+ return false;
+ }
+ if ($settings['hostartifact'] === null ||
+ !is_string($settings['hostartifact'])) {
+ return false;
+ }
+
+ // TODO: Check if the host artifact is provided by previous build steps.
+
+ return true;
+ }
+
+ public function getSettingDefinitions() {
+ return array(
+ 'path' => array(
+ 'name' => 'Path',
+ 'description' =>
+ 'The path of the file that should be retrieved. Note that on '.
+ 'Windows machines running FreeSSHD, this path will be relative '.
+ 'to the SFTP root path (configured under the SFTP tab). You can '.
+ 'not specify an absolute path for Windows machines.',
+ 'type' => BuildStepImplementation::SETTING_TYPE_STRING),
+ 'name' => array(
+ 'name' => 'Local Name',
+ 'description' =>
+ 'The name for the file when it is stored in Phabricator.',
+ 'type' => BuildStepImplementation::SETTING_TYPE_STRING),
+ 'hostartifact' => array(
+ 'name' => 'Host Artifact',
+ 'description' =>
+ 'The host artifact that determines what machine the command '.
+ 'will run on.',
+ 'type' => BuildStepImplementation::SETTING_TYPE_ARTIFACT,
+ 'artifact_type' => HarbormasterBuildArtifact::TYPE_HOST));
+ }
+
+}
Index: src/applications/harbormaster/storage/build/HarbormasterBuild.php
===================================================================
--- src/applications/harbormaster/storage/build/HarbormasterBuild.php
+++ src/applications/harbormaster/storage/build/HarbormasterBuild.php
@@ -119,6 +119,19 @@
return $artifact;
}
+ public function loadArtifact($name) {
+ $artifact = id(new HarbormasterBuildArtifactQuery())
+ ->setViewer(PhabricatorUser::getOmnipotentUser())
+ ->withArtifactKeys(
+ $this->getPHID(),
+ array($name))
+ ->executeOne();
+ if ($artifact === null) {
+ throw new Exception("Artifact not found!");
+ }
+ return $artifact;
+ }
+
/**
* Checks for and handles build cancellation. If this method returns
* true, the caller should stop any current operations and return control
Index: src/applications/harbormaster/storage/build/HarbormasterBuildArtifact.php
===================================================================
--- src/applications/harbormaster/storage/build/HarbormasterBuildArtifact.php
+++ src/applications/harbormaster/storage/build/HarbormasterBuildArtifact.php
@@ -72,6 +72,30 @@
}
}
+ public function loadDrydockLease() {
+ if ($this->getArtifactType() !== self::TYPE_HOST) {
+ throw new Exception(
+ "`loadDrydockLease` may only be called on host artifacts.");
+ }
+
+ $data = $this->getArtifactData();
+
+ // FIXME: Is there a better way of doing this?
+ $lease = id(new DrydockLease())->load(
+ $data['drydock-lease']);
+ if ($lease === null) {
+ throw new Exception("Associated Drydock lease not found!");
+ }
+ $resource = id(new DrydockResource())->load(
+ $lease->getResourceID());
+ if ($resource === null) {
+ throw new Exception("Associated Drydock resource not found!");
+ }
+ $lease->attachResource($resource);
+
+ return $lease;
+ }
+
/* -( PhabricatorPolicyInterface )----------------------------------------- */

File Metadata

Mime Type
text/plain
Expires
Sun, Feb 9, 3:32 AM (21 h, 12 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7104976
Default Alt Text
D7582.id17445.diff (13 KB)

Event Timeline