فهرست منبع

N.890.1 and N.890.2: build a new run time environment into a separate "build" directory, then commit it by the mean of quick and bullet proof file copies/moves. Not yet used in the setup.

git-svn-id: http://svn.code.sf.net/p/itop/code/trunk@4881 a333f486-631f-4898-b8df-5754b55c2be0
romainq 7 سال پیش
والد
کامیت
5ee22e4e42
4فایلهای تغییر یافته به همراه174 افزوده شده و 19 حذف شده
  1. 3 3
      core/metamodel.class.php
  2. 1 1
      setup/applicationinstaller.class.inc.php
  3. 7 6
      setup/compiler.class.inc.php
  4. 163 9
      setup/runtimeenv.class.inc.php

+ 3 - 3
core/metamodel.class.php

@@ -3267,8 +3267,8 @@ abstract class MetaModel
 								{
 									if (!method_exists($sClass, $actionHandler))
 									{
-										$aErrors[$sClass][] = "Unknown function '$sActionHandler' in transition [$sStateCode/$sStimulusCode] for state attribute '$sStateAttCode'";
-										$aSugFix[$sClass][] = "Specify a function which prototype is in the form [public function $sActionHandler(\$sStimulusCode){return true;}]";
+										$aErrors[$sClass][] = "Unknown function '$actionHandler' in transition [$sStateCode/$sStimulusCode] for state attribute '$sStateAttCode'";
+										$aSugFix[$sClass][] = "Specify a function which prototype is in the form [public function $actionHandler(\$sStimulusCode){return true;}]";
 									}
 								}
 								else // if(is_array($actionHandler))
@@ -4597,7 +4597,7 @@ abstract class MetaModel
 
 	protected static $m_aExtensionClasses = array();
 
