Fabric: v2-kompatible 'Rollen' oder ähnliches

Erstellt am 22. Apr. 2017  ·  7Kommentare  ·  Quelle: fabric/fabric

Zusammenfassung

Zum Zeitpunkt des Schreibens hat der v2-Zweig eine Group Klasse, die in der Lage sein sollte, als die Einheiten zu dienen, die früher als "Rollen" bekannt waren, auch bekannt als "ein Haufen Hosts, mit denen/mit denen man Dinge tun kann".

Es gibt jedoch noch keine spezielle Möglichkeit, Group Objekte zu organisieren oder zu kennzeichnen; es ist "fertig" genug für den reinen API-Anwendungsfall von fortgeschrittenen Benutzern, die ihre eigene spezifische Art der Erstellung verfolgen möchten, aber es fehlt alles für CLI-orientierte Benutzer oder fortgeschrittene Benutzer, die etwas Frameworkisches zum Aufbauen wollen.

Anders ausgedrückt, es sei denn, Sie arbeiten ausschließlich mit der API, ist es nutzlos, Gruppenobjekte irgendwo herumliegen zu lassen, wenn die CLI oder die Task-Aufrufbits keine Möglichkeit haben, sie zu finden!

Hintergrund

In v1 waren Rollen effektiv ein einzelner flacher Namespace, der einfache Zeichenfolgenlabels zu den Gruppen in v2 zuordnete, und sie konnten zur Laufzeit auf der CLI ausgewählt ( fab --roles=web,db ) und/oder als Standardziele für Aufgaben registriert werden ( @task('db') \n def migrate(): ), ähnlich wie bei Gastgebern.

Benutzer haben sie in env.roledefs , einem einfachen Diktat; jede mittlere bis erweiterte Funktionalität drehte sich darum, sie zu modifizieren, normalerweise zur Laufzeit (über Pre-Task oder Subroutine), manchmal zur Ladezeit des Moduls.

