Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F14870618
D7582.id17445.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
13 KB
Referenced Files
None
Subscribers
None
D7582.id17445.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D7582: Implement "Upload Artifact" build step
Attached
Detach File
Event Timeline
Log In to Comment