-	protected static function IncludeModule($sToInclude, $sModuleType = null)
+	public static function IncludeModule($sToInclude, $sModuleType = null)
 	{
 		$sFirstChar = substr($sToInclude, 0, 1);
 		$sSecondChar = substr($sToInclude, 1, 1);

+ 1 - 1
setup/applicationinstaller.class.inc.php

@@ -1025,7 +1025,7 @@ class ApplicationInstaller
 		// Record which modules are installed...
 		$oProductionEnv = new RunTimeEnvironment($sTargetEnvironment);
 		$oProductionEnv->InitDataModel($oConfig, true);  // load data model and connect to the database
-		if (!$oProductionEnv->RecordInstallation($oConfig, $sDataModelVersion, $aSelectedModuleCodes, $aSelectedExtensionCodes, $sModulesDir))
+		if (!$oProductionEnv->RecordInstallation($oConfig, $sDataModelVersion, $aSelectedModuleCodes, $aSelectedExtensionCodes))
 		{
 			throw new Exception("Failed to record the installation information");
 		}

+ 7 - 6
setup/compiler.class.inc.php

@@ -93,13 +93,14 @@ class MFCompiler
 	 * @param string $sTargetDir The target directory where to put the resulting files
 	 * @param Page $oP For some output...
 	 * @param bool $bUseSymbolicLinks
+	 * @param bool $bSkipTempDir
 	 * @throws Exception
 	 * @return void
 	 */
-	public function Compile($sTargetDir, $oP = null, $bUseSymbolicLinks = false)
+	public function Compile($sTargetDir, $oP = null, $bUseSymbolicLinks = false, $bSkipTempDir = false)
 	{
 		$sFinalTargetDir = $sTargetDir;
-		if ($bUseSymbolicLinks)
+		if ($bUseSymbolicLinks || $bSkipTempDir)
 		{
 			// Skip the creation of a temporary dictionary, not compatible with symbolic links
 			$sTempTargetDir = $sFinalTargetDir;
@@ -480,16 +481,16 @@ EOF;
 			{
 					$this->Log("Compilation of module $sModuleName in version $sModuleVersion produced not code at all. No file written.");
 			}
-			
+
 			// files to include (PHP datamodels)
 			foreach($oModule->GetFilesToInclude('business') as $sRelFileName)
 			{
-				$aDataModelFiles[] = "MetaModel::IncludeModule('".basename($sFinalTargetDir).'/'.$sRelativeDir.'/'.$sRelFileName."');";
+				$aDataModelFiles[] = "MetaModel::IncludeModule(MODULESROOT.'/$sRelativeDir/$sRelFileName');";
 			}
 			// files to include (PHP webservices providers)
 			foreach($oModule->GetFilesToInclude('webservices') as $sRelFileName)
 			{
-				$aWebservicesFiles[] = "MetaModel::IncludeModule('".basename($sFinalTargetDir).'/'.$sRelativeDir.'/'.$sRelFileName."');";
+				$aWebservicesFiles[] = "MetaModel::IncludeModule(MODULESROOT.'/$sRelativeDir/$sRelFileName');";
 			}
 		} // foreach module
 		
@@ -577,7 +578,7 @@ EOF;
 EOF
 		;
 		
-		$sPHPFileContent .= "\nMetaModel::IncludeModule('".basename($sFinalTargetDir)."/core/main.php');\n";
+		$sPHPFileContent .= "\nMetaModel::IncludeModule(MODULESROOT.'/core/main.php');\n";
 		$sPHPFileContent .= implode("\n", $aDataModelFiles);
 		$sPHPFileContent .= implode("\n", $aWebservicesFiles);
 		$sPHPFileContent .= "\nfunction GetModulesInfo()\n{\nreturn ".var_export($aModulesInfo, true).";\n}\n";

+ 163 - 9
setup/runtimeenv.class.inc.php

@@ -40,21 +40,56 @@ define ('DATAMODEL_MODULE', 'datamodel'); // Convention to store the version of
 
 class RunTimeEnvironment
 {
+	/**
+	 * The name of the environment that the caller wants to build
+	 * @var string sFinalEnv
+	 */
+	protected $sFinalEnv;
+
+	/**
+	 * Environment into which the build will be performed
+	 * @var string sTargetEnv
+	 */
 	protected $sTargetEnv;
-	
+
 	/**
 	 * Extensions map of the source environment
 	 * @var iTopExtensionsMap
 	 */
 	protected $oExtensionsMap;
-	
-	public function __construct($sEnvironment = 'production')
+
+	/**
+	 * Toolset for building a run-time environment
+	 *
+	 * @param string $sEnvironment (e.g. 'test')
+	 * @param bool $bAutoCommit (make the target environment directly, or build a temporary one)
+	 */
+	public function __construct($sEnvironment = 'production', $bAutoCommit = true)
 	{
-		$this->sTargetEnv = $sEnvironment;
+		$this->sFinalEnv = $sEnvironment;
+		if ($bAutoCommit)
+		{
+			// Build directly onto the requested environment
+			$this->sTargetEnv = $sEnvironment;
+		}
+		else
+		{
+			// Build into a temporary target
+			$this->sTargetEnv = $sEnvironment.'-build';
+		}
 		$this->oExtensionsMap = null;
 	}
 
 	/**
+	 * Return the full path to the compiled code (do not use after commit)
+	 * @return string
+	 */
+	public function GetBuildDir()
+	{
+		return APPROOT.'env-'.$this->sTargetEnv;
+	}
+
+	/**
 	 * Callback function for logging the queries run by the setup.
 	 * According to the documentation the function must be defined before passing it to call_user_func...
 	 * @param string $sQuery
@@ -429,7 +464,7 @@ class RunTimeEnvironment
 			}
 		}
 		while($bModuleAdded);
-		
+
 		$sDeltaFile = APPROOT.'data/'.$this->sTargetEnv.'.delta.xml';
 		if (file_exists($sDeltaFile))
 		{
@@ -495,11 +530,12 @@ class RunTimeEnvironment
 				// No delta was loaded, let's save the datamodel now
 				$oFactory->SaveToFile(APPROOT.'data/datamodel-'.$this->sTargetEnv.'.xml');
 			}
-			
+
 			$sTargetDir = APPROOT.'env-'.$this->sTargetEnv;
 			self::MakeDirSafe($sTargetDir);
+			$bSkipTempDir = ($this->sFinalEnv != $this->sTargetEnv); // No need for a temporary directory if sTargetEnv is already a temporary directory
 			$oMFCompiler = new MFCompiler($oFactory);
-			$oMFCompiler->Compile($sTargetDir, null, $bUseSymLinks);
+			$oMFCompiler->Compile($sTargetDir, null, $bUseSymLinks, $bSkipTempDir);
 
 			$sCacheDir = APPROOT.'data/cache-'.$this->sTargetEnv;
 			SetupUtils::builddir($sCacheDir);
@@ -653,7 +689,7 @@ class RunTimeEnvironment
 		$oConfig->Set('access_mode', $iPrevAccessMode);
 	}
 	
-	public function RecordInstallation(Config $oConfig, $sDataModelVersion, $aSelectedModuleCodes, $aSelectedExtensionCodes, $sModulesRelativePath, $sShortComment = null)
+	public function RecordInstallation(Config $oConfig, $sDataModelVersion, $aSelectedModuleCodes, $aSelectedExtensionCodes, $sShortComment = null)
 	{
 		// Have it work fine even if the DB has been set in read-only mode for the users
 		$iPrevAccessMode = $oConfig->Get('access_mode');
@@ -698,7 +734,7 @@ class RunTimeEnvironment
 		// Record installed modules and extensions
 		//
 		$aAvailableExtensions = array();
-		$aAvailableModules = $this->AnalyzeInstallation($oConfig, APPROOT.$sModulesRelativePath);
+		$aAvailableModules = $this->AnalyzeInstallation($oConfig, $this->GetBuildDir());
 		foreach($aSelectedModuleCodes as $sModuleId)
 		{
 			$aModuleData = $aAvailableModules[$sModuleId];
@@ -870,4 +906,122 @@ class RunTimeEnvironment
 		}
 		return $oLatestDM->Get('version');
 	}
+
+	public function Commit()
+	{
+		if ($this->sFinalEnv != $this->sTargetEnv)
+		{
+			$this->CommitFile(
+				APPROOT.'data/'.$this->sTargetEnv.'.delta.xml',
+				APPROOT.'data/'.$this->sFinalEnv.'.delta.xml',
+				false
+			);
+			$this->CommitFile(
+				APPROOT.'data/datamodel-'.$this->sTargetEnv.'.xml',
+				APPROOT.'data/datamodel-'.$this->sFinalEnv.'.xml'
+			);
+			$this->CommitFile(
+				APPROOT.'data/datamodel-'.$this->sTargetEnv.'-with-delta.xml',
+				APPROOT.'data/datamodel-'.$this->sFinalEnv.'-with-delta.xml',
+				false
+			);
+			$this->CommitDir(
+				APPROOT.'data/'.$this->sTargetEnv.'-modules/',
+				APPROOT.'data/'.$this->sFinalEnv.'-modules/',
+				false
+			);
+			$this->CommitDir(
+				APPROOT.'data/cache-'.$this->sTargetEnv,
+				APPROOT.'data/cache-'.$this->sFinalEnv,
+				false
+			);
+			$this->CommitDir(
+				APPROOT.'env-'.$this->sTargetEnv,
+				APPROOT.'env-'.$this->sFinalEnv
+			);
+
+			$sTargetConfig = APPCONF.$this->sTargetEnv.'/config-itop.php';
+			$sFinalConfig = APPCONF.$this->sFinalEnv.'/config-itop.php';
+			@chmod($sFinalConfig, 0770); // In case it exists: RWX for owner and group, nothing for others
+			$this->CommitFile($sTargetConfig, $sFinalConfig);
+			@chmod($sFinalConfig, 0440); // Read-only for owner and group, nothing for others
+		}
+	}
+
+	/**
+	 * Overwrite or create the destination file
+	 *
+	 * @param $sSource
+	 * @param $sDest
+	 * @param bool $bSourceMustExist
+	 * @throws Exception
+	 */
+	protected function CommitFile($sSource, $sDest, $bSourceMustExist = true)
+	{
+		if (file_exists($sSource))
+		{
+			SetupUtils::builddir(dirname($sDest));
+			if (file_exists($sDest))
+			{
+				$bRes = @unlink($sDest);
+				if (!$bRes)
+				{
+					throw new Exception('Commit - Failed to cleanup destination file: '.$sDest);
+				}
+			}
+			rename($sSource, $sDest);
+		}
+		else
+		{
+			// The file does not exist
+			if ($bSourceMustExist)
+			{
+				throw new Exception('Commit - Missing file: '.$sSource);
+			}
+			else
+			{
+				// Align the destination with the source... make sure there is NO file
+				if (file_exists($sDest))
+				{
+					$bRes = @unlink($sDest);
+					if (!$bRes)
+					{
+						throw new Exception('Commit - Failed to cleanup destination file: '.$sDest);
+					}
+				}
+			}
+		}
+	}
+
+	/**
+	 * Overwrite or create the destination directory
+	 *
+	 * @param $sSource
+	 * @param $sDest
+	 * @param bool $bSourceMustExist
+	 * @throws Exception
+	 */
+	protected function CommitDir($sSource, $sDest, $bSourceMustExist = true)
+	{
+		if (file_exists($sSource))
+		{
+			SetupUtils::movedir($sSource, $sDest);
+		}
+		else
+		{
+			// The file does not exist
+			if ($bSourceMustExist)
+			{
+				throw new Exception('Commit - Missing directory: '.$sSource);
+			}
+			else
+			{
+				// Align the destination with the source... make sure there is NO file
+				if (file_exists($sDest))
+				{
+					SetupUtils::rrmdir($sDest);
+				}
+			}
+		}
+	}
 } // End of class