Spezifische Anwendungsfälle / Bedürfnisse / Unterfunktionen

  • Einfaches, naives Mapping zur Verwendung/Referenz an anderer Stelle im System: Geben Sie einen Namen ein, erhalten Sie einige iterierbare Group s und/oder Connection s zurück.

    • Aliasing möchte oft damit einhergehen, also zB ein Lexicon anstelle eines dict .

    • Noch tiefere Konstrukte, wie 'Bündelung', zB haben Sie direkte Zuordnungen namens db , web , lb , aber dann einen Namen der zweiten Ebene namens prod das ist immer die Vereinigung der anderen drei. Ich vergesse, wenn ich das schon zu Lexicon hinzugefügt habe. Möglicherweise gibt es andere Map-Unterklassen, die dies auch bereits tun.

    • Zusätzlich/alternativ Dinge wie Globbing oder andere String-Syntaxen, obwohl ich persönlich lieber die Tatsache nutzen würde, dass Python nicht "stringly typed" ist...

  • Nützliches "Reverse Mapping", damit Sie erkennen können, zu welchen Gruppen eine bestimmte Connection gehört.

    • Problematisch: Da es derzeit keinen globalen Shared State gibt, fällt die naive Antwort darauf - Identität verwenden - hin, da Sie technisch mehrere identische Connection-Objekte erstellen können.

    • Zumal Group sie implizit in Ihrem Namen erstellen kann, wenn Sie nur Host-Strings in Kurzform angeben, obwohl dies nur eine praktische Option ist.

    • Angesichts der Tatsache , dass Zwang keine globalen Zustand, ich nicht offensichtliche Probleme mit der Verwendung von Gleichheit zu testen , statt sehen, so dass machbar sein sollte, zB if cxn in group funktionieren würde, auch wenn cxn eine eindeutige Objekt vom gleichen Mitglied innerhalb von group .

    • Das einzige, was mir in den Sinn kommt, ist, wenn es starke, zustandsbehaftete Verbindungen von einer Verbindung zu einer Gruppe gäbe (müssen Gruppen sein, Plural), die sie hält, anstatt umgekehrt, aber ich sehe keine guten Gründe dafür.

  • Eng verwandt mit dem vorherigen: Fähigkeit, die "derzeit laufende Rolle" zu überprüfen / anzuzeigen (etwas, das die Leute in v1 seit langem wollten, was aufgrund seines Designs nicht trivial war)

    • Das Hauptproblem ist, dass dies wirklich zwei halb unterschiedliche Fragen sind: "Welche Rolle(n) ist der aktuelle Host-Teil im Allgemeinen" (im Grunde der frühere Anwendungsfall des Reverse-Lookups), aber auch "Welche Rolle(n) waren die" Hinrichtungsmaschinerie, die ausdrücklich aufgefordert wurde, gegen sie anzutreten".

    • Mit anderen Worten, ein gegebener Host 'foo', der zu den Rollen A, B und C gehört: innerhalb einer bestimmten Aufgabe, deren Kontext 'foo' ist, die jedoch aufgrund einer Anforderung zur 'Ausführung auf Rolle A' ausgeführt wurde, sucht ein Benutzer nach eine Antwort von "A, B und C" (die Rollen 'foo' ist insgesamt) oder nur "A" (die aktuell ausführende Rolle)?

    • Das fühlt sich wirklich wie zwei verschiedene API-Aufrufe an, obwohl die Funktionsanfragen, an die ich mich erinnere, die beiden miteinander vermischen.

  • Zielauswahl auf der CLI, global und/oder pro Aufgabe

    • Eine Erweiterung des CLI-Systems von Invoke, um "Flags zu berücksichtigen, die alle Tasks über ihre Definition erhalten" kann dafür nützlich oder erforderlich sein. Was in der Tat fest in das Gebiet von pyinvoke / invoke # 205 fällt, so dass es gerade eine höhere Priorität hat, als es bereits war (was ziemlich hoch war).

  • Dito Standardeinstellungen auf Aufgabenebene

    • Obwohl die Zielvorgaben auf Aufgabenebene wirklich eine der folgenden sein möchten: Verbindung, Verbindungen, Gruppen-Obj, Gruppen-Objs oder Name, der _to_-Gruppen-Objs auswertet (das letzte ist wohl das einzige, was direkt zu diesem Ticket gehört)

  • Dito Standardeinstellungen auf Sammlungsebene (NEU in v2!)

    • Dh "alle Aufgaben in $submodule werden standardmäßig gegen die Rolle db "

    • Gleiches Vorgehen wie im vorherigen Punkt - dieser Standard möchte eine Reihe verschiedener Werte zulassen, nicht nur einen Zeichenfolgenschlüssel.

  • Gibt es sonst noch etwas Neues und Aufregendes, das durch einen OO-Ansatz ermöglicht wird und das wirklich mitmachen möchte? Denken Sie daran, dass der Schwerpunkt auf Bausteinen liegen sollte und fortgeschrittenen Benutzern ermöglicht wird, und nicht beispielsweise darauf, Systeme wie Chef oder Ansible völlig neu zu erfinden.

