mirror of
https://we.phorge.it/source/phorge.git
synced 2025-02-07 20:38:32 +01:00
(stable) Promote 2015 Week 40
This commit is contained in:
commit
f760b3ec98
152 changed files with 5065 additions and 1830 deletions
1
bin/garbage
Symbolic link
1
bin/garbage
Symbolic link
|
@ -0,0 +1 @@
|
|||
../scripts/setup/manage_garbage.php
|
|
@ -151,7 +151,7 @@ return array(
|
|||
'rsrc/css/phui/phui-two-column-view.css' => '39ecafb1',
|
||||
'rsrc/css/phui/phui-workboard-view.css' => '6704d68d',
|
||||
'rsrc/css/phui/phui-workpanel-view.css' => 'adec7699',
|
||||
'rsrc/css/sprite-login.css' => '1ebb9bf9',
|
||||
'rsrc/css/sprite-login.css' => '60e8560e',
|
||||
'rsrc/css/sprite-main-header.css' => 'f07bbb87',
|
||||
'rsrc/css/sprite-menu.css' => '9dd65b92',
|
||||
'rsrc/css/sprite-projects.css' => 'e5ad842a',
|
||||
|
@ -310,8 +310,8 @@ return array(
|
|||
'rsrc/image/people/washington.png' => '40dd301c',
|
||||
'rsrc/image/phrequent_active.png' => 'a466a8ed',
|
||||
'rsrc/image/phrequent_inactive.png' => 'bfc15a69',
|
||||
'rsrc/image/sprite-login-X2.png' => 'a4bf0a98',
|
||||
'rsrc/image/sprite-login.png' => '5f4d0069',
|
||||
'rsrc/image/sprite-login-X2.png' => 'e3991e37',
|
||||
'rsrc/image/sprite-login.png' => '03d5af29',
|
||||
'rsrc/image/sprite-main-header.png' => '3673af44',
|
||||
'rsrc/image/sprite-menu-X2.png' => 'cfd8fca5',
|
||||
'rsrc/image/sprite-menu.png' => 'd7a99faa',
|
||||
|
@ -817,7 +817,7 @@ return array(
|
|||
'releeph-request-differential-create-dialog' => '8d8b92cd',
|
||||
'releeph-request-typeahead-css' => '667a48ae',
|
||||
'setup-issue-css' => 'db7e9c40',
|
||||
'sprite-login-css' => '1ebb9bf9',
|
||||
'sprite-login-css' => '60e8560e',
|
||||
'sprite-main-header-css' => 'f07bbb87',
|
||||
'sprite-menu-css' => '9dd65b92',
|
||||
'sprite-projects-css' => 'e5ad842a',
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1,000 B |
Binary file not shown.
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.2 KiB |
|
@ -9,7 +9,7 @@
|
|||
"login-Asana": {
|
||||
"name": "login-Asana",
|
||||
"rule": ".login-Asana",
|
||||
"hash": "f8d322843355da1abce614983044c0f8"
|
||||
"hash": "cfc35b62afb103c5d484a715071e3cc3"
|
||||
},
|
||||
"login-Bitbucket": {
|
||||
"name": "login-Bitbucket",
|
||||
|
@ -131,6 +131,6 @@
|
|||
1,
|
||||
2
|
||||
],
|
||||
"header": "\/**\n * @provides sprite-login-css\n * @generated\n *\/\n\n.sprite-login {\n background-image: url(\/rsrc\/image\/sprite-login.png);\n background-repeat: no-repeat;\n}\n\n@media\nonly screen and (min-device-pixel-ratio: 1.5),\nonly screen and (-webkit-min-device-pixel-ratio: 1.5),\nonly screen and (min-resolution: 1.5dppx) {\n .sprite-login {\n background-image: url(\/rsrc\/image\/sprite-login-X2.png);\n background-size: {X}px {Y}px;\n }\n}\n",
|
||||
"header": "/**\n * @provides sprite-login-css\n * @generated\n */\n\n.sprite-login {\n background-image: url(/rsrc/image/sprite-login.png);\n background-repeat: no-repeat;\n}\n\n@media\nonly screen and (min-device-pixel-ratio: 1.5),\nonly screen and (-webkit-min-device-pixel-ratio: 1.5),\nonly screen and (min-resolution: 1.5dppx) {\n .sprite-login {\n background-image: url(/rsrc/image/sprite-login-X2.png);\n background-size: {X}px {Y}px;\n }\n}\n",
|
||||
"type": "standard"
|
||||
}
|
||||
|
|
2
resources/sql/autopatches/20150928.drydock.rexpire.1.sql
Normal file
2
resources/sql/autopatches/20150928.drydock.rexpire.1.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_drydock.drydock_resource
|
||||
ADD until INT UNSIGNED;
|
25
resources/sql/autopatches/20150930.drydock.log.1.sql
Normal file
25
resources/sql/autopatches/20150930.drydock.log.1.sql
Normal file
|
@ -0,0 +1,25 @@
|
|||
TRUNCATE {$NAMESPACE}_drydock.drydock_log;
|
||||
|
||||
ALTER TABLE {$NAMESPACE}_drydock.drydock_log
|
||||
DROP resourceID;
|
||||
|
||||
ALTER TABLE {$NAMESPACE}_drydock.drydock_log
|
||||
DROP leaseID;
|
||||
|
||||
ALTER TABLE {$NAMESPACE}_drydock.drydock_log
|
||||
DROP message;
|
||||
|
||||
ALTER TABLE {$NAMESPACE}_drydock.drydock_log
|
||||
ADD blueprintPHID VARBINARY(64);
|
||||
|
||||
ALTER TABLE {$NAMESPACE}_drydock.drydock_log
|
||||
ADD resourcePHID VARBINARY(64);
|
||||
|
||||
ALTER TABLE {$NAMESPACE}_drydock.drydock_log
|
||||
ADD leasePHID VARBINARY(64);
|
||||
|
||||
ALTER TABLE {$NAMESPACE}_drydock.drydock_log
|
||||
ADD type VARCHAR(64) NOT NULL COLLATE {$COLLATE_TEXT};
|
||||
|
||||
ALTER TABLE {$NAMESPACE}_drydock.drydock_log
|
||||
ADD data LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT};
|
2
resources/sql/autopatches/20151001.drydock.rname.1.sql
Normal file
2
resources/sql/autopatches/20151001.drydock.rname.1.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_drydock.drydock_resource
|
||||
DROP name;
|
|
@ -0,0 +1,2 @@
|
|||
UPDATE {$NAMESPACE}_dashboard.dashboard
|
||||
SET status = 'active' WHERE status = 'open';
|
|
@ -0,0 +1,5 @@
|
|||
ALTER TABLE {$NAMESPACE}_harbormaster.harbormaster_build
|
||||
ADD buildParameters LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL;
|
||||
|
||||
UPDATE {$NAMESPACE}_harbormaster.harbormaster_build
|
||||
SET buildParameters = '{}' WHERE buildParameters = '';
|
File diff suppressed because one or more lines are too long
21
scripts/setup/manage_garbage.php
Executable file
21
scripts/setup/manage_garbage.php
Executable file
|
@ -0,0 +1,21 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
$root = dirname(dirname(dirname(__FILE__)));
|
||||
require_once $root.'/scripts/__init_script__.php';
|
||||
|
||||
$args = new PhutilArgumentParser($argv);
|
||||
$args->setTagline(pht('manage garbage colletors'));
|
||||
$args->setSynopsis(<<<EOSYNOPSIS
|
||||
**garbage** __command__ [__options__]
|
||||
Manage garbage collectors.
|
||||
|
||||
EOSYNOPSIS
|
||||
);
|
||||
$args->parseStandardArguments();
|
||||
|
||||
$workflows = id(new PhutilClassMapQuery())
|
||||
->setAncestorClass('PhabricatorGarbageCollectorManagementWorkflow')
|
||||
->execute();
|
||||
$workflows[] = new PhutilHelpArgumentWorkflow();
|
||||
$args->parseWorkflows($workflows);
|
|
@ -796,7 +796,6 @@ phutil_register_library_map(array(
|
|||
'DoorkeeperSchemaSpec' => 'applications/doorkeeper/storage/DoorkeeperSchemaSpec.php',
|
||||
'DoorkeeperTagView' => 'applications/doorkeeper/view/DoorkeeperTagView.php',
|
||||
'DoorkeeperTagsController' => 'applications/doorkeeper/controller/DoorkeeperTagsController.php',
|
||||
'DrydockAllocatorWorker' => 'applications/drydock/worker/DrydockAllocatorWorker.php',
|
||||
'DrydockAlmanacServiceHostBlueprintImplementation' => 'applications/drydock/blueprint/DrydockAlmanacServiceHostBlueprintImplementation.php',
|
||||
'DrydockApacheWebrootInterface' => 'applications/drydock/interface/webroot/DrydockApacheWebrootInterface.php',
|
||||
'DrydockBlueprint' => 'applications/drydock/storage/DrydockBlueprint.php',
|
||||
|
@ -830,25 +829,33 @@ phutil_register_library_map(array(
|
|||
'DrydockFilesystemInterface' => 'applications/drydock/interface/filesystem/DrydockFilesystemInterface.php',
|
||||
'DrydockInterface' => 'applications/drydock/interface/DrydockInterface.php',
|
||||
'DrydockLease' => 'applications/drydock/storage/DrydockLease.php',
|
||||
'DrydockLeaseAcquiredLogType' => 'applications/drydock/logtype/DrydockLeaseAcquiredLogType.php',
|
||||
'DrydockLeaseActivatedLogType' => 'applications/drydock/logtype/DrydockLeaseActivatedLogType.php',
|
||||
'DrydockLeaseActivationFailureLogType' => 'applications/drydock/logtype/DrydockLeaseActivationFailureLogType.php',
|
||||
'DrydockLeaseActivationYieldLogType' => 'applications/drydock/logtype/DrydockLeaseActivationYieldLogType.php',
|
||||
'DrydockLeaseController' => 'applications/drydock/controller/DrydockLeaseController.php',
|
||||
'DrydockLeaseDatasource' => 'applications/drydock/typeahead/DrydockLeaseDatasource.php',
|
||||
'DrydockLeaseDestroyWorker' => 'applications/drydock/worker/DrydockLeaseDestroyWorker.php',
|
||||
'DrydockLeaseDestroyedLogType' => 'applications/drydock/logtype/DrydockLeaseDestroyedLogType.php',
|
||||
'DrydockLeaseListController' => 'applications/drydock/controller/DrydockLeaseListController.php',
|
||||
'DrydockLeaseListView' => 'applications/drydock/view/DrydockLeaseListView.php',
|
||||
'DrydockLeasePHIDType' => 'applications/drydock/phid/DrydockLeasePHIDType.php',
|
||||
'DrydockLeaseQuery' => 'applications/drydock/query/DrydockLeaseQuery.php',
|
||||
'DrydockLeaseQueuedLogType' => 'applications/drydock/logtype/DrydockLeaseQueuedLogType.php',
|
||||
'DrydockLeaseReleaseController' => 'applications/drydock/controller/DrydockLeaseReleaseController.php',
|
||||
'DrydockLeaseReleasedLogType' => 'applications/drydock/logtype/DrydockLeaseReleasedLogType.php',
|
||||
'DrydockLeaseSearchEngine' => 'applications/drydock/query/DrydockLeaseSearchEngine.php',
|
||||
'DrydockLeaseStatus' => 'applications/drydock/constants/DrydockLeaseStatus.php',
|
||||
'DrydockLeaseUpdateWorker' => 'applications/drydock/worker/DrydockLeaseUpdateWorker.php',
|
||||
'DrydockLeaseViewController' => 'applications/drydock/controller/DrydockLeaseViewController.php',
|
||||
'DrydockLeaseWorker' => 'applications/drydock/worker/DrydockLeaseWorker.php',
|
||||
'DrydockLeaseWaitingForResourcesLogType' => 'applications/drydock/logtype/DrydockLeaseWaitingForResourcesLogType.php',
|
||||
'DrydockLog' => 'applications/drydock/storage/DrydockLog.php',
|
||||
'DrydockLogController' => 'applications/drydock/controller/DrydockLogController.php',
|
||||
'DrydockLogGarbageCollector' => 'applications/drydock/garbagecollector/DrydockLogGarbageCollector.php',
|
||||
'DrydockLogListController' => 'applications/drydock/controller/DrydockLogListController.php',
|
||||
'DrydockLogListView' => 'applications/drydock/view/DrydockLogListView.php',
|
||||
'DrydockLogQuery' => 'applications/drydock/query/DrydockLogQuery.php',
|
||||
'DrydockLogSearchEngine' => 'applications/drydock/query/DrydockLogSearchEngine.php',
|
||||
'DrydockLogType' => 'applications/drydock/logtype/DrydockLogType.php',
|
||||
'DrydockManagementCommandWorkflow' => 'applications/drydock/management/DrydockManagementCommandWorkflow.php',
|
||||
'DrydockManagementLeaseWorkflow' => 'applications/drydock/management/DrydockManagementLeaseWorkflow.php',
|
||||
'DrydockManagementReleaseLeaseWorkflow' => 'applications/drydock/management/DrydockManagementReleaseLeaseWorkflow.php',
|
||||
|
@ -858,9 +865,10 @@ phutil_register_library_map(array(
|
|||
'DrydockManagementWorkflow' => 'applications/drydock/management/DrydockManagementWorkflow.php',
|
||||
'DrydockQuery' => 'applications/drydock/query/DrydockQuery.php',
|
||||
'DrydockResource' => 'applications/drydock/storage/DrydockResource.php',
|
||||
'DrydockResourceActivationFailureLogType' => 'applications/drydock/logtype/DrydockResourceActivationFailureLogType.php',
|
||||
'DrydockResourceActivationYieldLogType' => 'applications/drydock/logtype/DrydockResourceActivationYieldLogType.php',
|
||||
'DrydockResourceController' => 'applications/drydock/controller/DrydockResourceController.php',
|
||||
'DrydockResourceDatasource' => 'applications/drydock/typeahead/DrydockResourceDatasource.php',
|
||||
'DrydockResourceDestroyWorker' => 'applications/drydock/worker/DrydockResourceDestroyWorker.php',
|
||||
'DrydockResourceListController' => 'applications/drydock/controller/DrydockResourceListController.php',
|
||||
'DrydockResourceListView' => 'applications/drydock/view/DrydockResourceListView.php',
|
||||
'DrydockResourcePHIDType' => 'applications/drydock/phid/DrydockResourcePHIDType.php',
|
||||
|
@ -870,11 +878,11 @@ phutil_register_library_map(array(
|
|||
'DrydockResourceStatus' => 'applications/drydock/constants/DrydockResourceStatus.php',
|
||||
'DrydockResourceUpdateWorker' => 'applications/drydock/worker/DrydockResourceUpdateWorker.php',
|
||||
'DrydockResourceViewController' => 'applications/drydock/controller/DrydockResourceViewController.php',
|
||||
'DrydockResourceWorker' => 'applications/drydock/worker/DrydockResourceWorker.php',
|
||||
'DrydockSFTPFilesystemInterface' => 'applications/drydock/interface/filesystem/DrydockSFTPFilesystemInterface.php',
|
||||
'DrydockSSHCommandInterface' => 'applications/drydock/interface/command/DrydockSSHCommandInterface.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',
|
||||
'DrydockWorker' => 'applications/drydock/worker/DrydockWorker.php',
|
||||
'DrydockWorkingCopyBlueprintImplementation' => 'applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php',
|
||||
|
@ -964,6 +972,7 @@ phutil_register_library_map(array(
|
|||
'HarbormasterBuildPlanTransaction' => 'applications/harbormaster/storage/configuration/HarbormasterBuildPlanTransaction.php',
|
||||
'HarbormasterBuildPlanTransactionQuery' => 'applications/harbormaster/query/HarbormasterBuildPlanTransactionQuery.php',
|
||||
'HarbormasterBuildQuery' => 'applications/harbormaster/query/HarbormasterBuildQuery.php',
|
||||
'HarbormasterBuildRequest' => 'applications/harbormaster/engine/HarbormasterBuildRequest.php',
|
||||
'HarbormasterBuildStep' => 'applications/harbormaster/storage/configuration/HarbormasterBuildStep.php',
|
||||
'HarbormasterBuildStepCoreCustomField' => 'applications/harbormaster/customfield/HarbormasterBuildStepCoreCustomField.php',
|
||||
'HarbormasterBuildStepCustomField' => 'applications/harbormaster/customfield/HarbormasterBuildStepCustomField.php',
|
||||
|
@ -1821,6 +1830,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorConfigAllController' => 'applications/config/controller/PhabricatorConfigAllController.php',
|
||||
'PhabricatorConfigApplication' => 'applications/config/application/PhabricatorConfigApplication.php',
|
||||
'PhabricatorConfigCacheController' => 'applications/config/controller/PhabricatorConfigCacheController.php',
|
||||
'PhabricatorConfigCollectorsModule' => 'applications/config/module/PhabricatorConfigCollectorsModule.php',
|
||||
'PhabricatorConfigColumnSchema' => 'applications/config/schema/PhabricatorConfigColumnSchema.php',
|
||||
'PhabricatorConfigConfigPHIDType' => 'applications/config/phid/PhabricatorConfigConfigPHIDType.php',
|
||||
'PhabricatorConfigController' => 'applications/config/controller/PhabricatorConfigController.php',
|
||||
|
@ -2187,7 +2197,9 @@ phutil_register_library_map(array(
|
|||
'PhabricatorFundApplication' => 'applications/fund/application/PhabricatorFundApplication.php',
|
||||
'PhabricatorGDSetupCheck' => 'applications/config/check/PhabricatorGDSetupCheck.php',
|
||||
'PhabricatorGarbageCollector' => 'infrastructure/daemon/garbagecollector/PhabricatorGarbageCollector.php',
|
||||
'PhabricatorGarbageCollectorConfigOptions' => 'applications/config/option/PhabricatorGarbageCollectorConfigOptions.php',
|
||||
'PhabricatorGarbageCollectorManagementCollectWorkflow' => 'infrastructure/daemon/garbagecollector/management/PhabricatorGarbageCollectorManagementCollectWorkflow.php',
|
||||
'PhabricatorGarbageCollectorManagementSetPolicyWorkflow' => 'infrastructure/daemon/garbagecollector/management/PhabricatorGarbageCollectorManagementSetPolicyWorkflow.php',
|
||||
'PhabricatorGarbageCollectorManagementWorkflow' => 'infrastructure/daemon/garbagecollector/management/PhabricatorGarbageCollectorManagementWorkflow.php',
|
||||
'PhabricatorGestureUIExample' => 'applications/uiexample/examples/PhabricatorGestureUIExample.php',
|
||||
'PhabricatorGitGraphStream' => 'applications/repository/daemon/PhabricatorGitGraphStream.php',
|
||||
'PhabricatorGitHubAuthProvider' => 'applications/auth/provider/PhabricatorGitHubAuthProvider.php',
|
||||
|
@ -4520,7 +4532,6 @@ phutil_register_library_map(array(
|
|||
'DoorkeeperSchemaSpec' => 'PhabricatorConfigSchemaSpec',
|
||||
'DoorkeeperTagView' => 'AphrontView',
|
||||
'DoorkeeperTagsController' => 'PhabricatorController',
|
||||
'DrydockAllocatorWorker' => 'DrydockWorker',
|
||||
'DrydockAlmanacServiceHostBlueprintImplementation' => 'DrydockBlueprintImplementation',
|
||||
'DrydockApacheWebrootInterface' => 'DrydockWebrootInterface',
|
||||
'DrydockBlueprint' => array(
|
||||
|
@ -4568,28 +4579,36 @@ phutil_register_library_map(array(
|
|||
'DrydockDAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
),
|
||||
'DrydockLeaseAcquiredLogType' => 'DrydockLogType',
|
||||
'DrydockLeaseActivatedLogType' => 'DrydockLogType',
|
||||
'DrydockLeaseActivationFailureLogType' => 'DrydockLogType',
|
||||
'DrydockLeaseActivationYieldLogType' => 'DrydockLogType',
|
||||
'DrydockLeaseController' => 'DrydockController',
|
||||
'DrydockLeaseDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||
'DrydockLeaseDestroyWorker' => 'DrydockWorker',
|
||||
'DrydockLeaseDestroyedLogType' => 'DrydockLogType',
|
||||
'DrydockLeaseListController' => 'DrydockLeaseController',
|
||||
'DrydockLeaseListView' => 'AphrontView',
|
||||
'DrydockLeasePHIDType' => 'PhabricatorPHIDType',
|
||||
'DrydockLeaseQuery' => 'DrydockQuery',
|
||||
'DrydockLeaseQueuedLogType' => 'DrydockLogType',
|
||||
'DrydockLeaseReleaseController' => 'DrydockLeaseController',
|
||||
'DrydockLeaseReleasedLogType' => 'DrydockLogType',
|
||||
'DrydockLeaseSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
'DrydockLeaseStatus' => 'DrydockConstants',
|
||||
'DrydockLeaseUpdateWorker' => 'DrydockWorker',
|
||||
'DrydockLeaseViewController' => 'DrydockLeaseController',
|
||||
'DrydockLeaseWorker' => 'DrydockWorker',
|
||||
'DrydockLeaseWaitingForResourcesLogType' => 'DrydockLogType',
|
||||
'DrydockLog' => array(
|
||||
'DrydockDAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
),
|
||||
'DrydockLogController' => 'DrydockController',
|
||||
'DrydockLogGarbageCollector' => 'PhabricatorGarbageCollector',
|
||||
'DrydockLogListController' => 'DrydockLogController',
|
||||
'DrydockLogListView' => 'AphrontView',
|
||||
'DrydockLogQuery' => 'DrydockQuery',
|
||||
'DrydockLogSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
'DrydockLogType' => 'Phobject',
|
||||
'DrydockManagementCommandWorkflow' => 'DrydockManagementWorkflow',
|
||||
'DrydockManagementLeaseWorkflow' => 'DrydockManagementWorkflow',
|
||||
'DrydockManagementReleaseLeaseWorkflow' => 'DrydockManagementWorkflow',
|
||||
|
@ -4602,9 +4621,10 @@ phutil_register_library_map(array(
|
|||
'DrydockDAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
),
|
||||
'DrydockResourceActivationFailureLogType' => 'DrydockLogType',
|
||||
'DrydockResourceActivationYieldLogType' => 'DrydockLogType',
|
||||
'DrydockResourceController' => 'DrydockController',
|
||||
'DrydockResourceDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||
'DrydockResourceDestroyWorker' => 'DrydockWorker',
|
||||
'DrydockResourceListController' => 'DrydockResourceController',
|
||||
'DrydockResourceListView' => 'AphrontView',
|
||||
'DrydockResourcePHIDType' => 'PhabricatorPHIDType',
|
||||
|
@ -4614,11 +4634,11 @@ phutil_register_library_map(array(
|
|||
'DrydockResourceStatus' => 'DrydockConstants',
|
||||
'DrydockResourceUpdateWorker' => 'DrydockWorker',
|
||||
'DrydockResourceViewController' => 'DrydockResourceController',
|
||||
'DrydockResourceWorker' => 'DrydockWorker',
|
||||
'DrydockSFTPFilesystemInterface' => 'DrydockFilesystemInterface',
|
||||
'DrydockSSHCommandInterface' => 'DrydockCommandInterface',
|
||||
'DrydockSlotLock' => 'DrydockDAO',
|
||||
'DrydockSlotLockException' => 'Exception',
|
||||
'DrydockSlotLockFailureLogType' => 'DrydockLogType',
|
||||
'DrydockWebrootInterface' => 'DrydockInterface',
|
||||
'DrydockWorker' => 'PhabricatorWorker',
|
||||
'DrydockWorkingCopyBlueprintImplementation' => 'DrydockBlueprintImplementation',
|
||||
|
@ -4740,6 +4760,7 @@ phutil_register_library_map(array(
|
|||
'HarbormasterBuildPlanTransaction' => 'PhabricatorApplicationTransaction',
|
||||
'HarbormasterBuildPlanTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||
'HarbormasterBuildQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'HarbormasterBuildRequest' => 'Phobject',
|
||||
'HarbormasterBuildStep' => array(
|
||||
'HarbormasterDAO',
|
||||
'PhabricatorApplicationTransactionInterface',
|
||||
|
@ -5745,6 +5766,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorConfigAllController' => 'PhabricatorConfigController',
|
||||
'PhabricatorConfigApplication' => 'PhabricatorApplication',
|
||||
'PhabricatorConfigCacheController' => 'PhabricatorConfigController',
|
||||
'PhabricatorConfigCollectorsModule' => 'PhabricatorConfigModule',
|
||||
'PhabricatorConfigColumnSchema' => 'PhabricatorConfigStorageSchema',
|
||||
'PhabricatorConfigConfigPHIDType' => 'PhabricatorPHIDType',
|
||||
'PhabricatorConfigController' => 'PhabricatorController',
|
||||
|
@ -6177,7 +6199,9 @@ phutil_register_library_map(array(
|
|||
'PhabricatorFundApplication' => 'PhabricatorApplication',
|
||||
'PhabricatorGDSetupCheck' => 'PhabricatorSetupCheck',
|
||||
'PhabricatorGarbageCollector' => 'Phobject',
|
||||
'PhabricatorGarbageCollectorConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||
'PhabricatorGarbageCollectorManagementCollectWorkflow' => 'PhabricatorGarbageCollectorManagementWorkflow',
|
||||
'PhabricatorGarbageCollectorManagementSetPolicyWorkflow' => 'PhabricatorGarbageCollectorManagementWorkflow',
|
||||
'PhabricatorGarbageCollectorManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
||||
'PhabricatorGestureUIExample' => 'PhabricatorUIExample',
|
||||
'PhabricatorGitGraphStream' => 'PhabricatorRepositoryGraphStream',
|
||||
'PhabricatorGitHubAuthProvider' => 'PhabricatorOAuth2AuthProvider',
|
||||
|
|
|
@ -3,7 +3,17 @@
|
|||
final class PhabricatorAuthSessionGarbageCollector
|
||||
extends PhabricatorGarbageCollector {
|
||||
|
||||
public function collectGarbage() {
|
||||
const COLLECTORCONST = 'auth.sessions';
|
||||
|
||||
public function getCollectorName() {
|
||||
return pht('Authentication Sessions');
|
||||
}
|
||||
|
||||
public function hasAutomaticPolicy() {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function collectGarbage() {
|
||||
$session_table = new PhabricatorAuthSession();
|
||||
$conn_w = $session_table->establishConnection('w');
|
||||
|
||||
|
|
|
@ -3,7 +3,17 @@
|
|||
final class PhabricatorAuthTemporaryTokenGarbageCollector
|
||||
extends PhabricatorGarbageCollector {
|
||||
|
||||
public function collectGarbage() {
|
||||
const COLLECTORCONST = 'auth.tokens';
|
||||
|
||||
public function getCollectorName() {
|
||||
return pht('Authentication Tokens');
|
||||
}
|
||||
|
||||
public function hasAutomaticPolicy() {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function collectGarbage() {
|
||||
$session_table = new PhabricatorAuthTemporaryToken();
|
||||
$conn_w = $session_table->establishConnection('w');
|
||||
|
||||
|
|
|
@ -3,13 +3,17 @@
|
|||
final class PhabricatorCacheGeneralGarbageCollector
|
||||
extends PhabricatorGarbageCollector {
|
||||
|
||||
public function collectGarbage() {
|
||||
$key = 'gcdaemon.ttl.general-cache';
|
||||
$ttl = PhabricatorEnv::getEnvConfig($key);
|
||||
if ($ttl <= 0) {
|
||||
return false;
|
||||
}
|
||||
const COLLECTORCONST = 'cache.general';
|
||||
|
||||
public function getCollectorName() {
|
||||
return pht('General Cache');
|
||||
}
|
||||
|
||||
public function getDefaultRetentionPolicy() {
|
||||
return phutil_units('30 days in seconds');
|
||||
}
|
||||
|
||||
protected function collectGarbage() {
|
||||
$cache = new PhabricatorKeyValueDatabaseCache();
|
||||
$conn_w = $cache->establishConnection('w');
|
||||
|
||||
|
@ -18,7 +22,7 @@ final class PhabricatorCacheGeneralGarbageCollector
|
|||
'DELETE FROM %T WHERE cacheCreated < %d
|
||||
ORDER BY cacheCreated ASC LIMIT 100',
|
||||
$cache->getTableName(),
|
||||
time() - $ttl);
|
||||
$this->getGarbageEpoch());
|
||||
|
||||
return ($conn_w->getAffectedRows() == 100);
|
||||
}
|
||||
|
|
|
@ -3,13 +3,17 @@
|
|||
final class PhabricatorCacheMarkupGarbageCollector
|
||||
extends PhabricatorGarbageCollector {
|
||||
|
||||
public function collectGarbage() {
|
||||
$key = 'gcdaemon.ttl.markup-cache';
|
||||
$ttl = PhabricatorEnv::getEnvConfig($key);
|
||||
if ($ttl <= 0) {
|
||||
return false;
|
||||
}
|
||||
const COLLECTORCONST = 'cache.markup';
|
||||
|
||||
public function getCollectorName() {
|
||||
return pht('Markup Cache');
|
||||
}
|
||||
|
||||
public function getDefaultRetentionPolicy() {
|
||||
return phutil_units('30 days in seconds');
|
||||
}
|
||||
|
||||
protected function collectGarbage() {
|
||||
$table = new PhabricatorMarkupCache();
|
||||
$conn_w = $table->establishConnection('w');
|
||||
|
||||
|
@ -17,7 +21,7 @@ final class PhabricatorCacheMarkupGarbageCollector
|
|||
$conn_w,
|
||||
'DELETE FROM %T WHERE dateCreated < %d LIMIT 100',
|
||||
$table->getTableName(),
|
||||
time() - $ttl);
|
||||
$this->getGarbageEpoch());
|
||||
|
||||
return ($conn_w->getAffectedRows() == 100);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,17 @@
|
|||
final class PhabricatorCacheTTLGarbageCollector
|
||||
extends PhabricatorGarbageCollector {
|
||||
|
||||
public function collectGarbage() {
|
||||
const COLLECTORCONST = 'cache.general.ttl';
|
||||
|
||||
public function getCollectorName() {
|
||||
return pht('General Cache (TTL)');
|
||||
}
|
||||
|
||||
public function hasAutomaticPolicy() {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function collectGarbage() {
|
||||
$cache = new PhabricatorKeyValueDatabaseCache();
|
||||
$conn_w = $cache->establishConnection('w');
|
||||
|
||||
|
@ -12,7 +22,7 @@ final class PhabricatorCacheTTLGarbageCollector
|
|||
'DELETE FROM %T WHERE cacheExpires < %d
|
||||
ORDER BY cacheExpires ASC LIMIT 100',
|
||||
$cache->getTableName(),
|
||||
time());
|
||||
PhabricatorTime::getNow());
|
||||
|
||||
return ($conn_w->getAffectedRows() == 100);
|
||||
}
|
||||
|
|
|
@ -3,21 +3,26 @@
|
|||
final class ConduitConnectionGarbageCollector
|
||||
extends PhabricatorGarbageCollector {
|
||||
|
||||
public function collectGarbage() {
|
||||
$key = 'gcdaemon.ttl.conduit-logs';
|
||||
$ttl = PhabricatorEnv::getEnvConfig($key);
|
||||
if ($ttl <= 0) {
|
||||
return false;
|
||||
}
|
||||
const COLLECTORCONST = 'conduit.connections';
|
||||
|
||||
public function getCollectorName() {
|
||||
return pht('Conduit Connections');
|
||||
}
|
||||
|
||||
public function getDefaultRetentionPolicy() {
|
||||
return phutil_units('180 days in seconds');
|
||||
}
|
||||
|
||||
protected function collectGarbage() {
|
||||
$table = new PhabricatorConduitConnectionLog();
|
||||
$conn_w = $table->establishConnection('w');
|
||||
|
||||
queryfx(
|
||||
$conn_w,
|
||||
'DELETE FROM %T WHERE dateCreated < %d
|
||||
ORDER BY dateCreated ASC LIMIT 100',
|
||||
$table->getTableName(),
|
||||
time() - $ttl);
|
||||
$this->getGarbageEpoch());
|
||||
|
||||
return ($conn_w->getAffectedRows() == 100);
|
||||
}
|
||||
|
|
|
@ -3,21 +3,26 @@
|
|||
final class ConduitLogGarbageCollector
|
||||
extends PhabricatorGarbageCollector {
|
||||
|
||||
public function collectGarbage() {
|
||||
$key = 'gcdaemon.ttl.conduit-logs';
|
||||
$ttl = PhabricatorEnv::getEnvConfig($key);
|
||||
if ($ttl <= 0) {
|
||||
return false;
|
||||
}
|
||||
const COLLECTORCONST = 'conduit.logs';
|
||||
|
||||
public function getCollectorName() {
|
||||
return pht('Conduit Logs');
|
||||
}
|
||||
|
||||
public function getDefaultRetentionPolicy() {
|
||||
return phutil_units('180 days in seconds');
|
||||
}
|
||||
|
||||
protected function collectGarbage() {
|
||||
$table = new PhabricatorConduitMethodCallLog();
|
||||
$conn_w = $table->establishConnection('w');
|
||||
|
||||
queryfx(
|
||||
$conn_w,
|
||||
'DELETE FROM %T WHERE dateCreated < %d
|
||||
ORDER BY dateCreated ASC LIMIT 100',
|
||||
$table->getTableName(),
|
||||
time() - $ttl);
|
||||
$this->getGarbageEpoch());
|
||||
|
||||
return ($conn_w->getAffectedRows() == 100);
|
||||
}
|
||||
|
|
|
@ -3,9 +3,20 @@
|
|||
final class ConduitTokenGarbageCollector
|
||||
extends PhabricatorGarbageCollector {
|
||||
|
||||
public function collectGarbage() {
|
||||
const COLLECTORCONST = 'conduit.tokens';
|
||||
|
||||
public function getCollectorName() {
|
||||
return pht('Conduit Tokens');
|
||||
}
|
||||
|
||||
public function hasAutomaticPolicy() {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function collectGarbage() {
|
||||
$table = new PhabricatorConduitToken();
|
||||
$conn_w = $table->establishConnection('w');
|
||||
|
||||
queryfx(
|
||||
$conn_w,
|
||||
'DELETE FROM %T WHERE expires <= %d
|
||||
|
|
|
@ -176,6 +176,10 @@ final class PhabricatorExtraConfigSetupCheck extends PhabricatorSetupCheck {
|
|||
'Inbound mail addresses are now configured for each application '.
|
||||
'in the Applications tool.');
|
||||
|
||||
$gc_reason = pht(
|
||||
'Garbage collectors are now configured with "%s".',
|
||||
'bin/garbage set-policy');
|
||||
|
||||
$ancient_config += array(
|
||||
'phid.external-loaders' =>
|
||||
pht(
|
||||
|
@ -280,6 +284,14 @@ final class PhabricatorExtraConfigSetupCheck extends PhabricatorSetupCheck {
|
|||
'auth.login-message' => pht(
|
||||
'This configuration option has been replaced with a modular '.
|
||||
'handler. See T9346.'),
|
||||
|
||||
'gcdaemon.ttl.herald-transcripts' => $gc_reason,
|
||||
'gcdaemon.ttl.daemon-logs' => $gc_reason,
|
||||
'gcdaemon.ttl.differential-parse-cache' => $gc_reason,
|
||||
'gcdaemon.ttl.markup-cache' => $gc_reason,
|
||||
'gcdaemon.ttl.task-archive' => $gc_reason,
|
||||
'gcdaemon.ttl.general-cache' => $gc_reason,
|
||||
'gcdaemon.ttl.conduit-logs' => $gc_reason,
|
||||
);
|
||||
|
||||
return $ancient_config;
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorConfigCollectorsModule extends PhabricatorConfigModule {
|
||||
|
||||
public function getModuleKey() {
|
||||
return 'collectors';
|
||||
}
|
||||
|
||||
public function getModuleName() {
|
||||
return pht('Garbage Collectors');
|
||||
}
|
||||
|
||||
public function renderModuleStatus(AphrontRequest $request) {
|
||||
$viewer = $request->getViewer();
|
||||
|
||||
$collectors = PhabricatorGarbageCollector::getAllCollectors();
|
||||
$collectors = msort($collectors, 'getCollectorConstant');
|
||||
|
||||
$rows = array();
|
||||
$rowc = array();
|
||||
foreach ($collectors as $key => $collector) {
|
||||
$class = null;
|
||||
if ($collector->hasAutomaticPolicy()) {
|
||||
$policy_view = phutil_tag('em', array(), pht('Automatic'));
|
||||
} else {
|
||||
$policy = $collector->getRetentionPolicy();
|
||||
if ($policy === null) {
|
||||
$policy_view = pht('Indefinite');
|
||||
} else {
|
||||
$days = ceil($policy / phutil_units('1 day in seconds'));
|
||||
$policy_view = pht(
|
||||
'%s Day(s)',
|
||||
new PhutilNumber($days));
|
||||
}
|
||||
|
||||
$default = $collector->getDefaultRetentionPolicy();
|
||||
if ($policy !== $default) {
|
||||
$class = 'highlighted';
|
||||
$policy_view = phutil_tag('strong', array(), $policy_view);
|
||||
}
|
||||
}
|
||||
|
||||
$rowc[] = $class;
|
||||
$rows[] = array(
|
||||
$collector->getCollectorConstant(),
|
||||
$collector->getCollectorName(),
|
||||
$policy_view,
|
||||
);
|
||||
}
|
||||
|
||||
$table = id(new AphrontTableView($rows))
|
||||
->setRowClasses($rowc)
|
||||
->setHeaders(
|
||||
array(
|
||||
pht('Constant'),
|
||||
pht('Name'),
|
||||
pht('Retention Policy'),
|
||||
))
|
||||
->setColumnClasses(
|
||||
array(
|
||||
null,
|
||||
'pri wide',
|
||||
null,
|
||||
));
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setHeader(pht('Garbage Collectors'))
|
||||
->setSubheader(
|
||||
pht(
|
||||
'Collectors with custom policies are highlighted. Use '.
|
||||
'%s to change retention policies.',
|
||||
phutil_tag('tt', array(), 'bin/garbage set-policy')));
|
||||
|
||||
return id(new PHUIObjectBoxView())
|
||||
->setHeader($header)
|
||||
->setTable($table);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorGarbageCollectorConfigOptions
|
||||
extends PhabricatorApplicationConfigOptions {
|
||||
|
||||
public function getName() {
|
||||
return pht('Garbage Collector');
|
||||
}
|
||||
|
||||
public function getDescription() {
|
||||
return pht('Configure the GC for old logs, caches, etc.');
|
||||
}
|
||||
|
||||
public function getFontIcon() {
|
||||
return 'fa-trash-o';
|
||||
}
|
||||
|
||||
public function getGroup() {
|
||||
return 'core';
|
||||
}
|
||||
|
||||
public function getOptions() {
|
||||
|
||||
$options = array(
|
||||
'gcdaemon.ttl.herald-transcripts' => array(
|
||||
30,
|
||||
pht('Number of seconds to retain Herald transcripts for.'),
|
||||
),
|
||||
'gcdaemon.ttl.daemon-logs' => array(
|
||||
7,
|
||||
pht('Number of seconds to retain Daemon logs for.'),
|
||||
),
|
||||
'gcdaemon.ttl.differential-parse-cache' => array(
|
||||
14,
|
||||
pht('Number of seconds to retain Differential parse caches for.'),
|
||||
),
|
||||
'gcdaemon.ttl.markup-cache' => array(
|
||||
30,
|
||||
pht('Number of seconds to retain Markup cache entries for.'),
|
||||
),
|
||||
'gcdaemon.ttl.task-archive' => array(
|
||||
14,
|
||||
pht('Number of seconds to retain archived background tasks for.'),
|
||||
),
|
||||
'gcdaemon.ttl.general-cache' => array(
|
||||
30,
|
||||
pht('Number of seconds to retain general cache entries for.'),
|
||||
),
|
||||
'gcdaemon.ttl.conduit-logs' => array(
|
||||
180,
|
||||
pht('Number of seconds to retain Conduit call logs for.'),
|
||||
),
|
||||
);
|
||||
|
||||
$result = array();
|
||||
foreach ($options as $key => $spec) {
|
||||
list($default_days, $description) = $spec;
|
||||
$result[] = $this
|
||||
->newOption($key, 'int', $default_days * (24 * 60 * 60))
|
||||
->setDescription($description)
|
||||
->addExample((7 * 24 * 60 * 60), pht('Retain for 1 week'))
|
||||
->addExample((14 * 24 * 60 * 60), pht('Retain for 2 weeks'))
|
||||
->addExample((30 * 24 * 60 * 60), pht('Retain for 30 days'))
|
||||
->addExample((60 * 24 * 60 * 60), pht('Retain for 60 days'))
|
||||
->addExample(0, pht('Retain indefinitely'));
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
|
@ -80,6 +80,17 @@ final class PhabricatorPHDConfigOptions
|
|||
'and the daemons. Primarily, this is a way to suppress the '.
|
||||
'"Daemons and Web Have Different Config" setup issue on a per '.
|
||||
'config key basis.')),
|
||||
$this->newOption('phd.garbage-collection', 'wild', array())
|
||||
->setLocked(true)
|
||||
->setLockedMessage(
|
||||
pht(
|
||||
'This option can not be edited from the web UI. Use %s to adjust '.
|
||||
'garbage collector policies.',
|
||||
phutil_tag('tt', array(), 'bin/garbage set-policy')))
|
||||
->setSummary(pht('Retention policies for garbage collection.'))
|
||||
->setDescription(
|
||||
pht(
|
||||
'Customizes retention policies for garbage collectors.')),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,12 +3,17 @@
|
|||
final class PhabricatorDaemonLogEventGarbageCollector
|
||||
extends PhabricatorGarbageCollector {
|
||||
|
||||
public function collectGarbage() {
|
||||
$ttl = PhabricatorEnv::getEnvConfig('gcdaemon.ttl.daemon-logs');
|
||||
if ($ttl <= 0) {
|
||||
return false;
|
||||
}
|
||||
const COLLECTORCONST = 'daemon.processes';
|
||||
|
||||
public function getCollectorName() {
|
||||
return pht('Daemon Processes');
|
||||
}
|
||||
|
||||
public function getDefaultRetentionPolicy() {
|
||||
return phutil_units('7 days in seconds');
|
||||
}
|
||||
|
||||
protected function collectGarbage() {
|
||||
$table = new PhabricatorDaemonLogEvent();
|
||||
$conn_w = $table->establishConnection('w');
|
||||
|
||||
|
@ -16,7 +21,7 @@ final class PhabricatorDaemonLogEventGarbageCollector
|
|||
$conn_w,
|
||||
'DELETE FROM %T WHERE epoch < %d LIMIT 100',
|
||||
$table->getTableName(),
|
||||
time() - $ttl);
|
||||
$this->getGarbageEpoch());
|
||||
|
||||
return ($conn_w->getAffectedRows() == 100);
|
||||
}
|
||||
|
|
|
@ -3,12 +3,17 @@
|
|||
final class PhabricatorDaemonLogGarbageCollector
|
||||
extends PhabricatorGarbageCollector {
|
||||
|
||||
public function collectGarbage() {
|
||||
$ttl = PhabricatorEnv::getEnvConfig('gcdaemon.ttl.daemon-logs');
|
||||
if ($ttl <= 0) {
|
||||
return false;
|
||||
}
|
||||
const COLLECTORCONST = 'daemon.logs';
|
||||
|
||||
public function getCollectorName() {
|
||||
return pht('Daemon Logs');
|
||||
}
|
||||
|
||||
public function getDefaultRetentionPolicy() {
|
||||
return phutil_units('7 days in seconds');
|
||||
}
|
||||
|
||||
protected function collectGarbage() {
|
||||
$table = new PhabricatorDaemonLog();
|
||||
$conn_w = $table->establishConnection('w');
|
||||
|
||||
|
@ -16,7 +21,7 @@ final class PhabricatorDaemonLogGarbageCollector
|
|||
$conn_w,
|
||||
'DELETE FROM %T WHERE dateCreated < %d AND status != %s LIMIT 100',
|
||||
$table->getTableName(),
|
||||
time() - $ttl,
|
||||
$this->getGarbageEpoch(),
|
||||
PhabricatorDaemonLog::STATUS_RUNNING);
|
||||
|
||||
return ($conn_w->getAffectedRows() == 100);
|
||||
|
|
|
@ -3,21 +3,25 @@
|
|||
final class PhabricatorDaemonTaskGarbageCollector
|
||||
extends PhabricatorGarbageCollector {
|
||||
|
||||
public function collectGarbage() {
|
||||
$key = 'gcdaemon.ttl.task-archive';
|
||||
$ttl = PhabricatorEnv::getEnvConfig($key);
|
||||
if ($ttl <= 0) {
|
||||
return false;
|
||||
}
|
||||
const COLLECTORCONST = 'worker.tasks';
|
||||
|
||||
public function getCollectorName() {
|
||||
return pht('Archived Tasks');
|
||||
}
|
||||
|
||||
public function getDefaultRetentionPolicy() {
|
||||
return phutil_units('14 days in seconds');
|
||||
}
|
||||
|
||||
protected function collectGarbage() {
|
||||
$table = new PhabricatorWorkerArchiveTask();
|
||||
$data_table = new PhabricatorWorkerTaskData();
|
||||
$conn_w = $table->establishConnection('w');
|
||||
|
||||
$tasks = id(new PhabricatorWorkerArchiveTaskQuery())
|
||||
->withDateCreatedBefore(time() - $ttl)
|
||||
->withDateCreatedBefore($this->getGarbageEpoch())
|
||||
->setLimit(100)
|
||||
->execute();
|
||||
|
||||
if (!$tasks) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -133,7 +133,7 @@ final class PhabricatorDifferentialConfigOptions
|
|||
'to affect existing revisions. For instructions, see '.
|
||||
'**[[ %s | Managing Caches ]]** in the documentation.',
|
||||
$caches_href))
|
||||
->addExample("/config\.h$/\n#/autobuilt/#", pht('Valid Setting')),
|
||||
->addExample("/config\.h$/\n#(^|/)autobuilt/#", pht('Valid Setting')),
|
||||
$this->newOption('differential.sticky-accept', 'bool', true)
|
||||
->setBoolOptions(
|
||||
array(
|
||||
|
|
|
@ -3,13 +3,17 @@
|
|||
final class DifferentialParseCacheGarbageCollector
|
||||
extends PhabricatorGarbageCollector {
|
||||
|
||||
public function collectGarbage() {
|
||||
$key = 'gcdaemon.ttl.differential-parse-cache';
|
||||
$ttl = PhabricatorEnv::getEnvConfig($key);
|
||||
if ($ttl <= 0) {
|
||||
return false;
|
||||
}
|
||||
const COLLECTORCONST = 'differential.parse';
|
||||
|
||||
public function getCollectorName() {
|
||||
return pht('Differential Parse Cache');
|
||||
}
|
||||
|
||||
public function getDefaultRetentionPolicy() {
|
||||
return phutil_units('14 days in seconds');
|
||||
}
|
||||
|
||||
protected function collectGarbage() {
|
||||
$table = new DifferentialChangeset();
|
||||
$conn_w = $table->establishConnection('w');
|
||||
|
||||
|
@ -17,7 +21,7 @@ final class DifferentialParseCacheGarbageCollector
|
|||
$conn_w,
|
||||
'DELETE FROM %T WHERE dateCreated < %d LIMIT 100',
|
||||
DifferentialChangeset::TABLE_CACHE,
|
||||
time() - $ttl);
|
||||
$this->getGarbageEpoch());
|
||||
|
||||
return ($conn_w->getAffectedRows() == 100);
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ final class HeraldDifferentialRevisionAdapter
|
|||
protected $changesets;
|
||||
private $haveHunks;
|
||||
|
||||
private $buildPlanPHIDs = array();
|
||||
private $buildRequests = array();
|
||||
|
||||
public function getAdapterApplicationClass() {
|
||||
return 'PhabricatorDifferentialApplication';
|
||||
|
@ -139,12 +139,13 @@ final class HeraldDifferentialRevisionAdapter
|
|||
return $this->getObject()->getPHID();
|
||||
}
|
||||
|
||||
public function getQueuedHarbormasterBuildPlanPHIDs() {
|
||||
return $this->buildPlanPHIDs;
|
||||
public function getQueuedHarbormasterBuildRequests() {
|
||||
return $this->buildRequests;
|
||||
}
|
||||
|
||||
public function queueHarbormasterBuildPlanPHID($phid) {
|
||||
$this->buildPlanPHIDs[] = $phid;
|
||||
public function queueHarbormasterBuildRequest(
|
||||
HarbormasterBuildRequest $request) {
|
||||
$this->buildRequests[] = $request;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -446,6 +446,13 @@ final class DifferentialDiff
|
|||
$results['repository.phid'] = $repo->getPHID();
|
||||
$results['repository.vcs'] = $repo->getVersionControlSystem();
|
||||
$results['repository.uri'] = $repo->getPublicCloneURI();
|
||||
|
||||
// TODO: We're just hoping to get lucky. Instead, `arc` should store
|
||||
// where it sent changes and we should only provide staging details
|
||||
// if we reasonably believe they are accurate.
|
||||
$staging_ref = 'refs/tags/phabricator/diff/'.$this->getID();
|
||||
$results['repository.staging.uri'] = $repo->getStagingURI();
|
||||
$results['repository.staging.ref'] = $staging_ref;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -466,6 +473,10 @@ final class DifferentialDiff
|
|||
pht('The version control system, either "svn", "hg" or "git".'),
|
||||
'repository.uri' =>
|
||||
pht('The URI to clone or checkout the repository from.'),
|
||||
'repository.staging.uri' =>
|
||||
pht('The URI of the staging repository.'),
|
||||
'repository.staging.ref' =>
|
||||
pht('The ref name for this change in the staging repository.'),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,12 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
private $auditAuthorityPHIDs;
|
||||
private $highlightedAudits;
|
||||
|
||||
private $commitParents;
|
||||
private $commitRefs;
|
||||
private $commitMerges;
|
||||
private $commitErrors;
|
||||
private $commitExists;
|
||||
|
||||
public function shouldAllowPublic() {
|
||||
return true;
|
||||
}
|
||||
|
@ -17,6 +23,7 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
|
||||
protected function processDiffusionRequest(AphrontRequest $request) {
|
||||
$user = $request->getUser();
|
||||
|
||||
// This controller doesn't use blob/path stuff, just pass the dictionary
|
||||
// in directly instead of using the AphrontRequest parsing mechanism.
|
||||
$data = $request->getURIMap();
|
||||
|
@ -45,10 +52,7 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
));
|
||||
|
||||
if (!$commit) {
|
||||
$exists = $this->callConduitWithDiffusionRequest(
|
||||
'diffusion.existsquery',
|
||||
array('commit' => $drequest->getCommit()));
|
||||
if (!$exists) {
|
||||
if (!$this->getCommitExists()) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
|
@ -95,18 +99,6 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
|
||||
require_celerity_resource('phabricator-remarkup-css');
|
||||
|
||||
$parents = $this->callConduitWithDiffusionRequest(
|
||||
'diffusion.commitparentsquery',
|
||||
array('commit' => $drequest->getCommit()));
|
||||
|
||||
if ($parents) {
|
||||
$parents = id(new DiffusionCommitQuery())
|
||||
->setViewer($user)
|
||||
->withRepository($repository)
|
||||
->withIdentifiers($parents)
|
||||
->execute();
|
||||
}
|
||||
|
||||
$headsup_view = id(new PHUIHeaderView())
|
||||
->setHeader(nonempty($commit->getSummary(), pht('Commit Detail')));
|
||||
|
||||
|
@ -115,7 +107,6 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
$commit_properties = $this->loadCommitProperties(
|
||||
$commit,
|
||||
$commit_data,
|
||||
$parents,
|
||||
$audit_requests);
|
||||
$property_list = id(new PHUIPropertyListView())
|
||||
->setHasKeyboardShortcuts(true)
|
||||
|
@ -148,8 +139,10 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
$message));
|
||||
|
||||
$headsup_view->setTall(true);
|
||||
|
||||
$object_box = id(new PHUIObjectBoxView())
|
||||
->setHeader($headsup_view)
|
||||
->setFormErrors($this->getCommitErrors())
|
||||
->addPropertyList($property_list)
|
||||
->addPropertyList($detail_list);
|
||||
|
||||
|
@ -216,6 +209,10 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
'This commit is enormous, and affects more than %d files. '.
|
||||
'Changes are not shown.',
|
||||
$hard_limit));
|
||||
} else if (!$this->getCommitExists()) {
|
||||
$content[] = $this->renderStatusMessage(
|
||||
pht('Commit No Longer Exists'),
|
||||
pht('This commit no longer exists in the repository.'));
|
||||
} else {
|
||||
$show_changesets = true;
|
||||
|
||||
|
@ -382,10 +379,8 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
private function loadCommitProperties(
|
||||
PhabricatorRepositoryCommit $commit,
|
||||
PhabricatorRepositoryCommitData $data,
|
||||
array $parents,
|
||||
array $audit_requests) {
|
||||
|
||||
assert_instances_of($parents, 'PhabricatorRepositoryCommit');
|
||||
$viewer = $this->getRequest()->getUser();
|
||||
$commit_phid = $commit->getPHID();
|
||||
$drequest = $this->getDiffusionRequest();
|
||||
|
@ -423,11 +418,6 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
if ($data->getCommitDetail('committerPHID')) {
|
||||
$phids[] = $data->getCommitDetail('committerPHID');
|
||||
}
|
||||
if ($parents) {
|
||||
foreach ($parents as $parent) {
|
||||
$phids[] = $parent->getPHID();
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: We should never normally have more than a single push log, but
|
||||
// it can occur naturally if a commit is pushed, then the branch it was
|
||||
|
@ -564,39 +554,47 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
$props['Differential Revision'] = $handles[$revision_phid]->renderLink();
|
||||
}
|
||||
|
||||
$parents = $this->getCommitParents();
|
||||
if ($parents) {
|
||||
$parent_links = array();
|
||||
foreach ($parents as $parent) {
|
||||
$parent_links[] = $handles[$parent->getPHID()]->renderLink();
|
||||
}
|
||||
$props['Parents'] = phutil_implode_html(" \xC2\xB7 ", $parent_links);
|
||||
$props['Parents'] = $viewer->renderHandleList(mpull($parents, 'getPHID'));
|
||||
}
|
||||
|
||||
$props['Branches'] = phutil_tag(
|
||||
'span',
|
||||
array(
|
||||
'id' => 'commit-branches',
|
||||
),
|
||||
pht('Unknown'));
|
||||
$props['Tags'] = phutil_tag(
|
||||
'span',
|
||||
array(
|
||||
'id' => 'commit-tags',
|
||||
),
|
||||
pht('Unknown'));
|
||||
if ($this->getCommitExists()) {
|
||||
$props['Branches'] = phutil_tag(
|
||||
'span',
|
||||
array(
|
||||
'id' => 'commit-branches',
|
||||
),
|
||||
pht('Unknown'));
|
||||
$props['Tags'] = phutil_tag(
|
||||
'span',
|
||||
array(
|
||||
'id' => 'commit-tags',
|
||||
),
|
||||
pht('Unknown'));
|
||||
|
||||
$callsign = $repository->getCallsign();
|
||||
$root = '/diffusion/'.$callsign.'/commit/'.$commit->getCommitIdentifier();
|
||||
Javelin::initBehavior(
|
||||
'diffusion-commit-branches',
|
||||
array(
|
||||
$root.'/branches/' => 'commit-branches',
|
||||
$root.'/tags/' => 'commit-tags',
|
||||
));
|
||||
$callsign = $repository->getCallsign();
|
||||
$root = '/diffusion/'.$callsign.'/commit/'.$commit->getCommitIdentifier();
|
||||
Javelin::initBehavior(
|
||||
'diffusion-commit-branches',
|
||||
array(
|
||||
$root.'/branches/' => 'commit-branches',
|
||||
$root.'/tags/' => 'commit-tags',
|
||||
));
|
||||
}
|
||||
|
||||
$refs = $this->buildRefs($drequest);
|
||||
$refs = $this->getCommitRefs();
|
||||
if ($refs) {
|
||||
$props['References'] = $refs;
|
||||
$ref_links = array();
|
||||
foreach ($refs as $ref_data) {
|
||||
$ref_links[] = phutil_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => $ref_data['href'],
|
||||
),
|
||||
$ref_data['ref']);
|
||||
}
|
||||
$props['References'] = phutil_implode_html(', ', $ref_links);
|
||||
}
|
||||
|
||||
if ($reverts_phids) {
|
||||
|
@ -860,26 +858,12 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
$drequest = $this->getDiffusionRequest();
|
||||
$repository = $drequest->getRepository();
|
||||
|
||||
$vcs = $repository->getVersionControlSystem();
|
||||
switch ($vcs) {
|
||||
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
|
||||
// These aren't supported under SVN.
|
||||
return null;
|
||||
}
|
||||
|
||||
$limit = 50;
|
||||
|
||||
$merges = $this->callConduitWithDiffusionRequest(
|
||||
'diffusion.mergedcommitsquery',
|
||||
array(
|
||||
'commit' => $drequest->getCommit(),
|
||||
'limit' => $limit + 1,
|
||||
));
|
||||
|
||||
$merges = $this->getCommitMerges();
|
||||
if (!$merges) {
|
||||
return null;
|
||||
}
|
||||
$merges = DiffusionPathChange::newFromConduit($merges);
|
||||
|
||||
$limit = $this->getMergeDisplayLimit();
|
||||
|
||||
$caption = null;
|
||||
if (count($merges) > $limit) {
|
||||
|
@ -961,27 +945,6 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
return $actions;
|
||||
}
|
||||
|
||||
private function buildRefs(DiffusionRequest $request) {
|
||||
// this is git-only, so save a conduit round trip and just get out of
|
||||
// here if the repository isn't git
|
||||
$type_git = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT;
|
||||
$repository = $request->getRepository();
|
||||
if ($repository->getVersionControlSystem() != $type_git) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$results = $this->callConduitWithDiffusionRequest(
|
||||
'diffusion.refsquery',
|
||||
array('commit' => $request->getCommit()));
|
||||
$ref_links = array();
|
||||
foreach ($results as $ref_data) {
|
||||
$ref_links[] = phutil_tag('a',
|
||||
array('href' => $ref_data['href']),
|
||||
$ref_data['ref']);
|
||||
}
|
||||
return phutil_implode_html(', ', $ref_links);
|
||||
}
|
||||
|
||||
private function buildRawDiffResponse(DiffusionRequest $drequest) {
|
||||
$raw_diff = $this->callConduitWithDiffusionRequest(
|
||||
'diffusion.rawdiffquery',
|
||||
|
@ -1137,4 +1100,140 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
return $toc_view;
|
||||
}
|
||||
|
||||
private function loadCommitState() {
|
||||
$viewer = $this->getViewer();
|
||||
$drequest = $this->getDiffusionRequest();
|
||||
$repository = $drequest->getRepository();
|
||||
$commit = $drequest->getCommit();
|
||||
|
||||
// TODO: We could use futures here and resolve these calls in parallel.
|
||||
|
||||
$exceptions = array();
|
||||
|
||||
try {
|
||||
$parent_refs = $this->callConduitWithDiffusionRequest(
|
||||
'diffusion.commitparentsquery',
|
||||
array(
|
||||
'commit' => $commit,
|
||||
));
|
||||
|
||||
if ($parent_refs) {
|
||||
$parents = id(new DiffusionCommitQuery())
|
||||
->setViewer($viewer)
|
||||
->withRepository($repository)
|
||||
->withIdentifiers($parent_refs)
|
||||
->execute();
|
||||
} else {
|
||||
$parents = array();
|
||||
}
|
||||
|
||||
$this->commitParents = $parents;
|
||||
} catch (Exception $ex) {
|
||||
$this->commitParents = false;
|
||||
$exceptions[] = $ex;
|
||||
}
|
||||
|
||||
$merge_limit = $this->getMergeDisplayLimit();
|
||||
|
||||
try {
|
||||
$merges = $this->callConduitWithDiffusionRequest(
|
||||
'diffusion.mergedcommitsquery',
|
||||
array(
|
||||
'commit' => $commit,
|
||||
'limit' => $merge_limit + 1,
|
||||
));
|
||||
|
||||
$this->commitMerges = DiffusionPathChange::newFromConduit($merges);
|
||||
} catch (Exception $ex) {
|
||||
$this->commitMerges = false;
|
||||
$exceptions[] = $ex;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
if ($repository->isGit()) {
|
||||
$refs = $this->callConduitWithDiffusionRequest(
|
||||
'diffusion.refsquery',
|
||||
array(
|
||||
'commit' => $commit,
|
||||
));
|
||||
} else {
|
||||
$refs = array();
|
||||
}
|
||||
|
||||
$this->commitRefs = $refs;
|
||||
} catch (Exception $ex) {
|
||||
$this->commitRefs = false;
|
||||
$exceptions[] = $ex;
|
||||
}
|
||||
|
||||
if ($exceptions) {
|
||||
$exists = $this->callConduitWithDiffusionRequest(
|
||||
'diffusion.existsquery',
|
||||
array(
|
||||
'commit' => $commit,
|
||||
));
|
||||
|
||||
if ($exists) {
|
||||
$this->commitExists = true;
|
||||
foreach ($exceptions as $exception) {
|
||||
$this->commitErrors[] = $exception->getMessage();
|
||||
}
|
||||
} else {
|
||||
$this->commitExists = false;
|
||||
$this->commitErrors[] = pht(
|
||||
'This commit no longer exists in the repository. It may have '.
|
||||
'been part of a branch which was deleted.');
|
||||
}
|
||||
} else {
|
||||
$this->commitExists = true;
|
||||
$this->commitErrors = array();
|
||||
}
|
||||
}
|
||||
|
||||
private function getMergeDisplayLimit() {
|
||||
return 50;
|
||||
}
|
||||
|
||||
private function getCommitExists() {
|
||||
if ($this->commitExists === null) {
|
||||
$this->loadCommitState();
|
||||
}
|
||||
|
||||
return $this->commitExists;
|
||||
}
|
||||
|
||||
private function getCommitParents() {
|
||||
if ($this->commitParents === null) {
|
||||
$this->loadCommitState();
|
||||
}
|
||||
|
||||
return $this->commitParents;
|
||||
}
|
||||
|
||||
private function getCommitRefs() {
|
||||
if ($this->commitRefs === null) {
|
||||
$this->loadCommitState();
|
||||
}
|
||||
|
||||
return $this->commitRefs;
|
||||
}
|
||||
|
||||
private function getCommitMerges() {
|
||||
if ($this->commitMerges === null) {
|
||||
$this->loadCommitState();
|
||||
}
|
||||
|
||||
return $this->commitMerges;
|
||||
}
|
||||
|
||||
private function getCommitErrors() {
|
||||
if ($this->commitErrors === null) {
|
||||
$this->loadCommitState();
|
||||
}
|
||||
|
||||
return $this->commitErrors;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ final class HeraldCommitAdapter
|
|||
protected $affectedPackages;
|
||||
protected $auditNeededPackages;
|
||||
|
||||
private $buildPlanPHIDs = array();
|
||||
private $buildRequests = array();
|
||||
|
||||
public function getAdapterApplicationClass() {
|
||||
return 'PhabricatorDiffusionApplication';
|
||||
|
@ -308,12 +308,13 @@ final class HeraldCommitAdapter
|
|||
return $this->getObject()->getRepository()->getPHID();
|
||||
}
|
||||
|
||||
public function getQueuedHarbormasterBuildPlanPHIDs() {
|
||||
return $this->buildPlanPHIDs;
|
||||
public function getQueuedHarbormasterBuildRequests() {
|
||||
return $this->buildRequests;
|
||||
}
|
||||
|
||||
public function queueHarbormasterBuildPlanPHID($phid) {
|
||||
$this->buildPlanPHIDs[] = $phid;
|
||||
public function queueHarbormasterBuildRequest(
|
||||
HarbormasterBuildRequest $request) {
|
||||
$this->buildRequests[] = $request;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -121,6 +121,25 @@ final class DiffusionCommitRemarkupRuleTestCase extends PhabricatorTestCase {
|
|||
),
|
||||
),
|
||||
),
|
||||
|
||||
// After an "@", we should not be recognizing references because these
|
||||
// are username mentions.
|
||||
'deadbeef' => array(
|
||||
'embed' => array(
|
||||
),
|
||||
'ref' => array(
|
||||
array(
|
||||
'offset' => 0,
|
||||
'id' => 'deadbeef',
|
||||
),
|
||||
),
|
||||
),
|
||||
'@deadbeef' => array(
|
||||
'embed' => array(
|
||||
),
|
||||
'ref' => array(
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
foreach ($cases as $input => $expect) {
|
||||
|
|
|
@ -88,6 +88,11 @@ final class DiffusionHistoryTableView extends DiffusionView {
|
|||
$drequest = $this->getDiffusionRequest();
|
||||
|
||||
$viewer = $this->getUser();
|
||||
|
||||
$show_revisions = PhabricatorApplication::isClassInstalledForViewer(
|
||||
'PhabricatorDifferentialApplication',
|
||||
$viewer);
|
||||
|
||||
$handles = $viewer->loadHandles($this->getRequiredHandlePHIDs());
|
||||
|
||||
$graph = null;
|
||||
|
@ -242,6 +247,10 @@ final class DiffusionHistoryTableView extends DiffusionView {
|
|||
$view->setColumnVisibility(
|
||||
array(
|
||||
$graph ? true : false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
$show_revisions,
|
||||
));
|
||||
$view->setDeviceVisibility(
|
||||
array(
|
||||
|
|
|
@ -47,7 +47,7 @@ final class PhabricatorDrydockApplication extends PhabricatorApplication {
|
|||
return array(
|
||||
'/drydock/' => array(
|
||||
'' => 'DrydockConsoleController',
|
||||
'blueprint/' => array(
|
||||
'(?P<type>blueprint)/' => array(
|
||||
'(?:query/(?P<queryKey>[^/]+)/)?' => 'DrydockBlueprintListController',
|
||||
'(?P<id>[1-9]\d*)/' => array(
|
||||
'' => 'DrydockBlueprintViewController',
|
||||
|
@ -55,29 +55,32 @@ final class PhabricatorDrydockApplication extends PhabricatorApplication {
|
|||
'DrydockBlueprintDisableController',
|
||||
'resources/(?:query/(?P<queryKey>[^/]+)/)?' =>
|
||||
'DrydockResourceListController',
|
||||
'logs/(?:query/(?P<queryKey>[^/]+)/)?' =>
|
||||
'DrydockLogListController',
|
||||
),
|
||||
'create/' => 'DrydockBlueprintCreateController',
|
||||
'edit/(?:(?P<id>[1-9]\d*)/)?' => 'DrydockBlueprintEditController',
|
||||
),
|
||||
'resource/' => array(
|
||||
'(?P<type>resource)/' => array(
|
||||
'(?:query/(?P<queryKey>[^/]+)/)?' => 'DrydockResourceListController',
|
||||
'(?P<id>[1-9]\d*)/' => array(
|
||||
'' => 'DrydockResourceViewController',
|
||||
'release/' => 'DrydockResourceReleaseController',
|
||||
'leases/(?:query/(?P<queryKey>[^/]+)/)?' =>
|
||||
'DrydockLeaseListController',
|
||||
'logs/(?:query/(?P<queryKey>[^/]+)/)?' =>
|
||||
'DrydockLogListController',
|
||||
),
|
||||
),
|
||||
'lease/' => array(
|
||||
'(?P<type>lease)/' => array(
|
||||
'(?:query/(?P<queryKey>[^/]+)/)?' => 'DrydockLeaseListController',
|
||||
'(?P<id>[1-9]\d*)/' => array(
|
||||
'' => 'DrydockLeaseViewController',
|
||||
'release/' => 'DrydockLeaseReleaseController',
|
||||
'logs/(?:query/(?P<queryKey>[^/]+)/)?' =>
|
||||
'DrydockLogListController',
|
||||
),
|
||||
),
|
||||
'log/' => array(
|
||||
'(?:query/(?P<queryKey>[^/]+)/)?' => 'DrydockLogListController',
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -69,8 +69,9 @@ final class DrydockAlmanacServiceHostBlueprintImplementation
|
|||
|
||||
$binding_phid = $binding->getPHID();
|
||||
|
||||
$resource = $this->newResourceTemplate($blueprint, $device_name)
|
||||
$resource = $this->newResourceTemplate($blueprint)
|
||||
->setActivateWhenAllocated(true)
|
||||
->setAttribute('almanacDeviceName', $device_name)
|
||||
->setAttribute('almanacServicePHID', $binding->getServicePHID())
|
||||
->setAttribute('almanacBindingPHID', $binding_phid)
|
||||
->needSlotLock("almanac.host.binding({$binding_phid})");
|
||||
|
@ -95,6 +96,15 @@ final class DrydockAlmanacServiceHostBlueprintImplementation
|
|||
return;
|
||||
}
|
||||
|
||||
public function getResourceName(
|
||||
DrydockBlueprint $blueprint,
|
||||
DrydockResource $resource) {
|
||||
$device_name = $resource->getAttribute(
|
||||
'almanacDeviceName',
|
||||
pht('<Unknown>'));
|
||||
return pht('Host (%s)', $device_name);
|
||||
}
|
||||
|
||||
public function canAcquireLeaseOnResource(
|
||||
DrydockBlueprint $blueprint,
|
||||
DrydockResource $resource,
|
||||
|
@ -163,7 +173,6 @@ final class DrydockAlmanacServiceHostBlueprintImplementation
|
|||
->withPHIDs(array($binding_phid))
|
||||
->executeOne();
|
||||
if (!$binding) {
|
||||
// TODO: This is probably a permanent failure, destroy this resource?
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Unable to load binding "%s" to create command interface.',
|
||||
|
|
|
@ -237,6 +237,19 @@ abstract class DrydockBlueprintImplementation extends Phobject {
|
|||
DrydockResource $resource);
|
||||
|
||||
|
||||
/**
|
||||
* Get a human readable name for a resource.
|
||||
*
|
||||
* @param DrydockBlueprint Blueprint which built the resource.
|
||||
* @param DrydockResource Resource to get the name of.
|
||||
* @return string Human-readable resource name.
|
||||
* @task resource
|
||||
*/
|
||||
abstract public function getResourceName(
|
||||
DrydockBlueprint $blueprint,
|
||||
DrydockResource $resource);
|
||||
|
||||
|
||||
/* -( Resource Interfaces )------------------------------------------------ */
|
||||
|
||||
|
||||
|
@ -250,46 +263,6 @@ abstract class DrydockBlueprintImplementation extends Phobject {
|
|||
/* -( Logging )------------------------------------------------------------ */
|
||||
|
||||
|
||||
/**
|
||||
* @task log
|
||||
*/
|
||||
protected function logException(Exception $ex) {
|
||||
$this->log($ex->getMessage());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @task log
|
||||
*/
|
||||
protected function log($message) {
|
||||
self::writeLog(null, null, $message);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @task log
|
||||
*/
|
||||
public static function writeLog(
|
||||
DrydockResource $resource = null,
|
||||
DrydockLease $lease = null,
|
||||
$message = null) {
|
||||
|
||||
$log = id(new DrydockLog())
|
||||
->setEpoch(time())
|
||||
->setMessage($message);
|
||||
|
||||
if ($resource) {
|
||||
$log->setResourceID($resource->getID());
|
||||
}
|
||||
|
||||
if ($lease) {
|
||||
$log->setLeaseID($lease->getID());
|
||||
}
|
||||
|
||||
$log->save();
|
||||
}
|
||||
|
||||
|
||||
public static function getAllBlueprintImplementations() {
|
||||
return id(new PhutilClassMapQuery())
|
||||
->setAncestorClass(__CLASS__)
|
||||
|
@ -300,16 +273,13 @@ abstract class DrydockBlueprintImplementation extends Phobject {
|
|||
return idx(self::getAllBlueprintImplementations(), $class);
|
||||
}
|
||||
|
||||
protected function newResourceTemplate(
|
||||
DrydockBlueprint $blueprint,
|
||||
$name) {
|
||||
protected function newResourceTemplate(DrydockBlueprint $blueprint) {
|
||||
|
||||
$resource = id(new DrydockResource())
|
||||
->setBlueprintPHID($blueprint->getPHID())
|
||||
->attachBlueprint($blueprint)
|
||||
->setType($this->getType())
|
||||
->setStatus(DrydockResourceStatus::STATUS_PENDING)
|
||||
->setName($name);
|
||||
->setStatus(DrydockResourceStatus::STATUS_PENDING);
|
||||
|
||||
// Pre-allocate the resource PHID.
|
||||
$resource->setPHID($resource->generatePHID());
|
||||
|
@ -325,14 +295,18 @@ abstract class DrydockBlueprintImplementation extends Phobject {
|
|||
$lease_status = $lease->getStatus();
|
||||
|
||||
switch ($lease_status) {
|
||||
case DrydockLeaseStatus::STATUS_PENDING:
|
||||
case DrydockLeaseStatus::STATUS_ACQUIRED:
|
||||
// TODO: Temporary failure.
|
||||
throw new Exception(pht('Lease still activating.'));
|
||||
throw new PhabricatorWorkerYieldException(15);
|
||||
case DrydockLeaseStatus::STATUS_ACTIVE:
|
||||
return;
|
||||
default:
|
||||
// TODO: Permanent failure.
|
||||
throw new Exception(pht('Lease in bad state.'));
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Lease ("%s") is in bad state ("%s"), expected "%s".',
|
||||
$lease->getPHID(),
|
||||
$lease_status,
|
||||
DrydockLeaseStatus::STATUS_ACTIVE));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,10 +37,37 @@ final class DrydockWorkingCopyBlueprintImplementation
|
|||
DrydockResource $resource,
|
||||
DrydockLease $lease) {
|
||||
|
||||
$have_phid = $resource->getAttribute('repositoryPHID');
|
||||
$need_phid = $lease->getAttribute('repositoryPHID');
|
||||
$need_map = $lease->getAttribute('repositories.map');
|
||||
if (!is_array($need_map)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($need_phid !== $have_phid) {
|
||||
$have_map = $resource->getAttribute('repositories.map');
|
||||
if (!is_array($have_map)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$have_as = ipull($have_map, 'phid');
|
||||
$need_as = ipull($need_map, 'phid');
|
||||
|
||||
foreach ($need_as as $need_directory => $need_phid) {
|
||||
if (empty($have_as[$need_directory])) {
|
||||
// This resource is missing a required working copy.
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($have_as[$need_directory] != $need_phid) {
|
||||
// This resource has a required working copy, but it contains
|
||||
// the wrong repository.
|
||||
return false;
|
||||
}
|
||||
|
||||
unset($have_as[$need_directory]);
|
||||
}
|
||||
|
||||
if ($have_as && $lease->getAttribute('repositories.strict')) {
|
||||
// This resource has extra repositories, but the lease is strict about
|
||||
// which repositories are allowed to exist.
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -70,14 +97,7 @@ final class DrydockWorkingCopyBlueprintImplementation
|
|||
DrydockBlueprint $blueprint,
|
||||
DrydockLease $lease) {
|
||||
|
||||
$repository_phid = $lease->getAttribute('repositoryPHID');
|
||||
$repository = $this->loadRepository($repository_phid);
|
||||
|
||||
$resource = $this->newResourceTemplate(
|
||||
$blueprint,
|
||||
pht(
|
||||
'Working Copy (%s)',
|
||||
$repository->getCallsign()));
|
||||
$resource = $this->newResourceTemplate($blueprint);
|
||||
|
||||
$resource_phid = $resource->getPHID();
|
||||
|
||||
|
@ -90,8 +110,17 @@ final class DrydockWorkingCopyBlueprintImplementation
|
|||
// TODO: Add some limits to the number of working copies we can have at
|
||||
// once?
|
||||
|
||||
$map = $lease->getAttribute('repositories.map');
|
||||
foreach ($map as $key => $value) {
|
||||
$map[$key] = array_select_keys(
|
||||
$value,
|
||||
array(
|
||||
'phid',
|
||||
));
|
||||
}
|
||||
|
||||
return $resource
|
||||
->setAttribute('repositoryPHID', $repository->getPHID())
|
||||
->setAttribute('repositories.map', $map)
|
||||
->setAttribute('host.leasePHID', $host_lease->getPHID())
|
||||
->allocateResource();
|
||||
}
|
||||
|
@ -103,26 +132,32 @@ final class DrydockWorkingCopyBlueprintImplementation
|
|||
$lease = $this->loadHostLease($resource);
|
||||
$this->requireActiveLease($lease);
|
||||
|
||||
$repository_phid = $resource->getAttribute('repositoryPHID');
|
||||
$repository = $this->loadRepository($repository_phid);
|
||||
$repository_id = $repository->getID();
|
||||
|
||||
$command_type = DrydockCommandInterface::INTERFACE_TYPE;
|
||||
$interface = $lease->getInterface($command_type);
|
||||
|
||||
// TODO: Make this configurable.
|
||||
$resource_id = $resource->getID();
|
||||
$root = "/var/drydock/workingcopy-{$resource_id}";
|
||||
$path = "{$root}/repo/{$repository_id}/";
|
||||
|
||||
$interface->execx(
|
||||
'git clone -- %s %s',
|
||||
(string)$repository->getCloneURIObject(),
|
||||
$path);
|
||||
$map = $resource->getAttribute('repositories.map');
|
||||
|
||||
$repositories = $this->loadRepositories(ipull($map, 'phid'));
|
||||
foreach ($map as $directory => $spec) {
|
||||
// TODO: Validate directory isn't goofy like "/etc" or "../../lol"
|
||||
// somewhere?
|
||||
|
||||
$repository = $repositories[$spec['phid']];
|
||||
$path = "{$root}/repo/{$directory}/";
|
||||
|
||||
// TODO: Run these in parallel?
|
||||
$interface->execx(
|
||||
'git clone -- %s %s',
|
||||
(string)$repository->getCloneURIObject(),
|
||||
$path);
|
||||
}
|
||||
|
||||
$resource
|
||||
->setAttribute('workingcopy.root', $root)
|
||||
->setAttribute('workingcopy.path', $path)
|
||||
->activateResource();
|
||||
}
|
||||
|
||||
|
@ -135,49 +170,99 @@ final class DrydockWorkingCopyBlueprintImplementation
|
|||
// Destroy the lease on the host.
|
||||
$lease->releaseOnDestruction();
|
||||
|
||||
// Destroy the working copy on disk.
|
||||
$command_type = DrydockCommandInterface::INTERFACE_TYPE;
|
||||
$interface = $lease->getInterface($command_type);
|
||||
if ($lease->isActive()) {
|
||||
// Destroy the working copy on disk.
|
||||
$command_type = DrydockCommandInterface::INTERFACE_TYPE;
|
||||
$interface = $lease->getInterface($command_type);
|
||||
|
||||
$root_key = 'workingcopy.root';
|
||||
$root = $resource->getAttribute($root_key);
|
||||
if (strlen($root)) {
|
||||
$interface->execx('rm -rf -- %s', $root);
|
||||
$root_key = 'workingcopy.root';
|
||||
$root = $resource->getAttribute($root_key);
|
||||
if (strlen($root)) {
|
||||
$interface->execx('rm -rf -- %s', $root);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getResourceName(
|
||||
DrydockBlueprint $blueprint,
|
||||
DrydockResource $resource) {
|
||||
return pht('Working Copy');
|
||||
}
|
||||
|
||||
|
||||
public function activateLease(
|
||||
DrydockBlueprint $blueprint,
|
||||
DrydockResource $resource,
|
||||
DrydockLease $lease) {
|
||||
|
||||
$host_lease = $this->loadHostLease($resource);
|
||||
$command_type = DrydockCommandInterface::INTERFACE_TYPE;
|
||||
$interface = $lease->getInterface($command_type);
|
||||
$interface = $host_lease->getInterface($command_type);
|
||||
|
||||
$cmd = array();
|
||||
$arg = array();
|
||||
$map = $lease->getAttribute('repositories.map');
|
||||
$root = $resource->getAttribute('workingcopy.root');
|
||||
|
||||
$cmd[] = 'git clean -d --force';
|
||||
$cmd[] = 'git reset --hard HEAD';
|
||||
$cmd[] = 'git fetch';
|
||||
$default = null;
|
||||
foreach ($map as $directory => $spec) {
|
||||
$cmd = array();
|
||||
$arg = array();
|
||||
|
||||
$commit = $lease->getAttribute('commit');
|
||||
$branch = $lease->getAttribute('branch');
|
||||
$cmd[] = 'cd %s';
|
||||
$arg[] = "{$root}/repo/{$directory}/";
|
||||
|
||||
if ($commit !== null) {
|
||||
$cmd[] = 'git reset --hard %s';
|
||||
$arg[] = $commit;
|
||||
} else if ($branch !== null) {
|
||||
$cmd[] = 'git reset --hard %s';
|
||||
$arg[] = $branch;
|
||||
$cmd[] = 'git clean -d --force';
|
||||
$cmd[] = 'git fetch';
|
||||
|
||||
$commit = idx($spec, 'commit');
|
||||
$branch = idx($spec, 'branch');
|
||||
|
||||
$ref = idx($spec, 'ref');
|
||||
|
||||
if ($commit !== null) {
|
||||
$cmd[] = 'git reset --hard %s';
|
||||
$arg[] = $commit;
|
||||
} else if ($branch !== null) {
|
||||
$cmd[] = 'git checkout %s';
|
||||
$arg[] = $branch;
|
||||
|
||||
$cmd[] = 'git reset --hard origin/%s';
|
||||
$arg[] = $branch;
|
||||
} else if ($ref) {
|
||||
$ref_uri = $ref['uri'];
|
||||
$ref_ref = $ref['ref'];
|
||||
|
||||
$cmd[] = 'git fetch --no-tags -- %s +%s:%s';
|
||||
$arg[] = $ref_uri;
|
||||
$arg[] = $ref_ref;
|
||||
$arg[] = $ref_ref;
|
||||
|
||||
$cmd[] = 'git checkout %s';
|
||||
$arg[] = $ref_ref;
|
||||
|
||||
$cmd[] = 'git reset --hard %s';
|
||||
$arg[] = $ref_ref;
|
||||
} else {
|
||||
$cmd[] = 'git reset --hard HEAD';
|
||||
}
|
||||
|
||||
$cmd = implode(' && ', $cmd);
|
||||
$argv = array_merge(array($cmd), $arg);
|
||||
|
||||
$result = call_user_func_array(
|
||||
array($interface, 'execx'),
|
||||
$argv);
|
||||
|
||||
if (idx($spec, 'default')) {
|
||||
$default = $directory;
|
||||
}
|
||||
}
|
||||
|
||||
$cmd = implode(' && ', $cmd);
|
||||
$argv = array_merge(array($cmd), $arg);
|
||||
if ($default === null) {
|
||||
$default = head_key($map);
|
||||
}
|
||||
|
||||
$result = call_user_func_array(
|
||||
array($interface, 'execx'),
|
||||
$argv);
|
||||
// TODO: Use working storage?
|
||||
$lease->setAttribute('workingcopy.default', "{$root}/repo/{$default}/");
|
||||
|
||||
$lease->activateOnResource($resource);
|
||||
}
|
||||
|
@ -217,35 +302,44 @@ final class DrydockWorkingCopyBlueprintImplementation
|
|||
$host_lease = $this->loadHostLease($resource);
|
||||
$command_interface = $host_lease->getInterface($type);
|
||||
|
||||
$path = $resource->getAttribute('workingcopy.path');
|
||||
$path = $lease->getAttribute('workingcopy.default');
|
||||
$command_interface->setWorkingDirectory($path);
|
||||
|
||||
return $command_interface;
|
||||
}
|
||||
}
|
||||
|
||||
private function loadRepository($repository_phid) {
|
||||
$repository = id(new PhabricatorRepositoryQuery())
|
||||
private function loadRepositories(array $phids) {
|
||||
$repositories = id(new PhabricatorRepositoryQuery())
|
||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||
->withPHIDs(array($repository_phid))
|
||||
->executeOne();
|
||||
if (!$repository) {
|
||||
// TODO: Permanent failure.
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Repository PHID "%s" does not exist.',
|
||||
$repository_phid));
|
||||
->withPHIDs($phids)
|
||||
->execute();
|
||||
$repositories = mpull($repositories, null, 'getPHID');
|
||||
|
||||
foreach ($phids as $phid) {
|
||||
if (empty($repositories[$phid])) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Repository PHID "%s" does not exist.',
|
||||
$phid));
|
||||
}
|
||||
}
|
||||
|
||||
switch ($repository->getVersionControlSystem()) {
|
||||
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
|
||||
break;
|
||||
default:
|
||||
// TODO: Permanent failure.
|
||||
throw new Exception(pht('Unsupported VCS!'));
|
||||
foreach ($repositories as $repository) {
|
||||
$repository_vcs = $repository->getVersionControlSystem();
|
||||
switch ($repository_vcs) {
|
||||
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
|
||||
break;
|
||||
default:
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Repository ("%s") has unsupported VCS ("%s").',
|
||||
$repository->getPHID(),
|
||||
$repository_vcs));
|
||||
}
|
||||
}
|
||||
|
||||
return $repository;
|
||||
return $repositories;
|
||||
}
|
||||
|
||||
private function loadHostLease(DrydockResource $resource) {
|
||||
|
@ -258,8 +352,10 @@ final class DrydockWorkingCopyBlueprintImplementation
|
|||
->withPHIDs(array($lease_phid))
|
||||
->executeOne();
|
||||
if (!$lease) {
|
||||
// TODO: Permanent failure.
|
||||
throw new Exception(pht('Unable to load lease "%s".', $lease_phid));
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Unable to load lease ("%s").',
|
||||
$lease_phid));
|
||||
}
|
||||
|
||||
return $lease;
|
||||
|
|
|
@ -8,4 +8,8 @@ final class DrydockDefaultViewCapability extends PhabricatorPolicyCapability {
|
|||
return pht('Default Blueprint View Policy');
|
||||
}
|
||||
|
||||
public function shouldAllowPublicPolicySetting() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -56,11 +56,19 @@ final class DrydockBlueprintViewController extends DrydockBlueprintController {
|
|||
new DrydockBlueprintTransactionQuery());
|
||||
$timeline->setShouldTerminate(true);
|
||||
|
||||
$log_query = id(new DrydockLogQuery())
|
||||
->withBlueprintPHIDs(array($blueprint->getPHID()));
|
||||
|
||||
$log_box = $this->buildLogBox(
|
||||
$log_query,
|
||||
$this->getApplicationURI("blueprint/{$id}/logs/query/all/"));
|
||||
|
||||
return $this->buildApplicationPage(
|
||||
array(
|
||||
$crumbs,
|
||||
$object_box,
|
||||
$resource_box,
|
||||
$log_box,
|
||||
$timeline,
|
||||
),
|
||||
array(
|
||||
|
|
|
@ -15,7 +15,6 @@ final class DrydockConsoleController extends DrydockController {
|
|||
$nav->addFilter('blueprint', pht('Blueprints'));
|
||||
$nav->addFilter('resource', pht('Resources'));
|
||||
$nav->addFilter('lease', pht('Leases'));
|
||||
$nav->addFilter('log', pht('Logs'));
|
||||
|
||||
$nav->selectFilter(null);
|
||||
|
||||
|
@ -31,6 +30,7 @@ final class DrydockConsoleController extends DrydockController {
|
|||
$menu->addItem(
|
||||
id(new PHUIObjectItemView())
|
||||
->setHeader(pht('Blueprints'))
|
||||
->setFontIcon('fa-map-o')
|
||||
->setHref($this->getApplicationURI('blueprint/'))
|
||||
->addAttribute(
|
||||
pht(
|
||||
|
@ -40,6 +40,7 @@ final class DrydockConsoleController extends DrydockController {
|
|||
$menu->addItem(
|
||||
id(new PHUIObjectItemView())
|
||||
->setHeader(pht('Resources'))
|
||||
->setFontIcon('fa-map')
|
||||
->setHref($this->getApplicationURI('resource/'))
|
||||
->addAttribute(
|
||||
pht('View and manage resources Drydock has built, like hosts.')));
|
||||
|
@ -47,16 +48,10 @@ final class DrydockConsoleController extends DrydockController {
|
|||
$menu->addItem(
|
||||
id(new PHUIObjectItemView())
|
||||
->setHeader(pht('Leases'))
|
||||
->setFontIcon('fa-link')
|
||||
->setHref($this->getApplicationURI('lease/'))
|
||||
->addAttribute(pht('Manage leases on resources.')));
|
||||
|
||||
$menu->addItem(
|
||||
id(new PHUIObjectItemView())
|
||||
->setHeader(pht('Logs'))
|
||||
->setHref($this->getApplicationURI('log/'))
|
||||
->addAttribute(pht('View logs.')));
|
||||
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs();
|
||||
$crumbs->addTextCrumb(pht('Console'));
|
||||
|
||||
|
|
|
@ -85,4 +85,31 @@ abstract class DrydockController extends PhabricatorController {
|
|||
->addRawContent($table);
|
||||
}
|
||||
|
||||
protected function buildLogBox(DrydockLogQuery $query, $all_uri) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$logs = $query
|
||||
->setViewer($viewer)
|
||||
->setLimit(100)
|
||||
->execute();
|
||||
|
||||
$log_table = id(new DrydockLogListView())
|
||||
->setUser($viewer)
|
||||
->setLogs($logs)
|
||||
->render();
|
||||
|
||||
$log_header = id(new PHUIHeaderView())
|
||||
->setHeader(pht('Logs'))
|
||||
->addActionLink(
|
||||
id(new PHUIButtonView())
|
||||
->setTag('a')
|
||||
->setHref($all_uri)
|
||||
->setIconFont('fa-search')
|
||||
->setText(pht('View All Logs')));
|
||||
|
||||
return id(new PHUIObjectBoxView())
|
||||
->setHeader($log_header)
|
||||
->setTable($log_table);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ abstract class DrydockLeaseController
|
|||
$this->getApplicationURI('resource/'));
|
||||
|
||||
$crumbs->addTextCrumb(
|
||||
$resource->getName(),
|
||||
$resource->getResourceName(),
|
||||
$this->getApplicationURI("resource/{$id}/"));
|
||||
|
||||
$crumbs->addTextCrumb(
|
||||
|
|
|
@ -9,35 +9,33 @@ final class DrydockLeaseViewController extends DrydockLeaseController {
|
|||
$lease = id(new DrydockLeaseQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($id))
|
||||
->needUnconsumedCommands(true)
|
||||
->executeOne();
|
||||
if (!$lease) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$lease_uri = $this->getApplicationURI('lease/'.$lease->getID().'/');
|
||||
$id = $lease->getID();
|
||||
$lease_uri = $this->getApplicationURI("lease/{$id}/");
|
||||
|
||||
$title = pht('Lease %d', $lease->getID());
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setHeader($title);
|
||||
|
||||
if ($lease->isReleasing()) {
|
||||
$header->setStatus('fa-exclamation-triangle', 'red', pht('Releasing'));
|
||||
}
|
||||
|
||||
$actions = $this->buildActionListView($lease);
|
||||
$properties = $this->buildPropertyListView($lease, $actions);
|
||||
|
||||
$pager = new PHUIPagerView();
|
||||
$pager->setURI(new PhutilURI($lease_uri), 'offset');
|
||||
$pager->setOffset($request->getInt('offset'));
|
||||
$log_query = id(new DrydockLogQuery())
|
||||
->withLeasePHIDs(array($lease->getPHID()));
|
||||
|
||||
$logs = id(new DrydockLogQuery())
|
||||
->setViewer($viewer)
|
||||
->withLeaseIDs(array($lease->getID()))
|
||||
->executeWithOffsetPager($pager);
|
||||
|
||||
$log_table = id(new DrydockLogListView())
|
||||
->setUser($viewer)
|
||||
->setLogs($logs)
|
||||
->render();
|
||||
$log_table->appendChild($pager);
|
||||
$log_box = $this->buildLogBox(
|
||||
$log_query,
|
||||
$this->getApplicationURI("lease/{$id}/logs/query/all/"));
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs();
|
||||
$crumbs->addTextCrumb($title, $lease_uri);
|
||||
|
@ -51,10 +49,6 @@ final class DrydockLeaseViewController extends DrydockLeaseController {
|
|||
->addPropertyList($locks, pht('Slot Locks'))
|
||||
->addPropertyList($commands, pht('Commands'));
|
||||
|
||||
$log_box = id(new PHUIObjectBoxView())
|
||||
->setHeaderText(pht('Lease Logs'))
|
||||
->setTable($log_table);
|
||||
|
||||
return $this->buildApplicationPage(
|
||||
array(
|
||||
$crumbs,
|
||||
|
@ -78,6 +72,10 @@ final class DrydockLeaseViewController extends DrydockLeaseController {
|
|||
$id = $lease->getID();
|
||||
|
||||
$can_release = $lease->canRelease();
|
||||
if ($lease->isReleasing()) {
|
||||
$can_release = false;
|
||||
}
|
||||
|
||||
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
||||
$viewer,
|
||||
$lease,
|
||||
|
@ -110,6 +108,14 @@ final class DrydockLeaseViewController extends DrydockLeaseController {
|
|||
pht('Resource Type'),
|
||||
$lease->getResourceType());
|
||||
|
||||
$owner_phid = $lease->getOwnerPHID();
|
||||
if ($owner_phid) {
|
||||
$owner_display = $viewer->renderHandle($owner_phid);
|
||||
} else {
|
||||
$owner_display = phutil_tag('em', array(), pht('No Owner'));
|
||||
}
|
||||
$view->addProperty(pht('Owner'), $owner_display);
|
||||
|
||||
$resource_phid = $lease->getResourcePHID();
|
||||
if ($resource_phid) {
|
||||
$resource_display = $viewer->renderHandle($resource_phid);
|
||||
|
|
|
@ -3,13 +3,60 @@
|
|||
abstract class DrydockLogController
|
||||
extends DrydockController {
|
||||
|
||||
private $blueprint;
|
||||
private $resource;
|
||||
private $lease;
|
||||
|
||||
public function setBlueprint(DrydockBlueprint $blueprint) {
|
||||
$this->blueprint = $blueprint;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getBlueprint() {
|
||||
return $this->blueprint;
|
||||
}
|
||||
|
||||
public function setResource(DrydockResource $resource) {
|
||||
$this->resource = $resource;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getResource() {
|
||||
return $this->resource;
|
||||
}
|
||||
|
||||
public function setLease(DrydockLease $lease) {
|
||||
$this->lease = $lease;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLease() {
|
||||
return $this->lease;
|
||||
}
|
||||
|
||||
public function buildSideNavView() {
|
||||
$nav = new AphrontSideNavFilterView();
|
||||
$nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
|
||||
|
||||
id(new DrydockLogSearchEngine())
|
||||
->setViewer($this->getRequest()->getUser())
|
||||
->addNavigationItems($nav->getMenu());
|
||||
$engine = id(new DrydockLogSearchEngine())
|
||||
->setViewer($this->getRequest()->getUser());
|
||||
|
||||
$blueprint = $this->getBlueprint();
|
||||
if ($blueprint) {
|
||||
$engine->setBlueprint($blueprint);
|
||||
}
|
||||
|
||||
$resource = $this->getResource();
|
||||
if ($resource) {
|
||||
$engine->setResource($resource);
|
||||
}
|
||||
|
||||
$lease = $this->getLease();
|
||||
if ($lease) {
|
||||
$engine->setLease($lease);
|
||||
}
|
||||
|
||||
$engine->addNavigationItems($nav->getMenu());
|
||||
|
||||
$nav->selectFilter(null);
|
||||
|
||||
|
@ -18,9 +65,54 @@ abstract class DrydockLogController
|
|||
|
||||
protected function buildApplicationCrumbs() {
|
||||
$crumbs = parent::buildApplicationCrumbs();
|
||||
$crumbs->addTextCrumb(
|
||||
pht('Logs'),
|
||||
$this->getApplicationURI('log/'));
|
||||
|
||||
$blueprint = $this->getBlueprint();
|
||||
$resource = $this->getResource();
|
||||
$lease = $this->getLease();
|
||||
if ($blueprint) {
|
||||
$id = $blueprint->getID();
|
||||
|
||||
$crumbs->addTextCrumb(
|
||||
pht('Blueprints'),
|
||||
$this->getApplicationURI('blueprint/'));
|
||||
|
||||
$crumbs->addTextCrumb(
|
||||
$blueprint->getBlueprintName(),
|
||||
$this->getApplicationURI("blueprint/{$id}/"));
|
||||
|
||||
$crumbs->addTextCrumb(
|
||||
pht('Logs'),
|
||||
$this->getApplicationURI("blueprint/{$id}/logs/"));
|
||||
} else if ($resource) {
|
||||
$id = $resource->getID();
|
||||
|
||||
$crumbs->addTextCrumb(
|
||||
pht('Resources'),
|
||||
$this->getApplicationURI('resource/'));
|
||||
|
||||
$crumbs->addTextCrumb(
|
||||
$resource->getResourceName(),
|
||||
$this->getApplicationURI("resource/{$id}/"));
|
||||
|
||||
$crumbs->addTextCrumb(
|
||||
pht('Logs'),
|
||||
$this->getApplicationURI("resource/{$id}/logs/"));
|
||||
} else if ($lease) {
|
||||
$id = $lease->getID();
|
||||
|
||||
$crumbs->addTextCrumb(
|
||||
pht('Leases'),
|
||||
$this->getApplicationURI('lease/'));
|
||||
|
||||
$crumbs->addTextCrumb(
|
||||
$lease->getLeaseName(),
|
||||
$this->getApplicationURI("lease/{$id}/"));
|
||||
|
||||
$crumbs->addTextCrumb(
|
||||
pht('Logs'),
|
||||
$this->getApplicationURI("lease/{$id}/logs/"));
|
||||
}
|
||||
|
||||
return $crumbs;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,11 +8,53 @@ final class DrydockLogListController extends DrydockLogController {
|
|||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $request->getViewer();
|
||||
$querykey = $request->getURIData('queryKey');
|
||||
$engine = new DrydockLogSearchEngine();
|
||||
|
||||
$id = $request->getURIData('id');
|
||||
$type = $request->getURIData('type');
|
||||
switch ($type) {
|
||||
case 'blueprint':
|
||||
$blueprint = id(new DrydockBlueprintQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($id))
|
||||
->executeOne();
|
||||
if (!$blueprint) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
$engine->setBlueprint($blueprint);
|
||||
$this->setBlueprint($blueprint);
|
||||
break;
|
||||
case 'resource':
|
||||
$resource = id(new DrydockResourceQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($id))
|
||||
->executeOne();
|
||||
if (!$resource) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
$engine->setResource($resource);
|
||||
$this->setResource($resource);
|
||||
break;
|
||||
case 'lease':
|
||||
$lease = id(new DrydockLeaseQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($id))
|
||||
->executeOne();
|
||||
if (!$lease) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
$engine->setLease($lease);
|
||||
$this->setLease($lease);
|
||||
break;
|
||||
default:
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$query_key = $request->getURIData('queryKey');
|
||||
|
||||
$controller = id(new PhabricatorApplicationSearchController())
|
||||
->setQueryKey($querykey)
|
||||
->setSearchEngine(new DrydockLogSearchEngine())
|
||||
->setQueryKey($query_key)
|
||||
->setSearchEngine($engine)
|
||||
->setNavigation($this->buildSideNavView());
|
||||
|
||||
return $this->delegateToController($controller);
|
||||
|
|
|
@ -9,38 +9,38 @@ final class DrydockResourceViewController extends DrydockResourceController {
|
|||
$resource = id(new DrydockResourceQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($id))
|
||||
->needUnconsumedCommands(true)
|
||||
->executeOne();
|
||||
if (!$resource) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$title = pht('Resource %s %s', $resource->getID(), $resource->getName());
|
||||
$title = pht(
|
||||
'Resource %s %s',
|
||||
$resource->getID(),
|
||||
$resource->getResourceName());
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setUser($viewer)
|
||||
->setPolicyObject($resource)
|
||||
->setHeader($title);
|
||||
|
||||
if ($resource->isReleasing()) {
|
||||
$header->setStatus('fa-exclamation-triangle', 'red', pht('Releasing'));
|
||||
}
|
||||
|
||||
$actions = $this->buildActionListView($resource);
|
||||
$properties = $this->buildPropertyListView($resource, $actions);
|
||||
|
||||
$resource_uri = 'resource/'.$resource->getID().'/';
|
||||
$resource_uri = $this->getApplicationURI($resource_uri);
|
||||
$id = $resource->getID();
|
||||
$resource_uri = $this->getApplicationURI("resource/{$id}/");
|
||||
|
||||
$pager = new PHUIPagerView();
|
||||
$pager->setURI(new PhutilURI($resource_uri), 'offset');
|
||||
$pager->setOffset($request->getInt('offset'));
|
||||
$log_query = id(new DrydockLogQuery())
|
||||
->withResourcePHIDs(array($resource->getPHID()));
|
||||
|
||||
$logs = id(new DrydockLogQuery())
|
||||
->setViewer($viewer)
|
||||
->withResourceIDs(array($resource->getID()))
|
||||
->executeWithOffsetPager($pager);
|
||||
|
||||
$log_table = id(new DrydockLogListView())
|
||||
->setUser($viewer)
|
||||
->setLogs($logs)
|
||||
->render();
|
||||
$log_table->appendChild($pager);
|
||||
$log_box = $this->buildLogBox(
|
||||
$log_query,
|
||||
$this->getApplicationURI("resource/{$id}/logs/query/all/"));
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs();
|
||||
$crumbs->addTextCrumb(pht('Resource %d', $resource->getID()));
|
||||
|
@ -56,10 +56,6 @@ final class DrydockResourceViewController extends DrydockResourceController {
|
|||
|
||||
$lease_box = $this->buildLeaseBox($resource);
|
||||
|
||||
$log_box = id(new PHUIObjectBoxView())
|
||||
->setHeaderText(pht('Resource Logs'))
|
||||
->setTable($log_table);
|
||||
|
||||
return $this->buildApplicationPage(
|
||||
array(
|
||||
$crumbs,
|
||||
|
@ -82,6 +78,10 @@ final class DrydockResourceViewController extends DrydockResourceController {
|
|||
->setObject($resource);
|
||||
|
||||
$can_release = $resource->canRelease();
|
||||
if ($resource->isReleasing()) {
|
||||
$can_release = false;
|
||||
}
|
||||
|
||||
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
||||
$viewer,
|
||||
$resource,
|
||||
|
@ -116,6 +116,14 @@ final class DrydockResourceViewController extends DrydockResourceController {
|
|||
pht('Status'),
|
||||
$status);
|
||||
|
||||
$until = $resource->getUntil();
|
||||
if ($until) {
|
||||
$until_display = phabricator_datetime($until, $viewer);
|
||||
} else {
|
||||
$until_display = phutil_tag('em', array(), pht('Never'));
|
||||
}
|
||||
$view->addProperty(pht('Expires'), $until_display);
|
||||
|
||||
$view->addProperty(
|
||||
pht('Resource Type'),
|
||||
$resource->getType());
|
||||
|
|
|
@ -22,4 +22,8 @@ final class DrydockSlotLockException extends Exception {
|
|||
parent::__construct($message);
|
||||
}
|
||||
|
||||
public function getLockMap() {
|
||||
return $this->lockMap;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
final class DrydockLogGarbageCollector
|
||||
extends PhabricatorGarbageCollector {
|
||||
|
||||
const COLLECTORCONST = 'drydock.logs';
|
||||
|
||||
public function getCollectorName() {
|
||||
return pht('Drydock Logs');
|
||||
}
|
||||
|
||||
public function getDefaultRetentionPolicy() {
|
||||
return phutil_units('30 days in seconds');
|
||||
}
|
||||
|
||||
protected function collectGarbage() {
|
||||
$log_table = new DrydockLog();
|
||||
$conn_w = $log_table->establishConnection('w');
|
||||
|
||||
queryfx(
|
||||
$conn_w,
|
||||
'DELETE FROM %T WHERE epoch <= %d LIMIT 100',
|
||||
$log_table->getTableName(),
|
||||
$this->getGarbageEpoch());
|
||||
|
||||
return ($conn_w->getAffectedRows() == 100);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
final class DrydockLeaseAcquiredLogType extends DrydockLogType {
|
||||
|
||||
const LOGCONST = 'core.lease.acquired';
|
||||
|
||||
public function getLogTypeName() {
|
||||
return pht('Lease Acquired');
|
||||
}
|
||||
|
||||
public function getLogTypeIcon(array $data) {
|
||||
return 'fa-link yellow';
|
||||
}
|
||||
|
||||
public function renderLog(array $data) {
|
||||
return pht('Lease acquired.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
final class DrydockLeaseActivatedLogType extends DrydockLogType {
|
||||
|
||||
const LOGCONST = 'core.lease.activated';
|
||||
|
||||
public function getLogTypeName() {
|
||||
return pht('Lease Activated');
|
||||
}
|
||||
|
||||
public function getLogTypeIcon(array $data) {
|
||||
return 'fa-link green';
|
||||
}
|
||||
|
||||
public function renderLog(array $data) {
|
||||
return pht('Lease activated.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
final class DrydockLeaseActivationFailureLogType extends DrydockLogType {
|
||||
|
||||
const LOGCONST = 'core.lease.activation-failure';
|
||||
|
||||
public function getLogTypeName() {
|
||||
return pht('Activation Failed');
|
||||
}
|
||||
|
||||
public function getLogTypeIcon(array $data) {
|
||||
return 'fa-times red';
|
||||
}
|
||||
|
||||
public function renderLog(array $data) {
|
||||
$class = idx($data, 'class');
|
||||
$message = idx($data, 'message');
|
||||
|
||||
return pht('Lease activation failed: [%s] %s', $class, $message);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
final class DrydockLeaseActivationYieldLogType extends DrydockLogType {
|
||||
|
||||
const LOGCONST = 'core.lease.activation-yield';
|
||||
|
||||
public function getLogTypeName() {
|
||||
return pht('Waiting for Activation');
|
||||
}
|
||||
|
||||
public function getLogTypeIcon(array $data) {
|
||||
return 'fa-clock-o green';
|
||||
}
|
||||
|
||||
public function renderLog(array $data) {
|
||||
$duration = idx($data, 'duration');
|
||||
|
||||
return pht(
|
||||
'Waiting %s second(s) for lease to activate.',
|
||||
new PhutilNumber($duration));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
final class DrydockLeaseDestroyedLogType extends DrydockLogType {
|
||||
|
||||
const LOGCONST = 'core.lease.destroyed';
|
||||
|
||||
public function getLogTypeName() {
|
||||
return pht('Lease Destroyed');
|
||||
}
|
||||
|
||||
public function getLogTypeIcon(array $data) {
|
||||
return 'fa-link grey';
|
||||
}
|
||||
|
||||
public function renderLog(array $data) {
|
||||
return pht('Lease destroyed.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
final class DrydockLeaseQueuedLogType extends DrydockLogType {
|
||||
|
||||
const LOGCONST = 'core.lease.queued';
|
||||
|
||||
public function getLogTypeName() {
|
||||
return pht('Lease Queued');
|
||||
}
|
||||
|
||||
public function getLogTypeIcon(array $data) {
|
||||
return 'fa-link blue';
|
||||
}
|
||||
|
||||
public function renderLog(array $data) {
|
||||
return pht('Lease queued for acquisition.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
final class DrydockLeaseReleasedLogType extends DrydockLogType {
|
||||
|
||||
const LOGCONST = 'core.lease.released';
|
||||
|
||||
public function getLogTypeName() {
|
||||
return pht('Lease Released');
|
||||
}
|
||||
|
||||
public function getLogTypeIcon(array $data) {
|
||||
return 'fa-link black';
|
||||
}
|
||||
|
||||
public function renderLog(array $data) {
|
||||
return pht('Lease released.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
final class DrydockLeaseWaitingForResourcesLogType extends DrydockLogType {
|
||||
|
||||
const LOGCONST = 'core.lease.waiting-for-resources';
|
||||
|
||||
public function getLogTypeName() {
|
||||
return pht('Waiting For Resource');
|
||||
}
|
||||
|
||||
public function getLogTypeIcon(array $data) {
|
||||
return 'fa-clock-o yellow';
|
||||
}
|
||||
|
||||
public function renderLog(array $data) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$blueprint_phids = idx($data, 'blueprintPHIDs', array());
|
||||
|
||||
return pht(
|
||||
'Waiting for available resources from: %s.',
|
||||
$viewer->renderHandleList($blueprint_phids));
|
||||
}
|
||||
|
||||
}
|
41
src/applications/drydock/logtype/DrydockLogType.php
Normal file
41
src/applications/drydock/logtype/DrydockLogType.php
Normal file
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
abstract class DrydockLogType extends Phobject {
|
||||
|
||||
private $viewer;
|
||||
private $log;
|
||||
|
||||
abstract public function getLogTypeName();
|
||||
abstract public function getLogTypeIcon(array $data);
|
||||
abstract public function renderLog(array $data);
|
||||
|
||||
public function setViewer(PhabricatorUser $viewer) {
|
||||
$this->viewer = $viewer;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getViewer() {
|
||||
return $this->viewer;
|
||||
}
|
||||
|
||||
final public function setLog(DrydockLog $log) {
|
||||
$this->log = $log;
|
||||
return $this;
|
||||
}
|
||||
|
||||
final public function getLog() {
|
||||
return $this->log;
|
||||
}
|
||||
|
||||
final public function getLogTypeConstant() {
|
||||
return $this->getPhobjectClassConstant('LOGCONST', 64);
|
||||
}
|
||||
|
||||
final public static function getAllLogTypes() {
|
||||
return id(new PhutilClassMapQuery())
|
||||
->setAncestorClass(__CLASS__)
|
||||
->setUniqueMethod('getLogTypeConstant')
|
||||
->execute();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
final class DrydockResourceActivationFailureLogType extends DrydockLogType {
|
||||
|
||||
const LOGCONST = 'core.resource.activation-failure';
|
||||
|
||||
public function getLogTypeName() {
|
||||
return pht('Activation Failed');
|
||||
}
|
||||
|
||||
public function getLogTypeIcon(array $data) {
|
||||
return 'fa-times red';
|
||||
}
|
||||
|
||||
public function renderLog(array $data) {
|
||||
$class = idx($data, 'class');
|
||||
$message = idx($data, 'message');
|
||||
|
||||
return pht('Resource activation failed: [%s] %s', $class, $message);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
final class DrydockResourceActivationYieldLogType extends DrydockLogType {
|
||||
|
||||
const LOGCONST = 'core.resource.activation-yield';
|
||||
|
||||
public function getLogTypeName() {
|
||||
return pht('Waiting for Activation');
|
||||
}
|
||||
|
||||
public function getLogTypeIcon(array $data) {
|
||||
return 'fa-clock-o green';
|
||||
}
|
||||
|
||||
public function renderLog(array $data) {
|
||||
$duration = idx($data, 'duration');
|
||||
|
||||
return pht(
|
||||
'Waiting %s second(s) for resource to activate.',
|
||||
new PhutilNumber($duration));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
final class DrydockSlotLockFailureLogType extends DrydockLogType {
|
||||
|
||||
const LOGCONST = 'core.resource.slot-lock.failure';
|
||||
|
||||
public function getLogTypeName() {
|
||||
return pht('Slot Lock Failure');
|
||||
}
|
||||
|
||||
public function getLogTypeIcon(array $data) {
|
||||
return 'fa-lock yellow';
|
||||
}
|
||||
|
||||
public function renderLog(array $data) {
|
||||
$locks = idx($data, 'locks', array());
|
||||
if ($locks) {
|
||||
return pht(
|
||||
'Failed to acquire slot locks: %s.',
|
||||
implode(', ', array_keys($locks)));
|
||||
} else {
|
||||
return pht('Failed to acquire slot locks.');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -33,7 +33,7 @@ final class DrydockResourcePHIDType extends PhabricatorPHIDType {
|
|||
pht(
|
||||
'Resource %d: %s',
|
||||
$id,
|
||||
$resource->getName()));
|
||||
$resource->getResourceName()));
|
||||
|
||||
$handle->setURI("/drydock/resource/{$id}/");
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ final class DrydockLeaseQuery extends DrydockQuery {
|
|||
private $resourcePHIDs;
|
||||
private $statuses;
|
||||
private $datasourceQuery;
|
||||
private $needCommands;
|
||||
private $needUnconsumedCommands;
|
||||
|
||||
public function withIDs(array $ids) {
|
||||
$this->ids = $ids;
|
||||
|
@ -34,6 +34,11 @@ final class DrydockLeaseQuery extends DrydockQuery {
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function needUnconsumedCommands($need) {
|
||||
$this->needUnconsumedCommands = $need;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function newResultObject() {
|
||||
return new DrydockLease();
|
||||
}
|
||||
|
@ -71,6 +76,25 @@ final class DrydockLeaseQuery extends DrydockQuery {
|
|||
return $leases;
|
||||
}
|
||||
|
||||
protected function didFilterPage(array $leases) {
|
||||
if ($this->needUnconsumedCommands) {
|
||||
$commands = id(new DrydockCommandQuery())
|
||||
->setViewer($this->getViewer())
|
||||
->setParentQuery($this)
|
||||
->withTargetPHIDs(mpull($leases, 'getPHID'))
|
||||
->withConsumed(false)
|
||||
->execute();
|
||||
$commands = mgroup($commands, 'getTargetPHID');
|
||||
|
||||
foreach ($leases as $lease) {
|
||||
$list = idx($commands, $lease->getPHID(), array());
|
||||
$lease->attachUnconsumedCommands($list);
|
||||
}
|
||||
}
|
||||
|
||||
return $leases;
|
||||
}
|
||||
|
||||
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
|
||||
$where = parent::buildWhereClauseParts($conn);
|
||||
|
||||
|
|
|
@ -2,112 +2,125 @@
|
|||
|
||||
final class DrydockLogQuery extends DrydockQuery {
|
||||
|
||||
private $resourceIDs;
|
||||
private $leaseIDs;
|
||||
private $blueprintPHIDs;
|
||||
private $resourcePHIDs;
|
||||
private $leasePHIDs;
|
||||
|
||||
public function withResourceIDs(array $ids) {
|
||||
$this->resourceIDs = $ids;
|
||||
public function withBlueprintPHIDs(array $phids) {
|
||||
$this->blueprintPHIDs = $phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withLeaseIDs(array $ids) {
|
||||
$this->leaseIDs = $ids;
|
||||
public function withResourcePHIDs(array $phids) {
|
||||
$this->resourcePHIDs = $phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withLeasePHIDs(array $phids) {
|
||||
$this->leasePHIDs = $phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function newResultObject() {
|
||||
return new DrydockLog();
|
||||
}
|
||||
|
||||
protected function loadPage() {
|
||||
$table = new DrydockLog();
|
||||
$conn_r = $table->establishConnection('r');
|
||||
|
||||
$data = queryfx_all(
|
||||
$conn_r,
|
||||
'SELECT log.* FROM %T log %Q %Q %Q',
|
||||
$table->getTableName(),
|
||||
$this->buildWhereClause($conn_r),
|
||||
$this->buildOrderClause($conn_r),
|
||||
$this->buildLimitClause($conn_r));
|
||||
|
||||
return $table->loadAllFromArray($data);
|
||||
return $this->loadStandardPage($this->newResultObject());
|
||||
}
|
||||
|
||||
protected function willFilterPage(array $logs) {
|
||||
$resource_ids = array_filter(mpull($logs, 'getResourceID'));
|
||||
if ($resource_ids) {
|
||||
protected function didFilterPage(array $logs) {
|
||||
$blueprint_phids = array_filter(mpull($logs, 'getBlueprintPHID'));
|
||||
if ($blueprint_phids) {
|
||||
$blueprints = id(new DrydockBlueprintQuery())
|
||||
->setParentQuery($this)
|
||||
->setViewer($this->getViewer())
|
||||
->withPHIDs($blueprint_phids)
|
||||
->execute();
|
||||
$blueprints = mpull($blueprints, null, 'getPHID');
|
||||
} else {
|
||||
$blueprints = array();
|
||||
}
|
||||
|
||||
foreach ($logs as $key => $log) {
|
||||
$blueprint = null;
|
||||
$blueprint_phid = $log->getBlueprintPHID();
|
||||
if ($blueprint_phid) {
|
||||
$blueprint = idx($blueprints, $blueprint_phid);
|
||||
}
|
||||
$log->attachBlueprint($blueprint);
|
||||
}
|
||||
|
||||
$resource_phids = array_filter(mpull($logs, 'getResourcePHID'));
|
||||
if ($resource_phids) {
|
||||
$resources = id(new DrydockResourceQuery())
|
||||
->setParentQuery($this)
|
||||
->setViewer($this->getViewer())
|
||||
->withIDs(array_unique($resource_ids))
|
||||
->withPHIDs($resource_phids)
|
||||
->execute();
|
||||
$resources = mpull($resources, null, 'getPHID');
|
||||
} else {
|
||||
$resources = array();
|
||||
}
|
||||
|
||||
foreach ($logs as $key => $log) {
|
||||
$resource = null;
|
||||
if ($log->getResourceID()) {
|
||||
$resource = idx($resources, $log->getResourceID());
|
||||
if (!$resource) {
|
||||
unset($logs[$key]);
|
||||
continue;
|
||||
}
|
||||
$resource_phid = $log->getResourcePHID();
|
||||
if ($resource_phid) {
|
||||
$resource = idx($resources, $resource_phid);
|
||||
}
|
||||
$log->attachResource($resource);
|
||||
}
|
||||
|
||||
$lease_ids = array_filter(mpull($logs, 'getLeaseID'));
|
||||
if ($lease_ids) {
|
||||
$lease_phids = array_filter(mpull($logs, 'getLeasePHID'));
|
||||
if ($lease_phids) {
|
||||
$leases = id(new DrydockLeaseQuery())
|
||||
->setParentQuery($this)
|
||||
->setViewer($this->getViewer())
|
||||
->withIDs(array_unique($lease_ids))
|
||||
->withPHIDs($lease_phids)
|
||||
->execute();
|
||||
$leases = mpull($leases, null, 'getPHID');
|
||||
} else {
|
||||
$leases = array();
|
||||
}
|
||||
|
||||
foreach ($logs as $key => $log) {
|
||||
$lease = null;
|
||||
if ($log->getLeaseID()) {
|
||||
$lease = idx($leases, $log->getLeaseID());
|
||||
if (!$lease) {
|
||||
unset($logs[$key]);
|
||||
continue;
|
||||
}
|
||||
$lease_phid = $log->getLeasePHID();
|
||||
if ($lease_phid) {
|
||||
$lease = idx($leases, $lease_phid);
|
||||
}
|
||||
$log->attachLease($lease);
|
||||
}
|
||||
|
||||
// These logs are meaningless and their policies aren't computable. They
|
||||
// shouldn't exist, but throw them away if they do.
|
||||
foreach ($logs as $key => $log) {
|
||||
if (!$log->getResource() && !$log->getLease()) {
|
||||
unset($logs[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
return $logs;
|
||||
}
|
||||
|
||||
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
|
||||
$where = array();
|
||||
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
|
||||
$where = parent::buildWhereClauseParts($conn);
|
||||
|
||||
if ($this->resourceIDs !== null) {
|
||||
if ($this->blueprintPHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
'resourceID IN (%Ld)',
|
||||
$this->resourceIDs);
|
||||
$conn,
|
||||
'blueprintPHID IN (%Ls)',
|
||||
$this->blueprintPHIDs);
|
||||
}
|
||||
|
||||
if ($this->leaseIDs !== null) {
|
||||
if ($this->resourcePHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
'leaseID IN (%Ld)',
|
||||
$this->leaseIDs);
|
||||
$conn,
|
||||
'resourcePHID IN (%Ls)',
|
||||
$this->resourcePHIDs);
|
||||
}
|
||||
|
||||
$where[] = $this->buildPagingClause($conn_r);
|
||||
if ($this->leasePHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'leasePHID IN (%Ls)',
|
||||
$this->leasePHIDs);
|
||||
}
|
||||
|
||||
return $this->formatWhereClause($where);
|
||||
return $where;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,6 +2,43 @@
|
|||
|
||||
final class DrydockLogSearchEngine extends PhabricatorApplicationSearchEngine {
|
||||
|
||||
private $blueprint;
|
||||
private $resource;
|
||||
private $lease;
|
||||
|
||||
public function setBlueprint(DrydockBlueprint $blueprint) {
|
||||
$this->blueprint = $blueprint;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getBlueprint() {
|
||||
return $this->blueprint;
|
||||
}
|
||||
|
||||
public function setResource(DrydockResource $resource) {
|
||||
$this->resource = $resource;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getResource() {
|
||||
return $this->resource;
|
||||
}
|
||||
|
||||
public function setLease(DrydockLease $lease) {
|
||||
$this->lease = $lease;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLease() {
|
||||
return $this->lease;
|
||||
}
|
||||
|
||||
public function canUseInPanelContext() {
|
||||
// Prevent use on Dashboard panels since all log queries currently need a
|
||||
// parent object and these don't seem particularly useful in any case.
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getResultTypeDescription() {
|
||||
return pht('Drydock Logs');
|
||||
}
|
||||
|
@ -10,75 +47,59 @@ final class DrydockLogSearchEngine extends PhabricatorApplicationSearchEngine {
|
|||
return 'PhabricatorDrydockApplication';
|
||||
}
|
||||
|
||||
public function buildSavedQueryFromRequest(AphrontRequest $request) {
|
||||
$query = new PhabricatorSavedQuery();
|
||||
|
||||
$query->setParameter(
|
||||
'resourcePHIDs',
|
||||
$this->readListFromRequest($request, 'resources'));
|
||||
$query->setParameter(
|
||||
'leasePHIDs',
|
||||
$this->readListFromRequest($request, 'leases'));
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
|
||||
$resource_phids = $saved->getParameter('resourcePHIDs', array());
|
||||
$lease_phids = $saved->getParameter('leasePHIDs', array());
|
||||
|
||||
// TODO: Change logs to use PHIDs instead of IDs.
|
||||
$resource_ids = array();
|
||||
$lease_ids = array();
|
||||
|
||||
if ($resource_phids) {
|
||||
$resource_ids = id(new DrydockResourceQuery())
|
||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||
->withPHIDs($resource_phids)
|
||||
->execute();
|
||||
$resource_ids = mpull($resource_ids, 'getID');
|
||||
}
|
||||
|
||||
if ($lease_phids) {
|
||||
$lease_ids = id(new DrydockLeaseQuery())
|
||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||
->withPHIDs($lease_phids)
|
||||
->execute();
|
||||
$lease_ids = mpull($lease_ids, 'getID');
|
||||
}
|
||||
|
||||
public function newQuery() {
|
||||
$query = new DrydockLogQuery();
|
||||
if ($resource_ids) {
|
||||
$query->withResourceIDs($resource_ids);
|
||||
|
||||
$blueprint = $this->getBlueprint();
|
||||
if ($blueprint) {
|
||||
$query->withBlueprintPHIDs(array($blueprint->getPHID()));
|
||||
}
|
||||
if ($lease_ids) {
|
||||
$query->withLeaseIDs($lease_ids);
|
||||
|
||||
$resource = $this->getResource();
|
||||
if ($resource) {
|
||||
$query->withResourcePHIDs(array($resource->getPHID()));
|
||||
}
|
||||
|
||||
$lease = $this->getLease();
|
||||
if ($lease) {
|
||||
$query->withLeasePHIDs(array($lease->getPHID()));
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function buildSearchForm(
|
||||
AphrontFormView $form,
|
||||
PhabricatorSavedQuery $saved) {
|
||||
protected function buildQueryFromParameters(array $map) {
|
||||
$query = $this->newQuery();
|
||||
|
||||
$form
|
||||
->appendControl(
|
||||
id(new AphrontFormTokenizerControl())
|
||||
->setDatasource(new DrydockResourceDatasource())
|
||||
->setName('resources')
|
||||
->setLabel(pht('Resources'))
|
||||
->setValue($saved->getParameter('resourcePHIDs', array())))
|
||||
->appendControl(
|
||||
id(new AphrontFormTokenizerControl())
|
||||
->setDatasource(new DrydockLeaseDatasource())
|
||||
->setName('leases')
|
||||
->setLabel(pht('Leases'))
|
||||
->setValue($saved->getParameter('leasePHIDs', array())));
|
||||
return $query;
|
||||
}
|
||||
|
||||
protected function buildCustomSearchFields() {
|
||||
return array();
|
||||
}
|
||||
|
||||
protected function getURI($path) {
|
||||
return '/drydock/log/'.$path;
|
||||
$blueprint = $this->getBlueprint();
|
||||
if ($blueprint) {
|
||||
$id = $blueprint->getID();
|
||||
return "/drydock/blueprint/{$id}/logs/{$path}";
|
||||
}
|
||||
|
||||
$resource = $this->getResource();
|
||||
if ($resource) {
|
||||
$id = $resource->getID();
|
||||
return "/drydock/resource/{$id}/logs/{$path}";
|
||||
}
|
||||
|
||||
$lease = $this->getLease();
|
||||
if ($lease) {
|
||||
$id = $lease->getID();
|
||||
return "/drydock/lease/{$id}/logs/{$path}";
|
||||
}
|
||||
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Search engine has no blueprint, resource, or lease.'));
|
||||
}
|
||||
|
||||
protected function getBuiltinQueryNames() {
|
||||
|
|
|
@ -8,6 +8,7 @@ final class DrydockResourceQuery extends DrydockQuery {
|
|||
private $types;
|
||||
private $blueprintPHIDs;
|
||||
private $datasourceQuery;
|
||||
private $needUnconsumedCommands;
|
||||
|
||||
public function withIDs(array $ids) {
|
||||
$this->ids = $ids;
|
||||
|
@ -39,6 +40,11 @@ final class DrydockResourceQuery extends DrydockQuery {
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function needUnconsumedCommands($need) {
|
||||
$this->needUnconsumedCommands = $need;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function newResultObject() {
|
||||
return new DrydockResource();
|
||||
}
|
||||
|
@ -69,6 +75,25 @@ final class DrydockResourceQuery extends DrydockQuery {
|
|||
return $resources;
|
||||
}
|
||||
|
||||
protected function didFilterPage(array $resources) {
|
||||
if ($this->needUnconsumedCommands) {
|
||||
$commands = id(new DrydockCommandQuery())
|
||||
->setViewer($this->getViewer())
|
||||
->setParentQuery($this)
|
||||
->withTargetPHIDs(mpull($resources, 'getPHID'))
|
||||
->withConsumed(false)
|
||||
->execute();
|
||||
$commands = mgroup($commands, 'getTargetPHID');
|
||||
|
||||
foreach ($resources as $resource) {
|
||||
$list = idx($commands, $resource->getPHID(), array());
|
||||
$resource->attachUnconsumedCommands($list);
|
||||
}
|
||||
}
|
||||
|
||||
return $resources;
|
||||
}
|
||||
|
||||
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
|
||||
$where = parent::buildWhereClauseParts($conn);
|
||||
|
||||
|
|
|
@ -107,6 +107,17 @@ final class DrydockBlueprint extends DrydockDAO
|
|||
return $this->fields;
|
||||
}
|
||||
|
||||
public function logEvent($type, array $data = array()) {
|
||||
$log = id(new DrydockLog())
|
||||
->setEpoch(PhabricatorTime::getNow())
|
||||
->setType($type)
|
||||
->setData($data);
|
||||
|
||||
$log->setBlueprintPHID($this->getPHID());
|
||||
|
||||
return $log->save();
|
||||
}
|
||||
|
||||
|
||||
/* -( Allocating Resources )----------------------------------------------- */
|
||||
|
||||
|
@ -162,6 +173,16 @@ final class DrydockBlueprint extends DrydockDAO
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* @task resource
|
||||
*/
|
||||
public function getResourceName(DrydockResource $resource) {
|
||||
return $this->getImplementation()->getResourceName(
|
||||
$this,
|
||||
$resource);
|
||||
}
|
||||
|
||||
|
||||
/* -( Acquiring Leases )--------------------------------------------------- */
|
||||
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@ final class DrydockLease extends DrydockDAO
|
|||
protected $status = DrydockLeaseStatus::STATUS_PENDING;
|
||||
|
||||
private $resource = self::ATTACHABLE;
|
||||
private $unconsumedCommands = self::ATTACHABLE;
|
||||
|
||||
private $releaseOnDestruction;
|
||||
private $isAcquired = false;
|
||||
private $isActivated = false;
|
||||
|
@ -104,6 +106,25 @@ final class DrydockLease extends DrydockDAO
|
|||
return ($this->resource !== null);
|
||||
}
|
||||
|
||||
public function getUnconsumedCommands() {
|
||||
return $this->assertAttached($this->unconsumedCommands);
|
||||
}
|
||||
|
||||
public function attachUnconsumedCommands(array $commands) {
|
||||
$this->unconsumedCommands = $commands;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isReleasing() {
|
||||
foreach ($this->getUnconsumedCommands() as $command) {
|
||||
if ($command->getCommand() == DrydockCommand::COMMAND_RELEASE) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function queueForActivation() {
|
||||
if ($this->getID()) {
|
||||
throw new Exception(
|
||||
|
@ -114,14 +135,9 @@ final class DrydockLease extends DrydockDAO
|
|||
->setStatus(DrydockLeaseStatus::STATUS_PENDING)
|
||||
->save();
|
||||
|
||||
$task = PhabricatorWorker::scheduleTask(
|
||||
'DrydockAllocatorWorker',
|
||||
array(
|
||||
'leasePHID' => $this->getPHID(),
|
||||
),
|
||||
array(
|
||||
'objectPHID' => $this->getPHID(),
|
||||
));
|
||||
$this->scheduleUpdate();
|
||||
|
||||
$this->logEvent(DrydockLeaseQueuedLogType::LOGCONST);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
@ -216,19 +232,37 @@ final class DrydockLease extends DrydockDAO
|
|||
}
|
||||
|
||||
$this->openTransaction();
|
||||
|
||||
$this
|
||||
->setResourcePHID($resource->getPHID())
|
||||
->setStatus($new_status)
|
||||
->save();
|
||||
|
||||
try {
|
||||
DrydockSlotLock::acquireLocks($this->getPHID(), $this->slotLocks);
|
||||
$this->slotLocks = array();
|
||||
} catch (DrydockSlotLockException $ex) {
|
||||
$this->killTransaction();
|
||||
|
||||
$this->logEvent(
|
||||
DrydockSlotLockFailureLogType::LOGCONST,
|
||||
array(
|
||||
'locks' => $ex->getLockMap(),
|
||||
));
|
||||
|
||||
throw $ex;
|
||||
}
|
||||
|
||||
try {
|
||||
$this
|
||||
->setResourcePHID($resource->getPHID())
|
||||
->attachResource($resource)
|
||||
->setStatus($new_status)
|
||||
->save();
|
||||
} catch (Exception $ex) {
|
||||
$this->killTransaction();
|
||||
throw $ex;
|
||||
}
|
||||
$this->saveTransaction();
|
||||
|
||||
$this->isAcquired = true;
|
||||
|
||||
$this->logEvent(DrydockLeaseAcquiredLogType::LOGCONST);
|
||||
|
||||
if ($new_status == DrydockLeaseStatus::STATUS_ACTIVE) {
|
||||
$this->didActivate();
|
||||
}
|
||||
|
@ -295,6 +329,16 @@ final class DrydockLease extends DrydockDAO
|
|||
}
|
||||
}
|
||||
|
||||
public function canReceiveCommands() {
|
||||
switch ($this->getStatus()) {
|
||||
case DrydockLeaseStatus::STATUS_RELEASED:
|
||||
case DrydockLeaseStatus::STATUS_DESTROYED:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public function scheduleUpdate($epoch = null) {
|
||||
PhabricatorWorker::scheduleTask(
|
||||
'DrydockLeaseUpdateWorker',
|
||||
|
@ -304,14 +348,21 @@ final class DrydockLease extends DrydockDAO
|
|||
),
|
||||
array(
|
||||
'objectPHID' => $this->getPHID(),
|
||||
'delayUntil' => $epoch,
|
||||
'delayUntil' => ($epoch ? (int)$epoch : null),
|
||||
));
|
||||
}
|
||||
|
||||
public function setAwakenTaskIDs(array $ids) {
|
||||
$this->setAttribute('internal.awakenTaskIDs', $ids);
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function didActivate() {
|
||||
$viewer = PhabricatorUser::getOmnipotentUser();
|
||||
$need_update = false;
|
||||
|
||||
$this->logEvent(DrydockLeaseActivatedLogType::LOGCONST);
|
||||
|
||||
$commands = id(new DrydockCommandQuery())
|
||||
->setViewer($viewer)
|
||||
->withTargetPHIDs(array($this->getPHID()))
|
||||
|
@ -329,8 +380,33 @@ final class DrydockLease extends DrydockDAO
|
|||
if ($expires) {
|
||||
$this->scheduleUpdate($expires);
|
||||
}
|
||||
|
||||
$awaken_ids = $this->getAttribute('internal.awakenTaskIDs');
|
||||
if (is_array($awaken_ids) && $awaken_ids) {
|
||||
PhabricatorWorker::awakenTaskIDs($awaken_ids);
|
||||
}
|
||||
}
|
||||
|
||||
public function logEvent($type, array $data = array()) {
|
||||
$log = id(new DrydockLog())
|
||||
->setEpoch(PhabricatorTime::getNow())
|
||||
->setType($type)
|
||||
->setData($data);
|
||||
|
||||
$log->setLeasePHID($this->getPHID());
|
||||
|
||||
$resource_phid = $this->getResourcePHID();
|
||||
if ($resource_phid) {
|
||||
$resource = $this->getResource();
|
||||
|
||||
$log->setResourcePHID($resource->getPHID());
|
||||
$log->setBlueprintPHID($resource->getBlueprintPHID());
|
||||
}
|
||||
|
||||
return $log->save();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||
|
||||
|
|
|
@ -3,28 +3,38 @@
|
|||
final class DrydockLog extends DrydockDAO
|
||||
implements PhabricatorPolicyInterface {
|
||||
|
||||
protected $resourceID;
|
||||
protected $leaseID;
|
||||
protected $blueprintPHID;
|
||||
protected $resourcePHID;
|
||||
protected $leasePHID;
|
||||
protected $epoch;
|
||||
protected $message;
|
||||
protected $type;
|
||||
protected $data = array();
|
||||
|
||||
private $blueprint = self::ATTACHABLE;
|
||||
private $resource = self::ATTACHABLE;
|
||||
private $lease = self::ATTACHABLE;
|
||||
|
||||
protected function getConfiguration() {
|
||||
return array(
|
||||
self::CONFIG_TIMESTAMPS => false,
|
||||
self::CONFIG_SERIALIZATION => array(
|
||||
'data' => self::SERIALIZATION_JSON,
|
||||
),
|
||||
self::CONFIG_COLUMN_SCHEMA => array(
|
||||
'resourceID' => 'id?',
|
||||
'leaseID' => 'id?',
|
||||
'message' => 'text',
|
||||
'blueprintPHID' => 'phid?',
|
||||
'resourcePHID' => 'phid?',
|
||||
'leasePHID' => 'phid?',
|
||||
'type' => 'text64',
|
||||
),
|
||||
self::CONFIG_KEY_SCHEMA => array(
|
||||
'resourceID' => array(
|
||||
'columns' => array('resourceID', 'epoch'),
|
||||
'key_blueprint' => array(
|
||||
'columns' => array('blueprintPHID', 'type'),
|
||||
),
|
||||
'leaseID' => array(
|
||||
'columns' => array('leaseID', 'epoch'),
|
||||
'key_resource' => array(
|
||||
'columns' => array('resourcePHID', 'type'),
|
||||
),
|
||||
'key_lease' => array(
|
||||
'columns' => array('leasePHID', 'type'),
|
||||
),
|
||||
'epoch' => array(
|
||||
'columns' => array('epoch'),
|
||||
|
@ -33,6 +43,15 @@ final class DrydockLog extends DrydockDAO
|
|||
) + parent::getConfiguration();
|
||||
}
|
||||
|
||||
public function attachBlueprint(DrydockBlueprint $blueprint = null) {
|
||||
$this->blueprint = $blueprint;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getBlueprint() {
|
||||
return $this->assertAttached($this->blueprint);
|
||||
}
|
||||
|
||||
public function attachResource(DrydockResource $resource = null) {
|
||||
$this->resource = $resource;
|
||||
return $this;
|
||||
|
@ -51,6 +70,22 @@ final class DrydockLog extends DrydockDAO
|
|||
return $this->assertAttached($this->lease);
|
||||
}
|
||||
|
||||
public function isComplete() {
|
||||
if ($this->getBlueprintPHID() && !$this->getBlueprint()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->getResourcePHID() && !$this->getResource()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->getLeasePHID() && !$this->getLease()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||
|
||||
|
@ -62,21 +97,19 @@ final class DrydockLog extends DrydockDAO
|
|||
}
|
||||
|
||||
public function getPolicy($capability) {
|
||||
if ($this->getResource()) {
|
||||
return $this->getResource()->getPolicy($capability);
|
||||
}
|
||||
return $this->getLease()->getPolicy($capability);
|
||||
// NOTE: We let you see that logs exist no matter what, but don't actually
|
||||
// show you log content unless you can see all of the associated objects.
|
||||
return PhabricatorPolicies::getMostOpenPolicy();
|
||||
}
|
||||
|
||||
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
|
||||
if ($this->getResource()) {
|
||||
return $this->getResource()->hasAutomaticCapability($capability, $viewer);
|
||||
}
|
||||
return $this->getLease()->hasAutomaticCapability($capability, $viewer);
|
||||
return false;
|
||||
}
|
||||
|
||||
public function describeAutomaticCapability($capability) {
|
||||
return pht('Logs inherit the policy of their resources.');
|
||||
return pht(
|
||||
'To view log details, you must be able to view the associated '.
|
||||
'blueprint, resource and lease.');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,14 +7,15 @@ final class DrydockResource extends DrydockDAO
|
|||
protected $phid;
|
||||
protected $blueprintPHID;
|
||||
protected $status;
|
||||
|
||||
protected $until;
|
||||
protected $type;
|
||||
protected $name;
|
||||
protected $attributes = array();
|
||||
protected $capabilities = array();
|
||||
protected $ownerPHID;
|
||||
|
||||
private $blueprint = self::ATTACHABLE;
|
||||
private $unconsumedCommands = self::ATTACHABLE;
|
||||
|
||||
private $isAllocated = false;
|
||||
private $isActivated = false;
|
||||
private $activateWhenAllocated = false;
|
||||
|
@ -28,10 +29,10 @@ final class DrydockResource extends DrydockDAO
|
|||
'capabilities' => self::SERIALIZATION_JSON,
|
||||
),
|
||||
self::CONFIG_COLUMN_SCHEMA => array(
|
||||
'name' => 'text255',
|
||||
'ownerPHID' => 'phid?',
|
||||
'status' => 'text32',
|
||||
'type' => 'text64',
|
||||
'until' => 'epoch?',
|
||||
),
|
||||
self::CONFIG_KEY_SCHEMA => array(
|
||||
'key_type' => array(
|
||||
|
@ -48,6 +49,10 @@ final class DrydockResource extends DrydockDAO
|
|||
return PhabricatorPHID::generateNewPHID(DrydockResourcePHIDType::TYPECONST);
|
||||
}
|
||||
|
||||
public function getResourceName() {
|
||||
return $this->getBlueprint()->getResourceName($this);
|
||||
}
|
||||
|
||||
public function getAttribute($key, $default = null) {
|
||||
return idx($this->attributes, $key, $default);
|
||||
}
|
||||
|
@ -78,6 +83,25 @@ final class DrydockResource extends DrydockDAO
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function getUnconsumedCommands() {
|
||||
return $this->assertAttached($this->unconsumedCommands);
|
||||
}
|
||||
|
||||
public function attachUnconsumedCommands(array $commands) {
|
||||
$this->unconsumedCommands = $commands;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isReleasing() {
|
||||
foreach ($this->getUnconsumedCommands() as $command) {
|
||||
if ($command->getCommand() == DrydockCommand::COMMAND_RELEASE) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function setActivateWhenAllocated($activate) {
|
||||
$this->activateWhenAllocated = $activate;
|
||||
return $this;
|
||||
|
@ -96,6 +120,16 @@ final class DrydockResource extends DrydockDAO
|
|||
'Only new resources may be allocated.'));
|
||||
}
|
||||
|
||||
// We expect resources to have a pregenerated PHID, as they should have
|
||||
// been created by a call to DrydockBlueprint->newResourceTemplate().
|
||||
if (!$this->getPHID()) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Trying to allocate a resource with no generated PHID. Use "%s" to '.
|
||||
'create new resource templates.',
|
||||
'newResourceTemplate()'));
|
||||
}
|
||||
|
||||
$expect_status = DrydockResourceStatus::STATUS_PENDING;
|
||||
$actual_status = $this->getStatus();
|
||||
if ($actual_status != $expect_status) {
|
||||
|
@ -115,17 +149,40 @@ final class DrydockResource extends DrydockDAO
|
|||
|
||||
$this->openTransaction();
|
||||
|
||||
try {
|
||||
DrydockSlotLock::acquireLocks($this->getPHID(), $this->slotLocks);
|
||||
$this->slotLocks = array();
|
||||
} catch (DrydockSlotLockException $ex) {
|
||||
$this->killTransaction();
|
||||
|
||||
// NOTE: We have to log this on the blueprint, as the resource is not
|
||||
// going to be saved so the PHID will vanish.
|
||||
$this->getBlueprint()->logEvent(
|
||||
DrydockSlotLockFailureLogType::LOGCONST,
|
||||
array(
|
||||
'locks' => $ex->getLockMap(),
|
||||
));
|
||||
|
||||
throw $ex;
|
||||
}
|
||||
|
||||
try {
|
||||
$this
|
||||
->setStatus($new_status)
|
||||
->save();
|
||||
|
||||
DrydockSlotLock::acquireLocks($this->getPHID(), $this->slotLocks);
|
||||
$this->slotLocks = array();
|
||||
} catch (Exception $ex) {
|
||||
$this->killTransaction();
|
||||
throw $ex;
|
||||
}
|
||||
|
||||
$this->saveTransaction();
|
||||
|
||||
$this->isAllocated = true;
|
||||
|
||||
if ($new_status == DrydockResourceStatus::STATUS_ACTIVE) {
|
||||
$this->didActivate();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -164,6 +221,8 @@ final class DrydockResource extends DrydockDAO
|
|||
|
||||
$this->isActivated = true;
|
||||
|
||||
$this->didActivate();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -181,14 +240,16 @@ final class DrydockResource extends DrydockDAO
|
|||
}
|
||||
}
|
||||
|
||||
public function scheduleUpdate() {
|
||||
public function scheduleUpdate($epoch = null) {
|
||||
PhabricatorWorker::scheduleTask(
|
||||
'DrydockResourceUpdateWorker',
|
||||
array(
|
||||
'resourcePHID' => $this->getPHID(),
|
||||
'isExpireTask' => ($epoch !== null),
|
||||
),
|
||||
array(
|
||||
'objectPHID' => $this->getPHID(),
|
||||
'delayUntil' => ($epoch ? (int)$epoch : null),
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -209,6 +270,34 @@ final class DrydockResource extends DrydockDAO
|
|||
if ($need_update) {
|
||||
$this->scheduleUpdate();
|
||||
}
|
||||
|
||||
$expires = $this->getUntil();
|
||||
if ($expires) {
|
||||
$this->scheduleUpdate($expires);
|
||||
}
|
||||
}
|
||||
|
||||
public function canReceiveCommands() {
|
||||
switch ($this->getStatus()) {
|
||||
case DrydockResourceStatus::STATUS_RELEASED:
|
||||
case DrydockResourceStatus::STATUS_BROKEN:
|
||||
case DrydockResourceStatus::STATUS_DESTROYED:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public function logEvent($type, array $data = array()) {
|
||||
$log = id(new DrydockLog())
|
||||
->setEpoch(PhabricatorTime::getNow())
|
||||
->setType($type)
|
||||
->setData($data);
|
||||
|
||||
$log->setResourcePHID($this->getPHID());
|
||||
$log->setBlueprintPHID($this->getBlueprintPHID());
|
||||
|
||||
return $log->save();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -22,19 +22,10 @@ final class DrydockLeaseListView extends AphrontView {
|
|||
->setHeader($lease->getLeaseName())
|
||||
->setHref('/drydock/lease/'.$lease->getID().'/');
|
||||
|
||||
if ($lease->hasAttachedResource()) {
|
||||
$resource = $lease->getResource();
|
||||
|
||||
$resource_href = '/drydock/resource/'.$resource->getID().'/';
|
||||
$resource_name = $resource->getName();
|
||||
|
||||
$resource_phid = $lease->getResourcePHID();
|
||||
if ($resource_phid) {
|
||||
$item->addAttribute(
|
||||
phutil_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => $resource_href,
|
||||
),
|
||||
$resource_name));
|
||||
$viewer->renderHandle($resource_phid));
|
||||
}
|
||||
|
||||
$status = DrydockLeaseStatus::getNameForStatus($lease->getStatus());
|
||||
|
|
|
@ -16,57 +16,90 @@ final class DrydockLogListView extends AphrontView {
|
|||
|
||||
$view = new PHUIObjectItemListView();
|
||||
|
||||
$types = DrydockLogType::getAllLogTypes();
|
||||
|
||||
$rows = array();
|
||||
foreach ($logs as $log) {
|
||||
$resource_uri = '/drydock/resource/'.$log->getResourceID().'/';
|
||||
$lease_uri = '/drydock/lease/'.$log->getLeaseID().'/';
|
||||
$blueprint_phid = $log->getBlueprintPHID();
|
||||
if ($blueprint_phid) {
|
||||
$blueprint = $viewer->renderHandle($blueprint_phid);
|
||||
} else {
|
||||
$blueprint = null;
|
||||
}
|
||||
|
||||
$resource_name = $log->getResourceID();
|
||||
if ($log->getResourceID() !== null) {
|
||||
$resource_name = $log->getResource()->getName();
|
||||
$resource_phid = $log->getResourcePHID();
|
||||
if ($resource_phid) {
|
||||
$resource = $viewer->renderHandle($resource_phid);
|
||||
} else {
|
||||
$resource = null;
|
||||
}
|
||||
|
||||
$lease_phid = $log->getLeasePHID();
|
||||
if ($lease_phid) {
|
||||
$lease = $viewer->renderHandle($lease_phid);
|
||||
} else {
|
||||
$lease = null;
|
||||
}
|
||||
|
||||
if ($log->isComplete()) {
|
||||
$type_key = $log->getType();
|
||||
if (isset($types[$type_key])) {
|
||||
$type_object = id(clone $types[$type_key])
|
||||
->setLog($log)
|
||||
->setViewer($viewer);
|
||||
|
||||
$log_data = $log->getData();
|
||||
|
||||
$type = $type_object->getLogTypeName();
|
||||
$icon = $type_object->getLogTypeIcon($log_data);
|
||||
$data = $type_object->renderLog($log_data);
|
||||
} else {
|
||||
$type = pht('<Unknown: %s>', $type_key);
|
||||
$data = null;
|
||||
$icon = 'fa-question-circle red';
|
||||
}
|
||||
} else {
|
||||
$type = phutil_tag('em', array(), pht('Restricted'));
|
||||
$data = phutil_tag(
|
||||
'em',
|
||||
array(),
|
||||
pht('You do not have permission to view this log event.'));
|
||||
$icon = 'fa-lock grey';
|
||||
}
|
||||
|
||||
$rows[] = array(
|
||||
phutil_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => $resource_uri,
|
||||
),
|
||||
$resource_name),
|
||||
phutil_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => $lease_uri,
|
||||
),
|
||||
$log->getLeaseID()),
|
||||
$log->getMessage(),
|
||||
$blueprint,
|
||||
$resource,
|
||||
$lease,
|
||||
id(new PHUIIconView())->setIconFont($icon),
|
||||
$type,
|
||||
$data,
|
||||
phabricator_datetime($log->getEpoch(), $viewer),
|
||||
);
|
||||
}
|
||||
|
||||
$table = new AphrontTableView($rows);
|
||||
$table->setDeviceReadyTable(true);
|
||||
$table->setHeaders(
|
||||
array(
|
||||
pht('Resource'),
|
||||
pht('Lease'),
|
||||
pht('Message'),
|
||||
pht('Date'),
|
||||
));
|
||||
$table->setShortHeaders(
|
||||
array(
|
||||
pht('R'),
|
||||
pht('L'),
|
||||
pht('Message'),
|
||||
'',
|
||||
));
|
||||
$table->setColumnClasses(
|
||||
array(
|
||||
'',
|
||||
'',
|
||||
'wide',
|
||||
'',
|
||||
));
|
||||
$table = id(new AphrontTableView($rows))
|
||||
->setDeviceReadyTable(true)
|
||||
->setHeaders(
|
||||
array(
|
||||
pht('Blueprint'),
|
||||
pht('Resource'),
|
||||
pht('Lease'),
|
||||
null,
|
||||
pht('Type'),
|
||||
pht('Data'),
|
||||
pht('Date'),
|
||||
))
|
||||
->setColumnClasses(
|
||||
array(
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'icon',
|
||||
'',
|
||||
'wide',
|
||||
'',
|
||||
));
|
||||
|
||||
return $table;
|
||||
}
|
||||
|
|
|
@ -16,11 +16,12 @@ final class DrydockResourceListView extends AphrontView {
|
|||
|
||||
$view = new PHUIObjectItemListView();
|
||||
foreach ($resources as $resource) {
|
||||
$name = pht('Resource %d', $resource->getID()).': '.$resource->getName();
|
||||
$id = $resource->getID();
|
||||
|
||||
$item = id(new PHUIObjectItemView())
|
||||
->setHref('/drydock/resource/'.$resource->getID().'/')
|
||||
->setHeader($name);
|
||||
->setHref("/drydock/resource/{$id}/")
|
||||
->setObjectName(pht('Resource %d', $id))
|
||||
->setHeader($resource->getResourceName());
|
||||
|
||||
$status = DrydockResourceStatus::getNameForStatus($resource->getStatus());
|
||||
$item->addAttribute($status);
|
||||
|
|
|
@ -1,479 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @task allocate Allocator
|
||||
* @task resource Managing Resources
|
||||
* @task lease Managing Leases
|
||||
*/
|
||||
final class DrydockAllocatorWorker extends DrydockWorker {
|
||||
|
||||
protected function doWork() {
|
||||
$lease_phid = $this->getTaskDataValue('leasePHID');
|
||||
$lease = $this->loadLease($lease_phid);
|
||||
|
||||
$this->allocateAndAcquireLease($lease);
|
||||
}
|
||||
|
||||
|
||||
/* -( Allocator )---------------------------------------------------------- */
|
||||
|
||||
|
||||
/**
|
||||
* Find or build a resource which can satisfy a given lease request, then
|
||||
* acquire the lease.
|
||||
*
|
||||
* @param DrydockLease Requested lease.
|
||||
* @return void
|
||||
* @task allocator
|
||||
*/
|
||||
private function allocateAndAcquireLease(DrydockLease $lease) {
|
||||
$blueprints = $this->loadBlueprintsForAllocatingLease($lease);
|
||||
|
||||
// If we get nothing back, that means no blueprint is defined which can
|
||||
// ever build the requested resource. This is a permanent failure, since
|
||||
// we don't expect to succeed no matter how many times we try.
|
||||
if (!$blueprints) {
|
||||
$lease
|
||||
->setStatus(DrydockLeaseStatus::STATUS_BROKEN)
|
||||
->save();
|
||||
throw new PhabricatorWorkerPermanentFailureException(
|
||||
pht(
|
||||
'No active Drydock blueprint exists which can ever allocate a '.
|
||||
'resource for lease "%s".',
|
||||
$lease->getPHID()));
|
||||
}
|
||||
|
||||
// First, try to find a suitable open resource which we can acquire a new
|
||||
// lease on.
|
||||
$resources = $this->loadResourcesForAllocatingLease($blueprints, $lease);
|
||||
|
||||
// If no resources exist yet, see if we can build one.
|
||||
if (!$resources) {
|
||||
$usable_blueprints = $this->removeOverallocatedBlueprints(
|
||||
$blueprints,
|
||||
$lease);
|
||||
|
||||
// If we get nothing back here, some blueprint claims it can eventually
|
||||
// satisfy the lease, just not right now. This is a temporary failure,
|
||||
// and we expect allocation to succeed eventually.
|
||||
if (!$blueprints) {
|
||||
// TODO: More formal temporary failure here. We should retry this
|
||||
// "soon" but not "immediately".
|
||||
throw new Exception(
|
||||
pht('No blueprints have space to allocate a resource right now.'));
|
||||
}
|
||||
|
||||
$usable_blueprints = $this->rankBlueprints($blueprints, $lease);
|
||||
|
||||
$exceptions = array();
|
||||
foreach ($usable_blueprints as $blueprint) {
|
||||
try {
|
||||
$resources[] = $this->allocateResource($blueprint, $lease);
|
||||
|
||||
// Bail after allocating one resource, we don't need any more than
|
||||
// this.
|
||||
break;
|
||||
} catch (Exception $ex) {
|
||||
$exceptions[] = $ex;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$resources) {
|
||||
// TODO: We should distinguish between temporary and permament failures
|
||||
// here. If any blueprint failed temporarily, retry "soon". If none
|
||||
// of these failures were temporary, maybe this should be a permanent
|
||||
// failure?
|
||||
throw new PhutilAggregateException(
|
||||
pht(
|
||||
'All blueprints failed to allocate a suitable new resource when '.
|
||||
'trying to allocate lease "%s".',
|
||||
$lease->getPHID()),
|
||||
$exceptions);
|
||||
}
|
||||
|
||||
// NOTE: We have not acquired the lease yet, so it is possible that the
|
||||
// resource we just built will be snatched up by some other lease before
|
||||
// we can. This is not problematic: we'll retry a little later and should
|
||||
// suceed eventually.
|
||||
}
|
||||
|
||||
$resources = $this->rankResources($resources, $lease);
|
||||
|
||||
$exceptions = array();
|
||||
$allocated = false;
|
||||
foreach ($resources as $resource) {
|
||||
try {
|
||||
$this->acquireLease($resource, $lease);
|
||||
$allocated = true;
|
||||
break;
|
||||
} catch (Exception $ex) {
|
||||
$exceptions[] = $ex;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$allocated) {
|
||||
// TODO: We should distinguish between temporary and permanent failures
|
||||
// here. If any failures were temporary (specifically, failed to acquire
|
||||
// locks)
|
||||
|
||||
throw new PhutilAggregateException(
|
||||
pht(
|
||||
'Unable to acquire lease "%s" on any resouce.',
|
||||
$lease->getPHID()),
|
||||
$exceptions);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get all the @{class:DrydockBlueprintImplementation}s which can possibly
|
||||
* build a resource to satisfy a lease.
|
||||
*
|
||||
* This method returns blueprints which might, at some time, be able to
|
||||
* build a resource which can satisfy the lease. They may not be able to
|
||||
* build that resource right now.
|
||||
*
|
||||
* @param DrydockLease Requested lease.
|
||||
* @return list<DrydockBlueprintImplementation> List of qualifying blueprint
|
||||
* implementations.
|
||||
* @task allocator
|
||||
*/
|
||||
private function loadBlueprintImplementationsForAllocatingLease(
|
||||
DrydockLease $lease) {
|
||||
|
||||
$impls = DrydockBlueprintImplementation::getAllBlueprintImplementations();
|
||||
|
||||
$keep = array();
|
||||
foreach ($impls as $key => $impl) {
|
||||
// Don't use disabled blueprint types.
|
||||
if (!$impl->isEnabled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Don't use blueprint types which can't allocate the correct kind of
|
||||
// resource.
|
||||
if ($impl->getType() != $lease->getResourceType()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$impl->canAnyBlueprintEverAllocateResourceForLease($lease)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$keep[$key] = $impl;
|
||||
}
|
||||
|
||||
return $keep;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get all the concrete @{class:DrydockBlueprint}s which can possibly
|
||||
* build a resource to satisfy a lease.
|
||||
*
|
||||
* @param DrydockLease Requested lease.
|
||||
* @return list<DrydockBlueprint> List of qualifying blueprints.
|
||||
* @task allocator
|
||||
*/
|
||||
private function loadBlueprintsForAllocatingLease(
|
||||
DrydockLease $lease) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$impls = $this->loadBlueprintImplementationsForAllocatingLease($lease);
|
||||
if (!$impls) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$blueprints = id(new DrydockBlueprintQuery())
|
||||
->setViewer($viewer)
|
||||
->withBlueprintClasses(array_keys($impls))
|
||||
->withDisabled(false)
|
||||
->execute();
|
||||
|
||||
$keep = array();
|
||||
foreach ($blueprints as $key => $blueprint) {
|
||||
if (!$blueprint->canEverAllocateResourceForLease($lease)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$keep[$key] = $blueprint;
|
||||
}
|
||||
|
||||
return $keep;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load a list of all resources which a given lease can possibly be
|
||||
* allocated against.
|
||||
*
|
||||
* @param list<DrydockBlueprint> Blueprints which may produce suitable
|
||||
* resources.
|
||||
* @param DrydockLease Requested lease.
|
||||
* @return list<DrydockResource> Resources which may be able to allocate
|
||||
* the lease.
|
||||
* @task allocator
|
||||
*/
|
||||
private function loadResourcesForAllocatingLease(
|
||||
array $blueprints,
|
||||
DrydockLease $lease) {
|
||||
assert_instances_of($blueprints, 'DrydockBlueprint');
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$resources = id(new DrydockResourceQuery())
|
||||
->setViewer($viewer)
|
||||
->withBlueprintPHIDs(mpull($blueprints, 'getPHID'))
|
||||
->withTypes(array($lease->getResourceType()))
|
||||
->withStatuses(
|
||||
array(
|
||||
DrydockResourceStatus::STATUS_PENDING,
|
||||
DrydockResourceStatus::STATUS_ACTIVE,
|
||||
))
|
||||
->execute();
|
||||
|
||||
$keep = array();
|
||||
foreach ($resources as $key => $resource) {
|
||||
$blueprint = $resource->getBlueprint();
|
||||
|
||||
if (!$blueprint->canAcquireLeaseOnResource($resource, $lease)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$keep[$key] = $resource;
|
||||
}
|
||||
|
||||
return $keep;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove blueprints which are too heavily allocated to build a resource for
|
||||
* a lease from a list of blueprints.
|
||||
*
|
||||
* @param list<DrydockBlueprint> List of blueprints.
|
||||
* @return list<DrydockBlueprint> List with blueprints that can not allocate
|
||||
* a resource for the lease right now removed.
|
||||
* @task allocator
|
||||
*/
|
||||
private function removeOverallocatedBlueprints(
|
||||
array $blueprints,
|
||||
DrydockLease $lease) {
|
||||
assert_instances_of($blueprints, 'DrydockBlueprint');
|
||||
|
||||
$keep = array();
|
||||
foreach ($blueprints as $key => $blueprint) {
|
||||
if (!$blueprint->canAllocateResourceForLease($lease)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$keep[$key] = $blueprint;
|
||||
}
|
||||
|
||||
return $keep;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Rank blueprints by suitability for building a new resource for a
|
||||
* particular lease.
|
||||
*
|
||||
* @param list<DrydockBlueprint> List of blueprints.
|
||||
* @param DrydockLease Requested lease.
|
||||
* @return list<DrydockBlueprint> Ranked list of blueprints.
|
||||
* @task allocator
|
||||
*/
|
||||
private function rankBlueprints(array $blueprints, DrydockLease $lease) {
|
||||
assert_instances_of($blueprints, 'DrydockBlueprint');
|
||||
|
||||
// TODO: Implement improvements to this ranking algorithm if they become
|
||||
// available.
|
||||
shuffle($blueprints);
|
||||
|
||||
return $blueprints;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Rank resources by suitability for allocating a particular lease.
|
||||
*
|
||||
* @param list<DrydockResource> List of resources.
|
||||
* @param DrydockLease Requested lease.
|
||||
* @return list<DrydockResource> Ranked list of resources.
|
||||
* @task allocator
|
||||
*/
|
||||
private function rankResources(array $resources, DrydockLease $lease) {
|
||||
assert_instances_of($resources, 'DrydockResource');
|
||||
|
||||
// TODO: Implement improvements to this ranking algorithm if they become
|
||||
// available.
|
||||
shuffle($resources);
|
||||
|
||||
return $resources;
|
||||
}
|
||||
|
||||
|
||||
/* -( Managing Resources )------------------------------------------------- */
|
||||
|
||||
|
||||
/**
|
||||
* Perform an actual resource allocation with a particular blueprint.
|
||||
*
|
||||
* @param DrydockBlueprint The blueprint to allocate a resource from.
|
||||
* @param DrydockLease Requested lease.
|
||||
* @return DrydockResource Allocated resource.
|
||||
* @task resource
|
||||
*/
|
||||
private function allocateResource(
|
||||
DrydockBlueprint $blueprint,
|
||||
DrydockLease $lease) {
|
||||
$resource = $blueprint->allocateResource($lease);
|
||||
$this->validateAllocatedResource($blueprint, $resource, $lease);
|
||||
|
||||
// If this resource was allocated as a pending resource, queue a task to
|
||||
// activate it.
|
||||
if ($resource->getStatus() == DrydockResourceStatus::STATUS_PENDING) {
|
||||
PhabricatorWorker::scheduleTask(
|
||||
'DrydockResourceWorker',
|
||||
array(
|
||||
'resourcePHID' => $resource->getPHID(),
|
||||
),
|
||||
array(
|
||||
'objectPHID' => $resource->getPHID(),
|
||||
));
|
||||
}
|
||||
|
||||
return $resource;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check that the resource a blueprint allocated is roughly the sort of
|
||||
* object we expect.
|
||||
*
|
||||
* @param DrydockBlueprint Blueprint which built the resource.
|
||||
* @param wild Thing which the blueprint claims is a valid resource.
|
||||
* @param DrydockLease Lease the resource was allocated for.
|
||||
* @return void
|
||||
* @task resource
|
||||
*/
|
||||
private function validateAllocatedResource(
|
||||
DrydockBlueprint $blueprint,
|
||||
$resource,
|
||||
DrydockLease $lease) {
|
||||
|
||||
if (!($resource instanceof DrydockResource)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Blueprint "%s" (of type "%s") is not properly implemented: %s must '.
|
||||
'return an object of type %s or throw, but returned something else.',
|
||||
$blueprint->getBlueprintName(),
|
||||
$blueprint->getClassName(),
|
||||
'allocateResource()',
|
||||
'DrydockResource'));
|
||||
}
|
||||
|
||||
if (!$resource->isAllocatedResource()) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Blueprint "%s" (of type "%s") is not properly implemented: %s '.
|
||||
'must actually allocate the resource it returns.',
|
||||
$blueprint->getBlueprintName(),
|
||||
$blueprint->getClassName(),
|
||||
'allocateResource()'));
|
||||
}
|
||||
|
||||
$resource_type = $resource->getType();
|
||||
$lease_type = $lease->getResourceType();
|
||||
|
||||
if ($resource_type !== $lease_type) {
|
||||
// TODO: Destroy the resource here?
|
||||
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Blueprint "%s" (of type "%s") is not properly implemented: it '.
|
||||
'built a resource of type "%s" to satisfy a lease requesting a '.
|
||||
'resource of type "%s".',
|
||||
$blueprint->getBlueprintName(),
|
||||
$blueprint->getClassName(),
|
||||
$resource_type,
|
||||
$lease_type));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* -( Managing Leases )---------------------------------------------------- */
|
||||
|
||||
|
||||
/**
|
||||
* Perform an actual lease acquisition on a particular resource.
|
||||
*
|
||||
* @param DrydockResource Resource to acquire a lease on.
|
||||
* @param DrydockLease Lease to acquire.
|
||||
* @return void
|
||||
* @task lease
|
||||
*/
|
||||
private function acquireLease(
|
||||
DrydockResource $resource,
|
||||
DrydockLease $lease) {
|
||||
|
||||
$blueprint = $resource->getBlueprint();
|
||||
$blueprint->acquireLease($resource, $lease);
|
||||
|
||||
$this->validateAcquiredLease($blueprint, $resource, $lease);
|
||||
|
||||
// If this lease has been acquired but not activated, queue a task to
|
||||
// activate it.
|
||||
if ($lease->getStatus() == DrydockLeaseStatus::STATUS_ACQUIRED) {
|
||||
PhabricatorWorker::scheduleTask(
|
||||
'DrydockLeaseWorker',
|
||||
array(
|
||||
'leasePHID' => $lease->getPHID(),
|
||||
),
|
||||
array(
|
||||
'objectPHID' => $lease->getPHID(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make sure that a lease was really acquired properly.
|
||||
*
|
||||
* @param DrydockBlueprint Blueprint which created the resource.
|
||||
* @param DrydockResource Resource which was acquired.
|
||||
* @param DrydockLease The lease which was supposedly acquired.
|
||||
* @return void
|
||||
* @task lease
|
||||
*/
|
||||
private function validateAcquiredLease(
|
||||
DrydockBlueprint $blueprint,
|
||||
DrydockResource $resource,
|
||||
DrydockLease $lease) {
|
||||
|
||||
if (!$lease->isAcquiredLease()) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Blueprint "%s" (of type "%s") is not properly implemented: it '.
|
||||
'returned from "%s" without acquiring a lease.',
|
||||
$blueprint->getBlueprintName(),
|
||||
$blueprint->getClassName(),
|
||||
'acquireLease()'));
|
||||
}
|
||||
|
||||
$lease_phid = $lease->getResourcePHID();
|
||||
$resource_phid = $resource->getPHID();
|
||||
|
||||
if ($lease_phid !== $resource_phid) {
|
||||
// TODO: Destroy the lease?
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Blueprint "%s" (of type "%s") is not properly implemented: it '.
|
||||
'returned from "%s" with a lease acquired on the wrong resource.',
|
||||
$blueprint->getBlueprintName(),
|
||||
$blueprint->getClassName(),
|
||||
'acquireLease()'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class DrydockLeaseDestroyWorker extends DrydockWorker {
|
||||
|
||||
protected function doWork() {
|
||||
$lease_phid = $this->getTaskDataValue('leasePHID');
|
||||
$lease = $this->loadLease($lease_phid);
|
||||
$this->destroyLease($lease);
|
||||
}
|
||||
|
||||
private function destroyLease(DrydockLease $lease) {
|
||||
$status = $lease->getStatus();
|
||||
|
||||
switch ($status) {
|
||||
case DrydockLeaseStatus::STATUS_RELEASED:
|
||||
case DrydockLeaseStatus::STATUS_BROKEN:
|
||||
break;
|
||||
default:
|
||||
throw new PhabricatorWorkerPermanentFailureException(
|
||||
pht(
|
||||
'Unable to destroy lease ("%s"), lease has the wrong '.
|
||||
'status ("%s").',
|
||||
$lease->getPHID(),
|
||||
$status));
|
||||
}
|
||||
|
||||
$resource = $lease->getResource();
|
||||
$blueprint = $resource->getBlueprint();
|
||||
|
||||
$blueprint->destroyLease($resource, $lease);
|
||||
|
||||
$lease
|
||||
->setStatus(DrydockLeaseStatus::STATUS_DESTROYED)
|
||||
->save();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,15 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @task update Updating Leases
|
||||
* @task command Processing Commands
|
||||
* @task allocator Drydock Allocator
|
||||
* @task acquire Acquiring Leases
|
||||
* @task activate Activating Leases
|
||||
* @task release Releasing Leases
|
||||
* @task break Breaking Leases
|
||||
* @task destroy Destroying Leases
|
||||
*/
|
||||
final class DrydockLeaseUpdateWorker extends DrydockWorker {
|
||||
|
||||
protected function doWork() {
|
||||
|
@ -13,7 +23,7 @@ final class DrydockLeaseUpdateWorker extends DrydockWorker {
|
|||
|
||||
try {
|
||||
$lease = $this->loadLease($lease_phid);
|
||||
$this->updateLease($lease);
|
||||
$this->handleUpdate($lease);
|
||||
} catch (Exception $ex) {
|
||||
$lock->unlock();
|
||||
throw $ex;
|
||||
|
@ -22,54 +32,103 @@ final class DrydockLeaseUpdateWorker extends DrydockWorker {
|
|||
$lock->unlock();
|
||||
}
|
||||
|
||||
|
||||
/* -( Updating Leases )---------------------------------------------------- */
|
||||
|
||||
|
||||
/**
|
||||
* @task update
|
||||
*/
|
||||
private function handleUpdate(DrydockLease $lease) {
|
||||
try {
|
||||
$this->updateLease($lease);
|
||||
} catch (Exception $ex) {
|
||||
if ($this->isTemporaryException($ex)) {
|
||||
$this->yieldLease($lease, $ex);
|
||||
} else {
|
||||
$this->breakLease($lease, $ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @task update
|
||||
*/
|
||||
private function updateLease(DrydockLease $lease) {
|
||||
if ($lease->getStatus() != DrydockLeaseStatus::STATUS_ACTIVE) {
|
||||
$this->processLeaseCommands($lease);
|
||||
|
||||
$lease_status = $lease->getStatus();
|
||||
switch ($lease_status) {
|
||||
case DrydockLeaseStatus::STATUS_PENDING:
|
||||
$this->executeAllocator($lease);
|
||||
break;
|
||||
case DrydockLeaseStatus::STATUS_ACQUIRED:
|
||||
$this->activateLease($lease);
|
||||
break;
|
||||
case DrydockLeaseStatus::STATUS_ACTIVE:
|
||||
// Nothing to do.
|
||||
break;
|
||||
case DrydockLeaseStatus::STATUS_RELEASED:
|
||||
case DrydockLeaseStatus::STATUS_BROKEN:
|
||||
$this->destroyLease($lease);
|
||||
break;
|
||||
case DrydockLeaseStatus::STATUS_DESTROYED:
|
||||
break;
|
||||
}
|
||||
|
||||
$this->yieldIfExpiringLease($lease);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @task update
|
||||
*/
|
||||
private function yieldLease(DrydockLease $lease, Exception $ex) {
|
||||
$duration = $this->getYieldDurationFromException($ex);
|
||||
|
||||
$lease->logEvent(
|
||||
DrydockLeaseActivationYieldLogType::LOGCONST,
|
||||
array(
|
||||
'duration' => $duration,
|
||||
));
|
||||
|
||||
throw new PhabricatorWorkerYieldException($duration);
|
||||
}
|
||||
|
||||
|
||||
/* -( Processing Commands )------------------------------------------------ */
|
||||
|
||||
|
||||
/**
|
||||
* @task command
|
||||
*/
|
||||
private function processLeaseCommands(DrydockLease $lease) {
|
||||
if (!$lease->canReceiveCommands()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$viewer = $this->getViewer();
|
||||
$drydock_phid = id(new PhabricatorDrydockApplication())->getPHID();
|
||||
|
||||
// Check if the lease has expired. If it is, we're going to send it a
|
||||
// release command. This command will be handled immediately below, it
|
||||
// just generates a command log and improves consistency.
|
||||
$now = PhabricatorTime::getNow();
|
||||
$expires = $lease->getUntil();
|
||||
if ($expires && ($expires <= $now)) {
|
||||
$command = DrydockCommand::initializeNewCommand($viewer)
|
||||
->setTargetPHID($lease->getPHID())
|
||||
->setAuthorPHID($drydock_phid)
|
||||
->setCommand(DrydockCommand::COMMAND_RELEASE)
|
||||
->save();
|
||||
}
|
||||
$this->checkLeaseExpiration($lease);
|
||||
|
||||
$commands = $this->loadCommands($lease->getPHID());
|
||||
foreach ($commands as $command) {
|
||||
if ($lease->getStatus() != DrydockLeaseStatus::STATUS_ACTIVE) {
|
||||
// Leases can't receive commands before they activate or after they
|
||||
// release.
|
||||
if (!$lease->canReceiveCommands()) {
|
||||
break;
|
||||
}
|
||||
|
||||
$this->processCommand($lease, $command);
|
||||
$this->processLeaseCommand($lease, $command);
|
||||
|
||||
$command
|
||||
->setIsConsumed(true)
|
||||
->save();
|
||||
}
|
||||
|
||||
// If this is the task which will eventually release the lease after it
|
||||
// expires but it is still active, reschedule the task to run after the
|
||||
// lease expires. This can happen if the lease's expiration was pushed
|
||||
// forward.
|
||||
if ($lease->getStatus() == DrydockLeaseStatus::STATUS_ACTIVE) {
|
||||
if ($this->getTaskDataValue('isExpireTask') && $expires) {
|
||||
throw new PhabricatorWorkerYieldException($expires - $now);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function processCommand(
|
||||
|
||||
/**
|
||||
* @task command
|
||||
*/
|
||||
private function processLeaseCommand(
|
||||
DrydockLease $lease,
|
||||
DrydockCommand $command) {
|
||||
switch ($command->getCommand()) {
|
||||
|
@ -79,29 +138,597 @@ final class DrydockLeaseUpdateWorker extends DrydockWorker {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* -( Drydock Allocator )-------------------------------------------------- */
|
||||
|
||||
|
||||
/**
|
||||
* Find or build a resource which can satisfy a given lease request, then
|
||||
* acquire the lease.
|
||||
*
|
||||
* @param DrydockLease Requested lease.
|
||||
* @return void
|
||||
* @task allocator
|
||||
*/
|
||||
private function executeAllocator(DrydockLease $lease) {
|
||||
$blueprints = $this->loadBlueprintsForAllocatingLease($lease);
|
||||
|
||||
// If we get nothing back, that means no blueprint is defined which can
|
||||
// ever build the requested resource. This is a permanent failure, since
|
||||
// we don't expect to succeed no matter how many times we try.
|
||||
if (!$blueprints) {
|
||||
throw new PhabricatorWorkerPermanentFailureException(
|
||||
pht(
|
||||
'No active Drydock blueprint exists which can ever allocate a '.
|
||||
'resource for lease "%s".',
|
||||
$lease->getPHID()));
|
||||
}
|
||||
|
||||
// First, try to find a suitable open resource which we can acquire a new
|
||||
// lease on.
|
||||
$resources = $this->loadResourcesForAllocatingLease($blueprints, $lease);
|
||||
|
||||
// If no resources exist yet, see if we can build one.
|
||||
if (!$resources) {
|
||||
$usable_blueprints = $this->removeOverallocatedBlueprints(
|
||||
$blueprints,
|
||||
$lease);
|
||||
|
||||
// If we get nothing back here, some blueprint claims it can eventually
|
||||
// satisfy the lease, just not right now. This is a temporary failure,
|
||||
// and we expect allocation to succeed eventually.
|
||||
if (!$usable_blueprints) {
|
||||
$lease->logEvent(
|
||||
DrydockLeaseWaitingForResourcesLogType::LOGCONST,
|
||||
array(
|
||||
'blueprintPHIDs' => mpull($blueprints, 'getPHID'),
|
||||
));
|
||||
|
||||
throw new PhabricatorWorkerYieldException(15);
|
||||
}
|
||||
|
||||
$usable_blueprints = $this->rankBlueprints($usable_blueprints, $lease);
|
||||
|
||||
$exceptions = array();
|
||||
foreach ($usable_blueprints as $blueprint) {
|
||||
try {
|
||||
$resources[] = $this->allocateResource($blueprint, $lease);
|
||||
|
||||
// Bail after allocating one resource, we don't need any more than
|
||||
// this.
|
||||
break;
|
||||
} catch (Exception $ex) {
|
||||
$exceptions[] = $ex;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$resources) {
|
||||
throw new PhutilAggregateException(
|
||||
pht(
|
||||
'All blueprints failed to allocate a suitable new resource when '.
|
||||
'trying to allocate lease "%s".',
|
||||
$lease->getPHID()),
|
||||
$exceptions);
|
||||
}
|
||||
|
||||
// NOTE: We have not acquired the lease yet, so it is possible that the
|
||||
// resource we just built will be snatched up by some other lease before
|
||||
// we can. This is not problematic: we'll retry a little later and should
|
||||
// suceed eventually.
|
||||
}
|
||||
|
||||
$resources = $this->rankResources($resources, $lease);
|
||||
|
||||
$exceptions = array();
|
||||
$allocated = false;
|
||||
foreach ($resources as $resource) {
|
||||
try {
|
||||
$this->acquireLease($resource, $lease);
|
||||
$allocated = true;
|
||||
break;
|
||||
} catch (Exception $ex) {
|
||||
$exceptions[] = $ex;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$allocated) {
|
||||
throw new PhutilAggregateException(
|
||||
pht(
|
||||
'Unable to acquire lease "%s" on any resouce.',
|
||||
$lease->getPHID()),
|
||||
$exceptions);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get all the @{class:DrydockBlueprintImplementation}s which can possibly
|
||||
* build a resource to satisfy a lease.
|
||||
*
|
||||
* This method returns blueprints which might, at some time, be able to
|
||||
* build a resource which can satisfy the lease. They may not be able to
|
||||
* build that resource right now.
|
||||
*
|
||||
* @param DrydockLease Requested lease.
|
||||
* @return list<DrydockBlueprintImplementation> List of qualifying blueprint
|
||||
* implementations.
|
||||
* @task allocator
|
||||
*/
|
||||
private function loadBlueprintImplementationsForAllocatingLease(
|
||||
DrydockLease $lease) {
|
||||
|
||||
$impls = DrydockBlueprintImplementation::getAllBlueprintImplementations();
|
||||
|
||||
$keep = array();
|
||||
foreach ($impls as $key => $impl) {
|
||||
// Don't use disabled blueprint types.
|
||||
if (!$impl->isEnabled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Don't use blueprint types which can't allocate the correct kind of
|
||||
// resource.
|
||||
if ($impl->getType() != $lease->getResourceType()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$impl->canAnyBlueprintEverAllocateResourceForLease($lease)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$keep[$key] = $impl;
|
||||
}
|
||||
|
||||
return $keep;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get all the concrete @{class:DrydockBlueprint}s which can possibly
|
||||
* build a resource to satisfy a lease.
|
||||
*
|
||||
* @param DrydockLease Requested lease.
|
||||
* @return list<DrydockBlueprint> List of qualifying blueprints.
|
||||
* @task allocator
|
||||
*/
|
||||
private function loadBlueprintsForAllocatingLease(
|
||||
DrydockLease $lease) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$impls = $this->loadBlueprintImplementationsForAllocatingLease($lease);
|
||||
if (!$impls) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$blueprints = id(new DrydockBlueprintQuery())
|
||||
->setViewer($viewer)
|
||||
->withBlueprintClasses(array_keys($impls))
|
||||
->withDisabled(false)
|
||||
->execute();
|
||||
|
||||
$keep = array();
|
||||
foreach ($blueprints as $key => $blueprint) {
|
||||
if (!$blueprint->canEverAllocateResourceForLease($lease)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$keep[$key] = $blueprint;
|
||||
}
|
||||
|
||||
return $keep;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load a list of all resources which a given lease can possibly be
|
||||
* allocated against.
|
||||
*
|
||||
* @param list<DrydockBlueprint> Blueprints which may produce suitable
|
||||
* resources.
|
||||
* @param DrydockLease Requested lease.
|
||||
* @return list<DrydockResource> Resources which may be able to allocate
|
||||
* the lease.
|
||||
* @task allocator
|
||||
*/
|
||||
private function loadResourcesForAllocatingLease(
|
||||
array $blueprints,
|
||||
DrydockLease $lease) {
|
||||
assert_instances_of($blueprints, 'DrydockBlueprint');
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$resources = id(new DrydockResourceQuery())
|
||||
->setViewer($viewer)
|
||||
->withBlueprintPHIDs(mpull($blueprints, 'getPHID'))
|
||||
->withTypes(array($lease->getResourceType()))
|
||||
->withStatuses(
|
||||
array(
|
||||
DrydockResourceStatus::STATUS_PENDING,
|
||||
DrydockResourceStatus::STATUS_ACTIVE,
|
||||
))
|
||||
->execute();
|
||||
|
||||
$keep = array();
|
||||
foreach ($resources as $key => $resource) {
|
||||
$blueprint = $resource->getBlueprint();
|
||||
|
||||
if (!$blueprint->canAcquireLeaseOnResource($resource, $lease)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$keep[$key] = $resource;
|
||||
}
|
||||
|
||||
return $keep;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove blueprints which are too heavily allocated to build a resource for
|
||||
* a lease from a list of blueprints.
|
||||
*
|
||||
* @param list<DrydockBlueprint> List of blueprints.
|
||||
* @return list<DrydockBlueprint> List with blueprints that can not allocate
|
||||
* a resource for the lease right now removed.
|
||||
* @task allocator
|
||||
*/
|
||||
private function removeOverallocatedBlueprints(
|
||||
array $blueprints,
|
||||
DrydockLease $lease) {
|
||||
assert_instances_of($blueprints, 'DrydockBlueprint');
|
||||
|
||||
$keep = array();
|
||||
foreach ($blueprints as $key => $blueprint) {
|
||||
if (!$blueprint->canAllocateResourceForLease($lease)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$keep[$key] = $blueprint;
|
||||
}
|
||||
|
||||
return $keep;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Rank blueprints by suitability for building a new resource for a
|
||||
* particular lease.
|
||||
*
|
||||
* @param list<DrydockBlueprint> List of blueprints.
|
||||
* @param DrydockLease Requested lease.
|
||||
* @return list<DrydockBlueprint> Ranked list of blueprints.
|
||||
* @task allocator
|
||||
*/
|
||||
private function rankBlueprints(array $blueprints, DrydockLease $lease) {
|
||||
assert_instances_of($blueprints, 'DrydockBlueprint');
|
||||
|
||||
// TODO: Implement improvements to this ranking algorithm if they become
|
||||
// available.
|
||||
shuffle($blueprints);
|
||||
|
||||
return $blueprints;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Rank resources by suitability for allocating a particular lease.
|
||||
*
|
||||
* @param list<DrydockResource> List of resources.
|
||||
* @param DrydockLease Requested lease.
|
||||
* @return list<DrydockResource> Ranked list of resources.
|
||||
* @task allocator
|
||||
*/
|
||||
private function rankResources(array $resources, DrydockLease $lease) {
|
||||
assert_instances_of($resources, 'DrydockResource');
|
||||
|
||||
// TODO: Implement improvements to this ranking algorithm if they become
|
||||
// available.
|
||||
shuffle($resources);
|
||||
|
||||
return $resources;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Perform an actual resource allocation with a particular blueprint.
|
||||
*
|
||||
* @param DrydockBlueprint The blueprint to allocate a resource from.
|
||||
* @param DrydockLease Requested lease.
|
||||
* @return DrydockResource Allocated resource.
|
||||
* @task allocator
|
||||
*/
|
||||
private function allocateResource(
|
||||
DrydockBlueprint $blueprint,
|
||||
DrydockLease $lease) {
|
||||
$resource = $blueprint->allocateResource($lease);
|
||||
$this->validateAllocatedResource($blueprint, $resource, $lease);
|
||||
|
||||
// If this resource was allocated as a pending resource, queue a task to
|
||||
// activate it.
|
||||
if ($resource->getStatus() == DrydockResourceStatus::STATUS_PENDING) {
|
||||
PhabricatorWorker::scheduleTask(
|
||||
'DrydockResourceUpdateWorker',
|
||||
array(
|
||||
'resourcePHID' => $resource->getPHID(),
|
||||
),
|
||||
array(
|
||||
'objectPHID' => $resource->getPHID(),
|
||||
));
|
||||
}
|
||||
|
||||
return $resource;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check that the resource a blueprint allocated is roughly the sort of
|
||||
* object we expect.
|
||||
*
|
||||
* @param DrydockBlueprint Blueprint which built the resource.
|
||||
* @param wild Thing which the blueprint claims is a valid resource.
|
||||
* @param DrydockLease Lease the resource was allocated for.
|
||||
* @return void
|
||||
* @task allocator
|
||||
*/
|
||||
private function validateAllocatedResource(
|
||||
DrydockBlueprint $blueprint,
|
||||
$resource,
|
||||
DrydockLease $lease) {
|
||||
|
||||
if (!($resource instanceof DrydockResource)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Blueprint "%s" (of type "%s") is not properly implemented: %s must '.
|
||||
'return an object of type %s or throw, but returned something else.',
|
||||
$blueprint->getBlueprintName(),
|
||||
$blueprint->getClassName(),
|
||||
'allocateResource()',
|
||||
'DrydockResource'));
|
||||
}
|
||||
|
||||
if (!$resource->isAllocatedResource()) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Blueprint "%s" (of type "%s") is not properly implemented: %s '.
|
||||
'must actually allocate the resource it returns.',
|
||||
$blueprint->getBlueprintName(),
|
||||
$blueprint->getClassName(),
|
||||
'allocateResource()'));
|
||||
}
|
||||
|
||||
$resource_type = $resource->getType();
|
||||
$lease_type = $lease->getResourceType();
|
||||
|
||||
if ($resource_type !== $lease_type) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Blueprint "%s" (of type "%s") is not properly implemented: it '.
|
||||
'built a resource of type "%s" to satisfy a lease requesting a '.
|
||||
'resource of type "%s".',
|
||||
$blueprint->getBlueprintName(),
|
||||
$blueprint->getClassName(),
|
||||
$resource_type,
|
||||
$lease_type));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* -( Acquiring Leases )--------------------------------------------------- */
|
||||
|
||||
|
||||
/**
|
||||
* Perform an actual lease acquisition on a particular resource.
|
||||
*
|
||||
* @param DrydockResource Resource to acquire a lease on.
|
||||
* @param DrydockLease Lease to acquire.
|
||||
* @return void
|
||||
* @task acquire
|
||||
*/
|
||||
private function acquireLease(
|
||||
DrydockResource $resource,
|
||||
DrydockLease $lease) {
|
||||
|
||||
$blueprint = $resource->getBlueprint();
|
||||
$blueprint->acquireLease($resource, $lease);
|
||||
|
||||
$this->validateAcquiredLease($blueprint, $resource, $lease);
|
||||
|
||||
// If this lease has been acquired but not activated, queue a task to
|
||||
// activate it.
|
||||
if ($lease->getStatus() == DrydockLeaseStatus::STATUS_ACQUIRED) {
|
||||
PhabricatorWorker::scheduleTask(
|
||||
__CLASS__,
|
||||
array(
|
||||
'leasePHID' => $lease->getPHID(),
|
||||
),
|
||||
array(
|
||||
'objectPHID' => $lease->getPHID(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make sure that a lease was really acquired properly.
|
||||
*
|
||||
* @param DrydockBlueprint Blueprint which created the resource.
|
||||
* @param DrydockResource Resource which was acquired.
|
||||
* @param DrydockLease The lease which was supposedly acquired.
|
||||
* @return void
|
||||
* @task acquire
|
||||
*/
|
||||
private function validateAcquiredLease(
|
||||
DrydockBlueprint $blueprint,
|
||||
DrydockResource $resource,
|
||||
DrydockLease $lease) {
|
||||
|
||||
if (!$lease->isAcquiredLease()) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Blueprint "%s" (of type "%s") is not properly implemented: it '.
|
||||
'returned from "%s" without acquiring a lease.',
|
||||
$blueprint->getBlueprintName(),
|
||||
$blueprint->getClassName(),
|
||||
'acquireLease()'));
|
||||
}
|
||||
|
||||
$lease_phid = $lease->getResourcePHID();
|
||||
$resource_phid = $resource->getPHID();
|
||||
|
||||
if ($lease_phid !== $resource_phid) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Blueprint "%s" (of type "%s") is not properly implemented: it '.
|
||||
'returned from "%s" with a lease acquired on the wrong resource.',
|
||||
$blueprint->getBlueprintName(),
|
||||
$blueprint->getClassName(),
|
||||
'acquireLease()'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* -( Activating Leases )-------------------------------------------------- */
|
||||
|
||||
|
||||
/**
|
||||
* @task activate
|
||||
*/
|
||||
private function activateLease(DrydockLease $lease) {
|
||||
$resource = $lease->getResource();
|
||||
if (!$resource) {
|
||||
throw new Exception(
|
||||
pht('Trying to activate lease with no resource.'));
|
||||
}
|
||||
|
||||
$resource_status = $resource->getStatus();
|
||||
|
||||
if ($resource_status == DrydockResourceStatus::STATUS_PENDING) {
|
||||
throw new PhabricatorWorkerYieldException(15);
|
||||
}
|
||||
|
||||
if ($resource_status != DrydockResourceStatus::STATUS_ACTIVE) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Trying to activate lease on a dead resource (in status "%s").',
|
||||
$resource_status));
|
||||
}
|
||||
|
||||
// NOTE: We can race resource destruction here. Between the time we
|
||||
// performed the read above and now, the resource might have closed, so
|
||||
// we may activate leases on dead resources. At least for now, this seems
|
||||
// fine: a resource dying right before we activate a lease on it should not
|
||||
// be distinguisahble from a resource dying right after we activate a lease
|
||||
// on it. We end up with an active lease on a dead resource either way, and
|
||||
// can not prevent resources dying from lightning strikes.
|
||||
|
||||
$blueprint = $resource->getBlueprint();
|
||||
$blueprint->activateLease($resource, $lease);
|
||||
$this->validateActivatedLease($blueprint, $resource, $lease);
|
||||
}
|
||||
|
||||
/**
|
||||
* @task activate
|
||||
*/
|
||||
private function validateActivatedLease(
|
||||
DrydockBlueprint $blueprint,
|
||||
DrydockResource $resource,
|
||||
DrydockLease $lease) {
|
||||
|
||||
if (!$lease->isActivatedLease()) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Blueprint "%s" (of type "%s") is not properly implemented: it '.
|
||||
'returned from "%s" without activating a lease.',
|
||||
$blueprint->getBlueprintName(),
|
||||
$blueprint->getClassName(),
|
||||
'acquireLease()'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* -( Releasing Leases )--------------------------------------------------- */
|
||||
|
||||
|
||||
/**
|
||||
* @task release
|
||||
*/
|
||||
private function releaseLease(DrydockLease $lease) {
|
||||
$lease->openTransaction();
|
||||
$lease
|
||||
->setStatus(DrydockLeaseStatus::STATUS_RELEASED)
|
||||
->save();
|
||||
$lease
|
||||
->setStatus(DrydockLeaseStatus::STATUS_RELEASED)
|
||||
->save();
|
||||
|
||||
// TODO: Hold slot locks until destruction?
|
||||
DrydockSlotLock::releaseLocks($lease->getPHID());
|
||||
$lease->saveTransaction();
|
||||
|
||||
PhabricatorWorker::scheduleTask(
|
||||
'DrydockLeaseDestroyWorker',
|
||||
array(
|
||||
'leasePHID' => $lease->getPHID(),
|
||||
),
|
||||
array(
|
||||
'objectPHID' => $lease->getPHID(),
|
||||
));
|
||||
$lease->logEvent(DrydockLeaseReleasedLogType::LOGCONST);
|
||||
|
||||
$resource = $lease->getResource();
|
||||
$blueprint = $resource->getBlueprint();
|
||||
if ($resource) {
|
||||
$blueprint = $resource->getBlueprint();
|
||||
$blueprint->didReleaseLease($resource, $lease);
|
||||
}
|
||||
|
||||
$blueprint->didReleaseLease($resource, $lease);
|
||||
$this->destroyLease($lease);
|
||||
}
|
||||
|
||||
|
||||
/* -( Breaking Leases )---------------------------------------------------- */
|
||||
|
||||
|
||||
/**
|
||||
* @task break
|
||||
*/
|
||||
protected function breakLease(DrydockLease $lease, Exception $ex) {
|
||||
switch ($lease->getStatus()) {
|
||||
case DrydockLeaseStatus::STATUS_BROKEN:
|
||||
case DrydockLeaseStatus::STATUS_RELEASED:
|
||||
case DrydockLeaseStatus::STATUS_DESTROYED:
|
||||
throw new PhutilProxyException(
|
||||
pht(
|
||||
'Unexpected failure while destroying lease ("%s").',
|
||||
$lease->getPHID()),
|
||||
$ex);
|
||||
}
|
||||
|
||||
$lease
|
||||
->setStatus(DrydockLeaseStatus::STATUS_BROKEN)
|
||||
->save();
|
||||
|
||||
$lease->scheduleUpdate();
|
||||
|
||||
$lease->logEvent(
|
||||
DrydockLeaseActivationFailureLogType::LOGCONST,
|
||||
array(
|
||||
'class' => get_class($ex),
|
||||
'message' => $ex->getMessage(),
|
||||
));
|
||||
|
||||
throw new PhabricatorWorkerPermanentFailureException(
|
||||
pht(
|
||||
'Permanent failure while activating lease ("%s"): %s',
|
||||
$lease->getPHID(),
|
||||
$ex->getMessage()));
|
||||
}
|
||||
|
||||
|
||||
/* -( Destroying Leases )-------------------------------------------------- */
|
||||
|
||||
|
||||
/**
|
||||
* @task destroy
|
||||
*/
|
||||
private function destroyLease(DrydockLease $lease) {
|
||||
$resource = $lease->getResource();
|
||||
|
||||
if ($resource) {
|
||||
$blueprint = $resource->getBlueprint();
|
||||
$blueprint->destroyLease($resource, $lease);
|
||||
}
|
||||
|
||||
DrydockSlotLock::releaseLocks($lease->getPHID());
|
||||
|
||||
$lease
|
||||
->setStatus(DrydockLeaseStatus::STATUS_DESTROYED)
|
||||
->save();
|
||||
|
||||
$lease->logEvent(DrydockLeaseDestroyedLogType::LOGCONST);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,74 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class DrydockLeaseWorker extends DrydockWorker {
|
||||
|
||||
protected function doWork() {
|
||||
$lease_phid = $this->getTaskDataValue('leasePHID');
|
||||
$lease = $this->loadLease($lease_phid);
|
||||
|
||||
$this->activateLease($lease);
|
||||
}
|
||||
|
||||
|
||||
private function activateLease(DrydockLease $lease) {
|
||||
$actual_status = $lease->getStatus();
|
||||
|
||||
if ($actual_status != DrydockLeaseStatus::STATUS_ACQUIRED) {
|
||||
throw new PhabricatorWorkerPermanentFailureException(
|
||||
pht(
|
||||
'Trying to activate lease from wrong status ("%s").',
|
||||
$actual_status));
|
||||
}
|
||||
|
||||
$resource = $lease->getResource();
|
||||
if (!$resource) {
|
||||
throw new PhabricatorWorkerPermanentFailureException(
|
||||
pht('Trying to activate lease with no resource.'));
|
||||
}
|
||||
|
||||
$resource_status = $resource->getStatus();
|
||||
|
||||
if ($resource_status == DrydockResourceStatus::STATUS_PENDING) {
|
||||
// TODO: This is explicitly a temporary failure -- we are waiting for
|
||||
// the resource to come up.
|
||||
throw new Exception(pht('Resource still activating.'));
|
||||
}
|
||||
|
||||
if ($resource_status != DrydockResourceStatus::STATUS_ACTIVE) {
|
||||
throw new PhabricatorWorkerPermanentFailureException(
|
||||
pht(
|
||||
'Trying to activate lease on a dead resource (in status "%s").',
|
||||
$resource_status));
|
||||
}
|
||||
|
||||
// NOTE: We can race resource destruction here. Between the time we
|
||||
// performed the read above and now, the resource might have closed, so
|
||||
// we may activate leases on dead resources. At least for now, this seems
|
||||
// fine: a resource dying right before we activate a lease on it should not
|
||||
// be distinguisahble from a resource dying right after we activate a lease
|
||||
// on it. We end up with an active lease on a dead resource either way, and
|
||||
// can not prevent resources dying from lightning strikes.
|
||||
|
||||
$blueprint = $resource->getBlueprint();
|
||||
$blueprint->activateLease($resource, $lease);
|
||||
$this->validateActivatedLease($blueprint, $resource, $lease);
|
||||
}
|
||||
|
||||
private function validateActivatedLease(
|
||||
DrydockBlueprint $blueprint,
|
||||
DrydockResource $resource,
|
||||
DrydockLease $lease) {
|
||||
|
||||
if (!$lease->isActivatedLease()) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Blueprint "%s" (of type "%s") is not properly implemented: it '.
|
||||
'returned from "%s" without activating a lease.',
|
||||
$blueprint->getBlueprintName(),
|
||||
$blueprint->getClassName(),
|
||||
'acquireLease()'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class DrydockResourceDestroyWorker extends DrydockWorker {
|
||||
|
||||
protected function doWork() {
|
||||
$resource_phid = $this->getTaskDataValue('resourcePHID');
|
||||
$resource = $this->loadResource($resource_phid);
|
||||
$this->destroyResource($resource);
|
||||
}
|
||||
|
||||
private function destroyResource(DrydockResource $resource) {
|
||||
$status = $resource->getStatus();
|
||||
|
||||
switch ($status) {
|
||||
case DrydockResourceStatus::STATUS_RELEASED:
|
||||
case DrydockResourceStatus::STATUS_BROKEN:
|
||||
break;
|
||||
default:
|
||||
throw new PhabricatorWorkerPermanentFailureException(
|
||||
pht(
|
||||
'Unable to destroy resource ("%s"), resource has the wrong '.
|
||||
'status ("%s").',
|
||||
$resource->getPHID(),
|
||||
$status));
|
||||
}
|
||||
|
||||
$blueprint = $resource->getBlueprint();
|
||||
$blueprint->destroyResource($resource);
|
||||
|
||||
$resource
|
||||
->setStatus(DrydockResourceStatus::STATUS_DESTROYED)
|
||||
->save();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,13 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @task update Updating Resources
|
||||
* @task command Processing Commands
|
||||
* @task activate Activating Resources
|
||||
* @task release Releasing Resources
|
||||
* @task break Breaking Resources
|
||||
* @task destroy Destroying Resources
|
||||
*/
|
||||
final class DrydockResourceUpdateWorker extends DrydockWorker {
|
||||
|
||||
protected function doWork() {
|
||||
|
@ -11,22 +19,112 @@ final class DrydockResourceUpdateWorker extends DrydockWorker {
|
|||
$lock = PhabricatorGlobalLock::newLock($lock_key)
|
||||
->lock(1);
|
||||
|
||||
$resource = $this->loadResource($resource_phid);
|
||||
$this->updateResource($resource);
|
||||
try {
|
||||
$resource = $this->loadResource($resource_phid);
|
||||
$this->handleUpdate($resource);
|
||||
} catch (Exception $ex) {
|
||||
$lock->unlock();
|
||||
throw $ex;
|
||||
}
|
||||
|
||||
$lock->unlock();
|
||||
}
|
||||
|
||||
|
||||
/* -( Updating Resources )------------------------------------------------- */
|
||||
|
||||
|
||||
/**
|
||||
* Update a resource, handling exceptions thrown during the update.
|
||||
*
|
||||
* @param DrydockReosource Resource to update.
|
||||
* @return void
|
||||
* @task update
|
||||
*/
|
||||
private function handleUpdate(DrydockResource $resource) {
|
||||
try {
|
||||
$this->updateResource($resource);
|
||||
} catch (Exception $ex) {
|
||||
if ($this->isTemporaryException($ex)) {
|
||||
$this->yieldResource($resource, $ex);
|
||||
} else {
|
||||
$this->breakResource($resource, $ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update a resource.
|
||||
*
|
||||
* @param DrydockResource Resource to update.
|
||||
* @return void
|
||||
* @task update
|
||||
*/
|
||||
private function updateResource(DrydockResource $resource) {
|
||||
$this->processResourceCommands($resource);
|
||||
|
||||
$resource_status = $resource->getStatus();
|
||||
switch ($resource_status) {
|
||||
case DrydockResourceStatus::STATUS_PENDING:
|
||||
$this->activateResource($resource);
|
||||
break;
|
||||
case DrydockResourceStatus::STATUS_ACTIVE:
|
||||
// Nothing to do.
|
||||
break;
|
||||
case DrydockResourceStatus::STATUS_RELEASED:
|
||||
case DrydockResourceStatus::STATUS_BROKEN:
|
||||
$this->destroyResource($resource);
|
||||
break;
|
||||
case DrydockResourceStatus::STATUS_DESTROYED:
|
||||
// Nothing to do.
|
||||
break;
|
||||
}
|
||||
|
||||
$this->yieldIfExpiringResource($resource);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a temporary exception into a yield.
|
||||
*
|
||||
* @param DrydockResource Resource to yield.
|
||||
* @param Exception Temporary exception worker encountered.
|
||||
* @task update
|
||||
*/
|
||||
private function yieldResource(DrydockResource $resource, Exception $ex) {
|
||||
$duration = $this->getYieldDurationFromException($ex);
|
||||
|
||||
$resource->logEvent(
|
||||
DrydockResourceActivationYieldLogType::LOGCONST,
|
||||
array(
|
||||
'duration' => $duration,
|
||||
));
|
||||
|
||||
throw new PhabricatorWorkerYieldException($duration);
|
||||
}
|
||||
|
||||
|
||||
/* -( Processing Commands )------------------------------------------------ */
|
||||
|
||||
|
||||
/**
|
||||
* @task command
|
||||
*/
|
||||
private function processResourceCommands(DrydockResource $resource) {
|
||||
if (!$resource->canReceiveCommands()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->checkResourceExpiration($resource);
|
||||
|
||||
$commands = $this->loadCommands($resource->getPHID());
|
||||
foreach ($commands as $command) {
|
||||
if ($resource->getStatus() != DrydockResourceStatus::STATUS_ACTIVE) {
|
||||
// Resources can't receive commands before they activate or after they
|
||||
// release.
|
||||
if (!$resource->canReceiveCommands()) {
|
||||
break;
|
||||
}
|
||||
|
||||
$this->processCommand($resource, $command);
|
||||
$this->processResourceCommand($resource, $command);
|
||||
|
||||
$command
|
||||
->setIsConsumed(true)
|
||||
|
@ -34,7 +132,11 @@ final class DrydockResourceUpdateWorker extends DrydockWorker {
|
|||
}
|
||||
}
|
||||
|
||||
private function processCommand(
|
||||
|
||||
/**
|
||||
* @task command
|
||||
*/
|
||||
private function processResourceCommand(
|
||||
DrydockResource $resource,
|
||||
DrydockCommand $command) {
|
||||
|
||||
|
@ -45,24 +147,53 @@ final class DrydockResourceUpdateWorker extends DrydockWorker {
|
|||
}
|
||||
}
|
||||
|
||||
private function releaseResource(DrydockResource $resource) {
|
||||
if ($resource->getStatus() != DrydockResourceStatus::STATUS_ACTIVE) {
|
||||
// If we had multiple release commands
|
||||
// This command is only meaningful to resources in the "Open" state.
|
||||
return;
|
||||
|
||||
/* -( Activating Resources )----------------------------------------------- */
|
||||
|
||||
|
||||
/**
|
||||
* @task activate
|
||||
*/
|
||||
private function activateResource(DrydockResource $resource) {
|
||||
$blueprint = $resource->getBlueprint();
|
||||
$blueprint->activateResource($resource);
|
||||
$this->validateActivatedResource($blueprint, $resource);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @task activate
|
||||
*/
|
||||
private function validateActivatedResource(
|
||||
DrydockBlueprint $blueprint,
|
||||
DrydockResource $resource) {
|
||||
|
||||
if (!$resource->isActivatedResource()) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Blueprint "%s" (of type "%s") is not properly implemented: %s '.
|
||||
'must actually allocate the resource it returns.',
|
||||
$blueprint->getBlueprintName(),
|
||||
$blueprint->getClassName(),
|
||||
'allocateResource()'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* -( Releasing Resources )------------------------------------------------ */
|
||||
|
||||
|
||||
/**
|
||||
* @task release
|
||||
*/
|
||||
private function releaseResource(DrydockResource $resource) {
|
||||
$viewer = $this->getViewer();
|
||||
$drydock_phid = id(new PhabricatorDrydockApplication())->getPHID();
|
||||
|
||||
$resource->openTransaction();
|
||||
$resource
|
||||
->setStatus(DrydockResourceStatus::STATUS_RELEASED)
|
||||
->save();
|
||||
|
||||
// TODO: Hold slot locks until destruction?
|
||||
DrydockSlotLock::releaseLocks($resource->getPHID());
|
||||
$resource->saveTransaction();
|
||||
$resource
|
||||
->setStatus(DrydockResourceStatus::STATUS_RELEASED)
|
||||
->save();
|
||||
|
||||
$statuses = array(
|
||||
DrydockLeaseStatus::STATUS_PENDING,
|
||||
|
@ -86,14 +217,65 @@ final class DrydockResourceUpdateWorker extends DrydockWorker {
|
|||
$lease->scheduleUpdate();
|
||||
}
|
||||
|
||||
PhabricatorWorker::scheduleTask(
|
||||
'DrydockResourceDestroyWorker',
|
||||
array(
|
||||
'resourcePHID' => $resource->getPHID(),
|
||||
),
|
||||
array(
|
||||
'objectPHID' => $resource->getPHID(),
|
||||
));
|
||||
$this->destroyResource($resource);
|
||||
}
|
||||
|
||||
|
||||
/* -( Breaking Resources )------------------------------------------------- */
|
||||
|
||||
|
||||
/**
|
||||
* @task break
|
||||
*/
|
||||
private function breakResource(DrydockResource $resource, Exception $ex) {
|
||||
switch ($resource->getStatus()) {
|
||||
case DrydockResourceStatus::STATUS_BROKEN:
|
||||
case DrydockResourceStatus::STATUS_RELEASED:
|
||||
case DrydockResourceStatus::STATUS_DESTROYED:
|
||||
// If the resource was already broken, just throw a normal exception.
|
||||
// This will retry the task eventually.
|
||||
throw new PhutilProxyException(
|
||||
pht(
|
||||
'Unexpected failure while destroying resource ("%s").',
|
||||
$resource->getPHID()),
|
||||
$ex);
|
||||
}
|
||||
|
||||
$resource
|
||||
->setStatus(DrydockResourceStatus::STATUS_BROKEN)
|
||||
->save();
|
||||
|
||||
$resource->scheduleUpdate();
|
||||
|
||||
$resource->logEvent(
|
||||
DrydockResourceActivationFailureLogType::LOGCONST,
|
||||
array(
|
||||
'class' => get_class($ex),
|
||||
'message' => $ex->getMessage(),
|
||||
));
|
||||
|
||||
throw new PhabricatorWorkerPermanentFailureException(
|
||||
pht(
|
||||
'Permanent failure while activating resource ("%s"): %s',
|
||||
$resource->getPHID(),
|
||||
$ex->getMessage()));
|
||||
}
|
||||
|
||||
|
||||
/* -( Destroying Resources )----------------------------------------------- */
|
||||
|
||||
|
||||
/**
|
||||
* @task destroy
|
||||
*/
|
||||
private function destroyResource(DrydockResource $resource) {
|
||||
$blueprint = $resource->getBlueprint();
|
||||
$blueprint->destroyResource($resource);
|
||||
|
||||
DrydockSlotLock::releaseLocks($resource->getPHID());
|
||||
|
||||
$resource
|
||||
->setStatus(DrydockResourceStatus::STATUS_DESTROYED)
|
||||
->save();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class DrydockResourceWorker extends DrydockWorker {
|
||||
|
||||
protected function doWork() {
|
||||
$resource_phid = $this->getTaskDataValue('resourcePHID');
|
||||
$resource = $this->loadResource($resource_phid);
|
||||
|
||||
$this->activateResource($resource);
|
||||
}
|
||||
|
||||
|
||||
private function activateResource(DrydockResource $resource) {
|
||||
$resource_status = $resource->getStatus();
|
||||
|
||||
if ($resource_status != DrydockResourceStatus::STATUS_PENDING) {
|
||||
throw new PhabricatorWorkerPermanentFailureException(
|
||||
pht(
|
||||
'Trying to activate resource from wrong status ("%s").',
|
||||
$resource_status));
|
||||
}
|
||||
|
||||
$blueprint = $resource->getBlueprint();
|
||||
$blueprint->activateResource($resource);
|
||||
$this->validateActivatedResource($blueprint, $resource);
|
||||
}
|
||||
|
||||
|
||||
private function validateActivatedResource(
|
||||
DrydockBlueprint $blueprint,
|
||||
DrydockResource $resource) {
|
||||
|
||||
if (!$resource->isActivatedResource()) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Blueprint "%s" (of type "%s") is not properly implemented: %s '.
|
||||
'must actually allocate the resource it returns.',
|
||||
$blueprint->getBlueprintName(),
|
||||
$blueprint->getClassName(),
|
||||
'allocateResource()'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -50,4 +50,109 @@ abstract class DrydockWorker extends PhabricatorWorker {
|
|||
return $commands;
|
||||
}
|
||||
|
||||
protected function checkLeaseExpiration(DrydockLease $lease) {
|
||||
$this->checkObjectExpiration($lease);
|
||||
}
|
||||
|
||||
protected function checkResourceExpiration(DrydockResource $resource) {
|
||||
$this->checkObjectExpiration($resource);
|
||||
}
|
||||
|
||||
private function checkObjectExpiration($object) {
|
||||
// Check if the resource or lease has expired. If it has, we're going to
|
||||
// send it a release command.
|
||||
|
||||
// This command is sent from within the update worker so it is handled
|
||||
// immediately, but doing this generates a log and improves consistency.
|
||||
|
||||
$expires = $object->getUntil();
|
||||
if (!$expires) {
|
||||
return;
|
||||
}
|
||||
|
||||
$now = PhabricatorTime::getNow();
|
||||
if ($expires > $now) {
|
||||
return;
|
||||
}
|
||||
|
||||
$viewer = $this->getViewer();
|
||||
$drydock_phid = id(new PhabricatorDrydockApplication())->getPHID();
|
||||
|
||||
$command = DrydockCommand::initializeNewCommand($viewer)
|
||||
->setTargetPHID($object->getPHID())
|
||||
->setAuthorPHID($drydock_phid)
|
||||
->setCommand(DrydockCommand::COMMAND_RELEASE)
|
||||
->save();
|
||||
}
|
||||
|
||||
protected function yieldIfExpiringLease(DrydockLease $lease) {
|
||||
if (!$lease->canReceiveCommands()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->yieldIfExpiring($lease->getUntil());
|
||||
}
|
||||
|
||||
protected function yieldIfExpiringResource(DrydockResource $resource) {
|
||||
if (!$resource->canReceiveCommands()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->yieldIfExpiring($resource->getUntil());
|
||||
}
|
||||
|
||||
private function yieldIfExpiring($expires) {
|
||||
if (!$expires) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$this->getTaskDataValue('isExpireTask')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$now = PhabricatorTime::getNow();
|
||||
throw new PhabricatorWorkerYieldException($expires - $now);
|
||||
}
|
||||
|
||||
protected function isTemporaryException(Exception $ex) {
|
||||
if ($ex instanceof PhabricatorWorkerYieldException) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($ex instanceof DrydockSlotLockException) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($ex instanceof PhutilAggregateException) {
|
||||
$any_temporary = false;
|
||||
foreach ($ex->getExceptions() as $sub) {
|
||||
if ($this->isTemporaryException($sub)) {
|
||||
$any_temporary = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($any_temporary) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($ex instanceof PhutilProxyException) {
|
||||
return $this->isTemporaryException($ex->getPreviousException());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function getYieldDurationFromException(Exception $ex) {
|
||||
if ($ex instanceof PhabricatorWorkerYieldException) {
|
||||
return $ex->getDuration();
|
||||
}
|
||||
|
||||
if ($ex instanceof DrydockSlotLockException) {
|
||||
return 5;
|
||||
}
|
||||
|
||||
return 15;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,10 +3,20 @@
|
|||
final class PhabricatorFileTemporaryGarbageCollector
|
||||
extends PhabricatorGarbageCollector {
|
||||
|
||||
public function collectGarbage() {
|
||||
const COLLECTORCONST = 'files.ttl';
|
||||
|
||||
public function getCollectorName() {
|
||||
return pht('Files (TTL)');
|
||||
}
|
||||
|
||||
public function hasAutomaticPolicy() {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function collectGarbage() {
|
||||
$files = id(new PhabricatorFile())->loadAllWhere(
|
||||
'ttl < %d LIMIT 100',
|
||||
time());
|
||||
PhabricatorTime::getNow());
|
||||
|
||||
foreach ($files as $file) {
|
||||
$file->delete();
|
||||
|
|
|
@ -43,35 +43,7 @@ abstract class HarbormasterArtifact extends Phobject {
|
|||
}
|
||||
|
||||
final public function getArtifactConstant() {
|
||||
$class = new ReflectionClass($this);
|
||||
|
||||
$const = $class->getConstant('ARTIFACTCONST');
|
||||
if ($const === false) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'"%s" class "%s" must define a "%s" property.',
|
||||
__CLASS__,
|
||||
get_class($this),
|
||||
'ARTIFACTCONST'));
|
||||
}
|
||||
|
||||
$limit = self::getArtifactConstantByteLimit();
|
||||
if (!is_string($const) || (strlen($const) > $limit)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'"%s" class "%s" has an invalid "%s" property. Action constants '.
|
||||
'must be strings and no more than %s bytes in length.',
|
||||
__CLASS__,
|
||||
get_class($this),
|
||||
'ARTIFACTCONST',
|
||||
new PhutilNumber($limit)));
|
||||
}
|
||||
|
||||
return $const;
|
||||
}
|
||||
|
||||
final public static function getArtifactConstantByteLimit() {
|
||||
return 32;
|
||||
return $this->getPhobjectClassConstant('ARTIFACTCONST', 32);
|
||||
}
|
||||
|
||||
final public static function getAllArtifactTypes() {
|
||||
|
|
|
@ -62,7 +62,7 @@ final class HarbormasterPlanRunController extends HarbormasterController {
|
|||
|
||||
if (!$errors) {
|
||||
$buildable->save();
|
||||
$buildable->applyPlan($plan);
|
||||
$buildable->applyPlan($plan, array());
|
||||
|
||||
$buildable_uri = '/B'.$buildable->getID();
|
||||
return id(new AphrontRedirectResponse())->setURI($buildable_uri);
|
||||
|
|
|
@ -72,9 +72,9 @@ final class HarbormasterStepEditController extends HarbormasterController {
|
|||
|
||||
$e_name = true;
|
||||
$v_name = $step->getName();
|
||||
$e_description = true;
|
||||
$e_description = null;
|
||||
$v_description = $step->getDescription();
|
||||
$e_depends_on = true;
|
||||
$e_depends_on = null;
|
||||
$v_depends_on = $step->getDetail('dependsOn', array());
|
||||
|
||||
$errors = array();
|
||||
|
@ -82,9 +82,7 @@ final class HarbormasterStepEditController extends HarbormasterController {
|
|||
if ($request->isFormPost()) {
|
||||
$e_name = null;
|
||||
$v_name = $request->getStr('name');
|
||||
$e_description = null;
|
||||
$v_description = $request->getStr('description');
|
||||
$e_depends_on = null;
|
||||
$v_depends_on = $request->getArr('dependsOn');
|
||||
|
||||
$xactions = $field_list->buildFieldTransactionsFromRequest(
|
||||
|
@ -139,6 +137,12 @@ final class HarbormasterStepEditController extends HarbormasterController {
|
|||
->setError($e_name)
|
||||
->setValue($v_name));
|
||||
|
||||
$form->appendChild(id(new AphrontFormDividerControl()));
|
||||
|
||||
$field_list->appendFieldsToForm($form);
|
||||
|
||||
$form->appendChild(id(new AphrontFormDividerControl()));
|
||||
|
||||
$form
|
||||
->appendControl(
|
||||
id(new AphrontFormTokenizerControl())
|
||||
|
@ -152,8 +156,6 @@ final class HarbormasterStepEditController extends HarbormasterController {
|
|||
->setError($e_depends_on)
|
||||
->setValue($v_depends_on));
|
||||
|
||||
$field_list->appendFieldsToForm($form);
|
||||
|
||||
$form
|
||||
->appendChild(
|
||||
id(new PhabricatorRemarkupControl())
|
||||
|
|
|
@ -72,4 +72,8 @@ final class HarbormasterBuildStepCoreCustomField
|
|||
return;
|
||||
}
|
||||
|
||||
public function getBuildTargetFieldValue() {
|
||||
return $this->getProxy()->getFieldValue();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
<?php
|
||||
|
||||
abstract class HarbormasterBuildStepCustomField
|
||||
extends PhabricatorCustomField {}
|
||||
extends PhabricatorCustomField {
|
||||
|
||||
abstract public function getBuildTargetFieldValue();
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Structure used to ask Harbormaster to start a build.
|
||||
*
|
||||
* Requests to start builds sometimes originate several layers away from where
|
||||
* they are processed. For example, Herald rules which start builds pass the
|
||||
* requests through the adapter and then through the editor before they reach
|
||||
* Harbormaster.
|
||||
*
|
||||
* This class is just a thin wrapper around these requests so we can make them
|
||||
* more complex later without needing to rewrite any APIs.
|
||||
*/
|
||||
final class HarbormasterBuildRequest extends Phobject {
|
||||
|
||||
private $buildPlanPHID;
|
||||
private $buildParameters = array();
|
||||
|
||||
public function setBuildPlanPHID($build_plan_phid) {
|
||||
$this->buildPlanPHID = $build_plan_phid;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getBuildPlanPHID() {
|
||||
return $this->buildPlanPHID;
|
||||
}
|
||||
|
||||
public function setBuildParameters(array $build_parameters) {
|
||||
$this->buildParameters = $build_parameters;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getBuildParameters() {
|
||||
return $this->buildParameters;
|
||||
}
|
||||
|
||||
}
|
|
@ -206,7 +206,7 @@ final class HarbormasterTargetEngine extends Phobject {
|
|||
// resource and "own" it, so we don't try to handle this, but may need
|
||||
// to be more careful here if use of autotargets expands.
|
||||
|
||||
$build = $buildable->applyPlan($plan);
|
||||
$build = $buildable->applyPlan($plan, array());
|
||||
PhabricatorWorker::setRunAllTasksInProcess(false);
|
||||
} catch (Exception $ex) {
|
||||
PhabricatorWorker::setRunAllTasksInProcess(false);
|
||||
|
|
|
@ -4,8 +4,9 @@ interface HarbormasterBuildableAdapterInterface {
|
|||
|
||||
public function getHarbormasterBuildablePHID();
|
||||
public function getHarbormasterContainerPHID();
|
||||
public function getQueuedHarbormasterBuildPlanPHIDs();
|
||||
public function queueHarbormasterBuildPlanPHID($phid);
|
||||
public function getQueuedHarbormasterBuildRequests();
|
||||
public function queueHarbormasterBuildRequest(
|
||||
HarbormasterBuildRequest $request);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,9 @@ final class HarbormasterRunBuildPlansHeraldAction
|
|||
$phids = array_fuse(array_keys($targets));
|
||||
|
||||
foreach ($phids as $phid) {
|
||||
$adapter->queueHarbormasterBuildPlanPHID($phid);
|
||||
$request = id(new HarbormasterBuildRequest())
|
||||
->setBuildPlanPHID($phid);
|
||||
$adapter->queueHarbormasterBuildRequest($request);
|
||||
}
|
||||
|
||||
$this->logEffect(self::DO_BUILD, $phids);
|
||||
|
|
|
@ -89,7 +89,7 @@ final class HarbormasterManagementBuildWorkflow
|
|||
PhabricatorEnv::getProductionURI('/B'.$buildable->getID()));
|
||||
|
||||
PhabricatorWorker::setRunAllTasksInProcess(true);
|
||||
$buildable->applyPlan($plan);
|
||||
$buildable->applyPlan($plan, array());
|
||||
|
||||
$console->writeOut("%s\n", pht('Done.'));
|
||||
|
||||
|
|
|
@ -6,6 +6,16 @@
|
|||
abstract class HarbormasterBuildStepImplementation extends Phobject {
|
||||
|
||||
private $settings;
|
||||
private $currentWorkerTaskID;
|
||||
|
||||
public function setCurrentWorkerTaskID($id) {
|
||||
$this->currentWorkerTaskID = $id;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCurrentWorkerTaskID() {
|
||||
return $this->currentWorkerTaskID;
|
||||
}
|
||||
|
||||
public static function getImplementations() {
|
||||
return id(new PhutilClassMapQuery())
|
||||
|
@ -184,7 +194,7 @@ abstract class HarbormasterBuildStepImplementation extends Phobject {
|
|||
* @return string String with variables replaced safely into it.
|
||||
*/
|
||||
protected function mergeVariables($function, $pattern, array $variables) {
|
||||
$regexp = '/\\$\\{(?P<name>[a-z\\.]+)\\}/';
|
||||
$regexp = '@\\$\\{(?P<name>[a-z\\./-]+)\\}@';
|
||||
|
||||
$matches = null;
|
||||
preg_match_all($regexp, $pattern, $matches);
|
||||
|
|
|
@ -75,6 +75,12 @@ final class HarbormasterHTTPRequestBuildStepImplementation
|
|||
list($status, $body, $headers) = $future->resolve();
|
||||
|
||||
$header_lines = array();
|
||||
|
||||
// TODO: We don't currently preserve the entire "HTTP" response header, but
|
||||
// should. Once we do, reproduce it here faithfully.
|
||||
$status_code = $status->getStatusCode();
|
||||
$header_lines[] = "HTTP {$status_code}";
|
||||
|
||||
foreach ($headers as $header) {
|
||||
list($head, $tail) = $header;
|
||||
$header_lines[] = "{$head}: {$tail}";
|
||||
|
@ -89,7 +95,7 @@ final class HarbormasterHTTPRequestBuildStepImplementation
|
|||
->newLog($uri, 'http.body')
|
||||
->append($body);
|
||||
|
||||
if ($status->getStatusCode() != 200) {
|
||||
if ($status->isError()) {
|
||||
throw new HarbormasterBuildFailureException();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,14 +45,14 @@ final class HarbormasterLeaseWorkingCopyBuildStepImplementation
|
|||
->setResourceType($working_copy_type)
|
||||
->setOwnerPHID($build_target->getPHID());
|
||||
|
||||
$variables = $build_target->getVariables();
|
||||
$map = $this->buildRepositoryMap($build_target);
|
||||
|
||||
$repository_phid = idx($variables, 'repository.phid');
|
||||
$commit = idx($variables, 'repository.commit');
|
||||
$lease->setAttribute('repositories.map', $map);
|
||||
|
||||
$lease
|
||||
->setAttribute('repositoryPHID', $repository_phid)
|
||||
->setAttribute('commit', $commit);
|
||||
$task_id = $this->getCurrentWorkerTaskID();
|
||||
if ($task_id) {
|
||||
$lease->setAwakenTaskIDs(array($task_id));
|
||||
}
|
||||
|
||||
$lease->queueForActivation();
|
||||
|
||||
|
@ -100,7 +100,87 @@ final class HarbormasterLeaseWorkingCopyBuildStepImplementation
|
|||
'type' => 'text',
|
||||
'required' => true,
|
||||
),
|
||||
'repositoryPHIDs' => array(
|
||||
'name' => pht('Also Clone'),
|
||||
'type' => 'datasource',
|
||||
'datasource.class' => 'DiffusionRepositoryDatasource',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
private function buildRepositoryMap(HarbormasterBuildTarget $build_target) {
|
||||
$viewer = PhabricatorUser::getOmnipotentUser();
|
||||
$variables = $build_target->getVariables();
|
||||
|
||||
$repository_phid = idx($variables, 'repository.phid');
|
||||
if (!$repository_phid) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Unable to determine how to clone the repository for this '.
|
||||
'buildable: it is not associated with a tracked repository.'));
|
||||
}
|
||||
|
||||
$also_phids = $build_target->getFieldValue('repositoryPHIDs');
|
||||
|
||||
$all_phids = $also_phids;
|
||||
$all_phids[] = $repository_phid;
|
||||
|
||||
$repositories = id(new PhabricatorRepositoryQuery())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs($all_phids)
|
||||
->execute();
|
||||
$repositories = mpull($repositories, null, 'getPHID');
|
||||
|
||||
foreach ($all_phids as $phid) {
|
||||
if (empty($repositories[$phid])) {
|
||||
throw new PhabricatorWorkerPermanentFailureException(
|
||||
pht(
|
||||
'Unable to load repository with PHID "%s".',
|
||||
$phid));
|
||||
}
|
||||
}
|
||||
|
||||
$map = array();
|
||||
|
||||
foreach ($also_phids as $also_phid) {
|
||||
$also_repo = $repositories[$also_phid];
|
||||
$map[$also_repo->getCloneName()] = array(
|
||||
'phid' => $also_repo->getPHID(),
|
||||
'branch' => 'master',
|
||||
);
|
||||
}
|
||||
|
||||
$repository = $repositories[$repository_phid];
|
||||
|
||||
$commit = idx($variables, 'buildable.commit');
|
||||
$ref_uri = idx($variables, 'repository.staging.uri');
|
||||
$ref_ref = idx($variables, 'repository.staging.ref');
|
||||
if ($commit) {
|
||||
$spec = array(
|
||||
'commit' => $commit,
|
||||
);
|
||||
} else if ($ref_uri && $ref_ref) {
|
||||
$spec = array(
|
||||
'ref' => array(
|
||||
'uri' => $ref_uri,
|
||||
'ref' => $ref_ref,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Unable to determine how to fetch changes: this buildable does not '.
|
||||
'identify a commit or a staging ref. You may need to configure a '.
|
||||
'repository staging area.'));
|
||||
}
|
||||
|
||||
$directory = $repository->getCloneName();
|
||||
$map[$directory] = array(
|
||||
'phid' => $repository->getPHID(),
|
||||
'default' => true,
|
||||
) + $spec;
|
||||
|
||||
return $map;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -14,19 +14,7 @@ abstract class HarbormasterBuildStepGroup extends Phobject {
|
|||
}
|
||||
|
||||
final public function getGroupKey() {
|
||||
$class = new ReflectionClass($this);
|
||||
|
||||
$const = $class->getConstant('GROUPKEY');
|
||||
if ($const === false) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'"%s" class "%s" must define a "%s" property.',
|
||||
__CLASS__,
|
||||
get_class($this),
|
||||
'GROUPKEY'));
|
||||
}
|
||||
|
||||
return $const;
|
||||
return $this->getPhobjectClassConstant('GROUPKEY');
|
||||
}
|
||||
|
||||
final public static function getAllGroups() {
|
||||
|
|
|
@ -96,15 +96,21 @@ final class HarbormasterBuildable extends HarbormasterDAO
|
|||
}
|
||||
|
||||
/**
|
||||
* Looks up the plan PHIDs and applies the plans to the specified
|
||||
* object identified by it's PHID.
|
||||
* Start builds for a given buildable.
|
||||
*
|
||||
* @param phid PHID of the object to build.
|
||||
* @param phid Container PHID for the buildable.
|
||||
* @param list<HarbormasterBuildRequest> List of builds to perform.
|
||||
* @return void
|
||||
*/
|
||||
public static function applyBuildPlans(
|
||||
$phid,
|
||||
$container_phid,
|
||||
array $plan_phids) {
|
||||
array $requests) {
|
||||
|
||||
if (!$plan_phids) {
|
||||
assert_instances_of($requests, 'HarbormasterBuildRequest');
|
||||
|
||||
if (!$requests) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -116,31 +122,49 @@ final class HarbormasterBuildable extends HarbormasterDAO
|
|||
return;
|
||||
}
|
||||
|
||||
$viewer = PhabricatorUser::getOmnipotentUser();
|
||||
|
||||
$buildable = self::createOrLoadExisting(
|
||||
PhabricatorUser::getOmnipotentUser(),
|
||||
$viewer,
|
||||
$phid,
|
||||
$container_phid);
|
||||
|
||||
$plan_phids = mpull($requests, 'getBuildPlanPHID');
|
||||
$plans = id(new HarbormasterBuildPlanQuery())
|
||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs($plan_phids)
|
||||
->execute();
|
||||
foreach ($plans as $plan) {
|
||||
$plans = mpull($plans, null, 'getPHID');
|
||||
|
||||
foreach ($requests as $request) {
|
||||
$plan_phid = $request->getBuildPlanPHID();
|
||||
$plan = idx($plans, $plan_phid);
|
||||
|
||||
if (!$plan) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Failed to load build plan ("%s").',
|
||||
$plan_phid));
|
||||
}
|
||||
|
||||
if ($plan->isDisabled()) {
|
||||
// TODO: This should be communicated more clearly -- maybe we should
|
||||
// create the build but set the status to "disabled" or "derelict".
|
||||
continue;
|
||||
}
|
||||
|
||||
$buildable->applyPlan($plan);
|
||||
$parameters = $request->getBuildParameters();
|
||||
$buildable->applyPlan($plan, $parameters);
|
||||
}
|
||||
}
|
||||
|
||||
public function applyPlan(HarbormasterBuildPlan $plan) {
|
||||
public function applyPlan(HarbormasterBuildPlan $plan, array $parameters) {
|
||||
|
||||
$viewer = PhabricatorUser::getOmnipotentUser();
|
||||
$build = HarbormasterBuild::initializeNewBuild($viewer)
|
||||
->setBuildablePHID($this->getPHID())
|
||||
->setBuildPlanPHID($plan->getPHID())
|
||||
->setBuildParameters($parameters)
|
||||
->setBuildStatus(HarbormasterBuild::STATUS_PENDING);
|
||||
|
||||
$auto_key = $plan->getPlanAutoKey();
|
||||
|
|
|
@ -9,6 +9,7 @@ final class HarbormasterBuild extends HarbormasterDAO
|
|||
protected $buildPlanPHID;
|
||||
protected $buildStatus;
|
||||
protected $buildGeneration;
|
||||
protected $buildParameters = array();
|
||||
protected $planAutoKey;
|
||||
|
||||
private $buildable = self::ATTACHABLE;
|
||||
|
@ -156,6 +157,9 @@ final class HarbormasterBuild extends HarbormasterDAO
|
|||
protected function getConfiguration() {
|
||||
return array(
|
||||
self::CONFIG_AUX_PHID => true,
|
||||
self::CONFIG_SERIALIZATION => array(
|
||||
'buildParameters' => self::SERIALIZATION_JSON,
|
||||
),
|
||||
self::CONFIG_COLUMN_SCHEMA => array(
|
||||
'buildStatus' => 'text32',
|
||||
'buildGeneration' => 'uint32',
|
||||
|
@ -258,6 +262,10 @@ final class HarbormasterBuild extends HarbormasterDAO
|
|||
'build.id' => null,
|
||||
);
|
||||
|
||||
foreach ($this->getBuildParameters() as $key => $value) {
|
||||
$results['build/'.$key] = $value;
|
||||
}
|
||||
|
||||
$buildable = $this->getBuildable();
|
||||
$object = $buildable->getBuildableObject();
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue