Overview

Namespaces

  • GAubry
    • ErrorHandler
    • Helpers
    • Logger
    • Shell
  • Himedia
    • Padocc
      • DB
      • Minifier
      • Numbering
      • Properties
      • Task
        • Base
        • Extended
  • None
  • Psr
    • Log

Classes

  • B2CSwitchSymlink
  • BuildLanguage
  • CVSExport
  • GitExport
  • Minifier
  • SwitchSymlink
  • TwengaServers
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  • Todo
  1: <?php
  2: 
  3: namespace Himedia\Padocc\Task\Extended;
  4: 
  5: use Himedia\Padocc\AttributeProperties;
  6: use Himedia\Padocc\Task\Base\Environment;
  7: use Himedia\Padocc\Task\Base\HTTP;
  8: use Himedia\Padocc\Task\Base\Link;
  9: 
 10: /**
 11:  * Si tous les attributs booléens sont à true, alors cette tâche qui se substitue à
 12:  * la tâche terminale SwitchSymlink effectue dans l'ordre :
 13:  * - notification sur le téléphone des admins d'une procédure de sortie de serveurs du cluster
 14:  * - sort du cluster les serveurs web concernés par le déploiement
 15:  * - permute les liens symboliques
 16:  * - redémarre Apache
 17:  * - réinitialise les caches Smarty
 18:  * - réintègre les serveurs web dans le cluster
 19:  * - switch les liens symboliques des serveurs statiques
 20:  * - ajoute une ligne dans la table SQL TWENGABUILD
 21:  * - et enfin envoie une seconde notification sur le téléphone des admins pour indiquer la fin du processus
 22:  *
 23:  * Tâche adhoc pour le projet front.
 24:  * À inclure en toute fin de tâche env ou target.
 25:  *
 26:  * Attributs :
 27:  * - 'src' : laisser à vide à moins d'être bien conscient des conséquences
 28:  * - 'target' : laisser à vide à moins d'être bien conscient des conséquences
 29:  * - 'server' : laisser à vide à moins d'être bien conscient des conséquences
 30:  * - 'sysopsnotifications' : envoyer ou non une notification sur le téléphone des admins
 31:  *   (appelle le script /home/prod/twenga/tools/send_nsca_fs3.sh)
 32:  * - 'addSQLTwBuild' : insérer une ligne dans la table SQL TWENGABUILD
 33:  *   (appelle le script Shell /home/prod/twenga/tools/add_twengabuild)
 34:  * - 'clusterRemoving' : retire du cluster avant restart Apache les serveurs web concernés par le déploiement
 35:  *   (appelle le script /home/prod/twenga/tools/wwwcluster)
 36:  * - 'clusterReintegration' : réintègre dans le cluster après restart Apache les serveurs web
 37:  *   (appelle le script /home/prod/twenga/tools/wwwcluster)
 38:  *
 39:  * Exemple :
 40:  * <b2cswitchsymlink
 41:  *     sysopsnotifications="false"
 42:  *     addSQLTwBuild="true"
 43:  *     clusterRemoving="false"
 44:  *     clusterReintegration="false"
 45:  * />
 46:  *
 47:  * @author Geoffroy AUBRY <gaubry@hi-media.com>
 48:  */
 49: class B2CSwitchSymlink extends SwitchSymlink
 50: {
 51:     /**
 52:      * Tâche de création d'appel cURL AAI sous-jacente.
 53:      * @var HTTP
 54:      */
 55:     private $oHTTPTask;
 56: 
 57:     /**
 58:      * {@inheritdoc}
 59:      */
 60:     protected function init()
 61:     {
 62:         parent::init();
 63: 
 64:         $this->aAttrProperties = array_merge(
 65:             $this->aAttrProperties,
 66:             array(
 67:                 'sysopsnotifications' => AttributeProperties::BOOLEAN,
 68:                 'addSQLTwBuild' => AttributeProperties::BOOLEAN,
 69:                 'clusterRemoving' => AttributeProperties::BOOLEAN,
 70:                 'clusterReintegration' => AttributeProperties::BOOLEAN
 71:             )
 72:         );
 73: 
 74:         $this->oNumbering->addCounterDivision();
 75:         // Parce qu'évidemment il n'y a pas de logique commune :
 76:         $aMappingAAI = array('qa' => 'qa', 'prod' => 'web');
 77:         $sEnv = $this->oProperties->getProperty('environment_name');
 78:         $sAppParameter = (isset($aMappingAAI[$sEnv]) ? $aMappingAAI[$sEnv] : $sEnv);
 79:         $sURL = 'http://aai.twenga.com/push.php?server=${WEB_SERVERS}&amp;app=' . $sAppParameter;
 80:         $aAttributes = array('url' => $sURL);
 81:         $this->oHTTPTask = HTTP::getNewInstance($aAttributes, $this->oProject, $this->oDIContainer);
 82:         $this->oNumbering->removeCounterDivision();
 83:     }
 84: 
 85:     /**
 86:      * {@inheritdoc}
 87:      * @codeCoverageIgnore
 88:      */
 89:     public static function getTagName ()
 90:     {
 91:         return 'b2cswitchsymlink';
 92:     }
 93: 
 94:     /**
 95:      * Vérifie au moyen de tests basiques que la tâche peut être exécutée.
 96:      * Lance une exception si tel n'est pas le cas.
 97:      *
 98:      * Comme toute les tâches sont vérifiées avant que la première ne soit exécutée,
 99:      * doit permettre de remonter au plus tôt tout dysfonctionnement.
100:      * Appelé avant la méthode execute().
101:      *
102:      * @throws \UnexpectedValueException en cas d'attribut ou fichier manquant
103:      * @throws \DomainException en cas de valeur non permise
104:      */
105:     public function check ()
106:     {
107:         parent::check();
108: 
109:         $aAttrToInit = array('sysopsnotifications', 'addSQLTwBuild', 'clusterRemoving', 'clusterReintegration');
110:         foreach ($aAttrToInit as $sAttrName) {
111:             if (! isset($this->aAttValues[$sAttrName])) {
112:                 $this->aAttValues[$sAttrName] = 'false';
113:             }
114:         }
115:     }
116: 
117:     /**
118:      * Phase de pré-traitements de l'exécution de la tâche.
119:      * Elle devrait systématiquement commencer par "parent::preExecute();".
120:      * Appelé par execute().
121:      * @see execute()
122:      */
123:     protected function preExecute ()
124:     {
125:         parent::preExecute();
126: 
127:         $this->getLogger()->info('+++');
128:         if ($this->aAttValues['sysopsnotifications'] == 'true') {
129:             $sEnv = $this->oProperties->getProperty('environment_name');
130:             $sID = $this->oProperties->getProperty('execution_id');
131:             $this->sendSysopsNotification('MEP-activation', 2, "Deploy to $sEnv servers (#$sID) is switching...");
132:         }
133:         $this->getLogger()->info('---');
134:     }
135: 
136:     /**
137:      * Phase de traitements centraux de l'exécution de la tâche.
138:      * Elle devrait systématiquement commencer par "parent::centralExecute();".
139:      * Appelé par execute().
140:      * @see execute()
141:      */
142:     protected function centralExecute ()
143:     {
144:         $this->getLogger()->info('+++');
145:         if ($this->oProperties->getProperty('with_symlinks') === 'true') {
146:             if ($this->oProperties->getProperty(Environment::SERVERS_CONCERNED_WITH_BASE_DIR) == '') {
147:                 $this->getLogger()->info('No release found.');
148:             } else {
149:                 $this->oProperties->setProperty('with_symlinks', 'false');
150:                 $this->checkTargets();
151: 
152:                 // Pour chaque serveur :
153:                 $aServers = $this->processPath('${WEB_SERVERS}');
154:                 foreach ($aServers as $sServer) {
155:                     $this->getLogger()->info("Switch '$sServer' server:+++");
156: 
157:                     if ($this->aAttValues['clusterRemoving'] == 'true') {
158:                         $this->setCluster($sServer, false);
159:                     }
160: 
161:                     // Switch du lien symbolique :
162:                     $aAttributes = array(
163:                         'src' => $this->aAttValues['src'],
164:                         'target' => $this->aAttValues['target'],
165:                         'server' => $sServer
166:                     );
167:                     $oLinkTask = Link::getNewInstance($aAttributes, $this->oProject, $this->oDIContainer);
168:                     $oLinkTask->setUp();
169:                     $oLinkTask->execute();
170: 
171:                     $this->restartServerApache($sServer);
172:                     $this->clearServerSmartyCaches($sServer);
173:                     if ($this->aAttValues['clusterReintegration'] == 'true') {
174:                         $this->setCluster($sServer, true);
175:                     }
176:                     $this->getLogger()->info('---');
177:                 }
178: 
179:                 // Switch des symlinks
180:                 // des éventuels serveurs de Task_Base_Environment::SERVERS_CONCERNED_WITH_BASE_DIR
181:                 // non inclus dans ${WEB_SERVERS}, comme les schedulers par exemple...
182:                 $aAllServers = $this->expandPath($this->aAttValues['server']);
183:                 $aDiff = array_diff($aAllServers, $aServers);
184:                 if (count($aDiff) > 0) {
185:                     $this->getLogger()->info('Switch other servers: ' . implode(', ', $aDiff) . '.+++');
186:                     $this->oProperties->setProperty('remaining_servers_to_switch', implode(' ', $aDiff));
187:                     $aAttributes = array(
188:                         'src' => $this->aAttValues['src'],
189:                         'target' => $this->aAttValues['target'],
190:                         'server' => '${remaining_servers_to_switch}'
191:                     );
192:                     $oLinkTask = Link::getNewInstance($aAttributes, $this->oProject, $this->oDIContainer);
193:                     $oLinkTask->setUp();
194:                     $oLinkTask->execute();
195:                     $this->getLogger()->info('---');
196:                 }
197: 
198:                 $this->oProperties->setProperty('with_symlinks', 'true');
199:             }
200:         } else {
201:             $this->getLogger()->info("Mode 'withsymlinks' is off: nothing to do.");
202:         }
203:         $this->getLogger()->info('---');
204:     }
205: 
206:     /**
207:      * Phase de post-traitements de l'exécution de la tâche.
208:      * Elle devrait systématiquement finir par "parent::postExecute();".
209:      * Appelé par execute().
210:      * @see execute()
211:      */
212:     protected function postExecute ()
213:     {
214:         $sEnv = $this->oProperties->getProperty('environment_name');
215:         $sRollbackID = $this->oProperties->getProperty('rollback_id');
216:         $sID = $sRollbackID !== '' ? $sRollbackID : $this->oProperties->getProperty('execution_id');
217:         $this->getLogger()->info('+++');
218: 
219:         if ($this->aAttValues['addSQLTwBuild'] == 'true') {
220:             $this->addSQLTwBuild($sID, $sEnv);
221:         }
222:         if ($this->aAttValues['sysopsnotifications'] == 'true') {
223:             $this->sendSysopsNotification('MEP-activation', 0, "Deploy to $sEnv servers (#$sID) finished.");
224:         }
225:         $this->oHTTPTask->execute();
226: 
227:         $this->getLogger()->info('---');
228:         parent::postExecute();
229:     }
230: 
231:     /**
232:      * Envoie une notification sur le téléphone des admins.
233:      *
234:      * @param string $sService catégorie
235:      * @param int $iStatus 0 ok, 1 warning, 2 critical
236:      * @param string $sMessage
237:      */
238:     private function sendSysopsNotification ($sService, $iStatus, $sMessage)
239:     {
240:         $this->getLogger()->info("Send notification to Sysops: '$sMessage'+++");
241:         $sCmd = "/home/prod/twenga/tools/send_nsca_fs3.sh $sService $iStatus \"$sMessage\"";
242:         $this->oShell->execSSH($sCmd, 'fs3:foo');
243:         $this->getLogger()->info('---');
244:     }
245: 
246:     /**
247:      * Insère le Twenga build number dans la table SQL centralisée 'TWENGABUILD'.
248:      *
249:      * @param string $sID Twenga build number
250:      * @param string $sEnv Environnement
251:      * @throws \DomainException quand environnement non capturé
252:      */
253:     private function addSQLTwBuild ($sID, $sEnv)
254:     {
255:         $aTypes = array('qa' => 'Q', 'bct' => 'B', 'internal' => 'I', 'preprod' => 'X', 'prod' => 'P');
256:         if (! isset($aTypes[$sEnv])) {
257:             throw new \DomainException("Environment not handled: '$sEnv'!");
258:         }
259:         $this->getLogger()->info("Add Twenga build number $sID into 'TWENGABUILD' SQL table.+++");
260:         $sCmd = "/home/prod/twenga/tools/add_twengabuild $sID " . $aTypes[$sEnv];
261:         $this->oShell->execSSH($sCmd, 'fs3:foo');
262:         $this->getLogger()->info('---');
263:     }
264: 
265:     /**
266:      * Redémarre le serveur Apache du serveur spécifié.
267:      *
268:      * @param string $sServer au format [user@]servername_or_ip
269:      */
270:     private function restartServerApache ($sServer)
271:     {
272:         $this->getLogger()->info("Restart Apache webserver '$sServer'.+++");
273:         $sToExec = $this->processSimplePath($sServer . ':/root/apache_restart');
274:         $aResult = $this->oShell->execSSH('sudo %s', $sToExec);
275:         $this->getLogger()->info(implode("\n", $aResult) . '---');
276:     }
277: 
278:     /**
279:      * Réinitialise les caches Smarty du serveur spécifié.
280:      *
281:      * @param string $sServer au format [user@]servername_or_ip
282:      */
283:     private function clearServerSmartyCaches ($sServer)
284:     {
285:         $this->getLogger()->info("Clear Smarty caches of server '$sServer':+++");
286: 
287:         $sCmd = "/home/prod/twenga/tools/clear_cache $sServer smarty";
288:         if (strcasecmp(strrchr($sServer, '.'), '.us1') === 0) {
289:             $sCmd = 'export FORCE_TWENGA_DC=US && ' . $sCmd . ' && export FORCE_TWENGA_DC=\'\'';
290:         }
291:         $aResult = $this->oShell->execSSH($sCmd, 'fs3:foo');
292:         $this->getLogger()->info(strip_tags(implode("\n", $aResult)) . '---');
293:     }
294: 
295:     /**
296:      * Sors ou réintègre le serveur spécifié du cluster.
297:      *
298:      * @param string $sServer au format [user@]servername_or_ip
299:      * @param bool $bStatus true pour réintégrer, false pour sortir.
300:      * @throws \Exception
301:      * @throws \RuntimeException
302:      */
303:     private function setCluster ($sServer, $bStatus)
304:     {
305:         $aMsgs = ($bStatus ? array('Reintegrate', 'into', '-e') : array('Remove', 'from', '-d'));
306: 
307:         if (preg_match('/wwwtest/i', $sServer) !== 1) {
308:             $this->getLogger()->info($aMsgs[0] . " '$sServer' server $aMsgs[1] the cluster.+++");
309:             $sCmd = "/home/prod/twenga/tools/wwwcluster -s $sServer $aMsgs[2]";
310:             try {
311:                 $aResult = $this->oShell->exec($sCmd);
312:                 $sResult = implode("\n", $aResult);
313:                 if ($sResult != '') {
314:                     $this->getLogger()->info($sResult);
315:                 }
316:             } catch (\RuntimeException $oException) {
317:                 if ($oException->getCode() == 2) {
318:                     $sResult = '[WARNING] ' . $oException->getMessage();
319:                     $this->getLogger()->warning($sResult);
320:                 } else {
321:                     throw $oException;
322:                 }
323:             }
324:             $this->getLogger()->info('---');
325:         } else {
326:             $this->getLogger()->info(" '$sServer' server is not handled by the cluster.");
327:         }
328:     }
329: 
330:     /**
331:      * Prépare la tâche avant exécution : vérifications basiques, analyse des serveurs concernés...
332:      */
333:     public function setUp ()
334:     {
335:         parent::setUp();
336:         $this->getLogger()->info('+++');
337:         $this->oHTTPTask->setUp();
338:         $this->getLogger()->info('---');
339:     }
340: }
341: 
Platform for Automatized Deployments with pOwerful Concise Configuration API documentation generated by ApiGen 2.8.0