Authentifier des utilisateurs Couchbase avec OpenID

Attention, pour les non techniciens, ça va piquer.

Donc, j’ai démarré récement une mission, dans laquelle je dois synchroniser une application mobile avec un serveur. le client comme le serveur utilise Couchbase, donc on va utiliser Sync Gateway. Sur le papier, ça paraît tout à fait raisonnable.

Là où ça l’est moins, c’est qu’on ne veut pas stocker de bases d’authentification (la CNIL, tout ça, c’est trop pénible). On va donc passer par de l’OpenID Connect (ce qui est conceputellement très cool). Et comme l’application est mobile-only, on va utiliser ce qu’ils appellent le flow implicite.

Il est assez bien décrit dans la doc de Couchbase Sync Gateway, et en particulier dans ce diagramme

images-003

Présenté comme ça, c’est simple. Mais en fait, c’est rempli de subtilités.

Un token, Quel token ?

D’abord, il faut obtenir le token JWT auprès de votre fournisseur.

Dans mon cas, c’était Auth0, donc un appel à /authorize avec les bons paramètres va renvoyer (via l’url de redirection) le token JWT. Pour ceux qui développent une application JavaFX, il y a une chouette question StackOverflow qui donne un bon exemple … mais je n’arrive plus à remettre la main dessus.

Poster le token ?

Bon alors vous voyez cette jolie image là-haut ? Elle donne toute la doc de Couchbase sur ce qu’il faut faire du token. J’ai essayé pendant une semaine différentes méthodes avant d’utiliser la seule vraie solution : regarder le source !

Ben oui, parce que la sync gateway de Couchbase est écrite en Go.

Donc, j’ai compris assez vite que ça se passait dans le dossier /rest, et en particulier dans le fichier session.go, qui sert de handler pour les urls de … session.

SAUF QUE en go, comme en d’autres langages, on utilise des intercepteurs pour la sécurité. Donc la gestion des authentifications se partage entre handler.go et auth.go. Pour être plus clair, dans handler.go, la gestion de l’authentification se fait dans cette fonction

En fait, si vous regardez le code, vous verrez que h.getBearerToken() lit en fait le token OpenId depuis les entêtes de la requête. Ca fait déja une réponse facile à trouver :

Il faut mettre le bearer token dans les entêtes HTTP, et PAS dans le corps du POST.

Oui, mais quel token ?

Donc, j’en étais là, et à chaque fois que je postais mon token, avec tous les logs activés en mode debug, j’avais à peu près cette suite de messages

Bon, je vous fais grâce des détails, mais dites-vous que ça signifie que mon token est bien passé à la couche go-oidc, qui le refuse. Et là, j’ai dû lire le code environ 2 jours dans tous les sens avant de comprendre.

Alors, autant vous le dire, il y a plusieurs types de token JWT.

maxresdefault

Et en fait, la différence se fait dans l’algorithme. Vous avez le choix entre HS256 et RS256. Et la seule différence, c’est que le RS256 est signé avec un algorithme assymétrique.

Par défaut, Auth0 propose du HS256.

Et évidement, go-oidc ne décode que les tokens RS256. Et le choix de l’algorithme se fait dans les paramètres avancés du client Auth0.

Autrement dit

Utilisez l’algorithme RS256.

Une fois que vous aurez fait tout ça, vous pourrez bénéficier de l’authentification OpenId Connect pour vos synchronisations. Et ça vous créera les utilisateurs automatiquement. Bon, le nom des utilisateurs dans votre sync gateway sera leur id Auth0, mais franchement, vous allez vous laisser arrêter par ce genre de détail ? Moi non plus. D’ailleurs, je m’en vais de ce pas proposer une pull request à Couchbase pour modifier les différents éléments manquants.

J’abandonne

Samedi dernier, Codingame lançait code4life, le dernier contest en date.

N’écoutant que mon courage, je m’y suis jeté. Avec d’abord une implémentation basique pour atteindre le bronze, puis deux déclinaisons successives d’une machine à état.

Et aujourd’hui, avec cette machine profondément optimisée, je suis environ 250ème en silver. Il ne fait aucun doute que d’ici Lundi matin, je vais perdre encore 500 places. Mais je vais arrêter là ce contest. Plusieurs raisons à ça, que je vais détailler ici (non, je ne mettrai pas de post-mortem sur GitHub).

Fais une chose, et fais-la bien

J’ai commencé mon nouveau poste chez Zenika la semaine dernière. Et j’ai entamé ma première mission ce jeudi. Je pourrais vous mettre la liste des mots-clés ici, mais ce serait sûrement un peu prétentieux. Toujours est-il que beaucoup de technologies dans ce projet sont pour moi une nouveauté, ce qui m’a forcé à courir à fond dans la direction des découvertes techniques, ce qui n’est pas forcément compatible avec la précision, la focalisation nécessaire à la réussite d’un contest. Du coup, depuis avant-hier, je n’ai plus de gaz pour réfléchir correctement au problème.

C’est dommage pour mon succès à Codingame, et contrairement à un autre Nicolas, je ne risque pas de gagner de t-shirt, mais d’un autre côté, avoir trop de choses intéressantes à faire, c’est clairement un problème de riche.

Les streams, c’est fini

A un moment donné, j’ai commencé à avoir des sales problèmes de timeout. J’ai pu, à ma surprise et ma déception, j’ai pu associer chacun de ces timeouts à une utilisation des streams Java. Vous imaginez le truc ? Chaque utilisation de stream a occasionné à un moment ou un autre un timeout.

Attention, je ne dis pas que les streams sont mauvais. Juste que pour un contest codingame, les streams doivent être soigneusement évités.

Et ça n’est pas tout.

Vous avez tous vus les déclarations de Comparator sauce Java8 comme cet exemple

Eh bien de la même manière, chaque déclaration de Comparator avec une fonction s’est révélée trop coûteuse en temps CPU. Du coup, j’ai tout viré et tout réécrit de façon traditionnelle.

