diff --git a/setup/unattended-install/InstallationFileService.php b/setup/unattended-install/InstallationFileService.php index 77dad4cbd..c73a13fe1 100644 --- a/setup/unattended-install/InstallationFileService.php +++ b/setup/unattended-install/InstallationFileService.php @@ -17,6 +17,9 @@ class InstallationFileService { /** @var \RunTimeEnvironment $oProductionEnv */ private $oProductionEnv; + /** @var \ItopExtensionsMap $oProductionEnv */ + private $oItopExtensionsMap; + private $sTargetEnvironment; private $sInstallationPath; private $aSelectedModules; @@ -30,7 +33,8 @@ class InstallationFileService { * @param string $sInstallationPath * @param string $sTargetEnvironment * @param array $aSelectedExtensions - * @param bool $bInstallationOptionalChoicesChecked : this option is used only when no extensions are selected (ie empty $aSelectedExtensions) + * @param bool $bInstallationOptionalChoicesChecked : this option is used only when no extensions are selected (ie empty + * $aSelectedExtensions) */ public function __construct(string $sInstallationPath, string $sTargetEnvironment='production', array $aSelectedExtensions = [], bool $bInstallationOptionalChoicesChecked=true) { $this->sInstallationPath = $sInstallationPath; @@ -42,6 +46,15 @@ class InstallationFileService { $this->bInstallationOptionalChoicesChecked = $bInstallationOptionalChoicesChecked; } + public function Init(): void { + clearstatcache(); + + $this->ProcessDefaultModules(); + $this->ProcessInstallationChoices(); + $this->ProcessExtensionModulesNotSpecifiedInChoices(); + $this->ProcessAutoSelectModules(); + } + public function GetProductionEnv(): RunTimeEnvironment { if (is_null($this->oProductionEnv)){ $this->oProductionEnv = new RunTimeEnvironment(); @@ -57,6 +70,17 @@ class InstallationFileService { return $this->aAfterComputationSelectedExtensions; } + public function SetItopExtensionsMap(ItopExtensionsMap $oItopExtensionsMap): void { + $this->oItopExtensionsMap = $oItopExtensionsMap; + } + + public function GetItopExtensionsMap(): ItopExtensionsMap { + if (is_null($this->oItopExtensionsMap)){ + $this->oItopExtensionsMap = new iTopExtensionsMap($this->sTargetEnvironment, true); + } + return $this->oItopExtensionsMap; + } + public function GetAutoSelectModules(): array { return $this->aAutoSelectModules; } @@ -69,14 +93,6 @@ class InstallationFileService { return $this->aUnSelectedModules; } - public function Init(): void { - clearstatcache(); - - $this->ProcessDefaultModules(); - $this->ProcessInstallationChoices(); - $this->ProcessAutoSelectModules(); - } - public function ProcessInstallationChoices(): void { $oXMLParameters = new XMLParameters($this->sInstallationPath); $aSteps = $oXMLParameters->Get('steps', []); @@ -274,4 +290,76 @@ class InstallationFileService { } } } + + public function CanChooseUnpackageExtension(iTopExtension $oExtension) : bool { + if ($oExtension->sSource === iTopExtension::SOURCE_REMOTE){ + SetupLog::Info("Data Extension can be selected", null, ['extension' => $oExtension->sCode]); + return true; + } + + $bSelectable = $this->bInstallationOptionalChoicesChecked && ($oExtension->sSource === iTopExtension::SOURCE_MANUAL); + if ($bSelectable){ + SetupLog::Info("Manual Extension can be selected", null, ['extension' => $oExtension->sCode]); + } else { + SetupLog::Debug("Manual Extension can NOT be selected", null, ['extension' => $oExtension->sCode]); + } + + return $bSelectable; + } + + public function ProcessExtensionModulesNotSpecifiedInChoices() { + /** @var \iTopExtension $oExtension */ + foreach($this->GetItopExtensionsMap()->GetAllExtensions() as $oExtension) { + if (in_array($oExtension->sCode, $this->aAfterComputationSelectedExtensions)){ + //extension already processed in installation.xml + SetupLog::Info("Extension already processed via installation choices", null, + [ + 'extension' => $oExtension->sCode, + ]) ; + continue; + } + if ($this->CanChooseUnpackageExtension($oExtension)){ + if (($oExtension->bVisible) && (count($oExtension->aMissingDependencies) === 0)) { + $aCurrentModules = []; + $aUnselectableModules = []; + $bIsExtensionSelectable = true; + foreach ($oExtension->aModules as $sModuleId) { + if (array_key_exists($sModuleId, $this->aSelectedModules)) { + //already selected + continue; + } + + if (array_key_exists($sModuleId, $this->aUnSelectedModules)) { + $aUnselectableModules[] = $sModuleId; + + //already unselected + $bIsExtensionSelectable = false; + } else { + $aCurrentModules[$sModuleId] = true; + } + } + + if ($bIsExtensionSelectable) { + SetupLog::Debug("Add modules from unpackaged extension", null, + [ + 'extension' => $oExtension->sCode, + 'source' => $oExtension->sSource, + 'modules to add' => array_keys($aCurrentModules), + ]); + $this->aSelectedModules = array_merge($this->aSelectedModules, $aCurrentModules); + $this->aAfterComputationSelectedExtensions[] = $oExtension->sCode; + } else { + SetupLog::Warning("Unpackaged extension can not be selected due to modules incompatible with installation choices", + null, + [ + 'extension' => $oExtension->sCode, + 'source' => $oExtension->sSource, + 'modules' => array_keys($aCurrentModules), + 'unselectable modules' => $aUnselectableModules, + ]); + } + } + } + } + } } diff --git a/tests/php-unit-tests/unitary-tests/setup/unattended-install/InstallationFileServiceTest.php b/tests/php-unit-tests/unitary-tests/setup/unattended-install/InstallationFileServiceTest.php index 9aefa4237..ee97bd96a 100644 --- a/tests/php-unit-tests/unitary-tests/setup/unattended-install/InstallationFileServiceTest.php +++ b/tests/php-unit-tests/unitary-tests/setup/unattended-install/InstallationFileServiceTest.php @@ -2,9 +2,12 @@ namespace Combodo\iTop\Test\UnitTest\Setup\UnattendedInstall; -use Combodo\iTop\Test\UnitTest\ItopDataTestCase; use Combodo\iTop\Test\UnitTest\ItopTestCase; -use PHPUnit\Framework\TestCase; +use ItopExtensionsMap; +use iTopExtension; +use RunTimeEnvironment; +use InstallationFileService; +use ModuleDiscovery; /** * @group itop-clone-only @@ -13,7 +16,7 @@ class InstallationFileServiceTest extends ItopTestCase { protected function setUp(): void { parent::setUp(); require_once(dirname(__FILE__, 6) . '/setup/unattended-install/InstallationFileService.php'); - \ModuleDiscovery::ResetCache(); + ModuleDiscovery::ResetCache(); } protected function tearDown(): void { @@ -108,9 +111,9 @@ class InstallationFileServiceTest extends ItopTestCase { * @dataProvider ProcessDefaultModulesProvider */ public function testProcessDefaultModules(array $aAllFoundModules, array $aExpectedSelectedModules, array $aExpectedAutoSelectModules) { - $oInstallationFileService = new \InstallationFileService('', 'production', [], true); + $oInstallationFileService = new InstallationFileService('', 'production', [], true); - $oProductionEnv = $this->createMock(\RunTimeEnvironment::class); + $oProductionEnv = $this->createMock(RunTimeEnvironment::class); $oProductionEnv->expects($this->once()) ->method('AnalyzeInstallation') ->willReturn($aAllFoundModules); @@ -141,8 +144,8 @@ class InstallationFileServiceTest extends ItopTestCase { */ public function testProcessInstallationChoices($bInstallationOptionalChoicesChecked) { $sPath = $this->GetInstallationPath(); - $oInstallationFileService = new \InstallationFileService($sPath, 'production', [], $bInstallationOptionalChoicesChecked); - $oProductionEnv = $this->createMock(\RunTimeEnvironment::class); + $oInstallationFileService = new InstallationFileService($sPath, 'production', [], $bInstallationOptionalChoicesChecked); + $oProductionEnv = $this->createMock(RunTimeEnvironment::class); $oProductionEnv->expects($this->never()) ->method('AnalyzeInstallation'); $oInstallationFileService->SetProductionEnv($oProductionEnv); @@ -224,8 +227,8 @@ class InstallationFileServiceTest extends ItopTestCase { */ public function testProcessInstallationChoicesWithItilChoices(array $aSelectedExtensions, bool $bKnownMgtSelected, bool $bCoreMgtSelected) { $sPath = $this->GetInstallationPath(); - $oInstallationFileService = new \InstallationFileService($sPath, 'production', $aSelectedExtensions, false); - $oProductionEnv = $this->createMock(\RunTimeEnvironment::class); + $oInstallationFileService = new InstallationFileService($sPath, 'production', $aSelectedExtensions, false); + $oProductionEnv = $this->createMock(RunTimeEnvironment::class); $oProductionEnv->expects($this->never()) ->method('AnalyzeInstallation'); $oInstallationFileService->SetProductionEnv($oProductionEnv); @@ -299,14 +302,20 @@ class InstallationFileServiceTest extends ItopTestCase { */ public function testGetAllSelectedModules($bInstallationOptionalChoicesChecked=false) { $sPath = $this->GetInstallationPath(); - $oInstallationFileService = new \InstallationFileService($sPath, 'production', [], $bInstallationOptionalChoicesChecked); + $oInstallationFileService = new InstallationFileService($sPath, 'production', [], $bInstallationOptionalChoicesChecked); - $oProductionEnv = $this->createMock(\RunTimeEnvironment::class); + $oProductionEnv = $this->createMock(RunTimeEnvironment::class); $oProductionEnv->expects($this->once()) ->method('AnalyzeInstallation') ->willReturn($this->GetMockListOfFoundModules()); $oInstallationFileService->SetProductionEnv($oProductionEnv); + $oItopExtensionsMap = $this->createMock(ItopExtensionsMap::class); + $oItopExtensionsMap->expects($this->once()) + ->method('GetAllExtensions') + ->willReturn([]); + $oInstallationFileService->SetItopExtensionsMap($oItopExtensionsMap); + $oInstallationFileService->Init(); $aSelectedModules = $oInstallationFileService->GetSelectedModules(); @@ -362,10 +371,10 @@ class InstallationFileServiceTest extends ItopTestCase { $this->ValidateNonItilExtensionComputation($oInstallationFileService, $bInstallationOptionalChoicesChecked); } - private function ValidateNonItilExtensionComputation($oInstallationFileService, bool $bInstallationOptionalChoicesChecked) { + private function ValidateNonItilExtensionComputation($oInstallationFileService, bool $bInstallationOptionalChoicesChecked, array $aAdditionalExtensions=[]) { $aGetAfterComputationSelectedExtensions = $oInstallationFileService->GetAfterComputationSelectedExtensions(); sort($aGetAfterComputationSelectedExtensions); - $aExpectedExtensions = [ + $aExpectedExtensions = array_merge($aAdditionalExtensions, [ 'itop-change-mgmt-simple', 'itop-config-mgmt-core', 'itop-config-mgmt-datacenter', @@ -375,7 +384,7 @@ class InstallationFileServiceTest extends ItopTestCase { 'itop-service-mgmt-enterprise', 'itop-ticket-mgmt-simple-ticket', 'itop-ticket-mgmt-simple-ticket-enhanced-portal', - ]; + ]); if ($bInstallationOptionalChoicesChecked){ $aExpectedExtensions []= "itop-problem-mgmt"; $aExpectedExtensions []= 'itop-kown-error-mgmt'; @@ -465,14 +474,20 @@ class InstallationFileServiceTest extends ItopTestCase { */ public function testGetAllSelectedModules_withItilExtensions(array $aSelectedExtensions, bool $bKnownMgtSelected, bool $bCoreMgtSelected) { $sPath = $this->GetInstallationPath(); - $oInstallationFileService = new \InstallationFileService($sPath, 'production', $aSelectedExtensions); + $oInstallationFileService = new InstallationFileService($sPath, 'production', $aSelectedExtensions); - $oProductionEnv = $this->createMock(\RunTimeEnvironment::class); + $oProductionEnv = $this->createMock(RunTimeEnvironment::class); $oProductionEnv->expects($this->once()) ->method('AnalyzeInstallation') ->willReturn($this->GetMockListOfFoundModules()); $oInstallationFileService->SetProductionEnv($oProductionEnv); + $oItopExtensionsMap = $this->createMock(ItopExtensionsMap::class); + $oItopExtensionsMap->expects($this->once()) + ->method('GetAllExtensions') + ->willReturn([]); + $oInstallationFileService->SetItopExtensionsMap($oItopExtensionsMap); + $oInstallationFileService->Init(); $aSelectedModules = $oInstallationFileService->GetSelectedModules(); @@ -564,4 +579,228 @@ class InstallationFileServiceTest extends ItopTestCase { @rmdir($sFromDir); } + + private function CreateItopExtension(string $sSource, string $sCode, array $aModules, array $aMissingDependencies, bool $bIsVisible) : iTopExtension{ + $oExtension = new iTopExtension(); + $oExtension->sCode = $sCode; + $oExtension->sSource = $sSource; + $oExtension->aModules = $aModules; + $oExtension->aMissingDependencies = $aMissingDependencies; + $oExtension->bVisible = $bIsVisible; + return $oExtension; + } + + public function CanChooseUnpackageExtensionProvider() { + return [ + 'extension in SOURCE_REMOTE' => [ + 'sCode' => "extension-from-designer", + 'bInstallationOptionalChoicesChecked' => false, + 'sSource' => 'data', + 'bExpectedRes' => true + ], + 'extension in SOURCE_WIZARD' => [ + 'sCode' => 'extension-from-package', + 'bInstallationOptionalChoicesChecked' => true, + 'sSource' => 'datamodels', + 'bExpectedRes' => false + ], + 'extension in SOURCE_MANUAL + optional OK' => [ + 'sCode' => 'extension-from-package', + 'bInstallationOptionalChoicesChecked' => true, + 'sSource' => 'extensions', + 'bExpectedRes' => true + ], + 'extension in SOURCE_MANUAL + optional NOT OK' => [ + 'sCode' => 'extension-from-package', + 'bInstallationOptionalChoicesChecked' => false, + 'sSource' => 'extensions', + 'bExpectedRes' => false + ], + ]; + } + + /** + * @dataProvider CanChooseUnpackageExtensionProvider + */ + public function testCanChooseUnpackageExtension(string $sCode, bool $bInstallationOptionalChoicesChecked, string $sSource, bool $bExpectedRes) { + $sPath = $this->GetInstallationPath(); + $oInstallationFileService = new InstallationFileService($sPath, 'production', [], $bInstallationOptionalChoicesChecked); + + $oItopExtension = $this->CreateItopExtension($sSource, $sCode, [], [], true); + $this->assertEquals($bExpectedRes, $oInstallationFileService->CanChooseUnpackageExtension($oItopExtension)); + } + + public function ProcessExtensionModulesNotSpecifiedInChoicesProvider() { + return [ + 'extensions to install OK' => [ + 'aExtensionData' => [ + 'extension1' => [ + //'itop-request-mgmt-itil', //unselected + 'combodo-monitoring', + 'itop-config-mgmt', //already selected + ], + 'extension2' => [ + //'itop-incident-mgmt-itil', //unselected + 'combodo-monitoring2', + 'itop-attachments', //already selected + ] + ], + 'bExtensionCanBeChoosen' => true, + 'aMissingDependencies' => [], + 'bIsVisible' => true, + 'bExpectedAdditionalExtensions' => [ + 'extension1', 'extension2' + ], + 'bExpectedAdditionalModules' => [ + 'combodo-monitoring', 'combodo-monitoring2' + ] + ], + 'extensions to install cannot be choose,' => [ + 'aExtensionData' => [ + 'extension1' => [ + 'combodo-monitoring', + ], + 'extension2' => [ + 'combodo-monitoring2', + ] + ], + 'bExtensionCanBeChoosen' => false, + 'aMissingDependencies' => [], + 'bIsVisible' => true, + 'bExpectedAdditionalExtensions' => [], + 'bExpectedAdditionalModules' => [] + ], + 'extensions to install not visible' => [ + 'aExtensionData' => [ + 'extension1' => [ + 'combodo-monitoring', + ], + 'extension2' => [ + 'combodo-monitoring2', + ] + ], + 'bExtensionCanBeChoosen' => true, + 'aMissingDependencies' => [], + 'bIsVisible' => false, + 'bExpectedAdditionalExtensions' => [], + 'bExpectedAdditionalModules' => [] + ], + 'extensions to install with missing dependencies' => [ + 'aExtensionData' => [ + 'extension1' => [ + 'combodo-monitoring', + ], + 'extension2' => [ + 'combodo-monitoring2', + ] + ], + 'bExtensionCanBeChoosen' => true, + 'aMissingDependencies' => ['missing-module'], + 'bIsVisible' => true, + 'bExpectedAdditionalExtensions' => [], + 'bExpectedAdditionalModules' => [] + ], + 'extensions to install with unselectable ITIL module' => [ + 'aExtensionData' => [ + 'extension1' => [ + 'itop-request-mgmt-itil', //unselected + 'combodo-monitoring', + ], + 'extension2' => [ + 'itop-incident-mgmt-itil', //unselected + 'combodo-monitoring2', + ] + ], + 'bExtensionCanBeChoosen' => true, + 'aMissingDependencies' => [], + 'bIsVisible' => true, + 'bExpectedAdditionalExtensions' => [], + 'bExpectedAdditionalModules' => [] + ], + 'extensions already processed' => [ + 'aExtensionData' => [ + 'itop-config-mgmt-core' => [ + 'itop-config-mgmt', //already selected + ], + ], + 'bExtensionCanBeChoosen' => true, + 'aMissingDependencies' => [], + 'bIsVisible' => true, + 'bExpectedAdditionalExtensions' => [ + ], + 'bExpectedAdditionalModules' => [ + ] + ], + ]; + } + + /** + * @dataProvider ProcessExtensionModulesNotSpecifiedInChoicesProvider + */ + public function testProcessExtensionModulesNotSpecifiedInChoices(array $aExtensionData, bool $bExtensionCanBeChoosen, + array $aMissingDependencies, bool $bIsVisible, array $bExpectedAdditionalExtensions, array $bExpectedAdditionalModules) { + $sPath = $this->GetInstallationPath(); + $oInstallationFileService = new InstallationFileService($sPath, 'production', [], true); + + $oProductionEnv = $this->createMock(RunTimeEnvironment::class); + $oProductionEnv->expects($this->once()) + ->method('AnalyzeInstallation') + ->willReturn($this->GetMockListOfFoundModules()); + $oInstallationFileService->SetProductionEnv($oProductionEnv); + + $oItopExtensionsMap = $this->createMock(ItopExtensionsMap::class); + $aItopExtensionMap = []; + + $sSource = $bExtensionCanBeChoosen ? iTopExtension::SOURCE_REMOTE : iTopExtension::SOURCE_WIZARD; + foreach ($aExtensionData as $sExtensionCode => $aModules){ + $aItopExtensionMap[]= $this->CreateItopExtension($sSource, $sExtensionCode, $aModules, $aMissingDependencies, $bIsVisible); + } + $oItopExtensionsMap->expects($this->once()) + ->method('GetAllExtensions') + ->willReturn($aItopExtensionMap); + $oInstallationFileService->SetItopExtensionsMap($oItopExtensionsMap); + + $oInstallationFileService->Init(); + + $aSelectedModules = array_keys($oInstallationFileService->GetSelectedModules()); + sort($aSelectedModules); + $aExpectedInstallationModules = array_merge($bExpectedAdditionalModules, [ + "itop-config-mgmt", + "itop-attachments", + "itop-profiles-itil", + "itop-welcome-itil", + "itop-tickets", + "itop-files-information", + "combodo-db-tools", + "itop-core-update", + "itop-hub-connector", + "itop-oauth-client", + "itop-datacenter-mgmt", + "itop-endusers-devices", + "itop-storage-mgmt", + "itop-virtualization-mgmt", + "itop-service-mgmt", + "itop-request-mgmt", + "itop-portal", + "itop-portal-base", + "itop-change-mgmt", + "itop-problem-mgmt", + "itop-knownerror-mgmt", + 'authent-cas', + 'authent-external', + 'authent-ldap', + 'authent-local', + 'itop-backup', + 'itop-config', + 'itop-sla-computation', + 'itop-bridge-virtualization-storage', + ]); + sort($aExpectedInstallationModules); + + $this->assertEquals($aExpectedInstallationModules, $aSelectedModules); + + $this->ValidateNonItilExtensionComputation($oInstallationFileService, true, $bExpectedAdditionalExtensions); + } + + }