Update notes

This commit is contained in:
Fred Pauchet 2023-02-17 10:46:18 +01:00
parent 17b3d7336a
commit ae881a52fe
9 changed files with 802 additions and 100 deletions

55
annexes/gilded-roses.tex Normal file
View File

@ -0,0 +1,55 @@
\chapter{Gilded Roses}
\url{https://github.com/emilybache/GildedRose-Refactoring-Kata}
\begin{listing}[H]
\begin{minted}[tabsize=4]{python}
# -*- coding: utf-8 -*-
class GildedRose(object):
def __init__(self, items):
self.items = items
def update_quality(self):
for item in self.items:
if item.name != "Aged Brie" and item.name != "Backstage passes to a TAFKAL80ETC concert":
if item.quality > 0:
if item.name != "Sulfuras, Hand of Ragnaros":
item.quality = item.quality - 1
else:
if item.quality < 50:
item.quality = item.quality + 1
if item.name == "Backstage passes to a TAFKAL80ETC concert":
if item.sell_in < 11:
if item.quality < 50:
item.quality = item.quality + 1
if item.sell_in < 6:
if item.quality < 50:
item.quality = item.quality + 1
if item.name != "Sulfuras, Hand of Ragnaros":
item.sell_in = item.sell_in - 1
if item.sell_in < 0:
if item.name != "Aged Brie":
if item.name != "Backstage passes to a TAFKAL80ETC concert":
if item.quality > 0:
if item.name != "Sulfuras, Hand of Ragnaros":
item.quality = item.quality - 1
else:
item.quality = item.quality - item.quality
else:
if item.quality < 50:
item.quality = item.quality + 1
class Item:
def __init__(self, name, sell_in, quality):
self.name = name
self.sell_in = sell_in
self.quality = quality
def __repr__(self):
return "%s, %s, %s" % (self.name, self.sell_in, self.quality)
\end{minted}
\end{listing}

View File

@ -20,6 +20,27 @@ Expliquer pourquoi une API est intéressante/primordiale/la première chose à r
Voir peut-être aussi
\url{https://christophergs.com/python/2021/12/04/fastapi-ultimate-tutorial/}
Remarque : quatre statuts = le minimum syndical. \cite[p. 297]{restful_web_apis} :
\begin{enumerate}
\item
\textbf{200 (OK)}.
Tout va bien.
Le document qui se trouve dans le corps de la réponse, s'il y en a un, est la représentation d'une ressource.
\item
\textbf{301 (Moved Permanently)}.
Reçu lorsque la ressource n'est plus disponible à cette URI.
\item
\textbf{400 (Bad Request)}.
Indique qu'il y a eu un problème côté client.
Le document qui se trouve dans le corps de la réponse, s'il existe, est un message d'erreur.
Avec un peu de chance, le client a la possibilité d'interpréter ce message d'erreur, afin de corriger le problème.
\item
\textbf{500 (Internal Server Error)}.
Il y a un problème côté serveur. Le document présent dans le corps de la réponse, toujours s'il existe, indique le problème.
Comme celui-ci se situe au niveau du serveur, le client ne pourra rien faire pour le résoudre.
\end{enumerate}
Au niveau du modèle, nous allons partir de quelque chose de très simple: des personnes, des contrats, des types de contrats, et un service d'affectation.
Quelque chose comme ceci:
@ -361,3 +382,15 @@ Une solution plus facile que les sérializeurs de DRF consistera à
\section{OData}
...
\section{Bonnes pratiques}
\subsection{Authentification}
\subsection{Validation des données}
\subsection{Utilisation d'une API Gateway}
\subsection{Rate limiting}
\subsection{Partage des données nécessaires uniquement}

View File

@ -1,10 +1,10 @@
\chapter{Intégration continue / Déploiement Continu}
\begin{quote}
New code should never go live without first being audited for correctness and security. \cite{django_for_startup_founders}
\textit{New code should never go live without first being audited for correctness and security}. \cite{django_for_startup_founders}
\end{quote}
Les processus d'intégration continue et de déploiement continu (respectivement, CI/CD - Continuous Integration / Continuous Deployment) permettent d'automatiser les étapes de vérifications, autrement réalisées par un être humain, voire manuellement, par des automates réagissant à des évènements particuliers - typiquement, une nouvelle version du code, un nouveau \textit{commit}, la mise à jour d'un des composants, \ldots
Les processus d'intégration continue \footnote{CI - Continuous Integration} et de déploiement continu \footnote{CD - Continuous Deployment} permettent d'automatiser des étapes de vérifications, initialement réalisées par un être humain, par des automates réagissant à des évènements particuliers: typiquement, une nouvelle version du code, un nouveau \textit{commit}, la mise à jour d'un des composants, \ldots
\section{Intégration continue}
@ -25,11 +25,10 @@ matrix:
- 3.8
pipeline:
static_checks:
image: acidrain/python-poetry:${PYTHON_VERSION}
lint:
image: python:${PYTHON_VERSION}
commands:
- python -V
- poetry -V
- pip3 install poetry
- poetry install
- poetry run flake8 src/
- poetry run pylint src/
@ -37,18 +36,15 @@ pipeline:
- poetry run mypy .
unit_tests:
image: acidrain/python-poetry:${PYTHON_VERSION}
image: python:${PYTHON_VERSION}
commands:
- python -V
- poetry -V
- poetry install
- pip3 install poetry
- poetry config virtualenvs.create false
- poetry run pytest --cov-report term-missing tests/unit/
\end{minted}
\section{Déploiement continu}
Le déploiement continu est une extension de l'intégration continue, qui est jusqu'au-boutiste à s'occuper lui-même de mettre à disposition une nouvelle version, sous réserve qu'elle ait passé les différentes étapes du pipeline.
C'est ainsi qu'Amazon ou certaines grandes \footnote{ie. envahissantes} entreprises publient jusqu'à plusieurs miliers de nouvelles fonctionnalités \textbf{par jour}.
C'est ainsi qu'Amazon ou certaines grandes \footnote{ie. \textit{"envahissantes"}} entreprises publient jusqu'à plusieurs miliers de nouvelles fonctionnalités \textbf{par jour}.

View File

@ -95,14 +95,23 @@ Ce dépôt n'est pas uniquement destiné à hébergé le code source, mais égal
Chaque installation ou configuration doit être toujours réalisée de la même manière, et doit pouvoir être répétée quel que soit l'environnement cible.
Ceci permet d'éviter que l'application n'utilise une dépendance qui ne soit installée que sur l'un des sytèmes de développement, et qu'elle soit difficile, voire impossible, à réinstaller sur un autre environnement.
\begin{memorizebox}
Chaque dépendance devra être déclarée dans un fichier présent au niveau de la base de code.
Lors de la création d'un nouvel environnement vierge, il suffira d'utiliser ce fichier comme paramètre afin d'installer les prérequis au bon fonctionnement de notre application, afin d'assurer une reproductibilité quasi parfaite de l'environnement d'exécution.
\end{memorizebox}
Il est important de bien "épingler" les versions liées aux dépendances de l'application.
Il est important de bien "épingler" la version liée à chacune des dépendances de l'application.
Ceci peut éviter des effets de bord comme une nouvelle version d'une librairie dans laquelle un bug aurait pu avoir été introduit.\footnote{Le paquet PyLint dépend par exemple d'Astroid; \href{https://github.com/PyCQA/pylint-django/issues/343}{en janvier 2022}, ce dernier a été mis à jour sans respecter le principe de versions sémantiques et introduisant une régression. PyLint spécifiait que sa dépendance avec Astroid devait respecter une version ~2.9. Lors de sa mise à jour en 2.9.1, Astroid a introduit un changement majeur, qui faisait planter Pylint. L'épinglage explicite aurait pu éviter ceci.}.
Dans le cas de Python, la déclaration explicite et l'épinglage pourront notamment être réalisés au travers de \href{https://pypi.org/project/pip/}{PIP} ou \href{https://python-poetry.org/}{Poetry}.
La majorité des langages modernes proposent des mécanismes similaires (\href{https://rubygems.org/}{Gem} pour Ruby, \href{https://www.npmjs.com/}{NPM} pour NodeJS, \ldots)
La majorité des langages modernes proposent des mécanismes similaires :
\begin{itemize}
\item \href{https://rubygems.org/}{Gem} pour Ruby,
\item \href{https://www.npmjs.com/}{NPM} pour NodeJS,
\item \href{Maven}{https://maven.apache.org/} pour Java,
\item \ldots
\end{itemize}
\section{Configuration applicative}
@ -118,15 +127,48 @@ que:
En pratique, toute information susceptible d'évoluer ou de changer (un seuil, une ressource externe, un couple utilisateur/mot de passe, \ldots) doit se trouver dans un fichier ou dans une variable d'environnement, et doit être facilement modifiable.
Ceci permet de paramétrer facilement un environnement (par exemple, un container), simplement en modifiant une variable de configuration qui spécifierait la base de données sur laquelle l'application devra se connecter.
En pratique, avec du code Python/Django, nous pourrions utiliser la libraririe \href{django-environ}{https://pypi.org/project/django-environ/}, qui permet de gérer facilement ce fichier de configuration :
Toute clé de configuration (nom du serveur de base de données, adresse d'un service Web externe, clé d'API pour l'interrogation d'une ressource, \ldots) sera définie directement au niveau de l'hôte - à aucun moment, nous ne devons trouver un mot de passe en clair dans le dépôt source ou une valeur susceptible d'évoluer, écrite en dur dans le code. \footnote{Ainsi, nous pourrions faire une \href{https://github.com/search?q=filenamefilename:.env DB_USERNAME&type=Code}{recherche sur Github} pour retrouver certaines variables d'environnement qui auraient été laissées en dur dans le code source de certains projets. Le \href{https://github.com/techgaun/github-dorks}{dépôt suivant} liste quelques idées de variables à rechercher\ldots}.
\begin{listing}[H]
\begin{minted}[tabsize=4]{python}
import environ
import os
Au moment de développer une nouvelle fonctionnalité, réfléchissez si
l'un des paramètres utilisés risquerait de subir une modification ou s'il concerne un principe de sécurité: ce composant peut concerner une nouvelle chaîne de connexion, un point de terminaison nécessaire à télécharger des données officielles ou un chemin vers un répertoire partagé pour y déposer un fichier.
env = environ.Env(
DEBUG=(bool, False)
)
Le risque est de se retrouver avec une liste colossale de paramètres; pensez à leur assigner une variable par défaut.
Par exemple, Gitea expose la liste suivante de paramètres \footnote{\url{https://docs.gitea.io/en-us/config-cheat-sheet/}}; il serait impossible de tous les configurer un à un avant de pouvoir démarrer une instance.
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
environ.Env.read_env(os.path.join(BASE_DIR, '.env'))
DEBUG = env('DEBUG')
SECRET_KEY = env('SECRET_KEY')
DATABASES = {
# Parse database connection url strings
# like psql://user:pass@127.0.0.1:8458/db
'default': env.db(),
}
CACHES = {
'default': env.cache(),
}
\end{minted}
\end{listing}
Il suffira ensuite d'appeler ces variables grâce à \texttt{from django.conf import settings} pour récupérer la valeur qui aura été configurée.
Ceci permet de paramétrer facilement n'importe quel environnement, simplement en modifiant une variable de ce fichier de configuration.
Toute clé de configuration, qu'il s'agisse d'une chaîne de connexion vers une base de données, l'adresse d'un service Web externe, une clé d'API pour l'interrogation d'une ressource, d'un chemin vers un fichier à interprêter ou à transférer, \ldots, ou n'importe quelle valeur susceptible d'évoluer, ne doit se trouver écrite en dur dans le code.
\begin{dangerbox}
Il est important de ne surtout pas ajouter ce fichier \texttt{.env} dans le dépôt de sources: à aucun moment, nous ne devons y trouver de mot de passe ou d'information confidentielle en clair. \footnote{Ainsi, nous pourrions faire une \href{https://github.com/search?q=filenamefilename:.env DB_USERNAME&type=Code}{recherche sur Github} pour retrouver certaines variables d'environnement qui auraient été laissées en dur dans le code source de certains projets. Le \href{https://github.com/techgaun/github-dorks}{dépôt suivant} liste quelques idées de variables à rechercher\ldots}.
\end{dangerbox}
Au moment de développer une nouvelle fonctionnalité, réfléchissez si l'un des paramètres utilisés risquerait de subir une modification ou s'il concerne un principe de sécurité.
Le risque de se retrouver avec une liste colossale de paramètres n'est cependant pas négligeable; pensez à leur assigner une variable par défaut \footnote{Par exemple, Gitea expose \href{la liste suivante de paramètres disponibles}{https://docs.gitea.io/en-us/config-cheat-sheet/}; il serait impossible d'utiliser cette plateforme si chacun d'entre eux devait obligatoirement être configuré avant de pouvoir démarrer une instance.}
\section{Ressources externes}
@ -142,7 +184,7 @@ Ces ressources sont donc spécifiés grâce à des variables d'environnement, et
\caption{Gestion des ressources attachées}
\end{graphic}
Nous serons ainsi ravis de simplement pouvoir modifier la chaîne de connexion \texttt{sqlite:////tmp/my-tmp-sqlite.db} en \texttt{psql://user:pass@127.0.0.1:8458/db} lorsque ce sera nécessaire, sans avoir à recompiler ou redéployer les modifications.
Nous serons ainsi ravis de pouvoir simplement modifier la chaîne de connexion \texttt{sqlite:////tmp/my-tmp-sqlite.db} en \texttt{psql://user:pass@127.0.0.1:8458/db} lorsque ce sera nécessaire, sans avoir à recompiler ou redéployer les modifications.
\section{Séparation des phases de construction}
@ -150,30 +192,41 @@ Nous serons ainsi ravis de simplement pouvoir modifier la chaîne de connexion \
\item
La \textbf{construction} (\emph{build}) convertit un code source en un ensemble de fichiers exécutables, associé à une version et à une transaction dans le système de gestion de sources.
\item
La \textbf{mise à disposition} (\emph{release}) associe cet ensemble à une configuration prête à être exécutée,
La \textbf{mise à disposition} (\emph{release}) associe cet ensemble à une configuration prête à être exécutée sur un environnement désigné,
\item
La phase d'\textbf{exécution} (\emph{run}) démarre les processus nécessaires au bon fonctionnement de l'application.
\end{enumerate}
\begin{graphic}{images/12factors/release.png}
\begin{graphic}{images/12factors/separate-run-steps.png}
\caption{Séparation des phases de construction, mise à disposition et exécution}
\end{graphic}
Parmi les solutions possibles, nous pourrions nous pourrions nous baser
sur les \emph{releases} de Gitea, sur un serveur d'artefacts (\href{https://fr.wikipedia.org/wiki/Capistrano_(logiciel)}{Capistrano}), voire directement au niveau de forge logicielle (Gitea, Github, Gitlab, \ldots).
\begin{memorizebox}
Dans le cas de Python, la phase de construction (\textit{Build}) correspondra plutôt à une phase d'\textit{empaquettage} (\textit{packaging}).
Une fois préparée, la librairie ou l'application pourra être publiée sur \url{pypi.org} ou un dépôt géré en interne.
La phase de \emph{release} revient à associer une version spécifiquement empaquêtée pour l'associer à une configuration particulière.
\end{memorizebox}
\section{Mémoire des processus d'exécution}
Toute information stockée en mémoire ou sur disque ne doit pas altérer le comportement futur de l'application, par exemple après un redémarrage non souhaité.
Dit autrement, l'exécution de l'application ne doit pas dépendre de la présence d'une information locale à son environnement d'exécution.
Pratiquement, si l'application devait rencontrer un problème - par exemple suite à un problème matériel -, il est nécessaire qu'elle puisse redémarrer rapidement, éventuellement en étant déployée sur un autre serveur.
Toute information stockée physiquement durant son exécution sur le premier hôte, sera perdue lors de son déploiement ultérieur à un autre endroit.
\begin{memorizebox}
L'exécution de l'application ne doit pas dépendre de la présence d'une information locale à son environnement d'exécution.
\end{memorizebox}
En pratique, si l'application devait rencontrer un problème - par exemple suite à un problème matériel, une coupure réseau, \ldots -, il est nécessaire qu'elle puisse redémarrer rapidement, éventuellement en étant déployée sur un autre serveur.
Toute information stockée physiquement sera alors perdue, puisque le contexte d'exécution aura été déplacée à un autre endroit.
Lors de l'initialisation ou de la réinitialisation d'une application, la solution consiste à jouer sur les variables d'environnement (cf. \#3) et sur les informations que l'on pourra trouver au niveau des ressources attachées (cf \#4), afin de faire en sorte que les informations et données primordiales puissent être récupérées ou reconstruites, et donc réutilisées, sans altérer le comportement attendu.
Ceci joue également sur les possibilités de mise à l'échelle: en ajoutant un nouveau serveur d'exécution, si une donnée indispensable à son fonctionnement devait se trouver sur la seule machine où elle est actuellement exécutée, elle en sera inconnue.
\begin{graphic}{images/12factors/execution-process-memory.drawio.png}
\caption{La mémoire des processus d'exécution doit rester accessible, même après leur disparition}
\end{graphic}
-> Ajouter un schéma.
Ceci joue énormément sur les possibilités de mise à l'échelle: lors de l'ajout d'un nouveau serveur, il est indispensable qu'il puisse accéder à l'ensemble des informations nécessaires au bon fonctionnement de l'application.
\section{Liaison des ports}
@ -189,34 +242,37 @@ Le serveur (= l'hôte) choisit d'appliquer une correspondance entre "son" port 4
\section{Connaissance et confiance des processus systèmes}
Comme décrit plus haut (cf. \#6), l'application doit utiliser des processus \emph{stateless} (sans état).
Nous pouvons créer et utiliser des processus supplémentaires pour tenir plus facilement une lourde charge, ou dédier des particuliers pour certaines tâches:
Comme décrit plus haut (cf. \#6), l'application doit utiliser des processus sans état (\textit{stateless}), c'est-à-dire qu'aucune information ne doit être stockée localement ou ne doit dépendre d'une information précédemment acquise et accessible uniquement à un processus en particulier.
Nous pouvons créer et utiliser des processus supplémentaires pour tenir plus facilement une lourde charge ou dédier des particuliers pour certaines tâches:
\begin{itemize}
\item Requêtes HTTP \emph{via} des processus Web;
\item \emph{long-running} jobs pour des processus asynchrones,
\item Jobs asynchrones pour des tâches à effectuer en arrière-plan,
\item \ldots
\end{itemize}
Si l'un des concepts dont vous avez besoin existe sur l'hôte hébergeant l'application, ne vous fatiguez pas: utilisez le et ne réinventez pas la roue.
Si l'un des concepts dont vous avez besoin existe déjà et peut être utilisé, ne vous fatiguez pas: utilisez le et ne réinventez pas surtout la roue.
\begin{graphic}{images/12factors/process-types.png}
\caption{Charge et types de processus}
\end{graphic}
\section{Arrêts élégants et démarrages rapides}
Par "arrêt élégant", nous voulons surtout éviter le fameux \texttt{kill\ -9\ \textless{}pid\textgreater{}} (ou équivalent), ou tout autre arrêt brutal d'un processus qui nécessiterait une intervention urgente du
superviseur.
Par "arrêt élégant", nous voulons surtout éviter le fameux \texttt{kill -9 <pid>} (ou équivalent), ou tout autre arrêt brutal d'un processus qui nécessiterait une intervention urgente du superviseur.
En prévoyant une manière élégante d'envoyer un signal de terminaison:
\begin{enumerate}
\item Les requêtes en cours peuvent se terminer au mieux,
\item Le démarrage rapide de nouveaux processus améliorera la balance d'un processus en cours d'extinction vers des processus tout frais, en autorisant l'exécution parallèle d'anciens et de nouveaux "types" de processus
\item Les requêtes en cours peuvent se terminer sans impact majeur,
\item Le démarrage (rapide) de nouveaux processus améliore la balance de charge d'un processus en cours d'extinction vers des processus tout frais, en autorisant l'exécution parallèle d'anciens et de nouveaux "types" de processus
\end{enumerate}
L'intégration de ces mécanismes dès les premières étapes de développement limitera les perturbations et facilitera la prise en compte d'arrêts inopinés (problème matériel, redémarrage du système hôte, etc.).
\begin{graphic}{images/12factors/process-types.png}
\begin{graphic}{images/12factors/process-type-chronology.png}
\caption{Evolution des types de processus, en fonction des arrêts et démarrages}
\end{graphic}
L'intégration de ces mécanismes dès les premières étapes de développement limitera les perturbations et facilitera la prise en compte d'arrêts inopinés (problème matériel, redémarrage du système hôte, etc.).
\section{Similarité des environnements}
\begin{dangerbox}
@ -236,14 +292,19 @@ Faire en sorte que tous les environnements soient les plus similaires possibles
Conserver des environements similaires limite ce genre de désagréments.
\end{memorizebox}
Ceci permet également de proposer à nos utilisateurs un bac à sable dans lequel ils pourront explorer et réaliser des expérimentations en toute sécurité, sans quel cela n'ait d'impact sur un \textit{réel} environnement de production, où les conséquences pourraient être beaucoup plus graves. \cite[p. 9]{data_design}
Ceci permet également de proposer à nos utilisateurs un bac à sable dans lequel ils pourront explorer et réaliser des expérimentations en toute sécurité, sans quel cela n'ait d'impact sur un \textit{réel} environnement de production, où les conséquences pourraient être beaucoup plus graves. \cite[p. 9]{data_intensive}
\section{Journaux de flux évènementiels}
\section{Flux d'évènements}
Les journaux d'exécution sont la seule manière pour une application de communiquer son état et de décrire ce qu'elle est occupée à faire ou à réaliser.
Que nous soyons en phase de développement sur le poste d'une personne de l'équipe, avec une sortie console ou sur une machine de production avec un envoi vers une instance \href{https://www.graylog.org/}{Greylog} ou \href{https://sentry.io/welcome/}{Sentry}, le routage des évènements doit être réalisé en fonction de la nécessité du contexte d'exécution et de sa criticité.
\begin{memorizebox}
Une application ne doit jamais se soucier de l'endroit où les évènements qui la concerne seront écrits, mais se doit simplement de les envoyer sur la sortie \texttt{stdout}.
De cette manière, que nous soyons en développement sur le poste d'un développeur avec une sortie console ou sur une machine de production avec un envoi vers une instance \href{https://www.graylog.org/}{Greylog} ou \href{https://sentry.io/welcome/}{Sentry}, le routage des journaux sera réalisé en fonction de sa nécessité et de sa criticité, et non pas parce que le développeur l'a spécifié en dur dans son code.
\end{memorizebox}
Cette phase est critique, dans la mesure où les journaux d'exécution sont la seule manière pour une application de communiquer son état vers l'extérieur: recevoir une erreur interne de serveur est une chose; pouvoir obtenir un minimum d'informations, voire un contexte de plantage complet en est une autre.
Cette phase est essentielle, dans la mesure où recevoir une erreur interne de serveur est une chose; pouvoir obtenir un minimum d'informations, voire un contexte de plantage complet en est une autre.
La différence entre ces deux points vous fera, au mieux, gagner plusieurs heures sur l'identification ou la résolution d'un problème.
\section{Isolation des tâches administratives}
@ -262,7 +323,7 @@ Au fur et à mesure que le code est délibérément construit pour être mainten
\item
Conservation précise des dépendances nécessaires
\item
Résilience des services et plantage élégant (i.e. \textbf{sans finir un SEGFAULT avec l'OS dans les choux et un écran bleu})
Résilience des services et plantage élégant (i.e. sans finir un SEGFAULT avec l'OS dans les choux et un écran bleu)
\item
Compatibilité entre les différentes versions (n+1, \ldots)
\item
@ -272,5 +333,5 @@ Au fur et à mesure que le code est délibérément construit pour être mainten
\item
Traces des requêtes provenant des utilisateurs, indépendamment des services utilisés
\item
Centralisation de la configuration (\textbf{via} ZooKeeper, par exemple)
Centralisation de la configuration (\textit{via} ZooKeeper, par exemple)
\end{enumerate}

View File

@ -185,31 +185,31 @@ Ca évite, dans une boucle, de vérifier si la clé existe déjà, et si pas, de
def on_command_1():
print("Oune")
def on_command_2():
print("Dos")
def on_command_3():
print("Tresse")
def prrrt():
print("Won't be printed")
\end{minted}
Tu peux retrouver les trois méthodes \texttt{on\_command\_*}` via la fonction `dir()`:
\begin{minted}{python}
>>> import module_chelou
>>> dir(module_chelou)
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'on_command_1', 'on_command_2', 'on_command_3', 'prrrt']
>>> for function_name in [x for x in dir(module_chelou) if "on_command_" in x]:
... getattr(module_chelou, function_name)()
...
Oune
Dos
Tresse
```
\end{minted}
@ -301,17 +301,42 @@ Toute fonction dans la complexité est supérieure à cette valeur sera considé
\section{Conventions de documentation}
Python étant un langage interprété fortement typé, il est plus que conseillé, au même titre que les tests unitaires que nous verrons plus bas, de documenter son code.
Ceci impose une certaine rigueur, tout en améliorant énormément la qualité, la compréhension et la reprise du code par une tierce personne.
Ceci implique aussi de \textbf{tout} documenter: les modules, les paquets, les classes, les fonctions, méthodes, \ldots ce qui peut aller à contrecourant d'autres pratiques \cite{clean_code}{53-74} ; il y a donc une juste mesure à prendre entre "tout documenter" et "tout bien documenter":
Il y a une énorme différence entre la documentation du code et les commentaires associés au code:
\begin{enumerate}
\item \textbf{Les commentaires} décrivent comment le code fonctionne
\item \textbf{La documentation du code} décrit comment utiliser le code.
\end{enumerate}
Pour que le code soit utilisé correctement, il est dès lors important de documenter :
\begin{enumerate}
\item
Ce que représente une classe ou une API
\item
Les attentes d'une interface : codes d'erreurs, statuts possibles, etc.
\end{enumerate}
Les commentaires sont cependant utiles lorsque le code n'est pas évident à lire ou lorsque certaines optimisations auraient été appliquées dans un soucis de performances.
Dans la majorité des cas cependant, si des commentaires doivent être rédigés pour que le code devienne lisible, c'est que ce code n'est pas correctement écrit. \cite{clean_code}{53-74} \footnote{\url{https://www.youtube.com/watch?v=Bf7vDBBOBUA}}
Nous pouvons constater un risque de décorrélation entre le code et ses commentaires :
\begin{listing}[!ht]
\begin{minted}[tabsize=4]{python}
# Check that the maximum length of the array if less than 8
if len(array) < 10:
...
\end{minted}
\end{listing}
Il y a donc une juste mesure à prendre entre "tout documenter" (les modules, les paquets, les classes, les fonctions, méthodes, \ldots) et "bien documenter":
\begin{itemize}
\item
Inutile d'ajouter des watermarks, auteurs, \ldots
Git ou tout VCS s'en sortira très bien et sera beaucoup plus efficace que n'importe quelle chaîne de caractères que vous pourriez indiquer et qui sera fausse dans six mois,
\item
Inutile de décrire quelque chose qui est évident; documenter la méthode \mintinline{python}{get_age()} d'une personne n'aura pas beaucoup d'intérêt
Inutile de décrire quelque chose qui est évident; documenter la méthode \mintinline{python}{get_age()} d'une personne n'aura pas beaucoup d'intérêt,
\item
S'il est nécessaire de décrire un comportement au sein-même d'une fonction, avec un commentaire \emph{inline}, c'est que ce comportement pourrait être extrait dans une nouvelle fonction (qui, elle, pourra être documentée proprement).
\end{itemize}
@ -352,7 +377,141 @@ A priori, vous pourriez tout à fait utiliser le vôtre, sous réserve que les c
\subsection{Numpy}
A remplir
\href{https://numpydoc.readthedocs.io/en/latest/format.html}{Numpy} sépare chaque section avec un pseudo-formatage de titre.
Les sections sont définies selon les types suivants:
\begin{itemize}
\item
Une courte description (de la classe, du module, de la fonction, \ldots).
Chaque module devrait avoir une docstring au tout début du fichier.
Cette docstring peut s'étendre sur plusieurs lignes (\textit{Short summary})
\item
Des avertissements liés à la dépréciation: quand la fonctionnalité viendra à disparaitre (dans quelle version), les raisons de sa dépréciation, et les recommandations pour arriver au même résultat. (\textit{Deprecated})
\item
Une description plus précise des fonctionnalités proposées (et pas de la manière dont l'implémentation est réalisée). (\textit{Extended summary})
\item
Les paramètres attendus, leur type et une description (\textit{Parameters})
\item
La ou les valeurs de retour, accompagnées de leur type et d'une description (\textit{Returns})
\item
Les valeurs générées (\textit{yields}) dans le cas de générateurs (\textit{Yields})
\item
Un objet attendu par la méthode \texttt{send()} (toujours dans le cas de générateurs) (\textit{Receives})
\item
D'autres paramètres, notamment dans le cas des paramètres \texttt{*args} et \texttt{**kwargs}. (\textit{Other parameters})
\item
Les exceptions levées (\textit{Raises})
\item
Les avertissements destinés à l'utilisateur (\textit{Warnings})
\item
Une section "Pour continuer\ldots" (\textit{See also})
\item Des notes complémentaires (\textit{Notes})
\item Des références (\textit{References})
\item Des exemples (\textit{Examples})
\end{itemize}
\begin{listing}[!ht]
\begin{minted}[tabsize=4]{python}
"""Docstring for the example.py module.
Modules names should have short, all-lowercase names. The module name may
have underscores if this improves readability.
"""
import os
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
def foo(var1, var2, *args, long_var_name="hi", only_seldom_used_keyword=0, **kwargs):
r"""Summarize the function in one line.
Several sentences providing an extended description. Refer to
variables using back-ticks, e.g. `var`.
Parameters
----------
<parameter_name> : <parameter_type>
<parameter_description>
Returns
-------
type
Explanation of anonymous return value of type ``type``.
describe : type
Explanation of return value named `describe`.
out : type
Explanation of `out`.
type_without_description
Other Parameters
----------------
only_seldom_used_keyword : int, optional
Infrequently used parameters can be described under this optional
section to prevent cluttering the Parameters section.
**kwargs : dict
Other infrequently used keyword arguments. Note that all keyword
arguments appearing after the first parameter specified under the
Other Parameters section, should also be described under this
section.
Raises
------
BadException
Because you shouldn't have done that.
See Also
--------
numpy.array : Relationship (optional).
numpy.ndarray : Relationship (optional), which could be fairly long, in
which case the line wraps here.
numpy.dot, numpy.linalg.norm, numpy.eye
Notes
-----
Notes about the implementation algorithm (if needed).
This can have multiple paragraphs.
You may include some math:
.. math:: X(e^{j\omega } ) = x(n)e^{ - j\omega n}
And even use a Greek symbol like :math:`\omega` inline.
References
----------
Cite the relevant literature, e.g. [1]_. You may also cite these
references in the notes section above.
.. [1] O. McNoleg, "The integration of GIS, remote sensing,
expert systems and adaptive co-kriging for environmental habitat
modelling of the Highland Haggis using object-oriented, fuzzy-logic
and neural-network techniques," Computers & Geosciences, vol. 22,
pp. 585-588, 1996.
Examples
--------
These are written in doctest format, and should illustrate how to
use the function.
>>> a = [1, 2, 3]
>>> print([x + 3 for x in a])
[4, 5, 6]
>>> print("a\nb")
a
b
"""
pass
\end{minted}
\caption{Un exemple de documentation Numpy}
\end{listing}
\subsection{Napoleon}
@ -428,6 +587,309 @@ Un exemple (encore) plus complet peut être trouvé
le dépôt sphinxcontrib-napoleon}. Et ici, nous tombons peut-être dans
l'excès de zèle:
\begin{listing}[!ht]
\begin{minted}[tabsize=4]{python}
# -*- coding: utf-8 -*-
"""Example Google style docstrings.
This module demonstrates documentation as specified by the `Google Python
Style Guide`_. Docstrings may extend over multiple lines. Sections are created
with a section header and a colon followed by a block of indented text.
Example:
Examples can be given using either the ``Example`` or ``Examples``
sections. Sections support any reStructuredText formatting, including
literal blocks::
$ python example_google.py
Section breaks are created by resuming unindented text. Section breaks
are also implicitly created anytime a new section starts.
Attributes:
module_level_variable1 (int): Module level variables may be documented in
either the ``Attributes`` section of the module docstring, or in an
inline docstring immediately following the variable.
Either form is acceptable, but the two should not be mixed. Choose
one convention to document module level variables and be consistent
with it.
Todo:
* For module TODOs
* You have to also use ``sphinx.ext.todo`` extension
.. _Google Python Style Guide:
http://google.github.io/styleguide/pyguide.html
"""
module_level_variable1 = 12345
module_level_variable2 = 98765
"""int: Module level variable documented inline.
The docstring may span multiple lines. The type may optionally be specified
on the first line, separated by a colon.
"""
def function_with_types_in_docstring(param1, param2):
"""Example function with types documented in the docstring.
`PEP 484`_ type annotations are supported. If attribute, parameter, and
return types are annotated according to `PEP 484`_, they do not need to be
included in the docstring:
Args:
param1 (int): The first parameter.
param2 (str): The second parameter.
Returns:
bool: The return value. True for success, False otherwise.
.. _PEP 484:
https://www.python.org/dev/peps/pep-0484/
"""
def function_with_pep484_type_annotations(param1: int, param2: str) -> bool:
"""Example function with PEP 484 type annotations.
Args:
param1: The first parameter.
param2: The second parameter.
Returns:
The return value. True for success, False otherwise.
"""
def module_level_function(param1, param2=None, *args, **kwargs):
"""This is an example of a module level function.
Function parameters should be documented in the ``Args`` section. The name
of each parameter is required. The type and description of each parameter
is optional, but should be included if not obvious.
If \*args or \*\*kwargs are accepted,
they should be listed as ``*args`` and ``**kwargs``.
The format for a parameter is::
name (type): description
The description may span multiple lines. Following
lines should be indented. The "(type)" is optional.
Multiple paragraphs are supported in parameter
descriptions.
Args:
param1 (int): The first parameter.
param2 (:obj:`str`, optional): The second parameter. Defaults to None.
Second line of description should be indented.
*args: Variable length argument list.
**kwargs: Arbitrary keyword arguments.
Returns:
bool: True if successful, False otherwise.
The return type is optional and may be specified at the beginning of
the ``Returns`` section followed by a colon.
The ``Returns`` section may span multiple lines and paragraphs.
Following lines should be indented to match the first line.
The ``Returns`` section supports any reStructuredText formatting,
including literal blocks::
{
'param1': param1,
'param2': param2
}
Raises:
AttributeError: The ``Raises`` section is a list of all exceptions
that are relevant to the interface.
ValueError: If `param2` is equal to `param1`.
"""
if param1 == param2:
raise ValueError('param1 may not be equal to param2')
return True
def example_generator(n):
"""Generators have a ``Yields`` section instead of a ``Returns`` section.
Args:
n (int): The upper limit of the range to generate, from 0 to `n` - 1.
Yields:
int: The next number in the range of 0 to `n` - 1.
Examples:
Examples should be written in doctest format, and should illustrate how
to use the function.
>>> print([i for i in example_generator(4)])
[0, 1, 2, 3]
"""
for i in range(n):
yield i
class ExampleError(Exception):
"""Exceptions are documented in the same way as classes.
The __init__ method may be documented in either the class level
docstring, or as a docstring on the __init__ method itself.
Either form is acceptable, but the two should not be mixed. Choose one
convention to document the __init__ method and be consistent with it.
Note:
Do not include the `self` parameter in the ``Args`` section.
Args:
msg (str): Human readable string describing the exception.
code (:obj:`int`, optional): Error code.
Attributes:
msg (str): Human readable string describing the exception.
code (int): Exception error code.
"""
def __init__(self, msg, code):
self.msg = msg
self.code = code
class ExampleClass(object):
"""The summary line for a class docstring should fit on one line.
If the class has public attributes, they may be documented here
in an ``Attributes`` section and follow the same formatting as a
function's ``Args`` section. Alternatively, attributes may be documented
inline with the attribute's declaration (see __init__ method below).
Properties created with the ``@property`` decorator should be documented
in the property's getter method.
Attributes:
attr1 (str): Description of `attr1`.
attr2 (:obj:`int`, optional): Description of `attr2`.
"""
def __init__(self, param1, param2, param3):
"""Example of docstring on the __init__ method.
The __init__ method may be documented in either the class level
docstring, or as a docstring on the __init__ method itself.
Either form is acceptable, but the two should not be mixed. Choose one
convention to document the __init__ method and be consistent with it.
Note:
Do not include the `self` parameter in the ``Args`` section.
Args:
param1 (str): Description of `param1`.
param2 (:obj:`int`, optional): Description of `param2`. Multiple
lines are supported.
param3 (:obj:`list` of :obj:`str`): Description of `param3`.
"""
self.attr1 = param1
self.attr2 = param2
self.attr3 = param3 #: Doc comment *inline* with attribute
#: list of str: Doc comment *before* attribute, with type specified
self.attr4 = ['attr4']
self.attr5 = None
"""str: Docstring *after* attribute, with type specified."""
@property
def readonly_property(self):
"""str: Properties should be documented in their getter method."""
return 'readonly_property'
@property
def readwrite_property(self):
""":obj:`list` of :obj:`str`: Properties with both a getter and setter
should only be documented in their getter method.
If the setter method contains notable behavior, it should be
mentioned here.
"""
return ['readwrite_property']
@readwrite_property.setter
def readwrite_property(self, value):
value
def example_method(self, param1, param2):
"""Class methods are similar to regular functions.
Note:
Do not include the `self` parameter in the ``Args`` section.
Args:
param1: The first parameter.
param2: The second parameter.
Returns:
True if successful, False otherwise.
"""
return True
def __special__(self):
"""By default special members with docstrings are not included.
Special members are any methods or attributes that start with and
end with a double underscore. Any special member with a docstring
will be included in the output, if
``napoleon_include_special_with_doc`` is set to True.
This behavior can be enabled by changing the following setting in
Sphinx's conf.py::
napoleon_include_special_with_doc = True
"""
pass
def __special_without_docstring__(self):
pass
def _private(self):
"""By default private members are not included.
Private members are any methods or attributes that start with an
underscore and are *not* special. By default they are not included
in the output.
This behavior can be changed such that private members *are* included
by changing the following setting in Sphinx's conf.py::
napoleon_include_private_with_doc = True
"""
pass
def _private_without_docstring(self):
pass
\end{minted}
\caption{Un exemple de documentation Napoleon}
\end{listing}
\begin{figure}[!ht]
\centering
\scalebox{1.0}{\includegraphics[max size={\textwidth}{\textheight}]{images/napoleon-module-level-docstring.png}}
@ -442,6 +904,7 @@ générer automatiquement le squelette de documentation d'un bloc de code:
Nous le verrons plus loin, Django permet de rendre la documentation immédiatement accessible depuis l'interface d'administration.
Toute information pertinente peut donc lier le code à un cas d'utilisation concret, et rien n'est jamais réellement perdu.
\section{Vérification du code \index{lint}}
Il existe plusieurs niveaux de \emph{linters}:
@ -449,16 +912,11 @@ Il existe plusieurs niveaux de \emph{linters}:
\begin{enumerate}
\def\labelenumi{\arabic{enumi}.}
\item
Le premier niveau concerne
\href{https://pypi.org/project/pycodestyle/}{pycodestyle}
(anciennement, \texttt{pep8} justement\ldots\hspace{0pt}), qui analyse
votre code à la recherche d'erreurs de convention.
Le premier niveau est assuré par \href{https://pypi.org/project/pycodestyle/}{pycodestyle}, qui analyse votre code à la recherche d'erreurs de convention de style.
\item
Le deuxième niveau concerne
\href{https://pypi.org/project/pyflakes/}{pyflakes}. Pyflakes est un
\emph{simple} \footnote{Ce n'est pas moi qui le dit, c'est la doc du
projet} programme qui recherchera des erreurs parmi vos fichiers
Python.
Le deuxième niveau est \href{https://pypi.org/project/pyflakes/}{pyflakes}.
Il s'agit d'un \emph{simple} \footnote{Ce n'est pas moi qui le dit, c'est la doc du
projet} programme qui recherchera différents types d'erreurs parmi votre code source.
\item
Le troisième niveau est
\href{https://pypi.org/project/flake8/}{Flake8}, qui regroupe les deux
@ -469,18 +927,12 @@ Il existe plusieurs niveaux de \emph{linters}:
cercles à l'Enfer} est \href{https://pylint.org/}{PyLint}.
\end{enumerate}
PyLint est le meilleur ami de votre \emph{moi} futur, un peu comme quand
vous prenez le temps de faire la vaisselle pour ne pas avoir à la faire
le lendemain: il rendra votre code soyeux et brillant, en posant des
affirmations spécifiques. A vous de les traiter en corrigeant le code ou
en apposant un \emph{tag} indiquant que vous avez pris connaissance de
la remarque, que vous en avez tenu compte, et que vous choisissez malgré
tout de faire autrement.
PyLint est le meilleur ami de votre \emph{moi} futur, un peu comme quand vous prenez le temps de faire la vaisselle pour ne pas avoir à la faire le lendemain: il rendra votre code soyeux et brillant, en posant des affirmations spécifiques.
A vous de les traiter en corrigeant le code ou en apposant un \emph{tag} indiquant que vous avez pris connaissance de la remarque, que vous en avez tenu compte, et que vous choisissez malgré tout de faire autrement.
\begin{listing}[H]
\includegraphics{images/calvin/time-machine.jpg}
\begin{graphic}{images/calvin/time-machine.jpg}
\caption{Calvin \& Hobbes se rendent dans le futur pour récupérer les devoirs que son moi-du-futur aura fait (et pour ne pas avoir à les faire lui-même).}
\end{listing}
\end{graphic}
Pour vous donner une idée, voici ce que cela pourrait donner avec un
code pas très propre et qui ne sert à rien:
@ -506,6 +958,8 @@ code pas très propre et qui ne sert à rien:
\caption{Un morceau de code qui ne sert à rien}
\end{listing}
\subsection{Flake8}
Avec Flake8, nous obtiendrons ceci:
\begin{listing}[!ht]
@ -537,7 +991,10 @@ Nous trouvons des erreurs:
votre application se prendra méchamment les pieds dans le tapis).
\end{itemize}
L'étape d'après consiste à invoquer pylint. Lui, il est directement moins conciliant:
\subsection{Pylint}
L'étape d'après consiste à invoquer pylint.
Lui, il est directement moins conciliant:
\begin{verbatim}
$ pylint test.py
@ -601,16 +1058,97 @@ Cet élément peut être:
\begin{enumerate}
\item \textbf{Une ligne de code}
\item \textbf{Un bloc de code} - une fonction, une méthode, une classe, un module, \ldots
\item \textbf{Un projet entier}, en spécifiant la non-prise en compte au niveau du fichier \texttt{.pylintrc}, qui contient
\item \textbf{Un bloc de code} : une fonction, une méthode, une classe, un module, \ldots
\item \textbf{Un projet entier}
\end{enumerate}
\subsection{Ignorer une ligne de code}
\subsubsection{Ignorer une ligne de code}
\subsection{Ignorer un bloc de code}
\subsection{Ignorer une catégorie globalement}
\subsubsection{Ignorer un bloc de code}
\subsubsection{Ignorer une catégorie globalement}
\subsection{Ruff}
Lui, c'est le petit chouchou à la mode.
\href{https://beta.ruff.rs/docs/}{ruff}
\begin{itemize}
\item
10-100x faster than existing linters
\item
Installable via pip
\item
Python 3.11 compatibility
\item
pyproject.toml support
\item
Built-in caching, to avoid re-analyzing unchanged files
\item
Autofix support, for automatic error correction (e.g., automatically remove unused imports)
\item
Near-parity with the built-in Flake8 rule set
\item
Native re-implementations of dozens of Flake8 plugins, like flake8-bugbear
\item
First-party editor integrations for VS Code and more
\item
Monorepo-friendly, with hierarchical and cascading configuration
\end{itemize}
\begin{listing}[H]
\begin{verbatim}
[tool.ruff]
# Enable Pyflakes `E` and `F` codes by default.
select = ["E", "F"]
ignore = []
# Allow autofix for all enabled rules (when `--fix`) is provided.
fixable = ["A", "B", "C", "D", "E", "F", "..."]
unfixable = []
# Exclude a variety of commonly ignored directories.
exclude = [
".bzr",
".direnv",
".eggs",
".git",
".hg",
".mypy_cache",
".nox",
".pants.d",
".ruff_cache",
".svn",
".tox",
".venv",
"__pypackages__",
"_build",
"buck-out",
"build",
"dist",
"node_modules",
"venv",
]
per-file-ignores = {}
# Same as Black.
line-length = 88
# Allow unused variables when underscore-prefixed.
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
# Assume Python 3.10.
target-version = "py310"
[tool.ruff.mccabe]
# Unlike Flake8, default to a complexity level of 10.
max-complexity = 10
\end{verbatim}
\caption{Un morceau de code qui ne sert à rien}
\end{listing}
\section{Formatage de code}

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

@ -3,7 +3,7 @@
\begin{figure}[H]
\centering
\scalebox{1.0}{\includegraphics[max size={\textwidth}{\textheight}]{images/penguin-overflow.jpg}}
\caption{David Revoy}
\caption{"La Grande Vague" \url{https://www.peppercarrot.com/sr/viewer/framasoft-src__2020-11-06_la-grande-vague_by-David-Revoy.html} par David Revoy et Framasoft, CC-By}
\end{figure}
@ -19,23 +19,20 @@
--- Robert C. Martin Clean Architecture
\end{quote}
Il y a une raison très simple à aborder le déploiement dès maintenant : à trop attendre et à peaufiner son développement en local, on en oublie que sa finalité sera de se retrouver exposé et accessible depuis un
serveur.
Il est du coup probable d'oublier une partie des désidérata, de zapper une fonctionnalité essentielle ou simplement de passer énormément de temps à adapter les sources pour qu'elles puissent être mises à
disposition sur un environnement en particulier, une fois que leur développement aura été finalisé, testé et validé.
Un bon déploiement ne doit pas dépendre de dizaines de petits scripts éparpillés sur le disque.
L'objectif est qu'il soit rapide et fiable.
Ceci peut être atteint au travers d'un partitionnement correct, incluant le fait que le composant principal s'assure que chaque sous-composant est correctement démarré intégré et supervisé.
Il y a une raison très simple à aborder le déploiement dès maintenant : à trop attendre et à peaufiner son développement pour soi-même, on en oublie que sa finalité sera de se retrouver exposé et accessible depuis un serveur.
Il est probable d'oublier une partie des désidérata, de zapper une fonctionnalité essentielle ou simplement de passer énormément de temps à adapter les sources pour qu'elles puissent être mises à disposition sur un environnement en particulier, une fois que leur développement aura été finalisé, testé et validé.
Aborder le déploiement dès le début permet également de rédiger dès le début les procédures d'installation, de mises à jour et de sauvegardes.
Un bon déploiement ne doit pas dépendre de dizaines de petits scripts éparpillés sur le disque: l'objectif est que celui-ci soit rapide et fiable.
Cet objectif peut être atteint au travers d'un partitionnement correct, incluant le fait que le composant principal s'assure que chaque sous-composant est correctement démarré intégré et supervisé.
Aborder le déploiement dès le début du développement permet également de rédiger les procédures d'installation, de mises à jour et de sauvegardes adaptées.
A la fin de chaque intervalle de développement, les fonctionnalités auront dû avoir été intégrées, testées, fonctionnelles et un code propre, démontrable dans un environnement similaire à un environnement de production, et créées à partir d'un tronc commun au développement
\cite{devops_handbook}.
Déploier une nouvelle version doit être le plus simple possible, et doit se résumer, dans le pire des cas, à quelques lignes d'un script Bash.
\begin{quote}
Because value is created only when our services are running into production, we must ensure that we are not only delivering fast flow, but that our deployments can also be performed without causing chaos and
disruptions such as service outages, service impairments, or security or compliance failures.
Because value is created only when our services are running into production, we must ensure that we are not only delivering fast flow, but that our deployments can also be performed without causing chaos and disruptions such as service outages, service impairments, or security or compliance failures.
--- DevOps Handbook Introduction
\end{quote}
@ -50,11 +47,23 @@ En production:
En restant avec les paramètres par défaut, il est plus que probable que vous rencontriez rapidement des erreurs de verrou parce qu'un autre processus a déjà pris la main pour écrire ses données. En bref, vous avez quelque chose qui fonctionne, qui répond à un besoin, mais qui va attirer la grogne de ses utilisateurs pour des problèmes de latences, pour des erreurs de verrou ou simplement parce que le serveur répondra trop lentement.
\end{itemize}
L'objectif de cette partie est de parcourir les différentes possibilités qui s'offrent à nous en termes de déploiement, tout en faisant en sorte que le code soit le moins couplé possible à sa destination de
production. L'objectif est donc de faire en sorte qu'une même application puisse être hébergées par plusieurs hôtes sans avoir à subir de modifications. Nous vous renvoyons vers les 12-facteurs dont nous
avons déjà parlé et qui vous énormément nous aider, puisque ce sont des variables d'environnement qui vont réellement piloter le câblage entre l'application, ses composants et son hébergement.
L'objectif de cette partie est de parcourir les différentes possibilités qui s'offrent à nous en termes de déploiement, tout en faisant en sorte que le code soit le moins couplé possible à sa destination de production.
L'objectif est donc de faire en sorte qu'une même application puisse être hébergées par plusieurs hôtes sans avoir à subir de modifications. Nous vous renvoyons vers les 12-facteurs dont nous avons déjà parlé et qui vous énormément nous aider, puisque ce sont des variables d'environnement qui vont réellement piloter le câblage entre l'application, ses composants et son hébergement.
RedHat proposait récemment un article intitulé \emph{*What Is IaaS*}, qui présentait les principales différences entre types d'hébergement.
RedHat proposait récemment un article intitulé \emph{*What Is IaaS*} \footnote{\url{https://www.redhat.com/fr/topics/cloud-computing/what-is-iaas}}, qui présentait les principales différences entre types d'hébergement.
\begin{tabular}{r c c c c}
& On-site & IaaS & PaaS & SaaS \\
Applications & & & & \\
Données & & & & \\
Exécution & & & & \\
Middleware & & & & \\
Système d'exploitation & & & & \\
Virtualisation & & & & \\
Serveurs & & & & \\
Stockage & & & & \\
Réseau & & & & \\
\end{tabular}
\begin{figure}[H]
\centering
@ -82,7 +91,9 @@ Dans cette partie, nous aborderons les points suivants:
\item
Définir l'infrastructure et les composants nécessaires à notre application
\item
Configurer l'hôte qui hébergera l'application et y déployer notre application: dans une machine physique, virtuelle ou dans un container. Nous aborderons aussi les déploiements via Ansible et Salt. A ce stade, nous aurons déjà une application disponible.
Configurer l'hôte qui hébergera l'application et y déployer notre application: dans une machine physique, virtuelle ou dans un container.
Nous aborderons aussi les déploiements via Ansible et Salt.
A ce stade, nous aurons déjà une application disponible.
\item
Configurer les outils nécessaires à la bonne exécution de ce code et de ses fonctionnalités: les différentes méthodes de supervision de l'application, comment analyser les fichiers de logs, comment intercepter correctement une erreur si elle se présente et comment remonter correctement l'information.
\end{enumerate}

View File

@ -60,6 +60,14 @@
isbn = {978-1-491-95452-2},
url = {http://shop.oreilly.com/product/0636920049555.do}
}
@book{restful_web_apis,
title = {RESTful Web APIs},
booktitle = {Services for a changing world},
author = {Leonard Richardson and Mike Amundsen and Sam Ruby},
year = {2013},
isbn = {9781449359737},
url = {https://www.oreilly.com/library/view/restful-web-apis/9781449359713/},
}
@book{roads_and_bridges,
title = {Roads and Bridges},
booktitle = {The Unseen Labor Behind Our Digital Infrastructure},