Et ça, n’importe quel chef de projet informatique vous le dira, ça a un coût. En l’occurence, du temps passé. En regardant mes commits Git, je dirais que j’ai perdu une heure à corriger ces problèmes de performance, avec à chaque fois ces étapes

  1. Copier le test généré
  2. Exécuter le PerformanceTest avec debug et VisualVM connecté
  3. Trouver le bout de code lent
  4. Remplacer le stream par un foreach façon java5

Démoralisant.

Les machines à état, c’est caca

Quand on regarde les règles, clairement, il y a différents états. J’ai tenté de les détailler dans ce schéma (vous pouvez le regarder en détail, mais franchement, on s’en fout).

sequence

Ca a l’air sophistiqué, non ? Eh ben ça l’est … Et je suis loin d’être sûr de la justesse du truc

En fait, tout ça ne sert à rien.

Ca simplifie peut-être un peu le code, mais d’une façon malsaine : je me retrouve à micromanager alors que je devrais arbitrer. Et du coup, chaque état (qui a certes une classe associée) se complique, devient plus lourd, nécessite plus de maintenance.

En vrai, si j’écoutais ce que des gens plus intelligents disent, ce que j’aurais dû faire, c’est de la programmation dynamique.

C’est-à-dire qu’à chaque tour, j’aurais dû calculer tous les mouvements possibles avec autant de profondeur que possible, et choisir le mouvement qui m’apportait statistiquement le meilleur résultat.

Mais je me suis entêté avec ma machine à état. Et je me retrouve comme un con la veille de la fin, sans avoir le temps de corriger mon code. C’est moche. Très moche.

OK, donc t’es qu’une merde en fait ?

Pas tout à fait non plus.

J’ai un peu modifié mon générateur de tests pour y inclure automatique un assertThat(myCommand).isNotEqualsTo(« La commande exécutée ») et ça me fait gagner encore un peu de temps. Par ailleurs, je compte en gagner encore plus – un jour – via un système d’annotation pour écrire automatiquement la méthode de génération de test unitaire (JavaParser et annotations seront de la partie).

Et je vais prochainement releaser sur maven central mon plugin de génération de classe unique.

Ce ne sont pas des victoires, c’est vrai, et je ne gagne pas de XP. Mais je fais des choses qui m’amusent aussi.

Donc tu vas faire le prochain ?

On verra … Mais je pense que oui.

Une installation rapide …

Qui dit nouveau travail dit, en 2017, nouvel ordinateur prêté par l’employeur. En l’occurrence, un chouette Dell XPS 15 avec un très bel écran, un clavier assez moyen, de la RAM et du CPU en suffisance.

Évidemment, comme tout développeur, j’ai mon jeu d’outils de sélection. Et pour l’installation, le premier parmi les égaux est évidement Chocolatey, qui m’a permis d’installer facilement tous les programmes que j’avais noté dans ce gist

Et évidement, ça a très bien marché (sauf pour WinSplit Revolution, dont il va falloir que je retrouve une version compatible Windows 10 ou une alternative).

Il manquait deux ou trois trucs que j’avais précédemment installé séparément (Java, Eclipse, Maven, Groovy, ou encore OwnCloud).  Je les ai donc ajouté.

Et puis il a fallu installer les plugins des différents logiciels (qui ne sont hélas pas disponibles dans Chocolatey). Pour Firefox, c’est facile. Pour Eclipse, par contre, il faut le faire à la main … Comme pour Keepass.

Et c’est d’ailleurs avec ce dernier que j’ai apporté la plus grande modification en installant  KeeAgent. Avec ce dernier, je ne risque plus de perdre mes clés, puisqu’il transforme un outil déjà génial en porte-clé pour Pageant. En fait, à l’ouverture de la base, il injecte les clés dans Pageant, et comme ça je n’ai plus rien à taper … tout en restant en sécurité.

En fait, je pense que j’aurais pu avoir un poste fonctionnel en deux heures. Evidement, il faudra aussi que je reconfigure tout ça : les polices de caractère, les couleurs, les configurations d’écran, etc, … Mais ça, c’est quasiment de la blague par rapport à l’installation à la main.

Bon, par contre, j’ai un souci d’ordre domestique : ce superbe portable haut de gamme perd quand même facilement la connexion avec le wifi M de ma freebox (qui est à deux mètres). Du coup, périodiquement, je dois relancer la connexion wifi … pas très pratique, mais je crois qu’il va falloir que je fasse une vraie enquête d’engorgement des canaux wifi dans le quartier (habiter dans des maisons de 5 mètres de large, ça n’a pas vraiment que des avantages).

Enfin, ça, c’était avant que je change un paramètre de la carte wifi grâce à ce commentaire : Comment from discussion XPS15 9560 Wifi Problems.

Et depuis, ça marche bien mieux.

Comment bien documenter un système informatique ?

Je crois que je n’ai jamais écrit un titre aussi pompeux.

Enfin bref, cet article s’appuie sur différentes expériences que j’ai vécues … avec plus ou moins de succès.

Avant tout, posons la situation.

Quelques hypothèses

  • Vous faites partie d’une équipe de développement logiciel (je ne sais pas si ce que je vais décrire ici marche dans d’autres situations)
  • Vous disposez d’un outil de build moderne (Maven, Gradle, ou tous leurs équivalents)
  • Je vais parler ici de système, plus que de logiciel, parce que la plupart du temps, vous développez plusieurs logiciels livrables : un site web, un back-end, des plugins ou des applications clientes, …
  • Le système que vous développez est unique : personne d’autre dans le monde ne fait le même système que vous en utilisant les specs à votre disposition (ou pas)
  • Vous pouvez créer un nouvel artefact dans votre base de code pour stocker la partie principale de votre documentation

Quelques prises de conscience utiles

Vous avez donc, que vous utilisez une méthode agile ou en V, commencé par définir l’architecture de votre système avant de développer le code, qui a donné lieu à une structure de données.

Ca, c’est pour le système que vous allez livrer. Mais avez-vous conscience de l’autre système ?

