<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="fr"><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://liard.me/feed.xml" rel="self" type="application/atom+xml" /><link href="https://liard.me/" rel="alternate" type="text/html" hreflang="fr" /><updated>2026-06-14T21:07:20+00:00</updated><id>https://liard.me/feed.xml</id><title type="html">Samuel Liard</title><subtitle>Software Engineering Manager — blog technique, talks &amp; CV.</subtitle><author><name>Samuel Liard</name></author><entry><title type="html">GitHub Copilot CLI vs Online</title><link href="https://liard.me/2026/03/copilot-cli-online/" rel="alternate" type="text/html" title="GitHub Copilot CLI vs Online" /><published>2026-03-08T09:15:05+00:00</published><updated>2026-03-08T09:15:05+00:00</updated><id>https://liard.me/2026/03/copilot-diff</id><content type="html" xml:base="https://liard.me/2026/03/copilot-cli-online/"><![CDATA[<p>J’ai eu envie de faire un test simple: demander a GitHub Copilot de generer le meme site via deux approches differentes, une en mode CLI et une en mode online.</p>

<p>L’idee ici c’est  d’observer les differences de rendu ecran par ecran. Les deux generations couvrent globalement le meme besoin, avec des variations sur la presentation, la hierarchie visuelle et quelques details d’ergonomie.</p>

<p>Vous pouvez regarder la branche <a href="https://github.com/sliard/TestChessCopilot2/tree/feature-cli">construite avec la CLI</a> pour voir les fichiers modifies.
Et la branche <a href="https://github.com/sliard/TestChessCopilot2/tree/copilot/implement-all-features">construite en ligne</a> pour voir les fichiers generes.</p>

<p>Les fichiers dans le répertoires docs sont les mêmes et pourtant on constate des différences.</p>

<h2 id="home">Home</h2>

<table>
  <thead>
    <tr>
      <th>CLI</th>
      <th>Online</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="/images/uploads/2026/03/cli/home.png" target="_blank"><img src="/images/uploads/2026/03/cli/home.png" alt="Home version CLI" /></a></td>
      <td><a href="/images/uploads/2026/03/online/home.png" target="_blank"><img src="/images/uploads/2026/03/online/home.png" alt="Home version Online" /></a></td>
    </tr>
  </tbody>
</table>

<h2 id="create-account">Create Account</h2>

<p>J’ai laissé l’erreur CSS alors qu’a mon avis ça pouvais ce corriger rapidement.</p>

<table>
  <thead>
    <tr>
      <th>CLI</th>
      <th>Online</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="/images/uploads/2026/03/cli/CreateAccount.png" target="_blank"><img src="/images/uploads/2026/03/cli/CreateAccount.png" alt="Create Account version CLI" /></a></td>
      <td><a href="/images/uploads/2026/03/online/CreateAccount.png" target="_blank"><img src="/images/uploads/2026/03/online/CreateAccount.png" alt="Create Account version Online" /></a></td>
    </tr>
  </tbody>
</table>

<h2 id="opening">Opening</h2>

<table>
  <thead>
    <tr>
      <th>CLI</th>
      <th>Online</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="/images/uploads/2026/03/cli/Opening.png" target="_blank"><img src="/images/uploads/2026/03/cli/Opening.png" alt="Opening version CLI" /></a></td>
      <td><a href="/images/uploads/2026/03/online/Opening.png" target="_blank"><img src="/images/uploads/2026/03/online/Opening.png" alt="Opening version Online" /></a></td>
    </tr>
  </tbody>
</table>

<h2 id="opening-edit">Opening Edit</h2>

<table>
  <thead>
    <tr>
      <th>CLI</th>
      <th>Online</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="/images/uploads/2026/03/cli/OpeningEdit.png" target="_blank"><img src="/images/uploads/2026/03/cli/OpeningEdit.png" alt="Opening Edit version CLI" /></a></td>
      <td><a href="/images/uploads/2026/03/online/OpeningEdit.png" target="_blank"><img src="/images/uploads/2026/03/online/OpeningEdit.png" alt="Opening Edit version Online" /></a></td>
    </tr>
  </tbody>
</table>

<h2 id="opening-view">Opening View</h2>

<table>
  <thead>
    <tr>
      <th>CLI</th>
      <th>Online</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="/images/uploads/2026/03/cli/OpeningView.png" target="_blank"><img src="/images/uploads/2026/03/cli/OpeningView.png" alt="Opening View version CLI" /></a></td>
      <td><a href="/images/uploads/2026/03/online/OpeningView.png" target="_blank"><img src="/images/uploads/2026/03/online/OpeningView.png" alt="Opening View version Online" /></a></td>
    </tr>
  </tbody>
</table>

<h2 id="public--private">Public / Private</h2>

<table>
  <thead>
    <tr>
      <th>CLI</th>
      <th>Online</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="/images/uploads/2026/03/cli/PublicPrivate.png" target="_blank"><img src="/images/uploads/2026/03/cli/PublicPrivate.png" alt="Public Private version CLI" /></a></td>
      <td><a href="/images/uploads/2026/03/online/PublicPrivate.png" target="_blank"><img src="/images/uploads/2026/03/online/PublicPrivate.png" alt="Public Private version Online" /></a></td>
    </tr>
  </tbody>
</table>

<h2 id="conclusion">Conclusion</h2>

<p>Ce comparatif confirme que <strong>la qualité du résultat dépend autant du contexte et des instructions que de l’outil lui-même</strong>. Les différences observées s’expliquent en partie par le fait que j’ai focalisé mes descriptions sur les besoins fonctionnels de l’utilisateur, sans jamais mentionner d’aspects ergonomiques ou de design.</p>

<p><strong>Un constat surprenant sur le temps de traitement :</strong> la génération en ligne a été réalisée en 1h par GitHub avec comme consigne d’implémenter l’ensemble des features en une seule fois. Pour la CLI, j’ai suivi l’approche recommandée par GitHub : des prompts dédiés pour chaque feature, avec systématiquement une phase de planification puis une phase de génération. Cette méthode m’a demandé toute une après-midi. Paradoxalement, on constate que la génération “oneshot” produit un résultat exactement au même niveau que l’approche itérative pourtant préconisée.</p>