Umsetzungsideen/-anliegen

  • Wenn wir das Konfigurationssystem als Hauptspeichervektor verwenden, "wollen" Werte primitiv sein, damit sie in yaml, json usw. gespeichert werden können, aber das ist eine Dose Würmer, die mit "store all Group/Connection kwargs in a big ol' list-o-dicts" usw.
  • Wenn wir erwarten, dass die Definitionen hauptsächlich in Python vorliegen, können wir einfach "Gruppenobjekte instanziieren" sagen, und dann haben wir die Möglichkeit, diese Daten in das Konfigurationssystem einzufügen oder es irgendwie eigenständig zu belassen.

    • Ich denke, ich bevorzuge letzteres, weil es sich anfühlt, als würde es zu schlechten Nachrichten führen, wenn man buchstäblich alles in die verschachtelten Konfigurations-Diktate hineinfüllt.

  • Die tieferen Konstrukte wie Aliasing und Bündelung fügen Komplexität und Ordnungsprobleme hinzu (dh stellen Sie sich ein triviales Alias-Setup vor, bei dem der Wert von key1 eine Gruppe ist, aber der Wert von key2 ist key1; jetzt müssen Sie die Struktur zweimal crawlen, um key2 aufzulösen oder zu überprüfen)

    • Wenn wir uns jedoch hauptsächlich für einen "do it in-python" -Ansatz entscheiden, ähnelt er der API des Konfigurationssystems, bei der Sie mit einer deklarativen Struktur beginnen können, aber nach dieser anfänglichen Einrichtung wird alles weitere durch Methodenaufrufe aktiviert. Ich finde das nicht schlimm? EDIT: und ich denke, genau so funktioniert Lexicon sowieso.

  • Unabhängig vom Format müssen wir herausfinden, wie fortgeschrittene Benutzer es im Handumdrehen aus externen Quellen oder ähnlichem generieren möchten. Dies sowie die Probleme mit Aliasing und dergleichen implizieren, dass wir dies möglicherweise nicht in einer naiven Struktur "speichern" wollen, sondern als API für ein oder mehrere Objekte, die aufgerufen werden, um sie zu generieren.

    • Ich vermute, dass wir von der Auswahl der Rollen/Gruppen 'abwärts' arbeiten möchten, um zu der API auf höchstem Niveau zu gelangen, um "das, was der Benutzer geliefert hat, in eine umsetzbare Zieleinheit umzuwandeln", weil die fortgeschrittensten Benutzer dies unbedingt wollen werden haben die vollständige Kontrolle über die Implementierung dieses API-Aufrufs. Dann können wir wie immer einen nützlichen Standardfall liefern, der jedoch klar als "nur eine Möglichkeit" gekennzeichnet ist.

    • @RedKrieg hat eine raffinierte Idee in dieser Richtung, bei der wir @group wie @task haben und die Funktionen keine ausführbaren Arbeitseinheiten sind, sondern Gruppenobjekte ergeben.

    • Dieser Ansatz verwendet nativ die Aufgabenhierarchie (Sammlung), was praktisch (warum das Rad neu erfinden) und elegant ist (denn in realen Fällen werden Rollen-/Gruppendefinitionen häufig sehr genau auf die Aufgaben abgebildet, die sie verwenden!)



      • Es funktioniert auch gut, wenn Ihre Gruppen NICHT Ihren Aufgaben zugeordnet sind, da Sie die Definitionen einfach auf der Ebene Ihrer Stammsammlung schreiben können. Kinderleicht.



    • Es ist mir unklar, ob dies am besten eine einzelne Gruppe von jeder Funktion zurückgibt, oder ob wir die Möglichkeit haben möchten, mehrere Gruppen (oder Verbindungen) zu erhalten, oder ob es am besten ist, dies nicht als dekorierte Funktionen, sondern nur als API-Aufrufe zu tun Sammlung (z. B. wie Konfigurationen auf Sammlungsebene gespeichert werden).

    • Zum Beispiel muss der Anwendungsfall, in dem Gruppen-/Rollendaten dynamisch sind und außerhalb von Fabric liegen, hier noch gelöst werden (weshalb ich bereits erwähnt habe, dass wir zuerst die API der höchsten Ebene für diesen Bereich identifizieren müssen; dann müssen wir sehen, wie das ineinandergreift mit dieser Idee der mittleren Ebene.)

Feature

Hilfreichster Kommentar

Hallo, ich weiß nach vielen Jahren nicht, was mit dieser Software passiert ist, aber ich habe das "Rollen" -Konzept in [email protected] wirklich vermisst, besonders wenn ich $ fab -R dev

Alle 7 Kommentare

Aus der Mailingliste:

Wir haben unsere eigene interne REST-API implementiert, die env.roledefs je nach bereitgestelltem Projekt dynamisch auffüllt und stark darauf angewiesen ist, Host-Strings nicht in die Fabfile des Projekts einzubetten oder in der CLI anzugeben.

Unsere Anwendungsfälle sind:

  1. Umgebungsfreie Codebasis https://12factor.net/config. Umgebungen (Rollen) und ihre jeweiligen Host-Strings werden in einer zentralisierten Datenbank gespeichert. Jede fabfile.py hat so etwas (es füllt env.roledefs, wenn die Datei importiert wird):
