Fabric: « rôles » compatibles v2 ou similaires

Créé le 22 avr. 2017  ·  7Commentaires  ·  Source: fabric/fabric

Synopsis

Au moment de la rédaction, la branche v2 a une classe Group qui devrait être capable de servir d'unités anciennement connues sous le nom de "rôles", alias "un groupe d'hôtes pour faire des choses avec/sur".

Cependant, il n'y a pas encore de moyen spécifique d'organiser ou d'étiqueter les objets Group ; c'est assez "fait" pour le cas d'utilisation de l'API pure des utilisateurs avancés qui souhaitent utiliser leur propre manière spécifique de les créer, mais il manque quelque chose pour les utilisateurs orientés CLI ou les personnes intermédiaires qui veulent quelque chose de framework à construire.

En d'autres termes, à moins que vous ne rouliez uniquement avec l'API, avoir des objets Group quelque part est inutile si la CLI ou les bits d'appel de tâche n'ont aucun moyen de les trouver !

Fond

Dans la v1, les rôles étaient en fait un seul espace de noms plat mappant des étiquettes de chaîne simples à ce qui serait des groupes dans la v2, et ils pouvaient être sélectionnés sur l'interface de ligne de commande au moment de l'exécution ( fab --roles=web,db ) et/ou enregistrés comme cibles par défaut pour les tâches ( @task('db') \n def migrate(): ), un peu comme les hôtes.

Les utilisateurs les ont définis dans env.roledefs , un simple dict ; toute fonctionnalité intermédiaire à avancée tournait autour de sa modification, généralement au moment de l'exécution (via une pré-tâche ou un sous-programme), parfois au moment du chargement du module.

Cas d'utilisation / besoins / sous-fonctionnalités spécifiques

  • Mappage basique et naïf pour une utilisation/référence n'importe où ailleurs dans le système : saisissez un nom, récupérez un itérable de Group s et/ou Connection s.

    • L'aliasing veut souvent aller de pair avec cela, donc par exemple un Lexicon au lieu d'un dict .

    • Des constructions encore plus profondes, telles que 'bundling', par exemple, vous avez des mappages directs nommés db , web , lb , mais ensuite un nom de deuxième niveau appelé prod qui est toujours l'union des trois autres. J'oublie si j'ai encore ajouté cela à Lexicon . Il est possible qu'il existe d'autres sous-classes de cartes qui le font déjà aussi.

    • En plus/alternativement, des choses comme le globbing ou d'autres syntaxes de chaîne, bien que je préfère personnellement tirer parti du fait que Python n'est pas "stringly typé" ...

  • « Mappage inversé » utile pour que vous puissiez identifier à quels groupes appartient une connexion donnée.

    • Problématique : parce qu'il n'y a actuellement aucun état partagé global, la réponse naïve à cette question - en utilisant l'identité - tombe car vous pouvez techniquement créer plusieurs objets Connection identiques.

    • D'autant plus que Group peut les créer implicitement en votre nom si vous lui donnez simplement des chaînes d'hôte abrégées, bien que ce ne soit qu'une option pratique.

    • Cependant, étant donné cette contrainte d'absence d'état global, je ne vois pas de problèmes évidents avec l'utilisation des tests d'égalité à la place, donc cela devrait être faisable, par exemple if cxn in group fonctionnerait même si cxn est un objet distinct du membre égal à l'intérieur de group .

    • La seule chose qui me vient à l'esprit est s'il y avait des liens forts et avec état d'une connexion à un groupe (devrait être des groupes, au pluriel) le tenant, au lieu de l'inverse, mais je ne vois pas de bonnes raisons pour cela désinvolte.

  • Fortement lié au précédent : capacité à inspecter/afficher ce qu'est le « rôle en cours d'exécution » (quelque chose que les gens voulaient depuis longtemps dans la v1 qui n'était pas trivial en raison de sa conception)

    • Le problème principal est qu'il s'agit en réalité de deux questions semi distinctes : « de quel(s) rôle(s) l'hôte actuel fait-il partie, de manière générale » (en gros, ce cas d'utilisation précédent de la recherche inversée) mais aussi « de quel(s) rôle(s) était le mécanisme d'exécution spécifiquement appelé à se présenter contre ».

    • En d'autres termes, étant donné l'hôte « foo » appartenant aux rôles A, B et C : au sein d'une tâche donnée dont le contexte est « foo », mais qui a été exécutée en raison d'une demande d'« exécution sur le rôle A », est un utilisateur à la recherche de une réponse de « A, B et C » (les rôles « foo » sont dans l'ensemble) ou simplement « A » (le rôle en cours d'exécution) ?

    • Cela ressemble vraiment à deux appels d'API distincts, même si les demandes de fonctionnalités dont je me souviens avoir fusionné les deux.

  • Sélection de cible sur la CLI, globalement et/ou par tâche

    • Une extension du système CLI d'Invoke pour tenir compte des "indicateurs que toutes les tâches ajoutent à ce qu'elles définissent" peut être utile ou requise pour cela. Ce qui tombe fermement dans le territoire pyinvoke/invoke#205, en fait, de sorte que la priorité vient d'être plus élevée qu'elle ne l'était déjà (ce qui était assez élevé.)

  • Idem au niveau des tâches par défaut

    • Bien que les valeurs par défaut de la cible au niveau de la tâche veuillent vraiment être l'un des éléments suivants : connexion, connexions, groupe obj, groupe objs ou nom évaluant _to_ group objs (ce dernier est sans doute la seule chose qui se rapporte directement à ce ticket)

  • Idem par défaut au niveau de la collection (NOUVEAU dans la v2 !)

    • C'est-à-dire "toutes les tâches de $submodule s'exécutent par défaut sur le rôle db "

    • Même chose que le point précédent - cette valeur par défaut veut autoriser un certain nombre de valeurs différentes, pas seulement une clé de chaîne.

  • Y a-t-il autre chose de nouveau et d'excitant permis par une approche OO qui veut vraiment aller de pair avec cela ? N'oubliez pas que l'accent doit être mis sur les blocs de construction et sur l'habilitation des utilisateurs avancés, et non sur la réinvention totale, par exemple, de systèmes comme Chef ou Ansible.

Idées/préoccupations de mise en œuvre

  • Si nous avons utilisé le système de configuration comme vecteur de stockage principal, les valeurs "veulent" être des primitives afin qu'elles puissent être stockées dans yaml, json, etc. list-o-dicts", etc.
  • Si nous nous attendons à ce que les définitions soient principalement en Python, nous pouvons simplement dire "instancier des objets de groupe", puis nous avons la possibilité de fusionner ces données dans le système de configuration ou de les laisser autonomes d'une manière ou d'une autre.

    • Je pense que je préfère ce dernier parce que tout mettre littéralement dans les dicts de configuration imbriqués donne l'impression que cela conduira à de mauvaises nouvelles.

  • Les constructions plus profondes telles que l'alias et le regroupement ajoutent de la complexité et des problèmes d'ordre (par exemple, imaginez une configuration d'alias triviale où la valeur de key1 est un groupe mais la valeur de key2 est key1 ; maintenant vous devez explorer la structure deux fois pour résoudre ou vérifier key2)

    • Cependant, si nous optons pour une approche principalement "do it in-python", cela ressemble beaucoup à l'API du système de configuration, où vous pouvez commencer avec une structure déclarative, mais tout ce qui est activé en plus par les appels de méthode après cette configuration initiale. Je ne pense pas que ce soit horrible ? EDIT : et je pense que c'est exactement comme ça que Lexicon fonctionne de toute façon.

  • Quel que soit le format, nous devons déterminer comment les utilisateurs avancés voudront le générer à la volée à partir de sources externes ou similaires ; ceci, plus les problèmes d'alias et autres, implique que nous ne voulons peut-être pas cela dans une structure naïve "stockée" quelque part, mais en tant qu'API sur un ou plusieurs objets appelés à la générer.

    • Je soupçonne que nous pouvons vouloir travailler "vers le bas" à partir de la sélection des rôles/groupes, en arrivant à l'API de plus haut niveau pour "transformer ce que l'utilisateur a fourni en une unité de cibles exploitable", car les utilisateurs les plus avancés voudront nécessairement avoir un contrôle total sur la mise en œuvre de cet appel d'API. Ensuite, nous pouvons, comme toujours, fournir ce qui semble être un cas commun utile mais qui est clairement marqué comme "juste une façon de le faire".

    • @RedKrieg a une idée astucieuse dans ce sens où nous avons @group comme @task , et les fonctions ne sont pas des unités de travail exécutables, mais produisent à la place des objets Group.

    • Cette approche réutilise nativement la hiérarchie des tâches (Collection), qui est pratique (pourquoi réinventer la roue) et élégante (car dans des cas réels, les définitions de rôles/groupes correspondent fréquemment aux tâches qui les utilisent !)



      • Cela fonctionne également bien même si vos groupes NE correspondent PAS à vos tâches, car vous pouvez simplement écrire les définitions au niveau de votre collection racine. Peasy facile.



    • Je ne sais pas s'il est préférable de renvoyer un seul groupe à partir de chaque fonction, ou si nous voulons avoir la possibilité de générer plusieurs groupes (ou connexions), ou s'il est préférable de ne pas le faire du tout en tant que fonctions décorées, mais simplement en tant qu'appels d'API. Collection (comme la façon dont les configurations au niveau de la collection sont stockées).

    • Par exemple, le cas d'utilisation où les données de groupe/rôle sont dynamiques et en dehors de Fabric doit encore être résolu ici (c'est pourquoi j'ai noté plus tôt que nous devons d'abord identifier l'API de plus haut niveau pour cet espace ; nous devons ensuite voir comment cela s'articule avec cette idée de niveau intermédiaire.)

Feature

Commentaire le plus utile

Salut, je ne sais pas ce qui est arrivé à ce logiciel après de nombreuses années, mais j'ai vraiment raté le concept de "rôles" dans [email protected] , surtout lors de l'exécution de $ fab -R dev

Tous les 7 commentaires

À partir de la liste de diffusion :

Nous avons mis en place notre propre API REST interne qui remplit env.roledefs de manière dynamique en fonction du projet en cours de déploiement et dépend fortement du fait de ne pas intégrer les chaînes d'hôte dans le fabfile du projet ou de les spécifier dans la CLI.

Nos cas d'utilisation sont :

  1. Base de code sans environnement https://12factor.net/config. Les environnements (rôles) et leurs chaînes d'hôtes respectives sont stockés dans une base de données centralisée. Chaque fabfile.py a quelque chose comme ceci (il remplit env.roledefs lorsque le fichier est importé) :
EnvironmentDatabaseAPIClient(
    'https://rest.api.url/schema/',
    env.service_name,
).apply_env()
  1. Nombre d'environnements de serveurs - plusieurs environnements de test (certains d'entre eux sont privés, d'autres publics) et plusieurs environnements de production (pour différents clients). Chaque environnement se compose d'un ou plusieurs hôtes et est mappé au rôle Fabric.

  2. Chaque service ( env.service_name dans l'exemple ci-dessus) a un ensemble d'environnements différent.

  3. Nous avons aussi des méta-rôles (groupes de rôles). Ils sont préfixés par group- : group-production , group-test , group-external , group-internal , group-all . Cela nous permet de déployer sur plusieurs rôles de serveur sans les spécifier un par un, par exemple group-all déploie sur tous les rôles, à la fois en production et en test.

  4. Nous avons des tâches Fabric spéciales pour imprimer des informations sur les groupes de rôles, les rôles et les hôtes.

  5. Nous comptons également beaucoup sur le mappage inverse des chaînes d'hôtes vers les noms de rôle (les chaînes d'hôtes sont uniques par service_name). Ceci est utilisé pour la journalisation du déploiement et les notifications. Fondamentalement, nous enregistrons les déploiements de service sur chaque hôte et envoyons une notification Slack lorsque le service a été déployé sur tous les hôtes d'un rôle. Le serveur EnvironmentDatabaseAPI en est responsable (il conserve les journaux et l'état du déploiement). Cela se fait en décorant les tâches de tissu avec un décorateur qui soumet env.host , env.port et env.service_name (plus les informations de validation) au serveur API.

  6. Nous prévoyons d'ajouter l'authentification de déploiement à l'avenir, également très probablement pour extraire plus env variables

Merci @max-arnold ! Je reconnais également beaucoup de ceux de mes propres cas d'utilisation dans le passé. Le bit de mappage inverse en particulier, je me souviens d'être venu plusieurs fois dans la v1, je l'ai donc ajouté à la liste.

Pour que Fabric v2 me soit utile, j'aurais besoin d'un moyen de dire à fab quel ensemble d'hôtes exécuter une tâche.

Auparavant, je définissais des rôles, puis exécutais fab -R ... . (En fait, les rôles ont été définis par programme à l'aide d'une plage d'adresses IP, mais ce n'est pas une exigence et une liste statique dans un fichier YAML conviendrait.)

Je ne parviens pas à trouver d'équivalent dans Fabric v2 et je n'ai pas non plus réussi à émuler cette fonctionnalité en utilisant :

  • un fichier de configuration fabric.yaml contenant
active_hostset: null
hostsets:
  myhostset:
  - ...
  • active_hostset = config["hostsets"][config["active_hostset"]] en fabfile.py
  • env INVOKE_ACTIVE_HOSTSET=myhostset fab ...

Au lieu de la liste d'hôtes attendue, j'obtiens KeyError: 'active_hostset' .

Nous mappons différents ensembles d'hôtes à chaque rôle pour chacun de nos environnements dans Fabric v1, et l'environnement est défini en exécutant une tâche role.environment:staging pour le spécifier. Cette tâche influence donc les hôtes utilisés par les tâches suivantes.

Dans la v2, nous avons essayé d'utiliser une tâche personnalisée, mais le problème est que Executor.expand_calls s'exécute avant l'exécution de notre tâche role.environment et qu'aucune des tâches suivantes ne connaît l'environnement afin de créer dynamiquement ses listes d'hôtes.

Faire de Executor.expand_calls un générateur permet à l'exécution des tâches d'influencer l'exécution des tâches ultérieures. Donc, mon exemple ci-dessus fonctionne, où nous avons un Task qui doit connaître son environnement pour étendre correctement les rôles aux hôtes. par exemple fab role.environment dev deploy.app - la tâche role.environment est maintenant exécutée avant que deploy.app soit étendu, et donc deploy.app connaît l'environnement et peut configurer ses hôtes, puis est étendu dans le bon ensemble de tâches.

J'ai prototypé ceci dans mes forks:
https://github.com/pyinvoke/invoke/compare/master...rectalogic :expand-generator
https://github.com/fabric/fabric/compare/master...rectalogic :expand-generator

Salut, je ne sais pas ce qui est arrivé à ce logiciel après de nombreuses années, mais j'ai vraiment raté le concept de "rôles" dans [email protected] , surtout lors de l'exécution de $ fab -R dev

Nous utilisons également des rôles pour représenter le même ensemble d'opérations dans différents environnements. Peut-être que séparer le concept d'un rôle nommé et d'un environnement nommé serait utile ? Comme dans, le rôle Web dans l'environnement de développement.

Cette page vous a été utile?
0 / 5 - 0 notes