Blueprint-Eigentum: Was ein Update behält
Ein Blueprint-Update importiert die Seed-Daten erneut in einen Tenant, der eine Weile in Benutzung war. Der Import ersetzt jede berührte Entität vollständig. Ohne Regeln würde ein Seed mit einem leeren Wert daher löschen, was der Tenant seit der Installation eingetragen hat (eine Adapter-Konfiguration, einen Enforcement-Modus, einen Zähler, einen Mailtext). Die Regeln auf dieser Seite legen für jeden Wert fest, ob der Blueprint oder der Tenant ihn besitzt, und was die Plattform tut, wenn beide nicht übereinstimmen.
Es gibt vier Schichten. Jede ist unabhängig; ein Wert wird nur ersetzt, wenn keine von ihnen ihn schützt.
| Schicht | Frage | Entschieden durch |
|---|---|---|
| Eigentum an der Seed-Entität | Gehört dem Tenant die ganze Entität? | rtBlueprintLocked im Seed |
| Attribut-Ownership | Gehört dem Tenant dieses Attribut? | ownership im CK-Modell |
| Blanking-Guard | Würde das Seed einen gefüllten Wert leeren? | Die Engine, für jedes SeedOwned-Attribut |
| Vorschau und Bestätigung | Was würde das Update behalten oder leeren, und stimmt der Operator zu? | PreviewBlueprintUpdate, UpdateBlueprint |
Ein weiterer Schalter, ProtectBlueprintLocked, betrifft nicht Updates, sondern Benutzer: Er macht die Entitäten, die ein Blueprint besitzt, für sie schreibgeschützt.
Welche Attribute jeder Plattform- und App-Blueprint schreibt und wem sie jeweils gehören, steht im Seed-Ownership-Audit und in dessen vollständiger Tabelle.
Die vier Ownership-Werte
Jedes CK-Attribut deklariert mit ownership, wem sein Wert gehört. Die Ownership wird pro Attributzuweisung aufgelöst: Eine Überschreibung an der Zuweisung des Typs gewinnt gegen die Attributdefinition, und ein Attribut ohne Deklaration ist SeedOwned. (isRuntimeState: true ist die veraltete Schreibweise von RuntimeState.)
ownership | Wem der Wert gehört | Frische Installation | Blueprint-Update oder erzwungenes erneutes Anwenden | ExportRt |
|---|---|---|---|---|
SeedOwned (Standard) | Dem Produkt. Der Blueprint-Autor liefert ihn aus, eine neue Version darf ihn korrigieren. | Seed-Wert wird übernommen | Seed-Wert ersetzt den gespeicherten Wert, außer der Blanking-Guard behält einen gefüllten Wert, wenn das Seed leer ist | Exportiert |
TenantOwned | Dem Tenant. Ein Administrator oder Benutzer setzt ihn im Produkt. | Seed-Wert initialisiert ihn | Gespeicherter Wert bleibt; das Seed initialisiert nur eine neue Entität | Exportiert |
RuntimeState | Ein Dienst, ein Operator oder eine Pipeline schreibt ihn zur Laufzeit. | Seed-Wert initialisiert ihn | Gespeicherter Wert bleibt | Ausgelassen |
Secret | Zugangsdaten. Immer die Ownership des Werttyps Secret. | Seed-Wert initialisiert ihn (ein Secret-Werttyp ist im Seed leer) | Gespeicherter Wert bleibt | Nie exportiert |
attributes:
- id: PolicyEnforcementMode
valueType: Enum
valueCkEnumId: ${this}/DataPolicyEnforcementMode
ownership: TenantOwned
Beispiele aus den Plattformmodellen: System.Identity/DataPolicy PolicyEnforcementMode und PolicyScope sind TenantOwned (ein Administrator, der eine Policy auf Enforce gestellt hat, behält Enforce); System.Communication/Adapter.AdapterConfiguration ist Secret (die Verbindungseinstellungen eines Adapters, die ein Passwort enthalten können, überstehen ein Update und wandern nie in eine Exportdatei); System.StreamData Archive.Status ist RuntimeState.
Prüffrage für jedes neue oder geänderte Attribut: Könnte ein Operator oder Benutzer diesen Wert im Produkt eintippen? Wenn ja, ist er TenantOwned (oder Secret für Zugangsdaten); wenn ein Dienst ihn schreibt, RuntimeState; nur ein Wert, den das Produkt ausliefert und korrigieren können muss, ist SeedOwned. Die Änderung der Ownership eines bestehenden Attributs ist eine Minor-Erhöhung der Modellversion.
RuntimeState und TenantOwned schützen einen Tenant-Wert, und derselbe Schutz hindert einen Blueprint daran, ihn zu korrigieren. Eine Seed-Korrektur eines solchen Werts erreicht einen frischen Tenant und keinen bestehenden Tenant. Zwei Fälle sind gewollt:
Adapter.ChartVersion(und die übrigen Deployment-Werte eines Workloads) sindRuntimeState: Der Operator oder die CD-Pipeline setzt sie.- Die Redirect- und CORS-URIs eines Identity-
ClientsindRuntimeState: Der Tenant oder ein Overlay fügt sie hinzu.
Ein korrigierter Wert in einem von beiden muss durch einen CK-Migrationsschritt oder einen dokumentierten manuellen Schritt in die bestehenden Tenants geschrieben werden, nicht durch ein neues Seed.
Tenant-eigene Seed-Entitäten
Das Flag rtBlueprintLocked im Seed legt fest, wem eine Entität gehört, sobald sie im Tenant existiert:
| Seed sagt | Eigentümer | Erste Installation | Update, Tenant hat sie | Update, Tenant hat sie nicht |
|---|---|---|---|---|
Flag fehlt oder true (Standard) | Produkt | angelegt | aus dem Seed erneuert (Merge, Full) | erneut angelegt (eine gelöschte Entität kommt zurück) |
rtBlueprintLocked: false | Tenant | mit den Seed-Werten angelegt | nicht angefasst, kein Konflikt (nur der Blueprint-Stempel wandert) | nur angelegt, wenn sie neu ist; bleibt gelöscht, wenn das Seed der Vorgängerversion sie schon enthielt |
entities:
- rtId: 65f1c0a2b3d4e5f601234567
ckTypeId: MyModel/MailTemplate
rtWellKnownName: DefaultTemplate
rtBlueprintLocked: false # einmal angelegt, danach Eigentum des Tenants
attributes:
- id: Name
value: Default template
Verwenden Sie das für Vorgaben, die ein Tenant bearbeiten oder entfernen soll (Vorlagen, Beispielregeln). Halten Sie die Entitäten, die das Produkt braucht, etwa die Regeln, von denen ein Feature abhängt, gesperrt. Vorschau und Update-Ergebnis listen die Entitäten in TenantOwnedSkipped (behalten) und TenantOwnedStaysDeleted (nicht zurückgebracht); siehe unten, wo Sie diese Listen lesen. Ein Tenant, der noch den Stempel rtBlueprintLocked: true einer früheren Version trägt, wird ab dem ersten Update, dessen Seed die Entität als entsperrt deklariert, als tenant-eigen behandelt. ImportRt -r kennt keine Blueprints und folgt dieser Regel nicht; wenden Sie ein Seed mit UpdateBlueprint oder InstallBlueprint an.
Der Blanking-Guard
Die Attribut-Ownership schützt nur, was ein Modellautor markiert hat. Für alles andere (SeedOwned, der Standard) entscheidet die Engine pro Attribut bei jedem Upsert-Import (Blueprint-Installation, Update, erzwungenes erneutes Anwenden, ImportRt -r):
| Gespeicherter Wert | Seed | Ergebnis |
|---|---|---|
| keiner oder leer | beliebig | Seed-Wert wird übernommen |
| gefüllt | anderer, gefüllter Wert | Seed gewinnt (eine Korrektur des Blueprints, wie bisher) |
| gefüllt | leerer Wert (null, "", leere Liste, Record aus leeren Mitgliedern) | gespeicherter Wert bleibt, wird gemeldet |
| gefüllt | Attribut fehlt, kein CK-Standardwert | gespeicherter Wert bleibt, wird gemeldet |
| gefüllt und vom CK-Standardwert verschieden | Attribut fehlt, Attribut hat einen CK-Standardwert | gespeicherter Wert bleibt, wird als ResetToDefault gemeldet |
| JSON-Text mit gefülltem String | JSON-Text mit leerem String an derselben Stelle | gespeicherter Wert bleibt als Ganzes, wird gemeldet (das leere Skelett einer Adapter-Konfiguration) |
„Leer" ist bewusst eng gefasst: Zahlen, Booleans, Enums, Datumswerte und Zeitspannen sind nie leer, ein ausdrückliches 0, false oder das erste Enum-Mitglied im Seed ist also ein vom Autor gewählter Wert und ersetzt den Wert des Tenants. Records werden als Einheit behalten oder ersetzt.
Der Guard ist ein Sicherheitsnetz, kein Freibrief: Ein Wert, den ein Tenant eintippt, gehört im Modell zu TenantOwned oder Secret. Der Guard kann einen vom Tenant eingegebenen Wert nicht von einem Seed-Wert unterscheiden, den eine neue Version leeren möchte, und leert deshalb nie stillschweigend; siehe nächster Abschnitt.
Vorschau und explizite Bestätigung
PreviewBlueprintUpdate führt dieselbe Erkennung wie das Update aus und listet jedes Attribut, dessen gefüllten Wert das Seed leeren würde, ohne Werte (sie können Zugangsdaten sein): Entitäts-ID, Typ, Attribut, Grund (SeedEmpty, SeedOmitted, ResetToDefault; die CLI gibt den Grund so aus, wie der Dienst ihn meldet) sowie eine Art-und-Größe-Angabe des aktuellen und des eingehenden Werts, zum Beispiel string (223 chars) gegen empty string.
# Die Liste lesen. Das JSON-Ergebnis geht auf die Standardausgabe, die Tabelle ins Log.
octo-cli -c PreviewBlueprintUpdate -tv MyBlueprint-2.0.0
# CI-Gate: Exit-Code -5, wenn etwas geleert würde
octo-cli -c PreviewBlueprintUpdate -tv MyBlueprint-2.0.0 -fb
# Standard-Update: Tenant-Werte, die das Seed leeren würde, werden BEHALTEN und aufgelistet, Exit-Code 0
octo-cli -c UpdateBlueprint -tv MyBlueprint-2.0.0
# Genau diese Attribute leeren (wiederholbar): <rtId>:<attribute> wie in der Vorschau
octo-cli -c UpdateBlueprint -tv MyBlueprint-2.0.0 -cb 65a1b2c3d4e5f60718293a4b:configuration
# ALLE aufgelisteten Attribute leeren (laute Warnung; bevorzugen Sie -cb)
octo-cli -c UpdateBlueprint -tv MyBlueprint-2.0.0 -ab
| Kurz | Lang | An | Bedeutung |
|---|---|---|---|
-fb | --failOnBlanking | PreviewBlueprintUpdate | Mit Exit-Code -5 beenden, wenn das Update mindestens einen Tenant-Wert leeren würde |
-cb | --confirm-blanking | UpdateBlueprint | Ein Attribut bestätigen, <rtId>:<attribute>; die Option für mehrere wiederholen. Alles Unbestätigte bleibt erhalten |
-ab | --allow-blanking | UpdateBlueprint | Jedes aufgelistete Attribut bestätigen; hat Vorrang vor -cb |
Die vollständigen Optionslisten stehen in der Befehlsreferenz zu PreviewBlueprintUpdate und UpdateBlueprint. Die REST-API trägt denselben Vertrag: POST {tenantId}/v1/blueprints/updates/preview liefert blankedAttributes, und POST {tenantId}/v1/blueprints/updates/apply akzeptiert allowBlanking und confirmedBlankings ([{ rtId, attributeName }]) und antwortet wieder mit der Liste, in der appliedOnUpdate für einen behaltenen Wert false und für einen auf Bestätigung geleerten true ist. Nichts geschieht still.
Was Sie mit der Liste tun. Entscheiden Sie für jede Zeile, wer recht hat. Ein Wert, den der Tenant eingegeben hat und den das Produkt nicht anfassen soll: nichts tun (das Update behält ihn) und das Attribut als Modellproblem melden, denn es sollte TenantOwned sein. Ein Wert, den das Produkt wirklich entfernen will: mit -cb bestätigen. Studio wendet Updates und erneutes Anwenden mit dem Standard an und leert deshalb nie; es zeigt die Liste noch nicht an, verwenden Sie vor einem Update auf einem Tenant mit eingegebenen Daten PreviewBlueprintUpdate.
Vorschau und Guard sind nur so gut wie der Dienst, der sie ausführt: Ein Dienst, der älter als das Feature ist, meldet keine Liste und verhält sich wie zuvor (siehe Verfügbarkeit).
Wo Betreiber übersprungene und gelöscht bleibende Entitäten sehen
Ein Update lässt tenant-eigene Seed-Entitäten bewusst in Ruhe. Damit das sichtbar ist und kein stilles Auslassen, listen Vorschau und Update-Ergebnis sie auf, in REST, GraphQL, im SDK und in octo-cli. Sie sind kein Blanking: blankedAttributes, -cb, -ab und -fb beachten sie nicht.
| Liste | Bedeutung | entityId |
|---|---|---|
tenantOwnedSkipped | Der Tenant hält die Entität noch; das Update hat sie unberührt gelassen | Laufzeit-ID auf dem Tenant |
tenantOwnedStaysDeleted | Das vorherige Seed enthielt sie, der Tenant hat sie gelöscht, das Update bringt sie nicht zurück | null |
Jeder Eintrag trägt key (der rtWellKnownName der Seed-Entität, sonst ihre rtId), ckTypeId, entityId und wellKnownName. Nur Identität, nie Attributwerte: eine tenant-eigene Entität kann Zugangsdaten enthalten. Beide Listen werden in entitiesSkipped mitgezählt und sind leer, wenn es nichts zu melden gibt; ein Client, der sie ignoriert, verhält sich wie zuvor.
| Oberfläche | Wo |
|---|---|
| REST | POST {tenantId}/v1/blueprints/updates/preview (tenantOwnedSkipped, tenantOwnedStaysDeleted im JSON-Body) und POST {tenantId}/v1/blueprints/updates/apply (dieselben Member im Ergebnis) |
| GraphQL | blueprints.previewUpdate { tenantOwnedSkipped { key ckTypeId entityId wellKnownName } tenantOwnedStaysDeleted { ... } } und dieselben Felder am Ergebnis von applyUpdate (BlueprintApplyResult, bei einer Installation leer) |
| SDK | BlueprintUpdatePreviewDto und BlueprintUpdateResultDto: TenantOwnedSkipped, TenantOwnedStaysDeleted (BlueprintTenantOwnedEntityDto) |
| octo-cli | PreviewBlueprintUpdate und UpdateBlueprint protokollieren eine Tabelle „tenant-owned, skipped“ und eine „deleted by the tenant, stays deleted“ (Schlüssel, Typ, rtId, - bei einer gelöschten Entität); bei leeren Listen wird nichts ausgegeben |
1 tenant-owned seed entity(ies) would be left untouched (tenant-owned, skipped):
Key Type Entity (rtId)
OwnedKept Test/Rule 65a1b2c3d4e5f60718293a4b
1 tenant-owned seed entity(ies) deleted by the tenant would stay deleted:
Key Type Entity (rtId)
OwnedGone Test/Rule -
Der Installationspfad meldet den Umgang mit tenant-eigenen Entitäten nur in seinen Operationsmeldungen, ohne diese Listen.
Was ein Blueprint-Autor tut, um einen tenant-eigenen Wert zu ändern
Ein Seed kann einen Wert, den der Tenant besitzt, nicht ändern und einen gefüllten nicht leeren. Das ist gewolltes Verhalten. Muss eine neue Version einen solchen Wert in bestehenden Tenants wirklich ändern, wählen Sie eines davon:
- Ein CK-Migrationsschritt des Modells (
Update-Aktion auf den Entitäten, siehe CK-Modell-Migrationen) oder ein Blueprint-Migrationsskript (UpdateBlueprint -m Migration, siehe Blueprints). Die Änderung ist explizit, versioniert und prüfbar. - Ein dokumentierter manueller Schritt für den Operator, mit dem genauen zu setzenden Wert, wenn die Änderung von der Umgebung abhängt.
- Die explizite Blanking-Bestätigung (
-cb), nur wenn die Absicht ist, einen Wert zu leeren; einen neuen Wert kann sie nicht schreiben. - Die Ownership korrigieren, wenn das Attribut falsch eingestuft war: Ein Attribut, das ein Tenant eintippt, ist
TenantOwnedoderSecret, ein Attribut, das das Produkt korrigieren können muss, istSeedOwned. Eine Ownership-Änderung ist eine Minor-Erhöhung des CK-Modells und wirkt für einen Tenant, sobald er die neue Modellversion importiert hat.
Umgehen Sie die Regeln nicht, indem Sie den Wert in einer zweiten Seed-Entität ausliefern oder sich auf ein erzwungenes erneutes Anwenden verlassen: Ein erzwungenes erneutes Anwenden folgt genau denselben Regeln wie ein Update.
Blueprint-gesperrte Entitäten vor Benutzern schützen
rtBlueprintLocked allein steuert nur Updates; ein Benutzer kann eine gesperrte Entität weiterhin bearbeiten. Ein Blueprint kann die Entitäten, die er besitzt, für Benutzer schreibgeschützt machen, indem er für den Typ eine System.Identity/DataPolicy mit ProtectBlueprintLocked: true ausliefert. Verhalten, der Fehler BLUEPRINT_LOCKED (Meldung 6384) und die abgedeckten Pfade sind unter Blueprints beschrieben. Was ein Blueprint-Autor beachten muss:
- Untergrenze. Heben Sie die CK-Abhängigkeit des Blueprints auf
System.Identity-[2.24.0,3.0)an. Ein älteres System.Identity verwirft das Attribut stillschweigend. - Das Flag ist
SeedOwned. Jedes Update und jedes erzwungene erneute Anwenden setzt den ausgelieferten Wert wieder ein; Benutzer können den Schutz nicht dauerhaft abschalten, und eine spätere Version kann ihn verschärfen oder lockern. - Liefern Sie ein explizites
falseaus, um einen Schutz zurückzunehmen. Ein Seed, das das Attribut nur weglässt, behält ein gespeichertestrue, weil der Blanking-Guard den gefüllten Wert behält. - Eine Policy pro geschütztem Typ, in einer eigenen Policy-Gruppe. Die Policy ist eine Einschränkung, keine Berechtigung: Geben Sie den Rollen, die mit dem Typ arbeiten, ihre
PolicyPermission. - Entitäten, die der Tenant besitzen soll, bleiben mit
rtBlueprintLocked: falseim Seed bearbeitbar.
Verfügbarkeit
Die Schutzmaßnahmen kamen schrittweise. Das erste Plattform-Release, das sie enthält:
| Schutz | Plattform-Release | Voraussetzung |
|---|---|---|
Attribut-Ownership TenantOwned, RuntimeState, Secret | frühere Releases | das CK-Modell des Attributs trägt die Markierung |
Adapter-Konfiguration ist Secret | 3.5.7 | System.Communication 4.7.0 im Tenant importiert |
DataPolicy-Modus und -Scope, Schalter von Client und ApiResource sind TenantOwned | 3.5.7 | System.Identity 2.23.0 im Tenant importiert |
| Blanking-Guard | 3.5.3 | |
Update-Vorschau, blankedAttributes | 3.5.5 | Dienst und octo-cli |
tenantOwnedSkipped / tenantOwnedStaysDeleted in Vorschau und Ergebnis (REST, GraphQL, SDK, octo-cli) | die Version nach 3.5.9, die AB#6454 enthält | Dienst, octo-sdk und octo-cli |
-fb, -cb, -ab | 3.5.5 | octo-cli |
Tenant-eigene Seed-Entitäten (rtBlueprintLocked: false) | 3.5.8 | |
ImportRt beachtet die Blueprint-Sperre | 3.5.9 | ProtectBlueprintLocked-Policy |
ProtectBlueprintLocked | mit System.Identity 2.24.0 | die Untergrenze des Blueprints |
Eine Ownership-Markierung wirkt für einen Tenant, wenn der Tenant die Version des CK-Modells importiert hat, die sie trägt; der Dienst importiert seine eingebetteten System-Modelle, wenn ein Tenant aufgelöst wird. Prüfen Sie die Modellversion eines Tenants, bevor Sie sich auf eine Markierung verlassen.
Siehe auch
- Blueprints — Installation, Update, Rollback, Konflikte, Migrationen
- Seed-Ownership-Audit und die vollständige Tabelle — welche Attribute jeder Blueprint schreibt, wem sie gehören und die offenen Befunde
- Secret-Attribute — der Werttyp
Secret