EnvironmentDatabaseAPIClient(
    'https://rest.api.url/schema/',
    env.service_name,
).apply_env()
  1. Anzahl der Serverumgebungen – mehrere Testumgebungen (einige davon privat, andere öffentlich) und mehrere Produktionsumgebungen (für verschiedene Clients). Jede Umgebung besteht aus einem oder mehreren Hosts und ist einer Fabric-Rolle zugeordnet.

  2. Jeder Dienst ( env.service_name im obigen Beispiel) hat unterschiedliche Umgebungen.

  3. Wir haben auch Meta-Rollen (Rollengruppen). Ihnen wird group- vorangestellt: group-production , group-test , group-external , group-internal , group-all . Auf diese Weise können wir mehrere Serverrollen bereitstellen, ohne sie einzeln anzugeben, beispielsweise wird group-all für alle Rollen bereitgestellt, sowohl für die Produktion als auch für den Test.

  4. Wir haben spezielle Fabric-Aufgaben, um Informationen über Rollengruppen, Rollen und Hosts zu drucken.

  5. Wir verlassen uns auch stark auf die umgekehrte Zuordnung von Host-Strings zurück zu Rollennamen (Hosts-Strings sind pro service_name eindeutig). Dies wird für die Bereitstellungsprotokollierung und Benachrichtigungen verwendet. Grundsätzlich protokollieren wir Dienstbereitstellungen auf jedem Host und senden Slack-Benachrichtigungen, wenn der Dienst auf allen Hosts in einer Rolle bereitgestellt wurde. Dafür ist der EnvironmentDatabaseAPI-Server verantwortlich (er führt Protokolle und Bereitstellungsstatus). Dies geschieht durch Dekorieren von Stoffaufgaben mit einem Dekorateur, der env.host , env.port und env.service_name (plus Commit-Informationen) zurück an den API-Server sendet.

  6. Wir planen, in Zukunft die Deployment-Authentifizierung hinzuzufügen und sehr wahrscheinlich mehr env Variablen vom Server zu ziehen, um sie im Aufgabenkontext verfügbar zu machen.

Danke @max-arnold! Viele davon kenne ich auch aus meinen eigenen Anwendungsfällen in der Vergangenheit. Ich erinnere mich, dass insbesondere das Reverse-Mapping-Bit in v1 ein paar Mal auftauchte, also habe ich es der Liste hinzugefügt.

Damit Fabric v2 für mich nützlich wird, müsste ich fab mitteilen,

Zuvor habe ich Rollen definiert und dann fab -R ... . (Eigentlich wurden die Rollen programmgesteuert mit einem IP-Adressbereich definiert, aber das ist keine Voraussetzung und eine statische Liste in einer YAML-Datei wäre in Ordnung.)

Ich kann in Fabric v2 kein Äquivalent finden, und ich konnte diese Funktion auch nicht emulieren mit:

  • eine fabric.yaml Konfigurationsdatei mit
active_hostset: null
hostsets:
  myhostset:
  - ...
  • active_hostset = config["hostsets"][config["active_hostset"]] in fabfile.py
  • env INVOKE_ACTIVE_HOSTSET=myhostset fab ...

Anstelle der erwarteten Liste von Hosts bekomme ich KeyError: 'active_hostset' .

Wir ordnen jeder Rolle für jede unserer Umgebungen in Fabric v1 verschiedene Sätze von Hosts zu, und die Umgebung wird eingerichtet, indem eine role.environment:staging Aufgabe ausgeführt wird, um sie anzugeben. Diese Aufgabe beeinflusst also die Hosts, die von den folgenden Aufgaben verwendet werden.

In v2 haben wir versucht, eine benutzerdefinierte Aufgabe zu verwenden, aber das Problem ist, dass Executor.expand_calls bevor unsere Aufgabe role.environment Daher kennt keine der folgenden Aufgaben die Umgebung, um ihre Hostlisten dynamisch zu erstellen.

Wenn Sie Executor.expand_calls einem Generator machen, kann die Aufgabenausführung die spätere Aufgabenausführung beeinflussen. Mein obiges Beispiel funktioniert also, wo wir ein benutzerdefiniertes Task , das seine Umgebung kennen muss, um Rollen richtig auf Hosts zu erweitern. zB fab role.environment dev deploy.app - der role.environment Task wird jetzt ausgeführt bevor deploy.app erweitert wird, und somit kennt deploy.app die Umgebung und kann ihre Hosts konfigurieren und wird dann erweitert in die richtige Aufgabenstellung.

Ich habe dies in meinen Gabeln prototypiert:
https://github.com/pyinvoke/invoke/compare/master...rectalogic :expand-generator
https://github.com/fabric/fabric/compare/master...rectalogic :expand-generator

Hallo, ich weiß nach vielen Jahren nicht, was mit dieser Software passiert ist, aber ich habe das "Rollen" -Konzept in [email protected] wirklich vermisst, besonders wenn ich $ fab -R dev

Wir verwenden Rollen auch, um die gleichen Operationen in verschiedenen Umgebungen darzustellen. Vielleicht wäre es sinnvoll, das Konzept einer benannten Rolle und einer benannten Umgebung zu trennen? Wie in der Webrolle in der Entwicklungsumgebung.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen