hermes-client

Explications sur le fonctionnement ou la structure de certains composants clés d’hermes-client.

Boris Lechner 2025-05-05 e022507882f1c7d53ec4dc72b08922261dfdd25f

Sous-sections de hermes-client

Workflow

hermes-client

  • 1. charge son modèle de données à partir du fichier de configuration
  • 2. s’il existe, charge le modèle de données précédent depuis le cache
  • 3. le cas échéant, notifie les avertissements du modèle de données : type distant et attributs distants présents dans le modèle de données, mais pas dans le schéma de données
  • 4. si un schéma distant existe dans le cache, charge la file d’erreurs depuis le cache
  • 5. si le client n’a pas encore été initialisé (aucune séquence initSync complète n’a encore été traitée) :
    • 5.1. traite la séquence initSync, si une séquence initSync complète est disponible sur le bus de messages
    • 5.2. redémarre à partir de l’étape 5.
  • 6. si le client a déjà été initialisé (une séquence initSync complète a déjà été traitée) :
    • 6.1. s’il s’agit de la première itération de la boucle (l’étape 7. n’a jamais été atteinte) :
      • 6.1.1. si le modèle de données dans la configuration diffère de celui mis en cache, traite la mise à jour du modèle de données :
        • 6.1.1.1 génère les événements de suppression pour toutes les entrées des types de données supprimés, les traite et purge les fichiers de cache de ces types de données
        • 6.1.1.2 génère un différentiel entre les données mises en cache construites sur le modèle de données précédent et les mêmes données converties au nouveau modèle de données, puis génère les événements correspondants et les traite
    • 6.2. si errorQueue_retryInterval est écoulé depuis la dernière tentative, réessaye de traiter les événements présents dans la file d’erreurs
    • 6.3. si trashbin_purgeInterval est écoulé depuis la dernière tentative, purge les objets expirés de la corbeille
    • 6.4. boucle sur tous les événements disponibles sur le bus de messages et traite chacun d’eux pour appeler sa méthode de gestion des événements correspondante lorsqu’elle existe dans le plugin client
  • 7. lorsqu’au moins un événement a été traité ou si l’application a été invitée à s’arrêter :
    • 7.1. enregistre les fichiers cache de la file d’erreurs, de l’application, des données
    • 7.2. appelle la méthode de gestion de l’événement spécial onSave lorsqu’elle existe dans le plugin client
    • 7.3. notifie tout changement dans la file d’erreurs
  • 8. redémarre à partir de l’étape 5. si l’application n’a pas été invitée à s’arrêter

Si une exception est levée à l’étape 6.1.1, elle sera considérée comme une erreur fatale, notifiée et le client s’arrêtera.

Si une exception est levée aux étapes 5. à 6., elle est notifiée, son événement est ajouté à la file d’erreurs et le client redémarre à partir de l’étape 7..

Boris Lechner 2025-05-05 e022507882f1c7d53ec4dc72b08922261dfdd25f

Traitement des événements

Comme le modèle de données sur le serveur diffère de celui sur le client, les clients doivent convertir les événements distants reçus sur le bus de messages en événements locaux. Si l’événement local résultant est vide (le type de données ou les attributs modifiés dans l’événement distant ne sont pas définis sur le modèle de données client), l’événement est ignoré.

Lors de la mise à jour du modèle de données client, le client peut générer des événements locaux qui n’ont pas d’événement distant correspondant, i.e. pour mettre à jour une valeur d’attribut calculée avec un modèle Jinja qui vient d’être modifié.

flowchart TB
  subgraph Hermes-client
    direction TB
    datamodelUpdate[["mise à jour du modèle de données"]]
    remoteevent["Événement distant"]
    localevent["Événement local"]
    eventHandler(["Méthode de gestion de l'événement du plugin client"])
  end
  datamodelUpdate-->|génère|localevent
  MessageBus["Bus de message"]-->|fournit|remoteevent
  remoteevent-->|convertit en|localevent
  localevent-->|passe à la bonne|eventHandler
  eventHandler-->|traite|Target["Cible"]

  classDef external fill:#fafafa,stroke-dasharray: 5 5
  class MessageBus,Target external