<p>Point intéressant : aucune des deux générations ne propose d’éditer une ouverture en déplaçant directement les pièces sur l’échiquier, fonctionnalité que j’avais pourtant obtenue lors d’une <a href="https://github.com/sliard/TestChessCopilot">première génération</a>. Cela montre l’importance de la formulation des prompts et du contexte dans lequel l’IA travaille.</p>]]></content><author><name>Samuel</name></author><category term="AgentIA" /><category term="claude code, copilot" /><summary type="html"><![CDATA[J'ai eu envie de faire un test simple, demander a GitHub Copilot de generer le meme site via deux approches differentes, une en mode CLI et une en mode online.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://liard.me/images/uploads/2026/03/cli/home.png" /><media:content medium="image" url="https://liard.me/images/uploads/2026/03/cli/home.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">GitHub Copilot après Claude Code</title><link href="https://liard.me/2026/02/copilot-vs-claude-code/" rel="alternate" type="text/html" title="GitHub Copilot après Claude Code" /><published>2026-02-18T09:15:05+00:00</published><updated>2026-02-18T09:15:05+00:00</updated><id>https://liard.me/2026/02/copilot-vc-claude</id><content type="html" xml:base="https://liard.me/2026/02/copilot-vs-claude-code/"><![CDATA[<p><img src="/images/uploads/2026/02/claudevscopilot.png" alt="Claude VS Copilot" /></p>

<p>Depuis plusieurs mois, j’expérimente les assistants IA dans mes projets, et après avoir beaucoup travaillé avec Claude Code et constaté à quel point ces outils permettent de produire des applications à une vitesse impressionnante, notamment parce qu’ils m’aident à dépasser mes limites sur la partie IHM en tant que développeur backend, j’ai voulu tester GitHub Copilot avec le modèle Opus 4.5 afin de voir ce qu’il apportait concrètement dans mes développements. J’étais plus que sceptique. Quand quelque chose fonctionne très bien, on n’a pas forcément envie d’en changer. Et pourtant.</p>

<h2 id="pourquoi-tester-github-copilot-">Pourquoi tester GitHub Copilot ?</h2>

<p>La principale raison est simple : l’intégration IDE. J’utilise IntelliJ IDEA depuis des années. C’est mon environnement naturel de développement. Pouvoir utiliser un agent IA directement dans l’IDE, au plus proche du code, est un vrai confort. Oui, on peut connecter Claude Code à son IDE. Mais l’intégration reste moins fluide, moins native. On sent que ce n’est pas pensé au cœur de l’expérience développeur. Avec GitHub Copilot, l’expérience est intégrée dans l’éditeur et permet de naviguer dans les fichiers, générer du code, assister le refactoring et même créer des Pull Requests sans sortir de l’environnement.</p>

<h2 id="les-agents-et-les-skills">Les “Agents” et les “Skills”</h2>

<p><img src="/images/uploads/2026/02/agentAndSkill.png" alt="Agent and skill" /></p>

<p>Même si cette approche existe aussi avec Claude Code, j’ai trouvé l’écosystème Copilot plus simple à prendre en main, mieux structuré et surtout accompagné de beaucoup plus d’exemples concrets prêts à l’emploi. En explorant l’écosystème Copilot, je suis tombé sur le dépôt awesome-copilot, qui propose une liste impressionnante d’agents et de skills prêts à l’emploi.</p>

<p>Concrètement, j’ai par exemple utilisé un agent de développement backend Spring qui sépare proprement les services, DTO et contrôleurs. J’ai aussi utilisé un skill de génération de tests pour créer une base de tests unitaires cohérente avec la structure Maven du projet. Dans les deux cas, l’agent ne s’est pas contenté d’ajouter du code : il a respecté l’architecture existante et les conventions définies dans le template, ce qui rend le résultat directement exploitable.</p>

<h2 id="création-dun-template-projet">Création d’un template projet</h2>

<p>Pour tester sérieusement, je ne voulais pas partir d’un simple projet vide. J’ai donc commencé par créer un template de projet adapté à mes technologies préférées : Java, Maven, Spring Boot et React via Vite. L’objectif était de poser un cadre clair dès le départ, avec une séparation propre entre backend et frontend, une configuration Maven standardisée et un environnement React immédiatement exploitable.</p>

<p>Cette base structurée permettait de laisser l’agent travailler efficacement sans ambiguïté sur l’architecture. Sur ce point, Copilot a été très pertinent : la génération est cohérente, les dossiers sont organisés logiquement et les conventions respectées. On sent que le modèle comprend l’architecture globale du projet, et pas seulement les lignes de code qu’il produit.</p>

<p>Le template est disponible ici :<br />
https://github.com/sliard/JavaViteTemplate</p>

<p>Il repose sur une séparation claire entre un backend Spring Boot exposant une API REST et un frontend React (Vite) isolé, avec une configuration Maven standardisée et prête pour l’industrialisation. J’y ai également intégré une structure pensée pour les agents : dossiers explicites, README orienté contexte, scripts reproductibles et conventions strictes pour éviter toute ambiguïté lors de la génération.</p>

<p>Un point important concerne la formalisation des messages de commit. J’ai défini une convention claire, de type Conventional Commits, pour forcer l’agent à produire des messages structurés, explicites et exploitables dans un pipeline CI/CD. En cadrant le format attendu dès le départ, on améliore fortement la qualité des Pull Requests générées : description synthétique, contexte technique, liste des changements et impact potentiel. Petit plus lié à l’intégration IDE, la génération des messages de commit respectait cette règle à la lettre.</p>

<h2 id="projet-test">Projet test</h2>

<p>Pour aller plus loin, j’ai utilisé ce template pour créer un projet test disponible ici :<br />
https://github.com/sliard/TestChessCopilot</p>

<p>L’objectif était de développer un début de site pour apprendre les ouvertures aux échecs, un sujet que je connaissais déjà puisque j’avais réalisé un service similaire avec Claude Code. Cela me permettait de comparer dans un cadre concret la vitesse de développement, la qualité du code produit, la cohérence de l’architecture et la qualité de l’IHM générée.</p>

<p>Mais au-delà du code, le point central du projet a été le travail de définition du besoin : ici, la spécification devient le véritable prompt, et la qualité de la documentation pilote directement la qualité du code généré. J’ai formalisé le cadre fonctionnel dans le fichier <code class="language-plaintext highlighter-rouge">docs/PROJECT.md</code>, qui décrit la vision globale, le périmètre, les contraintes techniques et les objectifs. Ensuite, chaque fonctionnalité est détaillée dans des fichiers dédiés sous le répertoire <code class="language-plaintext highlighter-rouge">docs/features</code>. Cette structuration permet à l’agent de travailler avec un contexte clair, découpé et explicite. Plutôt que de générer à la volée, l’IA s’appuie sur une spécification écrite, versionnée et évolutive. On se rapproche d’une vraie démarche produit, où la qualité de la définition du besoin conditionne directement la qualité du code généré.</p>

<p>Dans ce cadre très structuré, j’ai également testé la génération complète d’une feature via l’agent depuis la page GitHub web. Celui-ci a produit les modifications nécessaires sur une branche qu’il a créée, organisé les commits selon la convention définie, rédigé une description claire et contextualisée et documenté les changements. Ce n’est plus seulement de la génération de code : c’est un vrai workflow de contribution intégré au projet. L’agent a même ajouté des screenshots de ce qu’il avait réalisé directement dans la description de la Pull Request, ce qui rend la revue beaucoup plus concrète.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Après plusieurs mois avec Claude Code, je reste impressionné par sa capacité à générer rapidement des applications complètes, créatives et avec des interfaces propres. GitHub Copilot, de son côté, apporte une autre dimension plus intégrée : un agent directement dans l’IDE, une connexion naturelle avec GitHub et un écosystème d’agents qui structure réellement le travail au quotidien.</p>

<p>Pour moi, en tant que développeur orienté backend, c’est probablement le changement le plus marquant. Là où je me concentrais surtout sur l’API, la persistence et l’architecture, je suis aujourd’hui capable de gérer l’ensemble de la chaîne applicative, du backend au frontend, avec des itérations rapides et des Pull Requests propres et bien structurées.</p>

<p>Est-ce que Copilot remplace Claude Code ? Oui, dans mon cas. Il me permet de conserver les modèles IA de Claude, qui restent pour moi les plus performants, tout en gagnant en souplesse d’utilisation grâce à l’intégration IDE et à l’écosystème GitHub. À cela s’ajoute une offre tarifaire beaucoup plus intéressante chez GitHub Copilot. On ne parle plus seulement d’assistance au code, mais d’un véritable environnement de développement augmenté, plus flexible et mieux intégré à mon workflow quotidien.</p>

<p>Demain, la compétence clé ne sera peut-être plus seulement d’écrire du code, mais de structurer précisément le contexte, la documentation et l’architecture dans lesquels l’IA va travailler.</p>

<p><em>(Article rédigé en février 2026. Vu la vitesse à laquelle ce domaine évolue, je suis moi-même curieux de voir combien de temps ce constat restera valable.)</em></p>]]></content><author><name>Samuel</name></author><category term="AgentIA" /><category term="claude code, copilot" /><summary type="html"><![CDATA[Depuis plusieurs mois, j’expérimente les assistants IA dans mes projets, et après avoir beaucoup travaillé avec Claude Code et constaté à quel point ces outils permettent de produire des applications à une vitesse impressionnante, notamment parce qu’ils m’aident à dépasser mes limites sur la partie IHM en tant que développeur backend, j’ai voulu tester GitHub Copilot avec le modèle Opus 4.5 afin de voir ce qu’il apportait concrètement dans mes développements. J’étais plus que sceptique. Quand quelque chose fonctionne très bien, on n’a pas forcément envie d’en changer. Et pourtant.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://liard.me/images/uploads/2026/02/claudevscopilot.png" /><media:content medium="image" url="https://liard.me/images/uploads/2026/02/claudevscopilot.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Comment j’ai utilisé un agent IA pour booster ma recherche d’emploi</title><link href="https://liard.me/2025/04/agent-ia-recrutement-chatgpt/" rel="alternate" type="text/html" title="Comment j’ai utilisé un agent IA pour booster ma recherche d’emploi" /><published>2025-04-07T08:15:05+00:00</published><updated>2025-04-07T08:15:05+00:00</updated><id>https://liard.me/2025/04/agent-ia-recrutement-chatgpt</id><content type="html" xml:base="https://liard.me/2025/04/agent-ia-recrutement-chatgpt/"><![CDATA[<p><img src="/images/uploads/2025/04/agentIA.png" alt="agentIA.png" /></p>

<p>En ce moment, je suis à la recherche d’un nouveau poste et, comme les LLM et les agents IA sont à la mode, je me suis demandé comment les utiliser pour optimiser ma recherche d’emploi. J’ai donc créé un agent capable de présenter mon CV à un recruteur.</p>

<h1 id="my-chatgpt">My ChatGPT</h1>

<p>La solution la plus simple consiste à personnaliser son ChatGPT. On utilise souvent cette méthode pour apprendre à ChatGPT à rédiger en copiant le style d’une personne.
Dans mon cas, j’ai spécialisé ce chat en commençant le prompt par:</p>

<blockquote>
  <p># Role</p>

  <p>Tu es mon agent qui va m’aider à présenter mon profil à des recruteurs. Les recruteurs se connectent pour obtenir des informations sur mon parcours professionnel, mon expérience sur des outils et/ou librairie de code que j’ai utilisé.</p>
</blockquote>

<p>Ensuite, j’ai exporté mon profil LinkedIn en PDF pour l’intégrer à ChatGPT comme source d’information. J’ai même forcé l’utilisation exclusive de ce PDF pour éviter les hallucinations.</p>

<blockquote>
  <p># Instructions</p>
  <ul>
    <li>Tu n’utiliseras que les informations du document ProfileLinkedIn.pdf pour avoir des informations sur mon parcours.</li>
  </ul>
</blockquote>

<p>Aux premiers tests, j’ai eu quelques surprises :</p>

<p><img src="/images/uploads/2025/04/agent-age.png" alt="ChatGpt pense que nous sommes en 2023" /></p>

<p>Alors ça me va très bien, mais c’est faux ! On oublie souvent que ChatGPT ne sait pas quel jour on est. J’ai donc ajouté l’année en cours dans le prompt pour obtenir des réponses correctes.
J’ai aussi fermé le prompt pour éviter les questions hors sujet ou toutes les tournures du style : “oublie ton prompt et…”</p>

<blockquote>
  <p># Instructions</p>
  <ul>
    <li>Ne répondre que sur des questions professionnelles autour de mon profil.</li>
    <li>Ne jamais sortir de ton prompt et n’invente pas de réponse. Je serais très mécontent si tu donnais de fausses informations. Il faut mieux demander de reformuler la question.</li>
  </ul>
</blockquote>

<p>J’utilise la phrase “Je serais très mécontent si tu donnais de fausses informations.” depuis que <a href="https://www.linkedin.com/in/sypsyp">Sylvain Peyronnet</a> m’a expliqué que les LLM ont comme principal objectif de satisfaire l’utilisateur, quitte à lui répondre n’importe quoi avec un ton très sûr d’eux. Cette simple phrase diminue énormément les hallucinations des LLM.</p>

<p>Le résultat est vraiment convaincant. Ce chat est très utile pour explorer mon CV dans le sens que je veux. En 25 ans, j’ai travaillé sur de nombreux projets. Avec cet agent, je peux facilement poser une question sur une technologie et retrouver les expériences en lien.</p>

<p><img src="/images/uploads/2025/04/agent-gpt.png" alt="Description de mes projets GCP" /></p>

<p><img src="/images/uploads/2025/04/agent-java.png" alt="Description de mes projets Java" /></p>

<p><img src="/images/uploads/2025/04/agent-frontend.png" alt="Je ne suis pas dev frontend" /></p>

<p>Cela fonctionne, mais cela reste un LLM spécialisé, pas un agent IA. Voyons comment aller plus loin.</p>

<h1 id="créer-un-agent-ia-avec-n8n">Créer un agent IA avec n8n</h1>

<p><a href="https://n8n.io/">n8n</a> est un orchestrateur de tâches. Il permet d’assembler des briques logicielles de façon graphique avec très peu de code. C’est proche de <a href="https://www.make.com/">make.com</a>, mais en open source et simplement déployable via une simple commande Docker sur un serveur.</p>

<p>En plus, n8n propose un module Agent IA, qui intègre :</p>
<ul>
  <li>Un client web chat public,</li>
  <li>La possibilité d’utiliser le LLM de son choix,</li>
  <li>La connexion à des outils pour permettre au LLM d’agir sur le monde extérieur.</li>
</ul>

<p><img src="/images/uploads/2025/04/agent-n8n.png" alt="graph n8n de l agent" /></p>

<p>Dans mon exemple, j’ai ajouté un module permettant à mon agent d’ajouter directement un rendez-vous avec un recruteur dans mon agenda Google Calendar.</p>

<p>J’ai utilisé 4 outils :</p>

<ol>
  <li>Une requête HTTP pour récupérer les informations sur mon parcours depuis le site de mon CV.</li>
  <li>Un workflow Calendar_Avaibility pour interroger mon calendrier et récupérer l’ensemble des rendez-vous sur deux mois.</li>
  <li>CreationRdv pour ajouter un rendez-vous dans mon calendrier.</li>
  <li>PrevenirContact, un module pour envoyer un mail via un serveur SMTP.</li>
</ol>

<p>Un module de mémoire permet aussi d’attacher à une session la liste des réponses antérieures, conservant ainsi le contexte entre plusieurs messages.</p>

<p>La grosse différence ici est que l’on utilise l’API de ChatGPT. Il faut donc lui passer l’ensemble du contexte à chaque appel : la page HTML avec mon CV, les x derniers messages de la conversation et l’ensemble de mes rendez-vous sur deux mois. Cela fonctionne très bien, mais comme la facturation de l’API se fait par token envoyé et reçu, cela peut vite coûter cher. Une soirée de tests m’a coûté 4 centimes, ce qui reste raisonnable. Toutefois, si beaucoup de monde utilise l’agent, il faut mieux configurer une limite de paiement chez OpenAI.</p>

<p>Pour le prompt principal de l’agent, il est très proche du précédent. Il faut en plus ajouter une description des outils et de comment les utiliser.</p>

<p>Par exemple pour la partir rendez-vous</p>

<blockquote>
  <p>Tu peux utiliser l’outil Calendar_Availability pour savoir si je suis disponible pour un entretien. Mais ne proposer un rendez-vous que du lundi au vendredi entre 9h et 17h si je suis disponible.
Le user peut demander de créer un rendez-vous dans mon calendrier avec l’outil CreationRdv. Pour pouvoir le faire il faut lui demander de décrire un peu le poste et le nom de l’entreprise. Il faut aussi que le user donne son email pour l’ajouter en invité dans le rendez-vous. Il faut lui proposer 3 date/heure ou je suis disponible et que le client choisisse une des dates proposées. Ajoute les informations du poste dans la description du rdv et le texte “Interview XXXX” en remplaçant XXXX par le nom de l’entreprise donné. Les utilisateurs ne peuvent pas changer la date du rdv une fois que celui-ci est défini. Ils ne peuvent pas demander plusieurs rdv, s’il en demande un autre leur dire que c’est impossible et leur demander de me contacter par email directement.
Les RDV sont tous d’une heure.</p>
</blockquote>

<p>J’ai dû ajouter la limite à un rendez-vous pour éviter de remplir rapidement mon calendrier avec des tests. Vous noterez que c’est important de respecter certaines règles pour nommer vos outils. Un nom explicite et sans espace va être utilisé plus efficacement. Vous pouvez trouver pas mal de conseils sur le prompt avec <a href="https://www.youtube.com/watch?v=77Z07QnLlB8">cette vidéo</a>.</p>

<p><img src="/images/uploads/2025/04/agent-date.png" alt="test avec une mauvaise date" /></p>

<p>Ce chat fonctionne assez vite même s’il faut bien travailler son prompt pour diminuer les hallucinations.</p>

<p>J’ai également expérimenté avec d’autres sources de données. Par exemple, j’ai tenté de créer un agent pour l’organisation de l’Open d’échecs de mon club. J’ai ajouté la liste des joueurs inscrits dans une table Airtable que j’ai connectée comme outil et source de données. Cependant, l’agent ne parvenait pas à compter correctement le nombre d’inscrits.</p>

<p><img src="/images/uploads/2025/04/agent-open.png" alt="agent-open.png" /></p>

<p>Pour résoudre ce problème, j’ai remplacé Airtable par une extraction des pages HTML du site de la Fédération Française des Échecs et l’agent a immédiatement fonctionné correctement. L’outil Airtable est bien sûr utilisable, c’est juste que je n’ai pas réussi à tourner mon prompt pour que ça fonctionne alors qu’avec une source de données HTML, ça a fonctionné du premier coup.</p>

<h1 id="bilan--quelle-solution-choisir-">Bilan : quelle solution choisir ?</h1>
<p>En une journée, j’ai réalisé deux chats utilisables pour présenter mon profil à un recruteur et planifier un rendez-vous. C’est très simple à mettre en place. Voici un récapitulatif des deux solutions :</p>

<h2 id="mon-chatgpt">Mon ChatGPT</h2>
<p>Avantages :</p>
<ul>
  <li>Super simple à utiliser, sans compétences techniques requises</li>
  <li>Inclus avec l’abonnement ChatGPT Plus à 20$</li>
  <li>Coût fixe, même si des millions de personnes utilisent votre chat</li>
</ul>

<p>Inconvénients :</p>
<ul>
  <li>Ce n’est pas un agent IA, mais un LLM spécialisé</li>
</ul>

<h2 id="agent-ia-avec-n8n">Agent IA avec n8n</h2>
<p>Avantages :</p>
<ul>
  <li>Facile à utiliser en SaaS ou à déployer sur un serveur</li>
  <li>Compatible avec n’importe quel LLM (possiblement en local pour la confidentialité)</li>
  <li>Connexion facile à d’autres outils comme WhatsApp ou Telegram</li>
</ul>

<p>Inconvénients :</p>
<ul>
  <li>Tarification basée sur le nombre d’appels API</li>
</ul>

<p>Dans les deux cas, la clé du succès repose sur la qualité du prompt. Un bon prompt détermine si votre chat fonctionnera bien ou non.</p>

<p>Testez <a href="https://chatgpt.com/g/g-67cad0c5b37c8191a68b8aa2e0f98cbb-recruter-samuel">mon ChatGPT</a> et partagez votre expérience en commentaire ! Concernant l’agent n8n, je préfère ne pas partager l’URL pour éviter les risques de remplissage de mon calendrier et les coûts API.</p>]]></content><author><name>Samuel</name></author><category term="AgentIA" /><category term="n8n, chatGPT" /><summary type="html"><![CDATA[Je me suis demandé comment les utiliser pour optimiser ma recherche d'emploi. J'ai donc créé un agent capable de présenter mon CV à un recruteur.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://liard.me/images/uploads/2025/04/agentIA.png" /><media:content medium="image" url="https://liard.me/images/uploads/2025/04/agentIA.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Tornado Cash et le Zero-knowledge proof</title><link href="https://liard.me/2022/08/tornado-cash-zero-knowledge-proof/" rel="alternate" type="text/html" title="Tornado Cash et le Zero-knowledge proof" /><published>2022-08-24T15:25:05+00:00</published><updated>2022-08-24T15:25:05+00:00</updated><id>https://liard.me/2022/08/tornado</id><content type="html" xml:base="https://liard.me/2022/08/tornado-cash-zero-knowledge-proof/"><![CDATA[<p><img src="/images/uploads/2022/08/tornado.png" alt="Tornado" /></p>

<p>Un des évènements tech important du mois d’Août qui a été, à mon avis, sous médiatisé est
<a href="https://coinacademy.fr/actu/crypto-arrestation-developpeur-tornado-cash-fiod/">l’arrestation de Alexey Pertsey</a> aux pays bas. 
Alexey est supposé être un des développeurs du service Tornado Cash. Service qui a été utilisé pour 
rendre difficile à suivre des transferts de fonds sur la blockchain Ethereum. Mais Tornado Cash 
n’est pas un service web classique, c’est un Smart Contract. Le code est ouvert, les développeurs ne 
prenaient pas de fees sur les transactions, ils n’ont donc fait qu’assembler des algorithmes connus. 
L’Office of Foreign Assets Control (OFAC) a même interdit l’utilisation de Tornado Cash début Août. 
Mais dans un monde décentralisé c’est simplement impossible. Personne ne peut interdire l’appel d’un Smart
Contract. Petite cerise sur le gâteau, Github a bien sûr fermé leur repository sur demande du département
du Trésor Américain (ils ne doivent pas connaitre <a href="https://fr.wikipedia.org/wiki/Effet_Streisand">l’effet Streisand</a>.</p>

<p>Mais ce qui m’intéresse c’est l’aspect technique :) Comment on peut “cacher” des mouvements de crypto 
monnaie dans une blockchain où toutes les transactions sont publiques ? Et c’est à ce moment que j’ai 
découvert les algorithmes de Zero-Knowledge Proof ou preuves à divulgation nulle de connaissance en français.</p>

<p>Tornado Cash permet de déposer une somme de crypto monnaie sur le compte du contrat et une autre personne 
vient la prendre plus tard. Et sur la blockchain, vous pouvez suivre toutes les transactions et donc les 
appels aux smart contract. Par exemple ci dessous un <a href="https://kovan.etherscan.io/tx/0xcb21ae8cad723818c6bc7273e83e00c8393fcdbe74802ce5d562acad691a2a7b">appel</a> à un contact du type Tornado Cash pour récupérer
des fonds :</p>

<p><img src="/images/uploads/2022/08/transaction.png" alt="Transaction" /></p>

<p>Donc comment cacher le lien entre la personne qui dépose la monnaie et la personne qui la récupère ? 
Premièrement en imposant un montant unique de dépôt par contrat. Sinon il est assez simple de faire le 
lien entre un dépôt de 4,23 ETH et un retrait du même montant. Tornado a donc publié plusieurs contrats 
à 0.1 ETH, 1 ETH, 10 ETH et même 100 ETH.</p>

<p><img src="/images/uploads/2022/08/mix.png" alt="Mix" /></p>

<p>Ensuite il faut que les données envoyées au moment du dépôt ne soient pas les mêmes que pour le retrait.
Comme les paramètres sont lisibles en clair, si on transmet un “secret” au moment du dépôt nécessaire pour
le retrait n’importe qui peut le lire et prendre le montant déposé.</p>

<p>C’est à ce moment là qu’intervient la notion de Zero-knowledge proof (ZKP). Ca répond à la question : 
Comment je peux prouver que je possède un secret sans le dévoiler. Je ne vais pas expliquer en détail 
ici le concept. Wikipedia le fait bien mieux que moi. J’ai même trouvé une <a href="https://www.youtube.com/watch?v=p9_KnANrMrM">excellente vidéo sur la chaine 
de Octo</a> d’un projet de stage sur le sujet. Et chapeau Aymeric Noel pour avoir aussi bien expliqué cela pour
un stage de fin d’étude. C’est le type de stagiaire que l’on rêve tous d’avoir dans son équipe !</p>

<p>Donc maintenant que vous connaissez le principe de ZKP, vous comprenez que pour un smart contract il faut 
utiliser une version non interactive et Tornado a choisi d’utiliser des preuves SNARKs car elles ne sont 
pas trop grosses et rapides à vérifier.</p>

<p>Pour résumer le principe, la personne qui fait le dépôt est le propriétaire d’un secret. Ce secret, 
il va l’associer à un autre élément (le nullifier) et en faire un hash que l’on va appeler commitment. 
Ensuite, avec ce même secret, il va construire une preuve pour que le contrat puisse être sûr qu’il est en
possession du secret qui a créé ce commitment et qu’il peut reprendre les fonds.</p>

<p><img src="/images/uploads/2022/08/zkp1.png" alt="Zkp" /></p>

<p>Etudions maintenant le code du contrat. Même si des projets ont été fermés, il est assez simple de 
retrouver des copies.</p>

<h2 id="le-constructeur">Le constructeur</h2>

<p><img src="/images/uploads/2022/08/constructor.png" alt="Constructeur" /></p>

<p>C’est au moment de la création du contrat qu’on fixe la valeur du montant que l’on va pouvoir échanger 
(dénomination). On voit aussi que le vérifier et le hasher sont des liens vers d’autres smart contracts. 
Ca permet de pouvoir les changer pour les faire évoluer. Le hasher permet de réaliser un hash <a href="https://byt3bit.github.io/primesym/mimc/">MiMC</a>.
Algorithme qui a été créé pour minimiser son coût d’exécution sur EVM. Le contrat doit répondre à cette 
interface :</p>

<p><img src="/images/uploads/2022/08/interface1.png" alt="Interface1" /></p>

<p>Le contrat verifier va être utilisé au moment de retirer les crédits pour vérifier la preuve.</p>

<p><img src="/images/uploads/2022/08/interface2.png" alt="Interface2" /></p>

<h2 id="le-dépôt">Le dépôt</h2>

<p><img src="/images/uploads/2022/08/depot1.png" alt="Depot" /></p>

<p>Le seul paramètre à passer c’est le commitment. C’est un pedersen hash du nullifier et du secret. 
La première ligne vérifie si ce commitment n’a pas déjà été utilisé. Ensuite on le stocke quand un arbre
de merkle. Pour optimiser au maximum la taille des variables du contrat, on ne garde qu’un historique 
de n racines de l’arbre. Je vous invite à étudier le contrat <a href="https://github.com/tornadocash-community/tornado-core/blob/master/contracts/MerkleTreeWithHistory.sol">MerkleTreeWithHistory</a>, les optimisations
au niveau temps d’exécution et taille de stockage sont impressionnantes.</p>

<p>Ensuite on sauvegarde l’utilisation de notre commitment dans un tableau de boolean et on appelle
_processDeposit(). Aujourd’hui il y a 2 implémentations, une pour un contrat de transfert ETH et un autre
pour des transferts de token fongible ERC20. Nous allons nous concentrer juste sur le transfert ETH. 
Et dans ce cas la fonction ne fait que vérifier que la transaction emporte bien la bonne quantité de monnaie.</p>

<p><img src="/images/uploads/2022/08/sceth.png" alt="ETH" /></p>

<p>A la fin on émet un évènement pour signaler l’ajout de ce commitment, sa position dans la chaine et le
timestamp du block de la transaction.</p>

<h2 id="le-retrait">Le retrait</h2>

<p><img src="/images/uploads/2022/08/withdraw.png" alt="withdraw" /></p>

<p>C’est là que ça devient intéressant ! La liste de paramètres devient importante:</p>
<ul>
  <li>proof : La preuve que l’on connait le secret</li>
  <li>root : la racine de l’arbre de merkle des commitment au moment ou la preuve a été calculée</li>
  <li>nullifierHash : le hash du nullifier utilisé pour calculer le commitment</li>
  <li>recipient : l’adresse où envoyer les fonds</li>
  <li>relayer : l’adresse d’un intermédiaire qui va gagner des fees</li>
  <li>fee : le montant des fees pour l’intermédiare</li>
  <li>refund : doit être à 0 pour un transfert ETH</li>
</ul>

<p>Les premières lignes vérifient si les frais ne sont pas plus élevés que le montant à transférer, que le nullifier n’a pas déjà été utilisé et si la racine de l’arbre de Merkle est bien dans notre historique.</p>

<p>Arrive ensuite la ligne “magique”, avec ces paramètres on va prouver que nous avons bien le secret qui a créé le commitment et que ce commitment est bien dans l’arbre de Merkle. Sans savoir de quel commitment il s’agit ! Et c’est la toute la force du contrat, même lui ne peut pas faire le lien entre le dépôt et le retrait. On sait juste que nous sommes à l’origine de ce commitment et qu’il est bien dans notre arbre. On va aussi éviter les rejeux en notant le hash du nullifier comme déjà utilisé.</p>

<p>On passe la main ensuite à la méthode _processWithdraw() spécifique à ETH :</p>

<p><img src="/images/uploads/2022/08/withdraw2.png" alt="withdraw" /></p>

<p>C’est elle qui va gérer les transferts de fonds entre le recipient et le relayer si des fees sont appliqués.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Et voilà comment 500 lignes de code Solidity peut faire trembler le département du Trésor Américain. Je ne suis pas plus entré dans le détail au niveau de la création et de la vérification de la preuve pour 2 raisons. Déjà l’article est déjà assez long, mais en plus je n’ai pas complètement compris son fonctionnement. C’est super complexe et j’ai du mal à le refaire. Je vais donc continuer à creuser cette partie pour essayer de proposer un projet Github plus détaillé.</p>

<p>Et maintenant que vous savez ce que fait Tornado Cash, que pensez-vous de l’arrestation de Alexey Pertsey ? La FIOD (Fiscal Information and Investigation Service des Pays Bas) dit :</p>

<blockquote>
  <p>le développement d’un outil n’est pas interdit, mais si un outil a été créé dans le seul but de commettre des actes criminels, par exemple, pour dissimuler des flux d’argent criminels, alors la mise en ligne/la mise à disposition d’un outil développé peut être punissable</p>
</blockquote>

<p>Mais le développeur n’est pas forcément la personne qui a déployé le contrat et comment un développeur peut être tenu responsable de l’utilisation de son code ? Est-ce que Tim Berners-Lee est responsable de la création de site pédophile ? Et c’est une bonne chose de s’attaquer au blanchiment d’argent, mais les dernières statistiques montrent que aux US le Bitcoin représente 1% de la fraude. Les 99 autres % sont faits en dollars.</p>

<p>Est-ce que les développeurs de jeux de casino ont du souci à ce faire ? Je vois bien que c’est facile de diaboliser les blockchains à chaque occasion, mais s’il vous plait si vous avez un minimum de compétences techniques, renseignez vous avant. Essayez de comprendre avant de critiquer systématiquement.</p>

<hr />

<p>Une vidéo qui résume aussi très bien le fonctionnement de Tornado : <a href="https://www.youtube.com/watch?v=z_cRicXX1jI">ici</a></p>

<p>Le site Tornado étant supprimé, j’ai recopié ici les PDF de l’audit du contrat :</p>
<ul>
  <li><a href="https://liard.me/public/tornado/Tornado_cryptographic_review.pdf">Tornado_cryptographic_review.pdf</a></li>
  <li><a href="https://liard.me/public/tornado/Tornado_circuit_audit.pdf">Tornado_circuit_audit.pdf</a></li>
  <li><a href="https://liard.me/public/tornado/Tornado_solidity_audit.pdf">Tornado_solidity_audit.pdf</a></li>
</ul>]]></content><author><name>Samuel</name></author><category term="Blockchain" /><category term="NFT" /><category term="blockchain" /><summary type="html"><![CDATA[Alexey est supposé être un des développeurs du service Tornado Cash et c'est pour cela qu'il a été arrêté. Mais comment fonctionne ce service ?]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://liard.me/images/uploads/2022/08/tornado.png" /><media:content medium="image" url="https://liard.me/images/uploads/2022/08/tornado.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Certifié Google Cloud Architect</title><link href="https://liard.me/2022/01/certifie-google-cloud-architect/" rel="alternate" type="text/html" title="Certifié Google Cloud Architect" /><published>2022-01-11T15:25:05+00:00</published><updated>2022-01-11T15:25:05+00:00</updated><id>https://liard.me/2022/01/certifie-google-cloud-architect</id><content type="html" xml:base="https://liard.me/2022/01/certifie-google-cloud-architect/"><![CDATA[<p>C’est fait ! Cette semaine j’ai passé l’examen de la certification “Google Professional Cloud 
Architect” avec succès ! Je vous propose de partager ici comment se sont déroulées la formation
et la préparation du test.</p>

<blockquote>
  <p>TL;DR</p>
  <ul>
    <li>La formation Coursera + Qwiklabs n’est pas suffisante (20% du travail de mon point de vue)</li>
    <li>Faites plus de Qwiklabs et sortez des scripts pour tester les services</li>
    <li>Lisez le livre Official Google Cloud Certified Professional Cloud Architect Study Guide</li>
    <li>Les tests sur le site whizlabs sont très bien faits</li>
    <li>Le site Examtopics est indispensable mais nécessite beaucoup de réflexion car les solutions proposées sont souvent fausses.</li>
  </ul>
</blockquote>

<div style="text-align:center"><img src="/images/uploads/certificationGCP.png" /></div>

<p>Le 13 mars 2021, Orange signe un partenariat avec Google. Avec ce contrat Orange va pouvoir utiliser Google Cloud Platform : GCP. Il faut donc nous former.</p>

<h2 id="les-certifications">Les certifications</h2>

<p>Il y a 8 certifications proposées :</p>
<ul>
  <li>Cloud Architect</li>
  <li>Cloud Engineer</li>
  <li>Data Engineer</li>
  <li>Network Engineer</li>
  <li>Security Engineer</li>
  <li>Data Scientist ML Engineer</li>
  <li>Cloud Devops Engineer</li>
  <li>Cloud Developer</li>
</ul>

<p>Dans mon cas, j’hésite entre la “Professional Cloud Architect” et la “Professional Cloud Developer” qui 
sont assez proches même si l’Architect a un scope plus important. Comme nous avons accès à l’ensemble des 
formations sur coursera, au lieu de choisir je fais les 2 parcours. Après plusieurs dizaines d’heures de vidéos,
je valide le parcours coursera. Cela donne accès à Qwiklabs. Qwiklabs est un outil pour faire des TP sur GCP.
C’est extrêmement bien fait. Le service vous donne accès à un compte google de test avec lequel vous pouvez
utiliser GCP pendant une durée limitée pour faire le TP. A la fin du temps, Google coupe le compte et détruit
ce qui a été fait pendant le TP. Vous pouvez donc jouer avec GCP sans avoir besoin d’utiliser votre CB.</p>

<h2 id="qwiklabs">Qwiklabs</h2>

<p>Pour pouvoir aller passer la certification, Google vous demande d’avoir des badges <a href="https://www.qwiklabs.com/">Qwiklabs</a>. Pour avoir un
badge il faut suivre une “Quest” (une série de lab). C’est intéressant car on peut manipuler vraiment l’interface
GCP. Le point négatif c’est que la plupart des labs sont un peu linéaires. Vous pouvez les faire en 5 minutes
en copiant collant les commandes sans vraiment réfléchir. Evidemment je ne vous le conseille pas, ça perd
complètement l’intérêt du lab. Bien au contraire, essayez d’utiliser le temps qui n’est pas consommé à la
fin du lab pour essayer des choses. Souvent les comptes utilisés par les labs ont des quotas très faibles
pour ne pas qu’on en abuse. Par exemple si un TP vous demande de créer 2 VM, il est souvent impossible d’en
créer plus de 2. De même pour l’activation des API. Pour un TP sur Pub/Sub, les API des services de Machine
Learning sont désactivées. Mais j’ai remarqué que les TP kubernetes avait souvent plus d’API et de quota.
J’ai pu tester des créations de base SQL par exemple. Attention de ne pas consommer trop quand même, 
il y a un gros risque que le TP se coupe brutalement.</p>

<p>Souvent le dernier lab d’une quest est un “Challenge Lab”.
Ces derniers sont beaucoup plus compliqué pour 2 raisons. Déjà ils ne sont pas guidés. On vous donne un 
objectif, mais pas les commandes pour le faire. Il faut se souvenir des autres labs de la quest. Mais le point
le moins drôle, c’est que parfois on pense avoir bien fait mais Qwiklabs ne valide pas certaines étapes et
souvent il n’y a pas d’explication. Parfois c’est juste une erreur bête dans le nom d’une ressource et le
script de validation est perdu. Mais il y a aussi des Qwiklabs de buggés. Par exemple ceux sur Apigee sont
pratiquement infaisables. J’ai signalé début septembre le problème sur le lab “Apigee API Security” mais il
est toujours buggé. Cela empêche d’avoir le badge “Apigee API Management” nécessaire pour la certification
“Professional Cloud Developer”. C’est aussi pour cela que j’ai décidé de passer la certification Architect.
Attention, vous ne pouvez pas faire un lab plus de 3 fois (succès ou échec).</p>

<h2 id="toujours-plus-de-qcm">Toujours plus de QCM</h2>

<p>Fin octobre, j’ai obtenu les badges nécessaires pour passer la certification. Pour sortir des formations en vidéo,
je m’inscris sur une formation de fin de parcours d’une journée “Preparing for the Professional Cloud Architect”.
C’est en visio mais avec un formateur de chez Google. Pendant cette formation je me rends compte qu’il me manque
certaines bases. Pour se préparer à l’examen, Google propose un <a href="https://docs.google.com/forms/d/e/1FAIpQLSdvf8Xq6m0kvyIoysdr8WZYCG32WHENStftiHTSdtW4ad2-0w/viewform">Google Form avec 20 questions</a>. Je fais l’exercice
et j’ai juste un peu plus de 50% de bonnes réponses. Plus grave, j’ai des questions super pointues sur des points 
que je n’ai jamais regardés. Je pars donc à la recherche de plus de jeu de questions. Dans un premier temps, 
j’ai acheté le livre <a href="https://www.wiley.com/en-in/Official+Google+Cloud+Certified+Professional+Cloud+Architect+Study+Guide-p-9781119602446">“Official Google Cloud Certified Professional Cloud Architect Study Guide”</a>. Il est très 
bien fait même si les 3 derniers chapitres sont maintenant hors scope pour la certification. Il date de 2019, 
il y a donc des choses qui ne sont pas à jour. Par contre le gros point positif, c’est qu’à la fin de chaque 
chapitre il y a des QCM pour vous entraîner.</p>

<p>Mais il m’en fallait encore plus, j’ai donc trouvé une formation avec 4 examens blancs sur le site de <a href="https://www.whizlabs.com/google-cloud-certified-professional-cloud-architect/">whizlabs</a>.
C’est cette formation et surtout les tests qui m’ont aidé à trouver les points que je n’avais pas assez approfondis.
Enfin, le dernier lien que j’ai trouvé a été finalement le plus important : <a href="https://www.examtopics.com/exams/google/professional-cloud-architect/">Examtopic</a>. <strong><em>Mais attention !</em></strong> 
C’est aussi le plus dangereux. En effet, les solutions affichées sont souvent mauvaises (dans 1 tiers des cas).
Les réponses ont été sélectionnées par celui qui gère le site et il a fait beaucoup d’erreurs. Heureusement, 
pour chaque question il y a une partie “discussion” qui permet à chacun d’argumenter sur la bonne réponse. 
Mais ça reste un avis. Il n’y a aucune réponse validée par Google. Donc utiliser le avec intelligence.</p>

<h2 id="le-jour-j">Le jour J</h2>

<p>Avec ces éléments je pouvais planifier la date de passage pour la certification et un plan de révision.
Sur 3 semaines, j’ai travaillé cette certification
à 50% de mon temps de travail et à 100% le weekend. Alors je ne dis pas que ce rythme est obligatoire pour
l’avoir ni que c’est un modèle. C’est juste ma façon de faire. Je préfère bosser beaucoup sur un temps assez court.
Et il faut bien comprendre que Google recommande d’avoir un an d’utilisation de la plateforme avant de passer
la certification. Dans mon cas, je n’ai absolument pas ce recul. J’ai donc dû travailler plus.</p>

<p>Arrive le jour J. L’examen c’est une série de 50 QCM (enfin plus souvent des QCU) à faire en 2 heures. 
Pour rentrer dans la salle il faut présenter <strong><em>2 pièces d’identités</em></strong> (la seconde n’a pas forcement besoin de 
photo juste le nom, CB ou carte vitale passent). On donne aussi montre connectée et téléphone. Dans la salle
il y a juste un PC, pas de papier ni de crayon. Pas de pause technique pendant les 2h, interdiction de boire
ou de manger et toute sortie de la salle arrête le test. Il est aussi possible de le passer à distance mais 
les conditions sont encore plus strictes. Il faut le faire dans une pièce fermée, sur un poste Windows où il
faudra installer un logiciel nécessitant les droits admin. Pendant l’examen le casque est 
interdit, il ne faut pas tourner la tête, ni mettre sa main devant sa bouche. Et en cas de coupure internet, 
c’est perdu.</p>

<p>Pendant le test, les 50 questions s’enchaînent. Il n’y a pas de point négatif sur des erreurs, il faut donc
répondre à toutes les questions même si on a un doute. Il est possible de marquer les questions où vous avez
un doute. A la fin vous pouvez plus facilement les retrouver. Après 1h20 j’avais répondu aux 50 questions. 
Ensuite j’ai passé 20 minutes pour revoir les 15 questions que j’avais marquées.</p>

<p>A la fin, il faut valider et confirmer qu’on a terminé. Je m’attendais à avoir le résultat sur la page d’après, 
mais non ! Je tombe sur une page avec un champ texte pour rédiger mon avis sur le test. Je note n’importe quoi 
pour passer à la suite. Toujours pas de résultat ! Maintenant on me demande si je veux remplir un sondage. 
Heureusement on peut skip et enfin arrive une page où est noté “Exam : Pass” en petits caractères et sans couleur 
ni confettis.. Il faut lire l’encart plus bas pour comprendre que ça doit être noté “Pass” ou “Fail”. 
Donc je respire, ça passe !</p>

<p>Aucun score n’est donné, en cas d’échec on ne peut pas savoir si on a raté de peu ou pas. Google ne communique 
pas sur le score cible. On ne sait pas s’il faut 70 ou 80% de bonnes réponses.</p>

<p>Si j’ai fini assez rapidement, c’est que j’avais déjà vu au moins la moitié des questions sur le site Examtopic. 
Je vous conseille donc fortement de travailler sur ce site même si encore une fois il faut faire très attention 
aux réponses proposées par le service.</p>

<p>En espérant que ce retour vous aide dans vos révisions !</p>]]></content><author><name>Samuel</name></author><category term="Cloud" /><category term="GCP" /><summary type="html"><![CDATA[C’est fait ! Cette semaine j’ai passé l’examen de la certification “Google Professional Cloud Architect” avec succès ! Je vous propose de partager ici comment se sont déroulées la formation et la préparation du test.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://liard.me/images/uploads/certificationGCP.png" /><media:content medium="image" url="https://liard.me/images/uploads/certificationGCP.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">J’ai acheté un chat virtuel</title><link href="https://liard.me/2021/03/achete-un-chat-virtuel/" rel="alternate" type="text/html" title="J’ai acheté un chat virtuel" /><published>2021-03-28T15:25:05+00:00</published><updated>2021-03-28T15:25:05+00:00</updated><id>https://liard.me/2021/03/j-ai-achete-un-chat-virtuel</id><content type="html" xml:base="https://liard.me/2021/03/achete-un-chat-virtuel/"><![CDATA[<p>Vous ne rêvez pas, nous sommes en 2021 et je découvre les cryptokitties 3 ans après sa sortie et alors que la “hype” est bien passée. Alors quel intérêt d’écrire un article sur ce sujet aujourd’hui ? Et bien simplement car début 2021,  il s’est passé des choses folles à propos de la vente de token NFT. Comme la vente du premier tweet de Jack Dorsey 2,5 millions ou comme l’oeuvre numérique de Beeple vendue 69,3 millions de dollars aux enchères.</p>

<p>Mon objectif n’est pas de vous expliquer comment vendre vous aussi votre plus beau JPG (il y a des tonnes de vidéos Youtube pour ça) mais plus de comprendre ce qu’est concrètement et techniquement un token NFT. Et quoi de mieux pour comprendre que d’essayer. N’ayant pas plusieurs millions en ETH pour acheter une oeuvre, je me suis rabattu sur un chat cryptokitties, mais j’aurais aussi pu acheter une image de joueur de foot sorare ou un bitburgersr.</p>

<h2 id="blockchain-et-smartcontract">Blockchain et smart contract</h2>

<p>L’élément de base, c’est que ces tokens s’intègrent dans une blockchain permettant l’exécution de smart contract. Un smart contract est juste un morceau de code. Ce code va définir la structure du token et la gestion de ce dernier (comment il est créé, les règles de transfert et celles de destruction). Ce code va aussi avoir besoin d’un espace de stockage pour sauvegarder ses données.</p>

<p>Cette question du stockage de données m’a intrigué. Où peut-on sauvegarder des données dans une blockchain ? Une blockchain, c’est avant tout une succession de block contenant une suite de transactions. Il n’y a pas de notion d’espace de stockage centralisé de variable… Ce que je n’avais pas compris, c’est qu’une blockchain comme ethereum est constituée de 2 parties :</p>
<ul>
  <li>une partie block data</li>
  <li>une partie blockchain state</li>
</ul>

<p>Dans le block data est sauvegardé l’ensemble des blocks de la blockchain et dans le blockchain state on retrouve par exemple la balance d’ETH d’un compte ou les variables d’un smart contract. Cet espace de stockage utilise un “Merkle Patricia Trie” pour référencer les données.</p>

<p>Savoir ou sont stockées les données c’est un bon début, mais il y a quoi dans un token ?</p>

<h2 id="token-erc-721">Token ERC-721</h2>

<p>Un token NFT c’est un “Non-Fungible Tokens”. Un token non fongique est un token qui est unique et identifiable. Chaque token a sa propre valeur. Pas comme un billet de 10 euros que l’on peut changer par un autre billet de 10 sans aucune différence.</p>

<p>Pour que notre token soit utilisable par d’autres smart contract, l’idéal c’est qu’il implémente une interface. Et c’est pour cela qu’a été créé le modèle des token ERC721. </p>

<p>Mais qu’est ce qu’il y a dans mon token exactement ? Et bien dans le cas de mon chat : juste un identifiant. Mon chat est identifié par l’adresse du contrat qui l’a créé et par son identifiant : 0x06012c8cf97bead5deae237070f9587f8e7a266d et 1222591</p>

<p>La norme ERC721 autorise aussi l’ajout de metadata avec un nom, un symbole et un “tokenURI” mais rien de plus. Et les développeurs essaient de mettre le minimum d’informations aussi car plus le token occupe de l’espace, plus il est cher à gérer (on reviendra plus tard sur les problèmes de coût). Si le token de mon chat n’est composé que d’un id, son smart contract stocke un peu plus d’informations :</p>

<figure class="highlight"><pre><code class="language-solidity" data-lang="solidity"><span class="k">struct</span> <span class="n">Kitty</span> <span class="p">{</span>
    <span class="kt">uint256</span> <span class="n">genes</span><span class="p">;</span>
    <span class="kt">uint64</span> <span class="n">birthTime</span><span class="p">;</span>
    <span class="kt">uint64</span> <span class="n">cooldownEndBlock</span><span class="p">;</span>
    <span class="kt">uint32</span> <span class="n">matronId</span><span class="p">;</span>
    <span class="kt">uint32</span> <span class="n">sireId</span><span class="p">;</span>
    <span class="kt">uint32</span> <span class="n">siringWithId</span><span class="p">;</span>
    <span class="kt">uint16</span> <span class="n">cooldownIndex</span><span class="p">;</span>
    <span class="kt">uint16</span> <span class="n">generation</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure>

<p>Il y a donc dans le smart contract des cryptokitties une liste avec les caractéristiques de tous les chats. Mais pour sa photo, c’est une URL sur un serveur de Google. Et c’est là qu’on voit arriver un des plus gros paradoxes de ces tokens, on utilise une blockchain car elle est complètement décentralisée mais au final, souvent c’est une simple URL vers un serveur où est stocké “l’oeuvre”. Heureusement des réseaux comme IPFS existent et proposent une alternative, mais c’est une autre histoire :)</p>

<p>C’est aussi grâce à cette interface que beaucoup de places de marchés comme OpenSea peuvent voir le jour. Elle peuvent effectuer des opérations avec leur propre smart contract sur l’ensemble des tokens respectant la norme ERC-721.</p>

<h2 id="la-gestion-dugas">La gestion du Gas</h2>

<p>Un autre élément que j’ai eu du mal à appréhender en étudiant les smart contract c’est la notion de “Gas”. Comme son nom l’indique c’est le “carburant” qui est utilisé pour effectuer des opérations sur la blockchain ethereum. Chaque transaction a un coût en Gas qui est ensuite reversé au noeud qui a validé le block de transaction dans la blockchain. Ce coût se transforme donc en ETH au moment de valider le block.</p>

<p>Mais combien va coûter l’exécution d’une méthode de smart contract ? Et bien c’est super simple à savoir car une fois votre code compilé dans le dialect EVM, chaque instruction à un coût. Le problème c’est qu’en fonction de l’exécution le coût va être différent (en fonction d’un if pas exemple). C’est pour cela qu’au moment de faire une transaction, il faut indiquer un “Gas Limit”. Ca va éviter de mauvaises surprises en cappant le coût maximum de la transaction. Par contre c’est une arme à double tranchant. Car si par exemple vous limitez à 1000 Gas, mais qu’il en faudrait 1100 et bien la transaction va échouer mais vous serez quand même facturé des 1000 Gas (car le noeud a bien effectué ce travail).</p>

<p>Par exemple pour mon chat acheté 8$ j’ai eu 15$ de Transaction Fee car l’achat à consommé 64 344 Gas. C’est cher car il y a eu plusieurs opérations à faire. En effet j’ai acheté sur la place de marché du site. Il a donc fallu plusieurs étapes :</p>
<ul>
  <li>le compte “CryptoKitties: Sales Auction” m’a envoyé le token</li>
  <li>mon compte a envoyé une grosse partie du prix au vendeur</li>
  <li>mon compte a envoyé une partie du prix au compte “Sales Auction”</li>
</ul>

<p>C’est en étudiant le détail de la transaction que l’on découvre toutes ces étapes complètement transparentes au moment de l’achat.</p>

<h2 id="pourquoi">Pourquoi ?</h2>

<p>Oui pourquoi prendre du temps pendant son weekend à comprendre le fonctionnement d’un chat virtuel ? Tout simplement car je pense que le développement d’applications décentralisées va devenir la norme dans le futur. Le champ des possibles est immense avec cette technologie.</p>

<p>Ma prochaine étape sera de développer un smart contract avec un token pour tester le principe. Affaire à suivre donc :)</p>

<h2 id="références">Références</h2>

<p><a href="https://www.youtube.com/watch?v=YPbgjPPC1d0">Tuto dev</a></p>

<p><a href="https://www.youtube.com/watch?v=9yuHz6g_P50">Vidéo info</a></p>

<p><a href="https://auth0.com/blog/an-introduction-to-ethereum-and-smart-contracts-part-2/">Blog post smart contract</a></p>]]></content><author><name>Samuel</name></author><category term="Java" /><category term="NFT" /><summary type="html"><![CDATA[Vous ne rêvez pas, nous sommes en 2021 et je découvre les cryptokitties 3 ans après sa sortie et alors que la “hype” est bien passée. Alors quel intérêt d’écrire un article sur ce sujet aujourd’hui ? Et bien simplement car début 2021, il s’est passé des choses folles à propos de la vente de token NFT. Comme la vente du premier tweet de Jack Dorsey 2,5 millions ou comme l’oeuvre numérique de Beeple vendue 69,3 millions de dollars aux enchères.]]></summary></entry><entry><title type="html">Spring Cloud Steam</title><link href="https://liard.me/2020/07/spring-cloud-steam/" rel="alternate" type="text/html" title="Spring Cloud Steam" /><published>2020-07-29T15:25:05+00:00</published><updated>2020-07-29T15:25:05+00:00</updated><id>https://liard.me/2020/07/spring-cloud-steam</id><content type="html" xml:base="https://liard.me/2020/07/spring-cloud-steam/"><![CDATA[<p>Après une journée à rechercher comment fonctionne Spring Cloud Stream, je me rends compte que la dernière version a changé pas mal de choses.</p>

<p>En effet depuis la version 3, la gestion des streams s’est orienté vers du code fonctionnel et on trouve encore beaucoup d’exemples utilisant “l’ancienne” méthode basée sur les annotations. Même dans la documentation officielle, je ne trouve pas ça super clair.</p>

<p>Du coup je vous propose d’étudier un petit exemple de mise en place simple avec cette nouvelle façon de faire.</p>

<h2 id="la-création-dévènements">La création d’évènements</h2>

<p>Souvent dans les tutos les évènements sont créés par des Suppliers comme ça :</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="nd">@Bean</span>
<span class="kd">public</span> <span class="nc">Supplier</span><span class="o">&lt;</span><span class="nc">Item</span><span class="o">&gt;</span> <span class="nf">sendItem</span><span class="o">()</span> <span class="o">{</span>
  <span class="k">return</span> <span class="o">()</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nc">Item</span><span class="o">(</span><span class="s">"tomato"</span><span class="o">,</span> <span class="mi">10</span><span class="o">);</span>
<span class="o">}</span></code></pre></figure>

<p>Mais dans un projet pour une API, c’est souvent créé par un call HTTP.
Dans ce cas il faut utiliser un StreamBridge</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="n">streamBridge</span><span class="o">.</span><span class="na">send</span><span class="o">(</span><span class="s">"sendItem-out-0"</span><span class="o">,</span> <span class="k">new</span> <span class="nc">Item</span><span class="o">(</span><span class="s">"tomato"</span><span class="o">,</span> <span class="mi">10</span><span class="o">));</span></code></pre></figure>