6bwCQwX

Parce que vous faite quoi de votre gestionnaire de source ? De votre chaîne d’intégration et de déploiement continu ? A ce que je sache, ces éléments sont au minimum configurés pour votre équipe (dans le cas de git, ça va vite), et dans la plupart des cas hautement personnalisés selon vos besoins. Typiquement, vous avez deux systèmes à documenter :

  1. Celui que vous allez livrer
  2. Celui qui vous permet de le livrer

Quelqu’un à l’esprit joueur pourrait y voir une forme d’inception : développer un logiciel avec un ensemble de logiciels qu’on est en train de développer …

Il vous faut documenter à la fois le système que vous livrez, et celui que vous utilisez pour livrer.

Par ailleurs, est-il judicieux de parler de votre système, ou des différentes incarnations de celui-ci ?

3Bm98kT

Parce qu’après tout, votre système va connaître de multiples incarnations :

  • Architecture logique ne décrivant que les différents composants de l’application hors de toute réalité
  • Architecture physique incluant les différents environnements
    • Environnements de dév,
      • de recette
      • de prod
    • Environnements locaux
      • en PAAS
      • dans des conteneurs Docker

Dans l’idéal, votre documentation doit pouvoir se projeter sur ces différents environnements.

Quelques outils et méthodes utiles

D’abord, mettons-nous d’accord pour dire que, comme tout le monde, vous n’avez pas envie de vous répéter. Heureusement, ce problème de la non répétition est connu depuis longtemps. Regardez par exemple cet article : Single Source Information: An Agile Best Practice for Effective Documentation. Il est un peu vieux et prône donc l’utilisation de Dita XML. Ne vous enfuyez pas tout de suite, je suis sûr que vous allez voir où je veux en venir.

Quel format de document source utiliser ?

Effectivement, il faut limiter les sources de documentation et multiplier au contraire les formats de sortie. Ca revient à dire qu’il ne faut pas utiliser Office pour créer de la documentation, et pour plusieurs raisons :

  • Le livrable et la source documentaire sont identiques
  • Le système de gestion de révision intégré est loin derrière les outils de développement à notre disposition
  • Le travail collaboratif est pratiquement impossible
  • La création de documents lourds ne marche pas
  • L’inclusion de données externes mobiles est difficile à maintenir

En face, évidement, il y a eu XML (avec Docbook et Dita). Eux aussi avaient leurs défauts

  • Une syntaxe lourde
  • Des include incroyablement difficiles à maintenir
  • La nécessité d’utiliser des éditeurs spécialisés

Du coup, on pourrait penser que LaTeX pourrait être une réponse. Malheureusement, je crois que sa syntaxe n’est pas vraiment légère.

Du coup, je recommande chaudement d’utiliser AsciiDoc.

  • La syntaxe est simple
  • Il est possible de créer des structures de documents complexes (avec les include de fragments de documents)
  • Il existe des éditeurs WYSIWYG (comme par exemple AsciidocFX) qui facilitent réellement la montée en compétence
  • Et enfin, les documents finaux peuvent être générés pendant le build de votre projet, ce qui permet de raprocher la documentation et le code

Comment dessiner les diagrammes ?

Evidement, ce qui vaut pour les documents vaut également pour les diagrammes : pourquoi utiliser Visio, yEd pour des diagrammes qui vont être partagés ?

Coup de bol, asciidoctor fournit une bibliothèque (asciidoctor-diagram) qui permet de générer un paquet de type de schémas différents en intégrant quelques bibliothèques bien pratiques, et en particulier PlantUML. C’est d’autant plus intéressant que ça limite, par la nature textuelle du diagramme, la taille du dessin qui va être créé (parce que je sais pas vous mais personnellement, quand je vois un diagramme avec plus de dix noeuds, je commence à pleurer un peu).

Quel plan utiliser ?

On arrive à l’une des questions cruciales.

Avant tout, une bonne documentation, c’est une bonne histoire. Par conséquent, tout plan de documentation qui ne permettra pas l’exposé de l’histoire du système ne pourra pas être un bon plan. Et par histoire, je ne veux pas dire son historique des décisions. Non, je parle d’une histoire au sens le plus journalistique du terme : qui ? quoi ? quand où ? comment ? et surtout pourquoi ? Si votre plan ne contient pas ces éléments sous une forme ou une autre, vous n’y arriverez pas.

Coup de bol, j’ai le bon plan. ou plutôt Simon Brown a le bon plan.

Vous connaissez Structurizr ? Non ? Eh bien allez y faire un tour, c’est très intéressant.

En revanche, à titre personnel, comme j’aime mettre plus de texte que de diagramme, je trouve sa page Documentation encore plus intéressante. Pour une raison en particulier : il y décrit le contenu d’une bonne documentation sous la forme d’un sommaire assez bien fichu. Le voici :

  1. Context
  2. Functional Overview
  3. Quality Attributes
  4. Constraints
  5. Principles
  6. Software Architecture
  7. Code
  8. Data
  9. Infrastructure Architecture
  10. Deployment
  11. Development Environment
  12. Operation and Support
  13. Decision Log

Eh bien le plan que je vous propose de suivre, c’est celui-là précisément.

Mais comment tout ça s’organise ?

Créer un artefact de documentation pour le système livrable

Dans votre gestionnaire de source, créez votre artefact de documentation. Généralement, je crée un module maven sous le projet principal. Si j’ai plusieurs sous-systèmes, je vais créer un module maven par sous-système et les agréger ensuite par chapitre (si le chapitre existe) au niveau principal

Pour ma part, ce sera un projet maven dans lequel je vais tout de suite ajouter le plugin asciidoc avec la configuration nécessaire pour générer du PlantUML.

Dans le dossier des sources asciidoc (donc dans mon cas dans src/docs/asciidoc), créez un dossier include dans lequel vous créez un fichier asciidoc pour chacun des chapitres de la doc.

Ecrivez votre doc