Boris Lechner 2025-05-05 e022507882f1c7d53ec4dc72b08922261dfdd25f

Clés étrangères

Il arrive que les objets soient liés entre eux par des clés étrangères. Lorsqu’une erreur survient sur un objet dont la clé primaire fait référence à celle d’un ou plusieurs autres objets “parents”, il peut être souhaitable d’interrompre le traitement de tout ou partie des événements de ces objets parents jusqu’à ce que ce premier événement ait été correctement traité. Cela peut être réalisé en ajoutant les événements des objets parents à la file d’attente des erreurs au lieu de tenter de les traiter.

La première chose à faire est de déclarer les clés étrangères via hermes-server.datamodel.data-type-name.foreignkeys dans la configuration d’hermes-server. Le serveur ne fera rien d’autre avec ces clés étrangères que de les propager aux clients.

Ensuite, il faut définir la politique à appliquer aux clients via hermes-client.foreignkeys_policy dans chaque configuration hermes-client. Il y en a trois possibles :

  • disabled : Aucun événement, la politique est désactivée. Probablement pas pertinent dans la plupart des cas, mais pourrait peut-être être utile à quelqu’un un jour.
  • on_remove_event : Uniquement sur les événements removed. Devrait suffire dans la plupart des cas.
  • on_every_event : Sur tous les types d’événements (added, modified, removed). Pour assurer une cohérence parfaite quoi qu’il arrive.

Boris Lechner 2025-05-05 e022507882f1c7d53ec4dc72b08922261dfdd25f

Auto remédiation

Parfois, un événement peut être stocké dans la file d’erreurs en raison d’un problème de données (e.g. un nom de groupe avec un point terminal génère une erreur sur Active Directory). Si le point terminal est ensuite supprimé du nom de groupe sur la source de données, l’événement modified sera stocké dans la file d’erreurs et ne sera pas traité tant que le précédent n’aura pas été traité, ce qui ne pourra jamais se produire sans procéder à une opération risquée et non-souhaitable : l’édition manuelle du fichier cache du client.

L’auto remédiation résout ce type de problèmes en fusionnant les événements d’un même objet dans la file d’erreurs. Elle n’est pas activée par défaut, car elle peut perturber l’ordre de traitement normal des événements.

Exemple

Prenons comme exemple un groupe créé avec un nom invalide. Comme son nom est invalide, le traitement de sa création échouera et l’événement sera stocké dans la file d’erreurs comme ceci :