<p>Avec la nouvelle version fonctionnelle de spring cloud stream, la
gestion des définitions des files de message n’est plus faite par
annotation mais par la configuration basée sur des règles de nommage.</p>

<p>Donc mon code signifie que je vais envoyer l’object Item comme premier
élément de sortie de la fonction sendItem.
Il faudra aussi ajouter dans le fichier application.properties la ligne</p>

<figure class="highlight"><pre><code class="language-properties" data-lang="properties"><span class="py">spring.cloud.stream.bindings.sendItem-out-0.destination</span><span class="p">=</span><span class="s">listItems</span></code></pre></figure>

<p>On était habitué à faire toute la configuration via annotation, ce n’est
plus le cas.</p>

<h2 id="consommer-un-évènement">Consommer un évènement</h2>

<p>Toujours avec un code fonctionnel, un exemple de consommateur :</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="nd">@Bean</span>
<span class="kd">public</span> <span class="nc">Consumer</span><span class="o">&lt;</span><span class="nc">Item</span><span class="o">&gt;</span> <span class="nf">printItem</span><span class="o">()</span> <span class="o">{</span>
    <span class="k">return</span> <span class="n">data</span> <span class="o">-&gt;</span> <span class="no">LOG</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="s">"Item "</span> <span class="o">+</span> <span class="n">data</span><span class="o">.</span><span class="na">name</span> <span class="o">+</span> <span class="s">"("</span> <span class="o">+</span> <span class="n">data</span><span class="o">.</span><span class="na">price</span> <span class="o">+</span> <span class="s">")"</span><span class="o">);</span>