Pour chacun de ces chapitres, commencez par décrire le système d’une façon générale. Ne plongez dans les détails que si ces détails sont pertinents.

Ou générez votre doc

Il y a tout un tas d’éléments pour lesquels il peut être pertinent de générer certains éléments :

  • des diagrammes de classes extraits du code
  • des listes de classes implémentant certaines interfaces (avec des propriétés utiles)

Par pitié, ne vous répétez pas : vous pouvez, et devez, générer ces informations en asciidoc à partir de votre code.

Pour ça, j’utilise de façon systématique groovy : avant la génération de la doc, j’ai des scripts Groovy qui vont générer (dans target/generated/docs) les différents documents utiles.

Générez la doc livrable

Comme les fichiers asciidoc sont dans votre doc et utilisent le même système de build, vous pouvez générer la doc à chaque livraison, et même l’inclure comme artefact de votre build.

Ca vous garantit une documentation

  • à jour (puisqu’il est facile pour les développeurs de la modifier
  • propre (parce que Asciidoc génère facilement quele chose de joli)
  • conforme aux différents besoin de doc (puisqu’on peut facilement inclure les différentes parties de la doc dans différents documents)

Et ça marche bien ?

Chaque fois que j’ai utilisé cette méthode, j’ai réussi à produire de la documentation efficace, propre, et complète. Je ne sais pas si c’est une preuve

Mais où documente-t-on le système de développement ?

Dans la partie Development Environement, tout simplement.

Ca n’est pas un peu fastidieux de tenir à jour les diagrammes PlantUML ?

Ca peut rapidement l’être, c’est pour ça que je vous présenterai bientôt les différentes solutions auxquelles je pense pour … les générer, évidement ! (je dois d’ailleurs les tester sur mes programmes Codingame).

Tout ça n’est pas un nouveau nom sur une vieille idée ?

Bien sûr que si. L’idée, c’est le litterate programming, qui a connu des dizaines d’incarnation. Celle-ci est juste un peu plus moderne.

Un chtijug dans le textile

Et paf, un chtijug pas à petit prix chez kiabi ! Le site est plutôt sympa, avec un côté plateforme pour théatre d’improvisation (par contre, les spots du fond qui clignotent, ça va pas être pratique pour les épileptiques). Julien (du chtijug) est bien content qu’on sorte des éternels ISEN/IUT B …​ et moi aussi : aller faire ces conférences dans des entreprises, pour des développeurs professionnels, c’est effectivement mieux.

OpenAPI chez Kiabi

Il y a deux ans, Kiabi a lancé une API web. Je ne vais pas vous reprendre tout l’argumentaire développé par le speaker sur les API, parce que personnellement, je connais déja (oui, je ne suis pas vraiment pédagogue). Cela dit, il explique bien les différents aspects positifs de la définition d’une API (quand les slides seront disponibles, ce sera plus clair). L’un des plus grands bénéfices étant évidement l’accélération des développements : en découplant le développement back-end et le développement front-end, on peut accélerer ce dernier.

OpenAPI, c’est le troisième niveau du développement d’API dans l’échelle suivante :

  1. API interne
  2. API publique, mais uniquement exposée aux partenaires (avec évidement de l’OAuth, et peut-être de l’API management)
  3. API publique, exposée et source de revenus.

Définition

Pour développer l’API, Kiabi a d’abord défini des concepts généraux :

  • API Kiss
    • incluant une API affordance, c’est-à-dire intuitive dans son usage, et suggérant même ses bons usages
    • avec une sémantique claire
    • et en ne fournissant qu’une seule façon de faire une chose
  • Réutiliser des standards et des types d’API existant

Par contre, il faut éviter d’utiliser le jargon fonctionnel interne (ce qui est un point à mon sens super intéressant). Laissez-moi détailler ce point un instant. Dans votre entreprise, vos fonctionnels sont là pour définir le vocabulaire métier. Vous comptez sur leur sérieux. Mais quand vous développez une API publique, vous n’utilisez pas ce vocabulaire … C’est de la schizophrénie ? Non, à mon avis, c’est plutôt que le vocabulaire interne dérive, se jargonifie, et perd son adhérence avec le réel. Du coup, c’est DDD ou pas ? A mon avis, et contrairement à certain homonyme, oui.

A partir de ces concepts, Kiabi a défini une refcard qui reprend l’ensemble des concepts et des règles de fonctionnement définies. Chose curieuse, cette refacrd inclut la liste des codes HTTP retournés par les applications Kiabi. Ca me paraît curieux : le W3C a défini ces codes pour des raisons qui peuvent arriver, non ? Alors pourquoi ne pas tous les utiliser ?

Implémentation

Et donc, les implémenteurs implémentent leur API à partir de ces règles, et elle est définie en comité (argh) avec une platrée d’architectes transverses (re-argh). Evidement, ça qualifie le projet comme non-agile, mais en un sens, je comprend le besoin de cohérence du bazar. Et de la même manière, l’implémentation se fait sur une plateforme standard : Tomcat, CXF, JSON, …​

Management

Pour gérer tout ça, Kiabi a mis en place un API Manager (Software AG Webmethods API machintruc) qui ne semble pas fournir tous les services, puisqu’il y a quand même un Apache en frontal (apparemment pour faire de l’URI rewriting). Ils ont également un portail d’API pour développeur, évidement indispensable pour permettre aux développeurs d’utiliser les différentes API proposées. D’une façon amusante, le speaker insiste lourdement sur le fait que ce portail d’API permet aux développeurs d’utiliser facilement les différentes API. Ca trahit à mon sens plus le choc culturel qu’est cette ouverture que le challenge technique de ce portail (ne serait-ce que parce que StackOverflow, Goodreads, le fournissent déja depuis …​ quoi …​ 5 ans ?).

Démo

Pub

Kiabi organise au mois de juin un hackathon. Bon, personnellement, je ne suis pas fan de ce genre d’événements. Mais si ça vous branche …​

traefik

Pourquoi faire un autre reverse proxy (par rapport à, par exemple, nginx ou HAProxy) ? A cause des miccroservices.

Avec les microservices, on gagne un tas de nouveaux outils :

  • Des conteneurs (Docker, rkt)
  • Des orchestrateurs (Docker Swarm, Kubernetes, Mesos, Rancher)
  • Des outils de service discovery (etcd, Consul, Zookeeper)
  • Et des reverse proxy

A quoi ça sert ? A connecter le réseau privé au web en respectant l’état des microservices. Souvenez-vous, Christophe Furmaniak en avait déja parlé lors d’une session sur Rancher.

Les reverse-proxyes open-source (HAProxy et nginx) ont une configuration statique. Du coup, quand les microservices sont déployés, il faut

  1. Arrêter le proxy
  2. Changer la configuration
  3. Redémarrer le proxy

Pas très pratique.

traefik, lui, lit sa configuration depuis l’orchestrateur. Du coup, pas besoin de redémarrage. Plutôt cool …​ Et pour être rapide, c’est du go. Et d’un coup, je comprend pourquoi le go progresse : avec le build statique, une image Docker d’un programme go ne contient pas de dépendances, et c’est chouette ! (pour ceux qui aiment ça). Attention, je ne dis pas que c’est bien. Je dis juste que Go est plutôt bien adapté au monde Docker.

Démo

Et d’un coup, je comprend l’affection de Quentin Adam pour les gestionnaires d’abréviation : lancer des conteneurs docker en ligne de commande pendant une démo, ça peut merder facilement …​ Parce qu’Emile a eu quelques soucis liés à la connexion internet … sensible et aux commandes docker un peu longues.

Ghost in the cell

La semaine dernière, j’étais plutôt pris par un … un contest codingame !

Malheureusement, j’ai un peu la flemme de faire le post-mortem avec Asciidoc pour ne pas avoir les images générées par PlantUML.

Donc je vais plutôt en faire une version raccourcie ici.

Qu’est-ce qu’il faut faire ?

Sur un terrain constitué d’un graphe de noeuds produisant des robots, il faut avoir plus de robots à la fin de la partie que l’adversaire.

Mais comment ?

A chaque tour, vous pouvez demander à chacun de vos noeuds d’envoyer autant de cyborgs que vous le voulez vers la destination de votre choix.

Oui, mais comment ?

Là, ça devient compliqué.

J’ai implémenté au début un système qui envoyait les cyborgs vers le noeud ennemi le plus proche (en en envoyant plus que ce qu’a l’ennemi au moment où j’arrive). Mais ça ne marchait pas trop bien. Même si ça m’a emmené jusqu’en bronze.

J’ai ensuite amélioré d’innombrables fois ce système en tentant les choses suivants

  • Envoyer les bombes vers les noeuds ennemis
  • Faire des upgrades des noeuds que je détiens (malin, parce que tout un tas d’ennemis envoient 2 cyborgs à chaque tour, en comptant sur les statistiques) … Ca m’a emmené en argent.
  • Implémenter un peu de solidarité en essayant d’équilibrer le nombre de cyborgs entre les noeuds. Ca a plutôt bien marché … mais je n’ai pas franchi l’argent
  • Et le dernier : trier les noeuds au début de chaque tour par distance au centre des noeuds de mon équipe. Et utiliser ce tri pour chacune des opérations précédentes. Et ça m’a emmené en or.

Résultat ?

683ème.

Pas forcément un mauvais score.

Mais nettement moins bon que mon collègue Nicolas, qui termine 41ème en Legend ! Et que je félicite.

Mais il y a des trucs qui n’ont pas marché ?

Tellement !

La plus grosse déception a été pour moi la tentative d’utiliser l’algorithme dynamique de résolution du problème du sac à dos, qui ne donnait pas vraiment de différence de score de mon algorithme naïf et brutal.

A côté de ça, j’aurais peut-être dû aussi tenter des algorithmes plus … intelligents.

Et des leçons à en tirer ?

Je connaissais bien Bomberman, donc j’ai pu utiliser un peu d’expertise pour sentir les bons algorithmes dans Hypersonic.

Par contre, là, les graphes, je n’y connais rien. Enfin, si, en terme d’algorithmique, de navigation, je m’y connais … un peu. Par contre, ce genre de jeux, vraiment, je n’y connais rien, et je suis nul. Et je sais pourquoi : on est là dans le même genre de problème que pour le go : c’est un jeu distribué sans valeur autre que positionnelle. En effet, un noeud vaut plus par le nombre de cyborgs qu’il produit que par toute autre valeur ou information.

Et du coup, c’est très difficile à appréhender pour l’esprit humain qui est fondamentalement focalisé sur un objectif, et pas sur une situation globale.

Et ça, c’est une super leçon : il est difficile pour moi de concevoir un algorithme intelligent si je ne connais pas vraiment bien l’espace du problème.

Oh, bien sûr, j’ai atteint une position au moins aussi bonne qu’à hypersonic. Mais quelque part, et même si j’étais hier extrêmement content d’atteindre l’or, j’ai quand même une petite pointe de déception de ne pas avoir fait un meilleur score (qui aurait pu être Legend, mais c’est ma prétention habituelle).

Asciidoc for the win !

Il y a quelques temps, j’avais écrit un article expliquant pourquoi les documents Office n’étaient pas de bons livrables. J’y indiquais par ailleurs qu’Asciidoc me semblait une bonne alternative.

Mais un format, ça n’est qu’un format. Une documentation, ça n’est hélas pas qu’un format technique de document. C’est aussi, avant tout, le moyen de raconter l’histoire du logiciel. Et parfois, ça n’est pas facile

A la recherche du bon format

Pas son histoire au sens biographique, mais plus son usage, son architecture, bref, l’ensemble des éléments qui contraignent son code à avoir sa structure. Autrement dit, les éléments qui donnent les réponses aux questions invraissemblables que se posent un développeur.

you_down_wit_opc-yeah_you_know_me

C’est pas que je déteste, c’est que je comprend pas l’histoire

Et ça, que j’écrive ma doc dans Word, en Asciidoc, en LaTeX, la question se pose toujours : pourquoi ces décisions ont été prises.

Heureusement, j’ai découvert il y a quelques temps le format de documentation agile de Simon Brown, qu’il documente dans la doc de Structurizr.

Regardons ensemble le sommaire :

  1. Context
  2. Functional Overview
  3. Quality Attributes
  4. Constraints
  5. Principles
  6. Software Architecture
  7. Code
  8. Data
  9. Infrastructure Architecture
  10. Deployment
  11. Development Environment
  12. Operation and Support
  13. Decision Log

Et maintenant, imaginez ce que ça donne pour votre projet. Tous les aspects sont documentés dans un seul gros document Word. C’est pénible … heureusement, là, asciidoc peut vous aider.

Je vous montre : si votre doc est en asciidoc, vous pouvez faire des include.

Et donc, vous pouvez avoir un dossier de doc qui ressemble un peu à ça

  • monprojet.adoc
  • images/ Vous mettez toutes vos images là-dedans
  • includes Et chacun des chapitres de votre doc est un document séparé dans ce dossier.
    • /00_intro.adoc
    • /01_context.adoc
    • /02_functional_overview.adoc
    • /03_quality_attributes.adoc
    • /04_constraints.adoc
    • /05_principles.adoc
    • /06_software_architecture.adoc
    • /07_code Si vous avez des éléments à détailler, n’hésitez pas à créer des sous-dossiers
      • /un_detail.adoc
    • /07_code.adoc
    • /08_data.adoc
    • /09_infrastructure_architecture.adoc
    • /10_deployment.adoc
    • /11_development_environment.adoc
    • /12_operation_and_support.adoc
    • /13_usage.adoc
    • /14_decision_log.adoc
    • /99_faq.adoc

Du coup, avec tout ça, vous avez une documentation correctement découpée, dans laquelle vous mettez vos diagrammes UML (ou autres) générés avec asciidoctor-diagram, et vous êtes content …

Bon, et puis si vous n’êtes pas contents de ce format, Simon Brown mentionne également le support d’arc42, qui a l’air tout aussi intéressant :

A un détail près.

Parfois, il y a du code que vous voulez voir généré pour le mettre dans la doc.

Par exemple, si vous utilisez le ServiceLoader de Java, savoir quelles classes implémentent une interface sans avoir à chercher dans Eclipse, c’est cool.

Tant qu’à générer la doc, pourquoi ne pas générer la doc ?

Parce qu’en fait, votre doc asciidoc, vous la générez, pas vrai ? Si vous faites du maven, vous utilisez sans doute le maven-asciidoc-plugin. Je vais faire l’hypothèse que vous êtes dans ce cas.

Et dans ce cas, si vous avez besoin d’avoir un morceau de la doc qui dépende plus directement du code, vous pouvez tout à fait … utiliser Groovy pour générer des fichiers asciidoc qui vont bien !

Typiquement, pour mon exemple, j’ai un script Groovy qui a dans son CLASSPATH tous les modules de mon application, et qui à l’aide de reflections recherche les implémentations de l’interface pour générer un tableau Asciidoc les affichant.

Et comme ce script Groovy est lancé grâce à gmaven avant la génération de la doc, cette partie est toujours à jour.

Et pour un projet multimodule ?

Pour l’instant, j’ai juste testé ça une fois, et d’une façon non satisfaisante. Comme j’arrivais sur un projet existant, j’ai simplement ajouté un module doc isolé du reste de l’application.

Je pense que j’aurais eu une doc d’une bien meilleure qualité si j’avais mis la doc dans chaque module avec des includes bien costauds dans la doc du module global (voire même des morceaux complètement générés). Evidement, dans ce genre de cas, la génération de la documentation doit également être documentée …

Donc ça marche bien ?

Oh que oui !

Ecrire de la doc est presque agréable, et les aspects générationnels sont une distraction encore plus agréable. Sans même parler de la fusion de cette doc et des différents diagrammes qui y sont intégrés.

Mais je crois qu’on peut aller encore beaucoup plus loin. Pas directement avec cette technique, mais sans doute en amont, via des graphes.

On pourrait faire encore mieux ?

Je vous donne quelques exemples.

Pour décrire votre applications, vous utilisez en fait plusieurs graphes

  • L’architecture conceptuelle (ou fonctionnelle) décrit comment les composants remplissent les fonctions
  • L’architecture applicative organise ces composants en modules
  • L’architecture technique montre comment ces composants se déploient.

Tout ça est censé être à peu près couvert par Structurizr.

Mais d’autres éléments ne le sont pas : quand je décris une organisation de classe, c’est aussi un graphe. Et un diagramme de séquence pourrait tout à fait être construit à partir du code un peu annoté.

Imaginez que je dispose d’un modèle comme celui de Structurizr, mais enrichi à la fois avec les informations du code (pour produire les différents diagrammes de bas niveau), et pour lequel je puisse générer des diagrammes via une quelconque syntaxe de recherche des noeuds dans un graphe. Je pourrais alors demander facilement des trucs comme

Dessine-moi le diagramme de séquence partant de cette interaction Javascript et incluant les échanges avec le serveur, la base de données, et le composant Java bidule

Et d’un coup, je n’ai plus un simple outil de documentation, mais un outil d’analyse et de compréhension de mon code, que je peux ensuite faire produire un truc que j’ai bien galéré à faire il y a quelques temps :

Fais-moi un diagramme de composant où les classes sont coloriés en fonction de leur dette technique

Bon, c’est une très chouette idée, et je sais à peu près comment l’implémenter … Mais c’est malheureusement encore un projet de plus. Et mon temps est déjà bien rempli.

Jenkins, sers-moi donc un Chtijug !

Nicolas Géraud était venu nous parler des pipelines il y déja deux ans.Est-ce que cette présentation allait être mieux ? En tout cas, elle est bien différente.

Petit point sponsoring de l’IUT sur la taxe d’apprentissage : les entreprises peuvent déclarer des organismes de formation bénéficiaires de leur taxe d’apprentissage. Ca peut avoir une influence sur le budget de ces organismes.

Et c’est parti !

Avec d’abord un petit sondage :

  • qui connait Jenkins ? A main levée, tout le monde dans la salle.
  • qui a déja migré vers Jenkins 2 ? Encore à main levée, pas grand monde.

Jenkins 2

Donc Jenkins 2 … est totalement compatible avec Jenkins 1. Et c’est bien, parce que la montée de version va être simple. Dans l’ensemble, les plugins sont compatibles.

Avec Jenkins 2, il y a aussi de nouveaux sites

  • http://jenkins.io qui est bien plus grand public
  • le project Voltron sert de plugin store avec une chouette présentation. Limite comme le google play store, en fait.

Il y a maintenant au premier démarrage un mot de passe à aller chercher dans les logs de la machine. Ca va permettre de sécuriser le serveur Jenkins avec un admin par défaut. Evidement, on peut toujours avoir un Jenkins non sécurisé, mais il faut le vouloir. Ce qui change pas mal de Jenkins 1 qui, par défaut, n’était pas sécurisé. En bonus, une liste de plugins par défaut est disponible, pour faire une première personnalisation de Jenkins.

Et ensuite, on arrive aux fameux pipeline-as-code.

Pipeline as code

Un job est défini par un Jenkinsfile, écrit dans un DSL Groovy, qu’on peut stocker dans son SCM. L’avantage, c’est aussi de permettre d’utiliser les structures de contrôle de Groovy pour personnaliser le job.

Et ensuite

Il y a tout un tas de nouveaux trucs qui ne vont pas tarder à arriver

BlueOcean (déja disponible en pas beta, mais pas release non plus)

La fameuse nouvelle interface graphique pour Jenkins. Les captures sont très chouettes !

Franchement, l’affichage des pipelines est très chouette. Si j’avais eu ça chez Perigee, j’aurais pu montrer un vraiment très beau pipeline. Le plus incroyable, c’est quand même de pouvoir faire « pousser » un pipeline graphiquement. Il faut voir l’écran pour le croire, je trouve.

Pipeline déclaratif

En bonus, ça permet à des non-développeurs de modifier le build, ce qui n’est pas une si mauvaise idée, puisque le code exécuté est toujours en SCM. En fait, le pipeline généré n’est pas exactement identique à celui qu’un développeur écrirait : c’est un pipeline déclaratif.

D’une façon amusante, le speaker décrit la syntaxe d’un DSL Groovy comme « à mi-chemin entre YAML et Groovy ». Guillaume Laforge et tous ses amis pleurent des larmes de sang en entendant ça. Parce que quand on regarde un DSL Groovy, comme par exemple le MarkupBuilder, on est exactement dans la syntaxe visible à l’écran. Bon, je ne vais pas en vouloir au speaker : il connaît sans doute mieux Jenkins que je connais Groovy.

Back-end storage

Manifestement, l’objectif est de découpler le fonctionnement de Jenkins et le stockage des configurations d’exécution et des résultats. C’est une chouette idée, mais pas vraiment assistée par les capacités des plugins à écrire eux-mêmes des fichiers.

Donc Jenkins 2, c’est ?

Jenkins 2, c’est mieux, et orienté sur le continuous delivery. Et pas le continuous deployment. Autrement dit, l’équipe de cloudbees ne vise pas trop à utiliser Jenkins pour le déploiement en prod.

Quelques questions

Si les pipelines sont déclaratifs, comme maven ou gradle, pourquoi ne pas réutiliser leur outillage ?

Jenkins est utilisé pour faire le delivery, ce que ne font pas ces outils. En un sens, je comprend cette réponse : le scope fonctionnel est différent, et ce serait assez curieux de dire « on peut faire du gradle pour le déploiement ». Et puis, Cloudbees et Gradle inc ne vont pas fusionner demain.

Mais quand même, en transformant les jobs en bouts de code, je trouve que Cloudbees fait rentrer Jenkins dans une espace de continuum de build qui va de mon poste jusqu’aux artefacts déployables en prod.

Comment se compare l’API de pipeline-as-code et de job-dsl ?

L’inspiration initiale de pipeline-as-code était buildflow, mais ça a vite divergé.

Et la démo

  • Premier truc cool : les plugins installés sont toujours installés à la dernière version.
  • Deuxième truc cool : BlueOcean est déjà disponible comme plugin, et donc installable.
  • Troisième truc cool : le travail d’administrateur Jenkins s’arrête, pour les jobs, à créer les jobs et les connecter à des SCM. Tout le reste vient du Jenkinsfile.

Et donc, on passe à la création du Jenkinsfile. Et là, la structure des DSL Groovy réapparaît vraiment, et c’est bien.

  • Quatrième truc cool : les commandes checkout de SCM utilisent la révision du Jenkinsfilecomme référence. Par conséquent, l’enchaînement d’étapes se passera beaucoup mieux que dans Jenkins 1, puisqu’il n’y aura pas de révision à passer. Par contre, curieusement, le DSL ne semble pas contenir de commande maven, puisque notre speaker exécute les commandes maven via un sh "mvn clean package". Arf … apparement, le plugin exécutant maven est « mauvais ».
  • Cinquième truc cool : l’interface de Jenkins inclut un générateurs de snippets façon vieux wizards qui permet d’apprendre très rapidement le DSL des pipelines.
  • Sixième truc cool : l’affichage des pipelines en cours est vraiment très sympa, avec des logs par noeud du pipeline affichés en temps réel.
  • Septième truc cool : le DSL inclut une notion de stashes, valable uniquement pour la durée du pipeline, qui permet donc de ne pas déployer dans Nexus les artefacts temporaires.
  • Premier truc pas cool : le pipeline est toujours exécuté sur le master. Du coup, la montée en charge pourrait mal se passer.
  • Huitième truc cool : pour les pipelines déclaratifs, il est possible de demander à Jenkins d’analyser la syntaxe.
  • Deuxième truc pas cool : on peut relancer un pipeline à partir d’une étape uniquement dans la version commerciale de Jenkins. C’est moins bien … mais je comprend tout à fait.
  • Neuvième truc cool : il est possible de créer des morceaux de code partagés entre pipelines.
  • Dixième truc cool : les milestones permettent d’éviter de lancer plusieurs builds concurrents sur une ressource critique.

Et si vous voulez voir le Jenkinsfile utilisé pour ce live-coding, il est disponible sur github.

D’autres questions

Comment tester un pipeline ?

Eh bien, on ne peut pas : il faut le lancer dans une instance de Jenkins. Autrement dit, c’est le genre de truc où il faut une instance de test de Jenkins. Et mon collègue me souffle à l’oreille qu’on peut très bien utiliser l’annotation @StaticCompile de Groovy pour vérifier la syntaxe autant que possible (parce que @StaticCompile, sur un DSL, il doit bien s’amuser, tiens).

Conclusion

J’ai quand même hâte de voir les projets passer au moins aux pipelines as code, histoire d’avoir dans mon SCM, à côté de mon pom.xml, le Jenkinsfile qui va bien. Et histoire aussi d’ajouter un peu de Groovy dans tout ça !

Compiler ou tester ?

En fin de cette semaine, une controverse intéressante a eu lieu sur twitter, habilement lancée par cet article de « Uncle » Bob Martin : The Dark Path.

Regardez par exemple cette sélection de tweets sur le sujet :

On peut dire que les avis sont contrastés.

Et comme la meilleure façon de ne laisser aucun doute sur le sujet de ma propre stupidité, laissez-moi vous donner mon avis sur le sujet.

A quoi sert le compilateur ?

Il y a des années, à l’époque du C, le compilateur servait à transformer le code source écrit dans un langage quasiment humainement compréhensible (le C) en un code exécutable par la machine.

Aujourd’hui, quelque soit la plateforme d’exécution (la JVM, la CLR de Microsoft, ou le résultat d’une transformation LLVM), le compilateur fait beaucoup plus de choses : il simplifie, optimise, vérifie le code d’une façon très poussée.

Et à mon avis, c’est l’argument de Romain Guy : si le système de type est suffisamment poussé, il est impossible d’écrire un code qui ne corresponde pas précisément aux souhaits du développeur. Mais à quel prix ? C’est ce que cherche à évaluer Uncle Bob.

Laisser le compilateur vérifier ? ou Tester

Parce que l’avantage des tests sur le compilateur, c’est quand même de laisser le développeur vérifier les choses à sa façon : il écrit l’histoire dans son test, et vérifie les choses comme il le souhaite. Qui plus est, le compilateur est certes capable de vérifier certaines erreurs bas niveau, mais il y a tout un tas de trucs que le compilateur ne sera absolument pas capable de vérifier, et qui nécessiteront des tests.

Alors quoi ?

Heureusement, aujourd’hui, la question est en fait tranchée : les développeurs utilisent les deux, en fonction à la fois du coût d’implémentation de chacun, et de l’expressivité de chacun. Cela dit, je continue à trouver cette question intéressante. Et, à titre personnel, je comprend parfaitement la décision de Kotlin et Swift d’empêcher les références null, même si je préfère une solution plus simple : je n’utilise autant que possible jamais de nullen Java. Et du coup, si NullPointerException il y a, c’est forcément un bug qui nécessitera un test. Est-ce que c’est plus coûteux que d’avoir des <Object>? à déréférencer ? Je n’ai pas l’impression. Mais bien sûr, je peux me tromper.

Ne livrez pas de document Office !

Vous travaillez sur un grand projet … ou un moyen.

Et par exemple, on vous demande de livrer de la doc sur le code, l’architecture. ou des éléments de volumétrie. Et là vous vous dites « je livre un Excel ! » ou « Je livre un Word ! ». Par pitié, ne le faites pas.

Pas parce que c’est moche, lourd, et peu conforme à vos valeurs éthiques de partage.

Mais plutôt parce que les fichiers Office ne sont pas compatibles avec un processus de développement, ou plutôt d’écriture ?

Parce qu’après tout, votre document a bien un cycle d’écriture, non ? Vous avez bien, sous une forme ou une autre, les étapes suivantes

  • Ecriture
  • Relecture
  • Livraison
  • Passage à la version suivante

Si ça vous rappelle le cycle de vie d’artefacts de développement, ça n’est pas un hasard, c’est la même chose.

Et d’un point de vue format, comment faire ?

D’abord, si votre livrable ne bouge plus, c’est que ça n’est pas le même format que celui dans lequel vous écrivez. Typiquement, quand vous livrez un PDF, par exemple, vous ne pouvez pas vraiment le modifier (mis à part hacks visibles).

Ensuite, dans l’idéal, vous cherchez un format qui vous permette de répondre aux critiques habituelles d’Office :

  • Pas de travail collaboratif possible
  • Pas de merge
  • Un outillage de composition de document assez faible

Autrement dit, vous cherchez un format source qui soit vraiment ça : un fichier source que vous pouvez gérer dans votre SCM, sur lequel vous pouvez faire des merge, et enfin que vous pouvez composer (ou compiler) dans votre chaîne de build. Eh bien, ce format existe. En fait, il y en a plusieurs, mais un seul qui marche : Asciidoc.

De nos jours, avec Asciidoctor, vous pouvez compiler votre doc via maven, et la gérer dans votre SCM (je le fais déja). Vous pouvez par ailleurs créer de gros documents en assemblant plusieurs fragments (voir leur fameux tag include). Et, comme vous créez votre doc avec maven, elle est packagée et livrée comme le reste de votre application.

Alors, pourquoi vous continuez à utiliser Office ? Parce que vos utilisateurs le demandent ? En vrai, je ne crois pas que ce soit une bonne raison.