diff --git a/config/lang/de.json b/config/lang/de.json index 2701d0da5376e11c67043aba759f5832904655e2..20ce33395f647a675691759439bd919936327784 100644 --- a/config/lang/de.json +++ b/config/lang/de.json @@ -242,6 +242,9 @@ "deploy": "Deploy", "deploy-configfile": "Konfiguration", "deploy-configfile-hint": "Hier können Variablen in Bash-Syntax hinterlegt werden. Bei einem Build werden diese in [root]/ci-custom-vars geschrieben und lassen sich vom onbuild oder ondeploy Hook lesen.", + "deploy-rollout-plugin": "Rollout-Plugin", + "deploy-rollout-plugin-hint": "Geben Sie vor, wie die Zielsystem angesteuert werden, um das Paket zu installieren.", + "deploy-rollout-plugin-config": "Konfiguration des Plugins", "deploy-hint": "Deploy der Queue von Phase [%s]", "deploy-impossible": "Deploy der Queue von Phase [%s] ist nicht möglich.", "deploy-settings": "Deployment-Einstellungen", diff --git a/config/lang/en.json b/config/lang/en.json index ce3575c1c14d687f50108d2aeaa856c8c5b5a693..9f575f385d20965c97c8dc3cd5abf9eaf3d3693b 100644 --- a/config/lang/en.json +++ b/config/lang/en.json @@ -244,6 +244,9 @@ "deploy": "Deploy", "deploy-configfile": "Configuration", "deploy-configfile-hint": "Here you can place variables in Bash syntax. During the build it will be writen as [root]/ci-custom-vars and is readable in the onbuild oder ondeploy hook.", + "deploy-rollout-plugin": "Rollout plugin", + "deploy-rollout-plugin-hint": "Here you can define how to trigger a remote system to deploy and install the built package.", + "deploy-rollout-plugin-config": "Configuration of the plugin", "deploy-hint": "Deploy queue of phase [%s]", "deploy-impossible": "Deploy queue of phase [%s] is not possible.", "deploy-settings": "Deployment settings", diff --git a/public_html/deployment/classes/formgen.class.php b/public_html/deployment/classes/formgen.class.php index a8f38502842d74c055d39dd4c18e885876886bb8..b6f9676733da30f211aed661346936db4f636289 100644 --- a/public_html/deployment/classes/formgen.class.php +++ b/public_html/deployment/classes/formgen.class.php @@ -192,7 +192,7 @@ class formgen { $s = preg_replace('/\W/iu', '', $sId . $idOption); $sOptionId = preg_replace('/[äöüß]/i', '', $s); $sFormElement.=' <input type="radio" id="' . $sOptionId . '" value="' . $idOption . '" '; - $sFormElement.=$this->_addHtmlAtrributes(explode(",", "$sDefaultAttributes,checked"), $aOptionData); + $sFormElement.=$this->_addHtmlAtrributes(explode(",", "$sDefaultAttributes,checked,disabled"), $aOptionData); $sFormElement.=" " . $this->_addHtmlAtrributes(explode(",", "name"), $elementData); $sFormElement.='/><label for="' . $sOptionId . '">' . $aOptionData["label"] . '</label></div>'; } diff --git a/public_html/deployment/classes/htmlguielements.class.php b/public_html/deployment/classes/htmlguielements.class.php index 7eb28ba75d56bac71977571f858d0ae39cb8b99e..7efad72e9dacde729c80dc8250176d80785aa913 100644 --- a/public_html/deployment/classes/htmlguielements.class.php +++ b/public_html/deployment/classes/htmlguielements.class.php @@ -152,6 +152,7 @@ class htmlguielements{ 'delete'=>'fas fa-trash', 'deploy'=>'fas fa-forward', 'deploy-configfile'=>'far fa-file-code', + 'deploy-rollout-plugin'=>'fas fa-plug', 'filter'=>'fas fa-filter', 'foreman'=>'fas fa-hard-hat', 'gotop'=>'fas fa-arrow-up', diff --git a/public_html/deployment/classes/project.class.php b/public_html/deployment/classes/project.class.php index dd56aceb4dae443f73a2400b2a40150453bc690e..ae1b32f1641b1c4b188a5cca44d03ad6dbda7b10 100644 --- a/public_html/deployment/classes/project.class.php +++ b/public_html/deployment/classes/project.class.php @@ -7,6 +7,7 @@ require_once __DIR__.'/../inc_functions.php'; require_once 'base.class.php'; require_once 'htmlguielements.class.php'; require_once 'messenger.class.php'; +require_once 'rollout_base.class.php'; /* ###################################################################### @@ -81,13 +82,25 @@ class project extends base { */ private $_iRcAll = 0; + + /** + * reference to html renderer class to draw output items + * @var object + */ protected $_oHtml = false; /** * object to access a version control, .e. git - * @var type + * @var object */ private $_oVcs = false; + + /** + * object for rollout + * @var type + */ + public $oRolloutPlugin = false; + private $_sBranchname = false; /** @@ -1096,6 +1109,12 @@ class project extends base { return $sNextPhase; } + /** + * get an array with deploy status ... + * 'inprogress'=>do versions differ from phase to phase = rollout of a version is in progress + 'hasQueue'=>is there a package in a queue (waiting for deployment time to get ready to be installed) + * @return array + */ public function getProgress(){ $this->getAllPhaseInfos(); return $this->_aData['progress']; @@ -1310,6 +1329,33 @@ class project extends base { return $this->_oVcs; } + /** + * get an array of enabled plugins + * @param string $sSection one of false|"rollout"|... + * @return array + */ + public function getConfiguredPlugins($sSection=false){ + $aReturn=array(); + if(!$sSection){ + $aReturn=$this->_aConfig["plugins"]; + } else { + foreach ($this->_aConfig["plugins"]["rollout"] as $sPluginName=>$aItem) { + $aReturn[$sPluginName] = $aItem; + } + } + return $aReturn; + } + + /** + * get a location of a plugin file with full path + * @param string $sType type of plugin, i.e. "rollout" + * @param string $sPluginName Name of plugin + * @return string + */ + protected function _getPluginFilename($sType, $sPluginName){ + return __DIR__.'/../plugins/'.$sType.'/'.$sPluginName.'/'.$sType.'_'.$sPluginName.'.php'; + } + /** * get a flat array of all existing ssh keys * @return array @@ -1435,6 +1481,28 @@ class project extends base { // echo "<pre>" . print_r($aData, true) . "</pre>"; $this->_verifyConfig(); + + // ----- init rollout plugin + // set name of the activated plugin for this project + $sPluginName=(isset($this->_aPrjConfig['deploy']['enabled_rollout_plugin']) && $this->_aPrjConfig['deploy']['enabled_rollout_plugin']) + ? $this->_aPrjConfig['deploy']['enabled_rollout_plugin'] + : 'default' + ; + $this->oRolloutPlugin = false; + try{ + require_once $this->_getPluginFilename('rollout', $sPluginName); + $sPluginClassname='rollout_'.$sPluginName; + $this->oRolloutPlugin = new $sPluginClassname(array( + 'lang'=>$this->_aConfig['lang'], + 'phase'=>false, + 'globalcfg'=>$this->_aConfig['plugins']['rollout'][$sPluginName], + 'projectcfg'=>$this->_aPrjConfig, + )); + // print_r($this->_oRolloutPlugin->getPluginfos()); + // print_r($this->_oRolloutPlugin->getName()); + } catch (Exception $ex) { + + } return true; } @@ -3219,7 +3287,7 @@ class project extends base { $sMessages = ''; require_once ("formgen.class.php"); - + $aSelectSlack = array( 'type' => 'hidden', 'name' => 'messenger[slack]', @@ -3254,6 +3322,73 @@ class project extends base { } + // ---------- Rollout plugins + $aRollout = array( + 'project-select' => array( + 'type' => 'radio', + 'name' => 'deploy[enabled_rollout_plugin]', + 'label' => t("deploy-rollout-plugin"), + ), + 'project-config' => '', + ); + foreach (array_keys($this->getConfiguredPlugins('rollout')) as $sPluginName){ + + $sPluginFile=$this->_getPluginFilename('rollout', $sPluginName); + $TmpRolloutPlugin = false; + $sMyClassname='rollout_'. $sPluginName; + $sMyDivId='rollout-'. $sPluginName.'-config'; + $sMyDivClass='rolloutconfigdiv'; + $sMyDivClassActive='rolloutconfigdiv-'. $sPluginName; + $bActive=$sPluginName === $this->oRolloutPlugin->getId(); + + if(file_exists($sPluginFile)){ + try{ + include_once $this->_getPluginFilename('rollout', $sPluginName); + $TmpRolloutPlugin = new $sMyClassname(array( + 'lang'=>$this->_aConfig['lang'], + 'phase'=>false, + 'globalcfg'=>$this->_aConfig['plugins']['rollout'][$sPluginName], + 'projectcfg'=>$this->_aPrjConfig, + )); + $aRollout['project-select']['options'][$sPluginName]=array( + 'label' => $TmpRolloutPlugin->getName(), + 'checked' => $bActive, + 'onclick' => '$(\'.'.$sMyDivClass.'\').hide(); $(\'.' . $sMyDivClassActive . '\').show();', + ); + + $aRollout['project-config'].='' + . '<div id="'.$sMyDivId.'" class="'.$sMyDivClass.' '.$sMyDivClassActive.'"' + . ($bActive ? '' : 'style="display: none;"' ) + . '>' + . $TmpRolloutPlugin->renderFormdata4Project() + . '</div>' + ; + + // generate form firlds for each phase + foreach(array_keys($this->getPhases()) as $sMyPhase){ + $aRollout[$sMyPhase].='' + . '<div id="'.$sMyDivId.'-'.$sMyPhase.'" class="'.$sMyDivClass.' '.$sMyDivClassActive.'"' + . ($bActive ? '' : 'style="display: none;"' ) + . '>' + . $TmpRolloutPlugin->renderFormdata4Phase($sMyPhase) + . '</div>' + ; + } + } catch (Exception $ex) { + + } + } else { + $aRollout['project-select']['options'][$sPluginName]=array( + 'label' => "not found: " . $sMyClassname, + 'checked' => false, + 'disabled' => "disabled", + ); + + + } + } + // ---------- /Rollout plugins + $aForemanHostgroups = false; $iForemanHostgroupDefault = false; $sForemanHostgroupDefault = false; @@ -3364,14 +3499,19 @@ class project extends base { <li><a href="#tab2" data-toggle="tab">' . $this->_oHtml->getIcon('repository').t('repositoryinfos') . '</a></li> <li><a href="#tab3" data-toggle="tab">' . $this->_oHtml->getIcon('deploy-configfile').t('deploy-configfile') . '</a></li> - <li><a href="#tab4" data-toggle="tab">' . $this->_oHtml->getIcon('phase').t('phases') . '</a></li> - <li><a href="#tab5" data-toggle="tab">' . $this->_oHtml->getIcon('raw-data').t('raw-data') . '</a></li> + <li><a href="#tab4" data-toggle="tab">' . $this->_oHtml->getIcon('deploy-rollout-plugin').t('deploy-rollout-plugin') . '</a></li> + <li><a href="#tab5" data-toggle="tab">' . $this->_oHtml->getIcon('phase').t('phases') . '</a></li> + <li><a href="#tab6" data-toggle="tab">' . $this->_oHtml->getIcon('raw-data').t('raw-data') . '</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="tab1"> ', ), + + // -------------------------------------------------- + // Tab for metadata + // -------------------------------------------------- 'input' . $i++ => array( 'type' => 'text', 'name' => 'label', @@ -3418,6 +3558,9 @@ class project extends base { ), 'input' . $i++ => $aSelectSlack, + + // -------------------------------------------------- + // Tab soources repository & build // -------------------------------------------------- 'input' . $i++ => array( 'type' => 'markup', @@ -3494,6 +3637,9 @@ class project extends base { ), ), ), + + // -------------------------------------------------- + // Tab for config and API key // -------------------------------------------------- 'input' . $i++ => array( 'type' => 'markup', @@ -3529,15 +3675,37 @@ class project extends base { . '</p></div>', ), + // -------------------------------------------------- + // Tab rollout plugin // -------------------------------------------------- 'input' . $i++ => array( 'type' => 'markup', 'value' => ' </div><div class="tab-pane" id="tab4"> + <p>' . t('deploy-rollout-plugin-hint') . '</p>', + ), + // select box for active rollout plugin + $aRollout['project-select'], + + // project based config + 'input' . $i++ => array( + 'type' => 'markup', + 'value' => '' + . '<hr>' + .'<label class="col-sm-2">'.t('deploy-rollout-plugin-config') .'</label>' + .'<div class="col-sm-10">'. $aRollout['project-config'].'</div>' + ), + // -------------------------------------------------- + 'input' . $i++ => array( + 'type' => 'markup', + 'value' => ' </div><div class="tab-pane" id="tab5"> <p>' . sprintf(t("class-project-info-setup-phaseinfos"), $this->getNextPhase()) . '</p>', ), ), ), ); + // -------------------------------------------------- + // Tab for phases + // -------------------------------------------------- if ($aSelectForemanGroups) { $aForms["setup"]["form"]['input' . $i++] = array( 'type' => 'markup', @@ -3649,7 +3817,7 @@ class project extends base { // 'required' => 'required', 'validate' => 'isastring', 'size' => 100, - 'placeholder' => 'http://' . $sPhase . '.[' . t("project") . '].[...]/', + 'placeholder' => 'https://' . $sPhase . '.[' . t("project") . '].[...]/', ); $aForms["setup"]["form"]['input' . $i++] = array( 'type' => 'radio', @@ -3667,7 +3835,8 @@ class project extends base { 'onclick' => '$(\'#' . $sDivId4TargetHosts . '\').css(\'display\', (this.checked ? \'none\' : \'block\') )', ), 'puppet' => array( - 'label' => t("deploymethod-puppet"), + // 'label' => t("deploymethod-puppet").' - '. $this->oRolloutPlugin->getName(), + 'label' => t("deploy-rollout-plugin"), 'checked' => $sDeploymethod === "puppet", 'onclick' => '$(\'#' . $sDivId4TargetHosts . '\').css(\'display\', (this.checked ? \'block\' : \'none\') )', ), @@ -3681,11 +3850,23 @@ class project extends base { */ ), ); + + $aForms["setup"]["form"]['input' . $i++] = array( 'type' => 'markup', 'value' => '' . '<div id="' . $sDivId4TargetHosts . '" ' . ($sDeploymethod !== "none" ? '' : ' style="display: none;"') . '">' ); + + // rollout plugin: phase specific overrides + $aForms["setup"]["form"]['input' . $i++] = array( + 'type' => 'markup', + 'value' => '' + // . '<hr>' + .'<label class="col-sm-2">'.t('deploy-rollout-plugin-config') .'</label>' + .'<div class="col-sm-10">'.$aRollout[$sPhase].'</div>' + ); + $aForms["setup"]["form"]['input' . $i++] = array( 'type' => 'text', 'name' => 'phases[' . $sPhase . '][hosts]', @@ -3758,11 +3939,15 @@ class project extends base { 'value' => '</td></tr></tbody></table>', ); } // END: loop over phases + + // -------------------------------------------------- + // Tab for raw data + // -------------------------------------------------- $aForms["setup"]["form"]['input' . $i++] = array( 'type' => 'markup', 'value' => '</div>' - . '<div class="tab-pane" id="tab5">' + . '<div class="tab-pane" id="tab6">' . '<br><pre>'.print_r($this->_aPrjConfig, 1).'</pre>' . '</div>' diff --git a/public_html/deployment/classes/rollout.interface.php b/public_html/deployment/classes/rollout.interface.php new file mode 100644 index 0000000000000000000000000000000000000000..ffdb3e482b74b64bd5be16d6c6862a3ed489db50 --- /dev/null +++ b/public_html/deployment/classes/rollout.interface.php @@ -0,0 +1,62 @@ +<?php +/** + * INTERFACE for rollout plugins + * + * @author hahn + */ +interface iRolloutplugin { + + // ---------------------------------------------------------------------- + // VERIFY + // ---------------------------------------------------------------------- + + /** + * check requirements if the plugin could work + */ + public function checkRequirements(); + + /** + * check access to a deploy target + */ + public function checkConnectionToTarget(); + + // ---------------------------------------------------------------------- + // SETTER + // ---------------------------------------------------------------------- + + // ---------------------------------------------------------------------- + // GETTER + // ---------------------------------------------------------------------- + + /** + * get configuration for the project .. or more specifi for a given phase + * @param string $sPhase + */ + public function getConfig($sPhase=false); + + /** + * get name of plugin as string ... language specific + */ + public function getName(); + + /** + * get description of plugin as string ... language specific + */ + public function getDescription(); + + /** + * get array of data in info.js + */ + public function getPluginInfos(); + + // ---------------------------------------------------------------------- + // RENDERER + // ---------------------------------------------------------------------- + + // ---------------------------------------------------------------------- + // ACTIONS + // ---------------------------------------------------------------------- + + +} + diff --git a/public_html/deployment/classes/rollout_base.class.php b/public_html/deployment/classes/rollout_base.class.php new file mode 100644 index 0000000000000000000000000000000000000000..c27d702b4a73547bbad24ca8dbe509394937d3d1 --- /dev/null +++ b/public_html/deployment/classes/rollout_base.class.php @@ -0,0 +1,417 @@ +<?php +require_once 'rollout.interface.php'; + +/** + * rollout_base class that will beextended in a rollout plugin + * + * + * @author axel + */ +class rollout_base implements iRolloutplugin{ + + // --------------------------------------------------------------- + // VARIABLES + // --------------------------------------------------------------- + /** + * identifier for current plugin; it us used to find the current plugin + * settings in the config structore for global and project based config + * @var string + */ + protected $_sPluginId='UNSET'; + /** + * data with plugin infos (read from info.json) + * @var array + */ + protected $_aPlugininfos=false; + + /** + * array with translation texts + * @var type + */ + protected $_aLang=false; + + /** + * set language; 2 letter code, i.e. "de"; default language is "en" ; a + * file "lang_en.json" is required in the plugin dir + * @var string + */ + protected $_sLang = 'en'; + + /** + * string with phase of project; one of preview|stage|live + * @var type + */ + protected $_sPhase = false; + + /** + * global configuration of the rollout plugin + * @var array + */ + protected $_aCfgGlobal = false; + /** + * configuration of the project + * @var array + */ + protected $_aCfgProject = false; + + protected $_sNamePrefix4Project=false; // set in constructor + protected $_sNamePrefix4Phase=false; // set in constructor + + // --------------------------------------------------------------- + // CONSTRUCTOR + // --------------------------------------------------------------- + + /** + * initialize rollout plugin + * @param array $aParams hash with those possible keys + * lang string language, i.e. 'de' + * phase string name of phase in a project + * globalcfg array given global config $aConfig + * projectcfg array project config to generate config + * for project and all phases + * @return boolean + */ + public function __construct($aParams) { + + // set current plugin id - taken from plugin directory name above + $oReflection=new ReflectionClass($this); + $this->_sPluginId=basename(dirname($oReflection->getFileName())); + + // ----- init language + if (isset($aParams['lang'])){ + $this->setLang($aParams['lang']); + } else { + $this->setLang(); + } + + // ----- init phase + if (isset($aParams['phase'])){ + $this->setPhase($aParams['phase']); + } + + // ----- init global config + if (isset($aParams['globalcfg'])){ + $this->setGlobalConfig($aParams['globalcfg']); + } + // ----- init project config + if (isset($aParams['projectcfg'])){ + $this->setProjectConfig($aParams['projectcfg']); + } + return true; + } + + // --------------------------------------------------------------- + // FORM HELPER + // --------------------------------------------------------------- + + /** + * get a string for a prefix for name attribute in form vars. + * It is important to store the value in the wanted structure. + * + * @param type $sPhase + * @return type + */ + protected function _getNamePrefix($sPhase=false){ + return ($sPhase + ? 'phases['.$sPhase.'][plugins][rollout]['.$this->getId().']' + : 'plugins[rollout]['.$this->getId().']' + ); + } + /** + * render a form by given form elementes + * @param array $aFormdata array of form elements + * @param string $sKey part of the identifier used in id of the input field + * @return string + */ + protected function _renderForm($aFormdata, $sKey){ + static $i; + if (!isset($i)){ + $i=0; + } + + $sReturn=''; + $sKeyPrefix=$this->getId().'_'.$sKey; + + $oForm = new formgen($aForms); + foreach ($aFormdata as $elementData) { + $elementKey=$sKeyPrefix.'_'.$i++; + $sReturn.=$oForm->renderHtmlElement($elementKey, $elementData); + } + return $sReturn; + } + + /** + * render form fields for global plugin variables + * @param string $sScope scope of vars ... one of global|project|phase + * @param string $sPhase optional: render global vars in a phase; if no phase was set it renders form fields for project based settings + * @return string + */ + protected function _renderForm4Vars($sScope, $sPhase=false){ + $sReturn=''; + + // test vars from info.json file + $aInfos=$this->getPluginInfos(); + if(!isset($aInfos['vars'][$sScope]) || !count($aInfos['vars'][$sScope])){ + return '---<br>'; + } + + $sKey=($sPhase ? 'phase_'.$sPhase : 'project'); + $sPrefixName=$this->_getNamePrefix($sPhase); + + // set defaults - to be used in placeholder attribute + // no phase = project level - take global defaults of ci config + // on a phase - fetch merged cofig data of project + $aDefaultValues=($sPhase ? $this->getConfig() : $this->_aCfgGlobal); + $aDefaultSource=($sPhase ? 'project' : 'global'); + + // set defaults - to be used in value attribute + $aValues=($sPhase + ? $this->_aCfgProject['phases'][$sPhase]['plugins']['rollout'][$this->getId()] + : $this->_aCfgProject['plugins']['rollout'][$this->getId()] + ); + + $aFormdata=array(); + + // create form fields + // $aFormdata[]=array('type' => 'markup','value' => '<br>'.$this->_t('section-override-'.$sScope.'-vars').':'); + $aFormdata[]=array('type' => 'markup','value' => '<div style="style: clear: left;"></div><h4>'.$this->getId() .' :: '. $sScope.'</h4>'); + + $sMiss=''; + foreach ($aInfos['vars'][$sScope] as $sVarname=>$aVarinfos){ + if ($sScope==='global' && !isset($this->_aCfgGlobal[$sVarname])){ + $sMiss.='- plugin var was not set in global CI config: plugins -> rollout -> '.$this->getId().' -> "'.$sVarname.'".<br>'; + } + if($aVarinfos['type']=="text"){ + $aFormdata[]=array( + 'type' => 'text', + 'name' => $sPrefixName.'['.$sVarname.']', + 'label' => $this->_t($sVarname.'-label'), + 'title' => $this->_t($sVarname.'-hint'), + 'value' => (isset($aValues[$sVarname]) ? $aValues[$sVarname] : ''), + // 'required' => 'required', + 'validate' => 'isastring', + // 'size' => 25, + // 'placeholder' => (isset($this->_aCfgGlobal[$sVarname]) ? $this->_aCfgGlobal[$sVarname] : '') . ' | '.$aDefaultValues[$sVarname], + 'placeholder' => (isset($aDefaultValues[$sVarname]) + ? $aDefaultSource.': '.$aDefaultValues[$sVarname] + : (isset($aVarinfos['default']) ? $aVarinfos['default'] : 'N.A.') + ), + ); + } else { + $sMiss.='- plugin var "'.$sVarname.'" does not have type text. It cannot be rendered so far.<br>'; + } + } + // $aFormdata[]=array('type' => 'markup','value' => '<div style="style: clear: left;"></div><br><br>'); + return $this->_renderForm($aFormdata, $sKey) + . ($sMiss ? '<pre>WARNINGS:<br>'.$sMiss.'</pre>' : '') + ; + } + + /** + * get a translated text from lang_XX.json in plugin dir; + * If the key is missed it returns "[KEY :: LANG]" + * + * @see setLang() + * @param string $sKey key to find in lang file + * @return string + */ + protected function _t($sKey){ + return (isset($this->_aLang[$sKey]) && $this->_aLang[$sKey]) + ? $this->_aLang[$sKey] + : "[ $sKey :: $this->_sLang ]" + ; + } + + // --------------------------------------------------------------- + // PUBLIC METHODS + // --------------------------------------------------------------- + + /** + * set language for output of formdata and other texts. + * This method loads the language file into a hash. The output of + * translated texts can be done with $this->_t("your_key") + * + * @see _t() + * @param string $sLang language code, i.e. "de" + * @return boolean + */ + public function setLang($sLang=false){ + $this->_sLang=$sLang ? $sLang : $this->_sLang; + + $oReflection=new ReflectionClass($this); + $sFile=dirname($oReflection->getFileName()) . '/lang_'.$this->_sLang.'.json'; + $this->_aLang=(file_exists($sFile)) ? json_decode(file_get_contents($sFile), 1) : $this->_aLang; + return true; + } + + /** + * set a phase for automatic use GETTER methods + */ + public function setPhase($sPhase){ + $this->_sPhase=$sPhase; + return true; + } + + + // ---------------------------------------------------------------------- + // INTERFACE :: CHECKS + // ---------------------------------------------------------------------- + + /** + * check requirements if the plugin could work + */ + public function checkRequirements(){ + // no specific checks needed ... always true + return true; + } + + /** + * check access to a deploy target + */ + public function checkConnectionToTarget(){ + // do nothing ... always true + return true; + } + + // ---------------------------------------------------------------------- + // INTERFACE :: SETTER + // ---------------------------------------------------------------------- + + + /** + * set Config ... by given global config of the current plugin + * @param array $aConfigArray + */ + public function setGlobalConfig($aConfigArray){ + return $this->_aCfgGlobal=$aConfigArray; + } + + + + /** + * set Config ... by given project config + */ + public function setProjectConfig($aProjectConfigArray){ + $this->_aCfgProject=$aProjectConfigArray; + // echo '<pre>'.print_r($aProjectConfigArray, 1).'</pre>'; + // ----- ensure that the config structure exists + // (it is easier fo handling in getConfig()) + if (!isset($this->_aCfgProject['plugins']['rollout'][$this->_sPluginId])){ + /* + if (!isset($this->_aCfgProject['plugins']['rollout'])){ + if (!isset($this->_aCfgProject['plugins'])){ + $this->_aCfgProject['plugins']=array(); + } + $this->_aCfgProject['plugins']['rollout']=array(); + } + * + */ + $this->_aCfgProject['plugins']['rollout'][$this->_sPluginId]=array('INFO'=>'created'); + } + + // unset empty project values to get global values + + foreach ($this->_aCfgProject['plugins']['rollout'][$this->_sPluginId] as $sVarname=>$value){ + if ($value===''){ + unset($this->_aCfgProject['plugins']['rollout'][$this->_sPluginId][$sVarname]); + } + } + foreach (array_keys($this->_aCfgProject['phases']) as $sMyPhase){ + if (isset($this->_aCfgProject['phases'][$sMyPhase]['plugins']['rollout'][$this->getId()])){ + foreach($this->_aCfgProject['phases'][$sMyPhase]['plugins']['rollout'][$this->getId()] as $sVarname=>$value){ + if ($value===''){ + unset($this->_aCfgProject['phases'][$sMyPhase]['plugins']['rollout'][$this->getId()][$sVarname]); + } + } + } + } + // TODO: + return $this->_aCfgProject; + } + + // ---------------------------------------------------------------------- + // INTERFACE :: GETTER + // ---------------------------------------------------------------------- + + /** + * get a hash with the merged config for project or mo specific: of a given + * phase + * @param string $sPhase + * @return array + */ + public function getConfig($sPhase=false){ + + return ($sPhase && isset($this->_aCfgProject['phases'][$sPhase]['plugins']['rollout'][$this->getId()])) + ? array_merge($this->_aCfgGlobal, $this->_aCfgProject['plugins']['rollout'][$this->getId()], $this->_aCfgProject['phases'][$sPhase]['plugins']['rollout'][$this->getId()]) + : array_merge($this->_aCfgGlobal, $this->_aCfgProject['plugins']['rollout'][$this->getId()]) + ; + } + + /** + * get string with current ID + * @return string + */ + public function getId(){ + return $this->_sPluginId; + } + + /** + * get string with plugin name (taken from plugin language file) + * @return string + */ + public function getName(){ + return $this->_t('plugin_name'); + } + + /** + * get string with plugin description (taken from plugin language file) + * @return string + */ + public function getDescription(){ + return $this->_t('description'); + } + /** + * get array read from info.json + * @return type + */ + public function getPluginInfos(){ + + if ($this->_aPlugininfos){ + return $this->_aPlugininfos; + } + + $oReflection=new ReflectionClass($this); + $sFile=dirname($oReflection->getFileName()) . '/info.json'; + $this->_aPlugininfos= (file_exists($sFile)) + ? json_decode(file_get_contents($sFile), 1) + : array('error'=> 'unable to read info file ['.$sFile.'].') + ; + return $this->_aPlugininfos; + } + + // ---------------------------------------------------------------------- + // INTERFACE :: RENDERER + // ---------------------------------------------------------------------- + public function renderFormdata4Project() { + return '' + . $this->_renderForm4Vars('global', false) + . $this->_renderForm4Vars('project', false) + // . $this->_renderFormProjectVars($this->_sNamePrefix4Project, false) + // . '<pre>DEBUG: GLOBAL settings - $this->_aCfgGlobal = ' . print_r($this->_aCfgGlobal, 1) . '</pre>' + // . '<pre>DEBUG: PROJECT settings - $this->getConfig() = ' . print_r($this->getConfig(), 1) . '</pre>' + // .'<pre>DEBUG: $this->_aCfgProject ... plugin = '.print_r($this->_aCfgProject, 1).'</pre>' + ; + } + public function renderFormdata4Phase($sMyPhase){ + return '' + . $this->_renderForm4Vars('global', $sMyPhase) + . $this->_renderForm4Vars('project', $sMyPhase) + . $this->_renderForm4Vars('phase', $sMyPhase) + // . $this->_renderForm($aFormdata, 'project') + // .$sReturn + // . '<pre>DEBUG: GLOBAL settings - $this->_aCfgGlobal = ' . print_r($this->_aCfgGlobal, 1) . '</pre>' + // . '<pre>DEBUG: PROJECT settings - $this->getConfig() = ' . print_r($this->getConfig(), 1) . '</pre>' + // . '<pre>DEBUG: PHASE settings - $this->getConfig("'.$sMyPhase.'") = ' . print_r($this->getConfig($sMyPhase), 1) . '</pre>' + ; + } +} diff --git a/public_html/deployment/pages/act_setup.php b/public_html/deployment/pages/act_setup.php index 701ee29ab20649dbc6207b100b296681d25ccf1b..8a762aa199d9d5b1334bf7992c96fcddb786543c 100644 --- a/public_html/deployment/pages/act_setup.php +++ b/public_html/deployment/pages/act_setup.php @@ -55,6 +55,35 @@ function maskEntries($aMask, $aConfig){ return $aConfig; } +/** + * recursive replace of values in a hash + * source: https://www.w3schools.in/php-script/recursive-array-replace-by-Key-or-Value/ + * FIX: 3x "=" in if($Key === $Find) + * + * @param array $Array Array + * @param string $Find key to scan for + * @param strin $Replace new value + * @return array + */ +function ArrayReplace($Array, $Find, $Replace) { + if (is_array($Array)) { + foreach ($Array as $Key => $Val) { + if (is_array($Array[$Key])) { + $Array[$Key] = ArrayReplace($Array[$Key], $Find, $Replace); + } else { + if ($Key === $Find) { + $Array[$Key] = $Replace; + } + } + } + } + return $Array; +} + +// --------------------------------------------------------------------- +// MAIN +// --------------------------------------------------------------------- + if ($aParams["prj"] == "all") { // ------------------------------------------------------------ @@ -62,7 +91,10 @@ if ($aParams["prj"] == "all") { // ------------------------------------------------------------ if (!array_key_exists("par3", $aParams)) { $oPrj = new project(); - $aTmp=maskEntries($aMask, $aConfig); + // $aTmp=maskEntries($aMask, $aConfig); + $aTmp=$aConfig; + $aTmp=ArrayReplace($aTmp, "password", $sFakePassword); + $aTmp=ArrayReplace($aTmp, "PwLdapUser", $sFakePassword); $sOut.= '<pre>'.print_r($aTmp, 1).'</pre>'; // print_r($aConfig); @@ -304,8 +336,14 @@ if ($aParams["prj"] == "all") { // setup page of a an existing project // ------------------------------------------------------------ $oPrj = new project($aParams["prj"]); - // $sOut.='<div style="float: right">aParams:<pre>'.print_r($aParams, true).'</pre></div>'; - + /* + $sOut.='<div style="float: right">' + // . 'aParams:<pre>'.print_r($aParams, true).'</pre>' + . 'configured rollout plugins:<pre>'.print_r($oPrj->getConfiguredPlugins('rollout'), true).'</pre>' + // . 'rollout plugin infos:<pre>'.print_r($oPrj->oRolloutPlugin->getPluginInfos(), true).'</pre>' + . 'prj config of rollout plugin:<pre>'.print_r($oPrj->oRolloutPlugin->getConfig(), true).'</pre>' + . '</div>'; + */ if (array_key_exists("setupaction", $aParams) && $aParams["setupaction"] == "save") { if ($oPrj->saveConfig()) { $sOut.=$oHtml->getBox("success", t("page-setup-info-settings-were-saved")); diff --git a/public_html/deployment/plugins/rollout/default/info.json b/public_html/deployment/plugins/rollout/default/info.json new file mode 100644 index 0000000000000000000000000000000000000000..b6f00239d4aeca148685ba6910b998995915184e --- /dev/null +++ b/public_html/deployment/plugins/rollout/default/info.json @@ -0,0 +1,16 @@ +{ + "name": "default", + "description": "Default rollout plugin - doing no action", + "author": "Axel Hahn; University odf Bern; Institute for Medical education", + + "version": "1.0", + "url": "[included]", + "license": "GNU GPL 3.0", + + "vars": { + "global": {}, + "project": {}, + "phase": {} + } +} + diff --git a/public_html/deployment/plugins/rollout/default/lang_de.json b/public_html/deployment/plugins/rollout/default/lang_de.json new file mode 100644 index 0000000000000000000000000000000000000000..23e15d2fc58a258794cc17eb38c4e66b10a8c382 --- /dev/null +++ b/public_html/deployment/plugins/rollout/default/lang_de.json @@ -0,0 +1,6 @@ +{ + "plugin_name": "Default: keine Aktion", + "description": "Es wird keine Aktion zum Rollout gestartet.", + + "no-cfg": "Es wird keine Einstellung benötigt." +} \ No newline at end of file diff --git a/public_html/deployment/plugins/rollout/default/lang_en.json b/public_html/deployment/plugins/rollout/default/lang_en.json new file mode 100644 index 0000000000000000000000000000000000000000..cfad0986ad295413adab4094cdff633f3a68a9ec --- /dev/null +++ b/public_html/deployment/plugins/rollout/default/lang_en.json @@ -0,0 +1,6 @@ +{ + "plugin_name": "Default: no action", + "description": "Doing nothing for rollout of a package.", + + "no-cfg": "No further settings needed," +} \ No newline at end of file diff --git a/public_html/deployment/plugins/rollout/default/rollout_default.php b/public_html/deployment/plugins/rollout/default/rollout_default.php new file mode 100644 index 0000000000000000000000000000000000000000..b0e1df57041a0e303b0cd6b448cb7fa59353ba49 --- /dev/null +++ b/public_html/deployment/plugins/rollout/default/rollout_default.php @@ -0,0 +1,35 @@ +<?php +/** + * + * Rollout plugin - default + * + * no action + * + * @author axel + */ +class rollout_default extends rollout_base { + + /** + * check requirements if the plugin could work + */ + public function checkRequirements(){ + // no specific checks needed ... always true + return true; + } + + /** + * check access to a deploy target + */ + public function checkConnectionToTarget(){ + // do nothing ... always true + return true; + } + + public function renderFormdata4Project() { + return $this->_t('no-cfg'); + } + + public function renderFormdata4Phase() { + return $this->_t('no-cfg'); + } +} diff --git a/public_html/deployment/plugins/rollout/ssh/info.json b/public_html/deployment/plugins/rollout/ssh/info.json new file mode 100644 index 0000000000000000000000000000000000000000..e148fb8c8dfaf33b63cd528bf76a55ae3a412c63 --- /dev/null +++ b/public_html/deployment/plugins/rollout/ssh/info.json @@ -0,0 +1,47 @@ +{ + "name": "SSH", + "description": "Run a SSH command on remote targets.", + "author": "Axel Hahn; University odf Bern; Institute for Medical education", + + "version": "1.0", + "url": "[included]", + "license": "GNU GPL 3.0", + + "vars": { + "global": { + "user": { + "type": "text", + "default": "example: remoteuser" + }, + "command": { + "type": "text", + "default": "example: /opt/somewhere/install.sh" + }, + "privatekey": { + "type": "text", + "default": "" + }, + "addkeycommand": { + "type": "text", + "default": "" + }, + "testcommand": { + "type": "text", + "default": "example: sudo echo OK" + } + }, + "project": { + "projectfile": { + "type": "text", + "default": "" + } + }, + "phase": { + "hosts": { + "type": "text", + "default": "example: myserver.example.com" + } + } + } +} + diff --git a/public_html/deployment/plugins/rollout/ssh/lang_de.json b/public_html/deployment/plugins/rollout/ssh/lang_de.json new file mode 100644 index 0000000000000000000000000000000000000000..8c73bfed5bb0077fc11ef21dbde4028e38f819aa --- /dev/null +++ b/public_html/deployment/plugins/rollout/ssh/lang_de.json @@ -0,0 +1,22 @@ +{ + "plugin_name": "SSH", + "description": "Ein SSH Kommando auf einem Zielsystem starten.", + + + "section-required": "Erforderliche Angaben", + "section-optional": "optionale Angaben", + "section-commands": "Kommandos", + + "user-label": "SSH User", + "user-hint": "Remote Account zu dem sich auf dem Zielsystem verbunden wird.", + "command-label": "Kommando", + "command-hint": "Auf dem Zielsystem zu startendes Kommando", + "privatekey-label": "Dateiname des Privatekeys", + "privatekey-hint": "Kompletter Pfad zur Datei des Privatekeys, wenn vom Default (z.B. ~/.ssh/id_rsa) abweichend.", + "addkeycommand-label": "known_hosts Kommando", + "addkeycommand-hint": "Kommando zum Einsetzen oder Erneuern eines SSH Hostkeys in der known_hosts.", + "testcommand-label": "Test-Remote-Kommando", + "testcommand-hint": "Auf dem Zielsystem zu startendes Testkommando, ob ein Rollout bereit wäre.", + + "endoffile": "" +} \ No newline at end of file diff --git a/public_html/deployment/plugins/rollout/ssh/lang_en.json b/public_html/deployment/plugins/rollout/ssh/lang_en.json new file mode 100644 index 0000000000000000000000000000000000000000..1a21a39a114fb69e6574f5638f9131d4281b0564 --- /dev/null +++ b/public_html/deployment/plugins/rollout/ssh/lang_en.json @@ -0,0 +1,4 @@ +{ + "plugin_name": "SSH", + "description": "Run a command on a remote system via SSH ." +} \ No newline at end of file diff --git a/public_html/deployment/plugins/rollout/ssh/rollout_ssh.php b/public_html/deployment/plugins/rollout/ssh/rollout_ssh.php new file mode 100644 index 0000000000000000000000000000000000000000..df5fa0c90b7af41fc58064450bc10281cdb7be85 --- /dev/null +++ b/public_html/deployment/plugins/rollout/ssh/rollout_ssh.php @@ -0,0 +1,124 @@ +<?php + +/** + * + * Rollout plugin - default + * + * no action + * + * @author axel + */ +class rollout_ssh extends rollout_base { + + /** + * check requirements if the plugin could work + */ + public function checkRequirements() { + // no specific checks needed ... always true + return true; + } + + /** + * check access to a deploy target + */ + public function checkConnectionToTarget() { + // do nothing ... always true + return true; + } + + /** + * form fields for project settings + * @return type + */ + public function __DISABLED__renderFormdata4Project() { + /* + $sReturn=''; + $i=0; + + $sNamePrefix='plugins[rollout]['.$this->getId().']'; + $aValues=$this->getConfig(); + + $aFormdata=array( + array('type' => 'markup','value' => '<br>'.$this->_t('section-required').':'), + array( + 'type' => 'text', + 'name' => $sNamePrefix.'[user]', + 'label' => $this->_t('user'), + 'value' => $aValues['user'], + // 'required' => 'required', + 'validate' => 'isastring', + 'size' => 25, + 'placeholder' => $this->_aCfgGlobal['user'], + ), + array( + 'type' => 'text', + 'name' => $sNamePrefix.'[command]', + 'label' => $this->_t('command'), + 'value' => $aValues['command'], + // 'required' => 'required', + 'validate' => 'isastring', + // 'size' => 100, + 'placeholder' => $this->_aCfgGlobal['command'], + ), + array('type' => 'markup','value' => '<br><br>'.$this->_t('section-optional').':'), + array( + 'type' => 'text', + 'name' => $sNamePrefix.'[privatekey]', + 'label' => $this->_t('privatekey'), + 'value' => $aValues['privatekey'], + // 'required' => 'required', + 'validate' => 'isastring', + // 'size' => 100, + 'placeholder' => $this->_aCfgGlobal['privatekey'], + ), + array('type' => 'markup','value' => '<br><br>'.$this->_t('section-commands').':'), + array( + 'type' => 'text', + 'name' => $sNamePrefix.'[addkeycommand]', + 'label' => $this->_t('addkeycommand'), + 'value' => $aValues['addkeycommand'], + // 'required' => 'required', + 'validate' => 'isastring', + // 'size' => 100, + 'placeholder' => $this->_aCfgGlobal['addkeycommand'], + ), + array( + 'type' => 'text', + 'name' => $sNamePrefix.'[testcommand]', + 'label' => $this->_t('testcommand'), + 'value' => $aValues['testcommand'], + // 'required' => 'required', + 'validate' => 'isastring', + // 'size' => 100, + 'placeholder' => $this->_aCfgGlobal['testcommand'], + ), + ); + * + */ + + return 'WIP: project based setup for plugin [' . $this->getId() . ']<br>' + . $this->_renderForm4Vars('global', false) + // . $this->_renderForm($aFormdata, 'project') + // .$sReturn + . '<pre>DEBUG: GLOBAL settings - $this->_aCfgGlobal = ' . print_r($this->_aCfgGlobal, 1) . '</pre>' + . '<pre>DEBUG: PROJECT settings - $this->getConfig() = ' . print_r($this->getConfig(), 1) . '</pre>' + // .'<pre>DEBUG: $this->_aCfgProject ... plugin = '.print_r($this->_aCfgProject, 1).'</pre>' + ; + } + + /** + * form fields for project settings + * @return type + */ + public function __DISABLED__renderFormdata4Phase($sMyPhase) { + return 'WIP: pase based setup for plugin [' . $this->getId() . '] in phase '.$sMyPhase.'<br>' + . $this->_renderForm4Vars('global', $sMyPhase) + // . $this->_renderForm($aFormdata, 'project') + // .$sReturn + . '<pre>DEBUG: GLOBAL settings - $this->_aCfgGlobal = ' . print_r($this->_aCfgGlobal, 1) . '</pre>' + . '<pre>DEBUG: PROJECT settings - $this->getConfig() = ' . print_r($this->getConfig(), 1) . '</pre>' + . '<pre>DEBUG: PHASE settings - $this->getConfig("'.$sMyPhase.'") = ' . print_r($this->getConfig($sMyPhase), 1) . '</pre>' + ; + } + +}