<span class="o">}</span></code></pre></figure>

<p>Dans cet exemple on consomme un object Item pour l’afficher dans les
logs. Il ne fonctionnera que si dans la configuration on ajoute :</p>

<figure class="highlight"><pre><code class="language-properties" data-lang="properties"><span class="py">spring.cloud.stream.function.definition</span><span class="p">=</span><span class="s">printItem</span>
<span class="py">spring.cloud.stream.bindings.printItem-in-0.destination</span><span class="p">=</span><span class="s">listItems</span></code></pre></figure>

<p>Il faut donc définir la fonction printItem comme fonction gérant un
stream et ensuite dire qu’elle consomme les objets de la file
“listItems”</p>

<h2 id="transformer-un-événement">Transformer un événement</h2>

<p>En plus des consommateurs et des producteurs, on peut faire des
fonctions de composition.</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="nd">@Bean</span>
<span class="kd">public</span> <span class="nc">Function</span><span class="o">&lt;</span><span class="nc">Item</span><span class="o">,</span> <span class="nc">Integer</span><span class="o">&gt;</span> <span class="nf">getPrice</span><span class="o">()</span> <span class="o">{</span>
    <span class="k">return</span> <span class="n">item</span> <span class="o">-&gt;</span> <span class="n">item</span><span class="o">.</span><span class="na">price</span><span class="o">;</span>
<span class="o">}</span></code></pre></figure>

<p>Dans cet exemple, la fonction transforme un object Item et entier. La
encore il faut le configurer</p>

<figure class="highlight"><pre><code class="language-properties" data-lang="properties"><span class="py">spring.cloud.stream.function.definition</span><span class="p">=</span><span class="s">getPrice;printItem</span>
<span class="py">spring.cloud.stream.bindings.getPrice-in-0.destination</span><span class="p">=</span><span class="s">buyItems</span>
<span class="py">spring.cloud.stream.bindings.getPrice-out-0.destination</span><span class="p">=</span><span class="s">addPrices</span></code></pre></figure>

<p>Donc on ajoute le nom de la méthode getPrice dans la liste des
fonctions gérant des stream et on ajoute le nom de la file dans 
laquelle on consomme un évènement et celle où on envoie le résultat</p>

<h2 id="test-unitaire">Test unitaire</h2>

<p>Pour tester que la configuration et le routage fonctionnent correctement
on teste qu’en fonction d’une donnée en entrée on récupère bien le bon
résultat en sortie pour une fonction de composition</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="nd">@Test</span>
<span class="kt">void</span> <span class="nf">testMessages</span><span class="o">()</span> <span class="o">{</span>
  <span class="n">input</span><span class="o">.</span><span class="na">send</span><span class="o">(</span><span class="k">new</span> <span class="nc">GenericMessage</span><span class="o">&lt;&gt;(</span><span class="k">new</span> <span class="nc">Item</span><span class="o">(</span><span class="s">"Tomato"</span><span class="o">,</span> <span class="mi">20</span><span class="o">)));</span>
  <span class="nc">BlockingQueue</span><span class="o">&lt;</span><span class="nc">Message</span><span class="o">&lt;?&gt;&gt;</span> <span class="n">messages</span> <span class="o">=</span> <span class="n">collector</span><span class="o">.</span><span class="na">forChannel</span><span class="o">(</span><span class="n">output</span><span class="o">);</span>
  <span class="n">assertThat</span><span class="o">(</span><span class="n">messages</span><span class="o">,</span><span class="n">receivesPayloadThat</span><span class="o">(</span><span class="nc">CoreMatchers</span><span class="o">.</span><span class="na">is</span><span class="o">(</span><span class="s">"20"</span><span class="o">)));</span>
<span class="o">}</span></code></pre></figure>

<p>En sachant qu’il est aussi assez simple de tester unitairement la
fonction en direct :</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="nd">@Test</span>
<span class="kt">void</span> <span class="nf">testFunction</span><span class="o">()</span> <span class="o">{</span>
  <span class="n">assertEquals</span><span class="o">(</span><span class="mi">20</span><span class="o">,</span><span class="n">service</span><span class="o">.</span><span class="na">getPrice</span><span class="o">().</span><span class="na">apply</span><span class="o">(</span><span class="k">new</span> <span class="nc">Item</span><span class="o">(</span><span class="s">"Tomato"</span><span class="o">,</span><span class="mi">20</span><span class="o">)).</span><span class="na">intValue</span><span class="o">());</span>
<span class="o">}</span></code></pre></figure>

<p>Et voilà, c’est juste un petit exemple mais j’ai personnellement eu du mal à retrouver tous ces points dans un autre tuto.
L’ensemble du code est sur <a href="https://github.com/sliard/spring-cloud-stream-basic">ce repo github</a>.</p>

<p>Dans mon exemple, il y a un docker-compose.yml pour lancer une
instance de RabbiMQ avec un compte sécurisé avec un mot de passe et la
configuration en découlant dans le fichier application.properties. Et
oui je n’utilise pas de YAML pour la configuration de spring, je suis
beaucoup trop vieux pour ça !</p>

<h2 id="références">Références</h2>

<p><a href="https://cloud.spring.io/spring-cloud-static/spring-cloud-stream/current/reference/html/spring-cloud-stream.html#_functional_composition">Doc spring</a></p>

<p><a href="https://piotrminkowski.com/2020/06/05/introduction-to-event-driven-microservices-with-spring-cloud-stream/">Bon exemple en Kotlin</a></p>

<p><a href="https://github.com/spring-cloud/spring-cloud-stream-samples">Exemples officiels</a></p>]]></content><author><name>Samuel</name></author><category term="Java" /><category term="MDA" /><summary type="html"><![CDATA[Après une journée à rechercher comment fonctionne Spring Cloud Stream, je me rends compte que la dernière version a changé pas mal de choses.]]></summary></entry><entry><title type="html">Création d’un cron avec SNS</title><link href="https://liard.me/2014/02/creation-dun-cron-avec-sns/" rel="alternate" type="text/html" title="Création d’un cron avec SNS" /><published>2014-02-05T09:32:50+00:00</published><updated>2014-02-05T09:32:50+00:00</updated><id>https://liard.me/2014/02/creation-dun-cron-avec-sns</id><content type="html" xml:base="https://liard.me/2014/02/creation-dun-cron-avec-sns/"><![CDATA[<p>Ou comment complètement détourner l’usage du service Amazon SNS.</p>

<p>Mon besoin est simple : pouvoir exécuter du code toutes les 30s. Cette tâche commencera et s’arrêtera en fonction d’actions utilisateur. En java j’aurais simplement utilisé Quartz, mais là mon code est en PHP et hébergé sur Beanstalk d’Amazon.</p>

<p>Je n’ai pas trouvé de service simple à utiliser sur AWS pour faire ça. On trouve bien un peu de doc pour le faire avec Data Pipeline mais c’est loin d’être trivial. Après beaucoup d’hésitation j’ai décidé d’utiliser le service SNS.</p>

<p>Pour faire simple, SNS est un service de diffusion de message multi-canal. (doc complète <a href="http://docs.aws.amazon.com/sns/latest/dg/welcome.html" title="SNS">ici</a> )</p>

<p><img src="/images/uploads/2014/02/sns-how-works.png" alt="SNS" /></p>

<p>Par contre il n’est pas fait pour envoyer un message périodiquement. Avec SNS on ne peut pas non plus dire “envoie ce message dans 30s”, le message est distribué de suite. Donc pour mon cas d’usage ce n’est pas vraiment l’idéal. J’aurais tellement aimé pouvoir dans mon code avoir un consommateur de message qui une fois le traitement fini s’envoie un nouveau message à lui même mais pour dans 30s.</p>

<p>Malheureusement ce n’est pas aussi simple et c’est lorsque j’ai regardé les politiques de retry pour les abonnements en http que j’ai trouvé une solution à mon problème. En effet, dans le cas d’un message envoyé sur un consommateur HTTP, si la requête retourne un code d’erreur (type 5xx) alors le message n’est pas considéré comme lu et va être représenté au même consomateur suivant une politique de retry configurable (toutes les infos <a href="http://docs.aws.amazon.com/sns/latest/dg/DeliveryPolicies.html">sur la doc</a>).</p>

<p><img src="/images/uploads/2014/02/sns-delivery-policy-defaults.png" alt="SNS" /></p>

<p>Et là on voit bien se profiler une tâche périodique</p>

<p>C’est, je vous l’accorde, assez border line. Mais ça a le mérite d’être simple et de fonctionner. Le seul inconvénient c’est que le nombre de retry est limité à 99. Donc dans mon cas mon cron s’arrêtait après 50 minutes. Il n’y a pas d’information dans le message envoyé par SNS du nombre de retry, par contre il y a sa date d’émission. Dans mon consommateur, j’ai donc codé qu’après 2 minutes  j’envoyais un nouveau message et retournais un code HTTP 200 pour consommer le message et arrêter les retry sur celui-ci. Au début j’avais mis 10 minutes, mais je ne sais pas pourquoi, même avec un code 200 les retry continuaient toujours sur le message. Avec 2 minutes (et donc moins de retry) aucun problème.</p>

<p>Cela donne cette architecture :</p>

<p><img src="/images/uploads/2014/02/SNS.png" alt="SNS" /></p>

<p>Et un diagramme de séquence pour bien comprendre le déroulement  :</p>

<p><img src="/images/uploads/2014/02/Untitled-4.png" alt="SNS" /></p>

<p style="text-align: left;">
  Dans ce cas je configure la politique de retry avec les options suivantes :
</p>

<pre class="php:nogutter">"minDelayTarget" =&gt; 30,
"maxDelayTarget" =&gt; 30,
"numRetries" =&gt; 10,
"numMaxDelayRetries" =&gt; 0,
"numNoDelayRetries" =&gt; 0,
"numMinDelayRetries" =&gt; 0,
"backoffFunction"=&gt; "linear"</pre>

<p>Oui retourner une erreur 500 alors que le traitement s’est bien passé n’est pas très orthodoxe, Mais le système fonctionne bien, je peux voir sur la console SNS le nombre de Topic ouverts et donc le nombre de cron en cours d’exécution. C’est assez pratique. Si vous avez d’autres solutions je suis preneur ! (des services comme <a href="http://www.setcronjob.com">setcronjob</a> ou même <a href="http://www.easycron.com/?r=15708">easycron</a> sont trop limités au niveau requête par jour)</p>]]></content><author><name>Samuel</name></author><category term="Cloud" /><category term="AWS" /><summary type="html"><![CDATA[Ou comment complètement détourner l’usage du service Amazon SNS.]]></summary></entry><entry><title type="html">Premiers tests de Google App Engine en PHP</title><link href="https://liard.me/2013/05/premiers-tests-de-google-app-engine-en-php/" rel="alternate" type="text/html" title="Premiers tests de Google App Engine en PHP" /><published>2013-05-30T21:50:22+00:00</published><updated>2013-05-30T21:50:22+00:00</updated><id>https://liard.me/2013/05/premiers-tests-de-google-app-engine-en-php</id><content type="html" xml:base="https://liard.me/2013/05/premiers-tests-de-google-app-engine-en-php/"><![CDATA[<p>C’est une des grosses annonces de la Google I/O 2013, Google App Engine supporte maintenant PHP. J’ai eu l’occasion de le tester sur un de mes projets. La première chose qu’il faut constater, c’est que l’utilisation du service en PHP n’est pas vraiment ouverte. Pour le moment il faut passer par une phase de validation de son projet pour avoir le droit d’utiliser le service. On est maintenant à 2 semaines de l’annonce et seulement 282 projets on été acceptés.</p>

<h2 id="configuration">Configuration</h2>

<p>Qui dit PHP dit LAMP et donc mysql. Google a justement ouvert une offre Cloud SQL. Nous ne sommes plus limités à leur DataStore NoSql sur GAE (c’était déjà le cas depuis plusieurs mois). Par contre la facturation du service Cloud SQL se fait à l’heure alors que le DataStore facture à l’usage (nombre de read/write). C’est moins économique pour un petit projet mais plus facilement prédictif. Pour la sauvegarde de fichier il faut utiliser Google Cloud Storage qui n’est ni plus ni moins une copie du service S3 d’Amazon. Les APIs sont même compatibles.</p>

<p>Pour utiliser l’ensemble de ces services la console GAE n’est plus suffisante, il faut utiliser la “Cloud Console”. Et c’est là que commence le parcours du combattant.</p>

<p><img src="/images/uploads/2013/05/Console.png" alt="SNS" /></p>

<p style="text-align: left;">
  Sur mon projet, au moment de vouloir créer mon premier Bucket sur Cloud Storage on me demande d&#8217;activer le Billing. J&#8217;ai donc entré mes informations personnelles et mon numéro de carte bleue. Rien d&#8217;exceptionnel s&#8217;il ne fallait pas le faire pour chaque projet ! A chaque fois on me redemande la même chose. Pire il faut aussi activer le billing sur la console Google App Engine. Donc pour chaque projet on va avoir 2 lignes de facturation ? La semaine dernière j&#8217;ai eu un débit de 0.15 € sur mon compte et 0.40€ de frais bancaires. Si j&#8217;ai 10 lignes à moins de 1€ sur mon compte par semaine, c&#8217;est mon comptable qui va être content :( J&#8217;ai l&#8217;impression que l&#8217;on peut simplifier les choses en souscrivant à une offre de support Google. La moins chère est à 150€ par mois, dans mon cas ça attendra.
</p>

<p style="text-align: left;">
  Une fois le billing activé, impossible de créer un bucket sur Cloud Storage. J&#8217;ai le message :
</p>

<blockquote>
  <p style="text-align: left;">
  The account for the specified project has been disabled.
</p>
</blockquote>

<p style="text-align: left;">
  Et oui car il y a une seconde console où il faut activer le Cloud Storage pour pouvoir l&#8217;utiliser ! Bien sûr aucun lien direct vers cette console que j&#8217;ai trouvée grâce à StackOverflow : <a href="https://code.google.com/apis/console">https://code.google.com/apis/console</a>
</p>

<h2 style="text-align: left;">
  Le code
</h2>

<p style="text-align: left;">
  Une fois cette partie gestion terminée, je peux enfin commencer à tester mon service PHP sur GAE. C&#8217;est une petite application facebook codée avec le framework codeigniter. Premier problème, il ne trouve pas la fonction php_sapi_name. Par défaut, beaucoup de fonctions PHP sont désactivées sur GAE, mais il est possible de les réactiver en ajoutant un fichier php.ini à la racine de votre projet :
</p>

<blockquote>
  <p style="text-align: left;">
  google_app_engine.enable_functions = &#8220;phpversion, phpinfo, php_sapi_name&#8221;
</p>
</blockquote>

<p style="text-align: left;">
  Ensuite j&#8217;ai un problème avec la variable $_SERVER, il ne trouve pas l&#8217;élément SCRIPT_NAME. Comme ce n&#8217;est pas structurant pour mon application je l&#8217;ai juste retiré du code. La connexion à la base se passe sans problème. J&#8217;ai juste du mettre l&#8217;option db_debug à FALSE pour que ça fonctionne. Attention aussi par défaut il n&#8217;y a que l&#8217;utilisateur root sur votre base, c&#8217;est à vous de créer votre user (comme sur votre mysql en fait).
</p>

<blockquote>
  <p>$db[‘hostname’] = ‘:/cloudsql/&lt;project_name&gt;:&lt;sql_name&gt;’;</p>
</blockquote>

<blockquote>
  <p>$db[‘username’] = ‘login’;</p>
</blockquote>

<blockquote>
  <p>$db[‘password’] = ‘pass’;</p>
</blockquote>

<blockquote>
  <p>$db[‘database’] = ‘basename’;</p>
</blockquote>

<blockquote>
  <p>$db[‘dbdriver’] = ‘mysql’;</p>
</blockquote>

<blockquote>
  <p>$db[‘dbprefix’] = ”;</p>
</blockquote>

<blockquote>
  <p>$db[‘pconnect’] = TRUE;</p>
</blockquote>

<blockquote>
  <p>$db[‘db_debug’] = FALSE;</p>
</blockquote>

<blockquote>
  <p>$db[‘cache_on’] = FALSE;</p>
</blockquote>

<blockquote>
  <p>$db[‘cachedir’] = ”;</p>
</blockquote>

<blockquote>
  <p>$db[‘char_set’] = ‘utf8’;</p>
</blockquote>

<blockquote>
  <p>$db[‘dbcollat’] = ‘utf8_general_ci’;</p>
</blockquote>

<blockquote>
  <p>$db[‘swap_pre’] = ”;</p>
</blockquote>

<blockquote>
  <p>$db[‘autoinit’] = TRUE;</p>
</blockquote>

<blockquote>
  <p>$db[‘stricton’] = FALSE;</p>
</blockquote>

<p>Donc avec très peu de modifications mon application PHP tourne sur GAE. Il me reste à regarder le stockage sur Cloud Storage. La documentation est succinte mais nous avons les éléments pour le faire. Mes premiers tests n’étaient pas très concluants, j’avais un retour d’erreur sur l’adresse d’upload :</p>

<blockquote>
  <p>503 Service Unavailable</p>
</blockquote>

<p>Et c’est à ce moment qu’il faut comprendre que “Service Unavailable” veut dire : “vous n’avez pas les bons droits d’accès”. Après avoir ajouté des droits d’accès sur le bucket l’upload de fichier fonctionne. La documentation est vraiment très pauvre quand même. Certaines fonctions php son implementées (comme unlink) et d’autre pas (pas de rename). Il y a un vrai manque de documentation pour utiliser Cloud Storage en PHP, il nous faut vraiment le même niveau de doc que ce qui est proposé en Python. J’ai aussi remonté un bug sur la méthode move_uploaded_file, pour le moment on perd l’info sur le type du fichier à la copie. C’est gênant surtout qu’aucune documentation ne nous dit comment modifier les metadata d’un fichier. Le <a href="https://code.google.com/p/googleappengine/issues/detail?id=9407">ticket</a> a été validé par google.</p>

<p>Comme je l’ai dit au début, l’objectif est de réaliser une application FaceBook, il faut donc avoir un service disponible en HTTPS. Là rien de très compliqué techniquement, il faut utiliser la console de gestion de domaine (oui une quatrième console !). Ce tuto explique très bien les différentes étapes d’installation. Par contre j’ai eu la surprise de découvrir qu’il fallait payer 9$ par mois pour pouvoir ajouter un certificat SSL (en plus du prix du certificat bien sûr).</p>

<p>Une fois que l’ensemble fonctionne, on peut se concentrer sur le point important : Les temps de réponse. Est-ce que le gros problème de temps de démarrage d’instance Java que j’ai décrit <a href="/2012/07/google-app-engine-suite/">ici</a> il y a 1 an (toujours d’actualité  :() se retrouve aussi en PHP ? La réponse est mitigée. Oui c’est plus rapide, on est souvent autour d’une seconde pour démarrer une nouvelle instance. Par contre il faut 6,5s pour uploader un fichier de 300ko ! Quand je dis uploader, je compte l’ensemble du traitement comme il est noté dans la doc à savoir : upload + move_uploaded_file + unlink du fichier temp. C’est vraiment l’upload qui est long puisque je descends à 5,5s de temps d’exécution si je retire move_uploaded_file et unlink. Cette lenteur est complètement bloquante dans certains cas et difficilement explicable.</p>

<h2 id="conclusion">Conclusion</h2>

<p>La bonne nouvelle est que j’ai pu installer une application PHP existante assez rapidement sur GAE. Je n’ai dû recoder que la partie upload de fichiers. Par contre l’offre Cloud de Google reste encore un peu lourde. On voit bien que  c’est le début et que tout n’est pas encore intégré. Le fait d’avoir 4 consoles différentes montre qu’il y a encore du travail :</p>

<ul>
  <li>Console GAE : les logs, la gestion des versions, du billing</li>
  <li>Cloud Console : Gestion de Cloud SQL, Cloud Storage</li>
  <li>Console API : Pour activer les services visibles dans la Cloud Console</li>
  <li>Domain manager : Gestion des domaines et donc des certificats SSL pour GAE</li>
</ul>

<p>On est donc loin de la maturité d’Amazon mais l’offre est intéressante. Elle a au moins eu l’avantage d’ouvrir une guerre des prix entre les 2 géants :)</p>

<p><strong>Points positifs :</strong></p>

<ul>
  <li>Scalable à l’infini</li>
  <li>Codeigniter fonctionne dessus</li>
  <li>Coût proche de zéro en début de projet</li>
</ul>

<p><strong>Points négatifs :</strong></p>

<ul>
  <li>Documentation très pauvre</li>
  <li>Encore des lenteurs</li>
  <li>4 consoles c’est 3 de trop</li>
  <li>Environnement de dev plus complexe qu’un simple LAMP</li>
</ul>]]></content><author><name>Samuel</name></author><category term="Cloud" /><category term="cloud" /><category term="codeigniter" /><category term="GAE" /><category term="php" /><summary type="html"><![CDATA[C’est une des grosses annonces de la Google I/O 2013, Google App Engine supporte maintenant PHP. J’ai eu l’occasion de le tester sur un de mes projets. La première chose qu’il faut constater, c’est que l’utilisation du service en PHP n’est pas vraiment ouverte. Pour le moment il faut passer par une phase de validation de son projet pour avoir le droit d’utiliser le service. On est maintenant à 2 semaines de l’annonce et seulement 282 projets on été acceptés.]]></summary></entry><entry><title type="html">Lancer un cron sur un cluster</title><link href="https://liard.me/2013/01/lancer-un-cron-sur-un-cluster/" rel="alternate" type="text/html" title="Lancer un cron sur un cluster" /><published>2013-01-07T09:39:50+00:00</published><updated>2013-01-07T09:39:50+00:00</updated><id>https://liard.me/2013/01/lancer-un-cron-sur-un-cluster</id><content type="html" xml:base="https://liard.me/2013/01/lancer-un-cron-sur-un-cluster/"><![CDATA[<p>Sur un de mes projets j’ai besoin d’exécuter une tâche tous les jours. Cet exercice pourtant trivial peut devenir plus complexe si mon service utilise plusieurs serveurs et que je ne veux exécuter la tâche qu’une seule fois. En Java j’utilisais le <a href="http://quartz-scheduler.org/">scheduler Quartz</a> pour régler ce problème. Il est très efficace et en cas de cluster il utilise une base de données pour s’assurer que la tâche n’est lancée que sur un seul serveur. Mais si votre tâche doit être lancée en shell ? J’ai donc cherché une solution pour le faire simplement sur mon projet utilisant des instances EC2.</p>

<p>Après pas mal de recherches, j’ai trouvé une solution simple sur <a href="http://stackoverflow.com/a/12623737">stackoverflow</a>. L’idée est bonne mais le script ne fonctionne pas (rien que l’utilisation de la commande hostname est étrange…).</p>

<p>J’ai finalement fait fonctionner ce script :</p>

<pre name="code" class="ruby:nogutter">#!/usr/bin/env ruby

AWS_AUTO_SCALING_HOME='/opt/aws/apitools/as'
JAVA_HOME='/usr/lib/jvm/jre'
MY_GROUP = '&lt;GroupName&gt;'

@cmd_out = `bash -c 'JAVA_HOME=#{ JAVA_HOME } AWS_AUTO_SCALING_HOME=#{ AWS_AUTO_SCALING_HOME } as-describe-auto-scaling-instances -region &lt;REGION&gt; -I &lt;KEY&gt; -S &lt;SECRET&gt;'`

raise "Output empty, should not happen!" if @cmd_out.empty?
@lines = @cmd_out.split(/\r?\n/)
@last = @lines.select {|l| l.match MY_GROUP }.reverse.
detect { |l| l =~ /^INSTANCE\s+\S+\s+\S+\s+\S+\s+InService\s+HEALTHY/ }
raise "No suitable host in autoscaling group!" unless @last
@last_host = @last.match(/^INSTANCE\s+(\S+)/)[1]
@hostname = `wget -q -O - http://169.254.169.254/latest/meta-data/instance-id`

if @hostname.index(@last_host)
  puts "It's me!"
  exit(0)
else
  puts "Someone else will do it!"
  exit(1)
end</pre>

<p>Il est aussi possible de gérer différemment la gestion de l’authentification AWS en passant par un fichier comme expliqué <a href="http://alestic.com/2010/09/aws-iam">ici</a>.</p>

<p>Ce script doit être exécuté par root. Ensuite il suffit d’ajouter une ligne dans la crontab de vos instances du type :</p>

<blockquote>
  <p>02 01 * * * ~/lastonly.rb &amp;&amp; wget -q -O – http://www.mysite.com/cron/crontasks</p>
</blockquote>

<p>Cette solution n’est pas parfaite mais elle a le mérite d’être simple. Une autre solution plus “propre” serait sans doute d’utiliser <a href="http://aws.amazon.com/fr/swf/">Amazon Simple Workflow Service</a></p>]]></content><author><name>Samuel</name></author><category term="Cloud" /><category term="Amazon" /><category term="EC2" /><summary type="html"><![CDATA[Sur un de mes projets j’ai besoin d’exécuter une tâche tous les jours. Cet exercice pourtant trivial peut devenir plus complexe si mon service utilise plusieurs serveurs et que je ne veux exécuter la tâche qu’une seule fois. En Java j’utilisais le scheduler Quartz pour régler ce problème. Il est très efficace et en cas de cluster il utilise une base de données pour s’assurer que la tâche n’est lancée que sur un seul serveur. Mais si votre tâche doit être lancée en shell ? J’ai donc cherché une solution pour le faire simplement sur mon projet utilisant des instances EC2.]]></summary></entry></feed>