Chez eXo platform nous utilisons Hudson pour notre intégration continue.
La combinatoire entre les différents types de builds (intégration continue, reporting, packaging), les différents projets et leurs différentes branches de maintenance représente des dizaines de jobs dans Hudson.
Jusqu’à présent nous utilisions les notifications standards de Hudson pour recevoir les emails avec les statuts courant des jobs.
Il faut cependant avouer que celles-ci offrent très peu souplesse en ce qui concerne le contenu et les conditions de notification.
J’ai donc installé le plugin Hudson Email Extension Plugin qui est beaucoup plus paramétrable.
Après quelques tests concluant sur un nombre limité de projets, j’en viens donc au déploiement global sur l’ensemble des jobs hudson.
Et là c’est la catastrophe !!!! Hudson ne propose pas d’édition en masse de ses jobs (à part le plugin Configuration Slicing plugin mais qui ne gère qu’un nombre très restreint d’éléments de configuration).
Alors comment faire ?
Après un rapide tour sur la liste de diffusion du projet on me conseil “tout simplement” d’utiliser la capacité de modification à chaud du serveur à l’aide de sa console Groovy.
Celle-ci est effectivement bien plus puissante qu’un simple “Bulk change” puisque l’on peut accéder à l’ensemble des objets en mémoire sur le serveur (et donc à la configuration des jobs).
Le gros avantage de cette solution “codée” c’est aussi qu’elle me permet de reprendre les listes de destinataires utilisées par le mécanisme de notification standard afin de les injecter dans la nouvelle configuration.
Ce script execute donc pour chaque job les étapes suivantes :
- Si le job est de type Maven alors j’enregistre la liste de destinataires depuis le “reporter” et je le retire du job.
- Dans tous les cas je regarde les “publishers” actuels et si il y a les notifications standards ou du plugin mail-ext j’enregistre la liste de destinataires (donc la liste de mail-ext écrase celle de la notification standard si elle existait qui elle même écrase celle de maven si elle existait) et je retire le ou les “publishers”.
- Je crée et rajoute au job mon nouveau “publisher” mail-ext configuré selon mes besoins.
J’ai ainsi pu mettre à jour tous les jobs de mon serveur sans erreur et sans tâche manuelle fastidieuse.
Ci dessous le script groovy (n’étant ni expert en groovy et encore moins dans les APIs d’Hudson que je découvre, ce code doit être facilement améliorable).
[sourcecode lang=”groovy”]
import hudson.plugins.emailext.*
import hudson.plugins.emailext.plugins.trigger.*
import hudson.plugins.jira.*
import hudson.model.*
import hudson.maven.*
import hudson.maven.reporters.*
import hudson.tasks.*
// For each project
for(item in Hudson.instance.items) {
def recipients
println(“JOB : “+item.name);
// Find current recipients defined in project
if(item instanceof MavenModuleSet) {
println(“>MAVEN MODULE SET”);
// Search for Maven Mailer Reporter
println(“>>Reporters”);
for(reporter in item.reporters) {
println(“>>> “+reporter);
if(reporter instanceof MavenMailer) {
recipients = reporter.recipients
// remove it
item.reporters.remove(reporter)
}
}
} else
if(item instanceof FreeStyleProject) {
println(“>FREESTYLE PROJECT”);
}
println(“>>Publishers”);
for(publisher in item.publishersList) {
println(“>>> “+publisher);
// Search for default Mailer Publisher
if(publisher instanceof Mailer) {
recipients = publisher.recipients
// remove it
item.publishersList.remove(publisher)
} else
// Or for Extended Email Publisher
if(publisher instanceof ExtendedEmailPublisher) {
recipients = publisher.recipientList
item.publishers.remove(publisher)
// remove it
item.publishersList.remove(publisher)
}
}
// If we found recipients list to send mail
if(recipients!=null){
println (“>CURRENT RECIPIENT : “+recipients)
// We create a new Extended Email Publisher
def eep = new ExtendedEmailPublisher();
eep.recipientList = recipients
eep.defaultSubject = “\$DEFAULT_SUBJECT”
eep.defaultContent = “\$DEFAULT_CONTENT”
// With some triggers
eep.configuredTriggers.add(new FailureTrigger(
email : new EmailType(sendToRecipientList : true,
body : ExtendedEmailPublisher.PROJECT_DEFAULT_BODY_TEXT,
subject : ExtendedEmailPublisher.PROJECT_DEFAULT_SUBJECT_TEXT )))
eep.configuredTriggers.add(new FixedTrigger(
email : new EmailType(sendToRecipientList : true,
body : ExtendedEmailPublisher.PROJECT_DEFAULT_BODY_TEXT,
subject : ExtendedEmailPublisher.PROJECT_DEFAULT_SUBJECT_TEXT )))
eep.configuredTriggers.add(new StillFailingTrigger(
email : new EmailType(sendToRecipientList : true,
body : ExtendedEmailPublisher.PROJECT_DEFAULT_BODY_TEXT,
subject : ExtendedEmailPublisher.PROJECT_DEFAULT_SUBJECT_TEXT )))
eep.configuredTriggers.add(new StillUnstableTrigger(
email : new EmailType(sendToRecipientList : true,
body : ExtendedEmailPublisher.PROJECT_DEFAULT_BODY_TEXT,
subject : ExtendedEmailPublisher.PROJECT_DEFAULT_SUBJECT_TEXT )))
eep.configuredTriggers.add(new UnstableTrigger(
email : new EmailType(sendToRecipientList : true,
body : ExtendedEmailPublisher.PROJECT_DEFAULT_BODY_TEXT,
subject : ExtendedEmailPublisher.PROJECT_DEFAULT_SUBJECT_TEXT )))
// And we add/replace it in the project
item.publishersList.replace(eep);
}else{
println (“>NO RECIPIENT”)
}
println(“\n=======\n”);
}
[/sourcecode]
Bonjour Arnaud,
Merci pour ce script, je ne connaissais pas les possibilités d’administration “console” de Hudson.
En passant j’ai une petite question sur les notifications. (nous passons de continuum à Hudson).
Hudson nous envoie deux notifications par build, je pense qu’il y’en a une pour le build et une pour le module (unique) de notre projet.
Voici un exemple de log :
[INFO] ————————————————————————
[INFO] BUILD SUCCESSFUL
[INFO] ————————————————————————
[INFO] Total time: 1 minute 47 seconds
[INFO] Finished at: Fri Jan 08 09:02:49 CET 2010
[INFO] Final Memory: 32M/159M
[INFO] ————————————————————————
En attente qu’Hudson finisse de récupérer les données
Sending e-mails to: xxxxxxxxxxxxxx
channel stopped
Skipping sonar analysis due to bad build status UNSTABLE
Sending e-mails to: xxxxxxxxxxxxxx
Finished: SUCCESS
As tu une idée ?
a+
Effectivement lorsque tu utilises un job de type “Maven” tu vas recevoir une notification pour le job et pour chaque module qui change de statut. C’est le truc super énervant car si tu as 10 modules, que ton build plante au deuxième (les 8 autres ne sont donc pas exécutés) lorsque tu corriges ton build tu vas recevoir 9 notifications pour te dire que les modules et le projet sont repassés dans l’état normal. C’est justement un cas qui peut être géré plus facilement avec le plugin mail-ext.
Ok, je vais donc regarder ce plugin plus attentivement. Merci 🙂
Merci pour l’exemple de script. Par contre, l’une des choses que j’aime dans Hudson est son administration web assez conviviale. La possibilité d’y configurer un lot de jobs d’un seul coup faciliterait la vie de l’admin (la mienne au moins en tout cas 😉 )
Complètement d’accord. J’ai lancé hier le sujet sur la mailing liste de dev 🙂
Merci pour l’exemple, c’est bien cool. J’avais un besoin : modifier en masse un role LDAP par un autre dans une centaine de jobs.
Autant dire la galère …
voici mon script :
import hudson.model.*
import hudson.maven.*
import hudson.maven.reporters.*
import hudson.tasks.*
import hudson.security.*
def userSearch=”ROLE_GROUPE1″
def userToReplace=”ROLE_GROUPE2″
// For each project
for(item in Hudson.instance.items) {
def auth = (AuthorizationMatrixProperty)item.getProperty(AuthorizationMatrixProperty.class);
if (auth.getAllSIDs().find { it.equals(userSearch)} == null) continue
println(“JOB : “+item.name);
def permissions = new HashMap<Permission, Set>(auth.getGrantedPermissions());
for (def key : permissions.keySet()) {
def users = permissions.get(key)
def userToRemove = users.find {it.equals(userSearch)}
if ( userToRemove != null ){
users.remove(userToRemove)
users.add(userToReplace)
}
}
item.save()
}
Bon exemple aussi !!
Après en avoir discuté avec l’équipe Hudson on a décidé de mettre ces exemples dans leur wiki.
C’est ici que ça se passe : http://wiki.hudson-ci.org/display/HUDSON/CLI+Samples
Si tu veux y mettre le tien ça doit pouvoir intéresser du monde !!!