flowchart TB
  subgraph errorqueue [File d'erreurs]
    direction TB
    ev1
  end

  ev1["`**événement 1**
     
    *eventtype*: added
    *objType*: ADGroup
    *objpkey*: 42
    *objattrs*: {
      grp_pkey: 42
      name: 'NomInvalide.'
      desc: 'Demo group'
    }`"]

  classDef leftalign text-align:left
  class ev1 leftalign

Comme l’erreur a été notifiée, quelqu’un corrige le nom du groupe dans la source de données. Cette modification entraîne un événement modified correspondant. Cet événement modified ne sera pas traité, mais ajouté à la file d’erreurs puisque son objet a déjà un événement dans la file d’erreurs.

  • sans auto-remédiation, tant que le premier événement n’aura pas été traité avec succès, le deuxième ne sera même pas tenté. La correction est bloquée.
  • avec l’auto-remédiation, la file d’erreurs fusionne les deux événements, puis lors du prochain traitement de la file d’erreurs, l’événement résultant sera traité avec succès.
flowchart TB
  subgraph errorqueuebis [avec auto remédiation]
    direction TB
    ev1bis
  end

  subgraph errorqueue [Sans auto remédiation]
    direction TB
    ev1
    ev2
  end

  ev1["`**événement 1**
     
    *eventtype*: added
    *objType*: ADGroup
    *objpkey*: 42
    *objattrs*: {
      grp_pkey: 42
      name: 'NomInvalide.'
      desc: 'Demo group'
    }`"]

  ev2["`**événement 2**
     
    *eventtype*: modified
    *objType*: ADGroup
    *objpkey*: 42
    *objattrs*: {
      modified: {
        name: 'NomValide'
      }
    }`"]

  ev1bis["`**événement 1**
     
    *eventtype*: added
    *objType*: ADGroup
    *objpkey*: 42
    *objattrs*: {
      grp_pkey: 42
      name: 'NomValide'
      desc: 'Demo group'
    }`"]

  classDef leftalign text-align:left
  class ev1,ev2,ev1bis leftalign

Boris Lechner 2025-05-05 e022507882f1c7d53ec4dc72b08922261dfdd25f

Fichiers cache

_hermes-client-name.json

Contient l’état du client :

  • queueErrors: dict[str, str]

    Dictionnaire contenant l’ensemble des messages d’erreurs des objets en file d’erreurs, pour pouvoir notifier de tout changement.

  • datamodelWarnings: dict[str, dict[str, dict[str, Any]]]

    Dictionnaire contenant les avertissements actuels du modèle de données, pour pouvoir notifier de tout changement.

  • exception: str | None

    Chaîne contenant la dernière trace d’exception.

  • initstartoffset: Any | None

    Contient l’offset du premier message de la séquence initSync sur le bus de messages.

  • initstopoffset: Any | None

    Contient l’offset du dernier message de la séquence initSync sur le bus de messages.

  • nextoffset: Any | None

    Contient l’offset du prochain message à traiter sur le bus de messages.

_hermesconfig.json

Cache de la configuration précédente, utilisé pour pouvoir construire le modèle de données précédent et pour générer les expressions Jinja avec les plugins d’attributs.

_dataschema.json

Cache du dernier schéma de données, reçu d’hermes-server.

_errorqueue.json

Cache de la file d’erreurs.

RemoteDataType.json

Un fichier par type de données distantes, contenant toutes les entrées distantes, telles qu’elles ont été traitées par le client sans générer d’erreur.

Lorsque la file d’erreurs est vide, doit avoir le même contenu que RemoteDataType_complete__.json

RemoteDataType_complete__.json

Un fichier par type de données distantes, contenant toutes les entrées distantes, telles qu’elles sont sur le serveur.

Lorsque la file d’erreurs est vide, doit avoir le même contenu que RemoteDataType.json

trashbin_RemoteDataType.json

Uniquement si la corbeille est activée. Un fichier par type de données distant, contenant toutes les entrées distantes qui se trouvent dans la corbeille, telles qu’elles ont été traitées par le client sans générer d’erreur.

Lorsque la file d’erreurs est vide, doit avoir le même contenu que trashbin_RemoteDataType_complete__.json

trashbin_RemoteDataType_complete__.json

Uniquement si la corbeille est activée. Un fichier par type de données distant, contenant toutes les entrées distantes qui se trouvent dans la corbeille, telles qu’elles sont sur le serveur.

Lorsque la file d’erreurs est vide, doit avoir le même contenu que trashbin_RemoteDataType.json

__LocalDataType.json

Un fichier par type de données local, contenant toutes les entrées locales, telles qu’elles ont été traitées par le client sans générer d’erreur.

Lorsque la file d’erreurs est vide, doit avoir le même contenu que __LocalDataType_complete__.json

__LocalDataType_complete__.json

Un fichier par type de données local, contenant toutes les entrées locales, telles qu’elles sont sur le serveur.

Lorsque la file d’erreurs est vide, doit avoir le même contenu que __LocalDataType.json

__trashbin_LocalDataType.json

Uniquement si la corbeille est activée. Un fichier par type de données local, contenant toutes les entrées locales qui se trouvent dans la corbeille, telles qu’elles ont été traitées par le client sans générer d’erreur.

Lorsque la file d’erreurs est vide, doit avoir le même contenu que __trashbin_LocalDataType_complete__.json

__trashbin_LocalDataType_complete__.json

Uniquement si la corbeille est activée. Un fichier par type de données local, contenant toutes les entrées locales qui se trouvent dans la corbeille, telles qu’elles sont sur le serveur.

Lorsque la file d’erreurs est vide, doit avoir le même contenu que __trashbin_LocalDataType.json