diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -880,10 +880,12 @@ 'DrydockResourceViewController' => 'applications/drydock/controller/DrydockResourceViewController.php', 'DrydockSFTPFilesystemInterface' => 'applications/drydock/interface/filesystem/DrydockSFTPFilesystemInterface.php', 'DrydockSSHCommandInterface' => 'applications/drydock/interface/command/DrydockSSHCommandInterface.php', + 'DrydockSetupCheckWinRM' => 'applications/drydock/check/DrydockSetupCheckWinRM.php', 'DrydockSlotLock' => 'applications/drydock/storage/DrydockSlotLock.php', 'DrydockSlotLockException' => 'applications/drydock/exception/DrydockSlotLockException.php', 'DrydockSlotLockFailureLogType' => 'applications/drydock/logtype/DrydockSlotLockFailureLogType.php', 'DrydockWebrootInterface' => 'applications/drydock/interface/webroot/DrydockWebrootInterface.php', + 'DrydockWinRMCommandInterface' => 'applications/drydock/interface/command/DrydockWinRMCommandInterface.php', 'DrydockWorker' => 'applications/drydock/worker/DrydockWorker.php', 'DrydockWorkingCopyBlueprintImplementation' => 'applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php', 'FeedConduitAPIMethod' => 'applications/feed/conduit/FeedConduitAPIMethod.php', @@ -4633,10 +4635,12 @@ 'DrydockResourceViewController' => 'DrydockResourceController', 'DrydockSFTPFilesystemInterface' => 'DrydockFilesystemInterface', 'DrydockSSHCommandInterface' => 'DrydockCommandInterface', + 'DrydockSetupCheckWinRM' => 'PhabricatorSetupCheck', 'DrydockSlotLock' => 'DrydockDAO', 'DrydockSlotLockException' => 'Exception', 'DrydockSlotLockFailureLogType' => 'DrydockLogType', 'DrydockWebrootInterface' => 'DrydockInterface', + 'DrydockWinRMCommandInterface' => 'DrydockCommandInterface', 'DrydockWorker' => 'PhabricatorWorker', 'DrydockWorkingCopyBlueprintImplementation' => 'DrydockBlueprintImplementation', 'FeedConduitAPIMethod' => 'ConduitAPIMethod', diff --git a/src/applications/drydock/check/DrydockSetupCheckWinRM.php b/src/applications/drydock/check/DrydockSetupCheckWinRM.php new file mode 100644 --- /dev/null +++ b/src/applications/drydock/check/DrydockSetupCheckWinRM.php @@ -0,0 +1,41 @@ + 'https://github.com/masterzen/winrm'), + 'https://github.com/masterzen/winrm'), + phutil_tag('tt', array(), 'PATH')); + + $message = pht( + 'You only need this binary if you are leasing Windows hosts in '. + 'Drydock or Harbormaster. If you don\'t need to run commands on '. + 'Windows machines, you can safely ignore this message.'); + + $this->newIssue('bin.winrm') + ->setShortName(pht("'%s' Missing", 'winrm')) + ->setName(pht("Missing '%s' Binary", 'winrm')) + ->setSummary( + pht("The '%s' binary could not be located or executed.", 'winrm')) + ->setMessage(pht("%s\n\n%s", $preamble, $message)); + } + + } + +} diff --git a/src/applications/drydock/interface/command/DrydockWinRMCommandInterface.php b/src/applications/drydock/interface/command/DrydockWinRMCommandInterface.php new file mode 100644 --- /dev/null +++ b/src/applications/drydock/interface/command/DrydockWinRMCommandInterface.php @@ -0,0 +1,76 @@ +passphraseWinRMPassword !== null) { + return; + } + + $credential = id(new PassphraseCredentialQuery()) + ->setViewer(PhabricatorUser::getOmnipotentUser()) + ->withIDs(array($this->getConfig('credential'))) + ->needSecrets(true) + ->executeOne(); + + if ($credential->getProvidesType() !== + PassphrasePasswordCredentialType::PROVIDES_TYPE) { + throw new Exception('Only password credentials are supported.'); + } + + $this->passphraseWinRMPassword = PassphrasePasswordKey::loadFromPHID( + $credential->getPHID(), + PhabricatorUser::getOmnipotentUser()); + } + + public function getExecFuture($command) { + $this->openCredentialsIfNotOpen(); + + $argv = func_get_args(); + + $change_directory = ''; + if ($this->getWorkingDirectory() !== null) { + $working_directory = $this->getWorkingDirectory(); + if (strlen($working_directory) >= 2 && $working_directory[1] === ':') { + // We must also change drive. + $drive = $working_directory[0]; + $change_directory .= 'cd '.$working_directory.' & '.$drive.': & '; + } else { + $change_directory .= 'cd '.$working_directory.' & '; + } + } + + // Encode the command to run under Powershell. + $command = id(new PhutilCommandString($argv)) + ->setEscapingMode(PhutilCommandString::MODE_POWERSHELL); + + // When Microsoft says "Unicode" they don't mean UTF-8. + $command = mb_convert_encoding($command, 'UTF-16LE'); + $command = base64_encode($command); + + $powershell = + 'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe'; + $powershell .= + ' -ExecutionPolicy Bypass'. + ' -NonInteractive'. + ' -InputFormat Text'. + ' -OutputFormat Text'. + ' -EncodedCommand '.$command; + + return new ExecFuture( + 'winrm '. + '-hostname=%s '. + '-username=%P '. + '-password=%P '. + '-port=%s '. + '%s', + $this->getConfig('host'), + $this->passphraseWinRMPassword->getUsernameEnvelope(), + $this->passphraseWinRMPassword->getPasswordEnvelope(), + $this->getConfig('port'), + $change_directory.$powershell); + } +}