Documentation
¶
Overview ¶
Package moduleseed — ПРИМЕНИТЕЛЬ раздела `seed` манифеста модуля (задача продукта #2452).
Чей это предмет и почему он появился ¶
Раздел `seed` объявляет то, что установка модуля заводит в службе доступа: личность модуля, его группы, выдачи и вступления в чужие группы. До этой задачи применителя у раздела не было НИ ОДНОГО: строки заводила применённая миграция службы, а раздел с ними лишь сверялся (`internal/moduleseedparity`).
Цена того устройства измерена и названа в задаче #2452: миграция применяется ВЕЗДЕ, включая установку, где платформы нет вовсе, — и самостоятельная служба доступа заводила пять служебных ЛИЧНОСТЕЙ чужого продукта. Теперь строки заводит применитель, а условием служит ДОСТАВКА манифеста: каталог доставки кладёт зонтичный чарт платформы и не кладёт чарт самостоятельной службы. Условность выражена тем, что объявила установка, а не догадкой кода.
Что применитель НЕ делает ¶
Он не читает каталог доставки (это `loadDeliveredManifests`) и не судит форму манифеста (это валидатор связности `internal/manifest`). Ему приезжают уже разобранные и уже проверенные манифесты, и он приводит базу к тому, что в них написано.
Он не СНИМАЕТ строк. Снятие модуля из доставки — отдельный предмет с отдельной ценой: снятая личность уносит с собой права, а её отсутствие снаружи неотличимо от «право не выдали». Применитель, снимающий молча, был бы отзывом без решения. Это ОСТАТОК, названный вслух, а не забытая половина.
Идемпотентность выражена ОПЕРАТОРОМ, а не сравнением в коде ¶
Каждая запись кладётся `ON CONFLICT … DO NOTHING` либо `DO UPDATE … WHERE <строка отличается>`. «Прочитать и сравнить» дало бы окно между чтением и записью (ban #10): под конкуренцией два применения увидели бы одну и ту же строку отсутствующей.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrGrantFormEmpty = errors.New("moduleseed: access binding names neither roleId nor grantedRelation")
ErrGrantFormEmpty — выдача не назвала НИ РОЛИ, НИ ОТНОШЕНИЯ.
Формы взаимоисключающи, и валидатор связности требует ровно одну ещё до применителя. Отказ здесь всё равно свой, а не оставленный базе: та отвергла бы строку ограничением `access_bindings_grant_form_ck`, и оператор прочёл бы имя ограничения вместо имени поля манифеста, которое надо править.
var ErrUnknownSubjectType = errors.New("moduleseed: unknown access binding subject type")
ErrUnknownSubjectType — субъект выдачи назван формой, которой применитель не знает. Отказ, а не пропуск: см. перечень выше.
Functions ¶
This section is empty.
Types ¶
type Applier ¶
type Applier struct {
// contains filtered or unexported fields
}
Applier — применитель. Держит только исполнителя транзакций: решать ему нечего сверх того, что написано в манифесте.
func NewApplier ¶
NewApplier собирает применитель над исполнителем транзакций.
func (*Applier) ApplyAll ¶
ApplyAll приводит базу к состоянию, объявленному разделами `seed` доставленных манифестов, и отдаёт перепись — ВСЕГДА, независимо от исхода.
Отказ на любом модуле прекращает применение: половина применённого посева ýже объявленного, и отличить её от исполненного можно только вызовом. Отказ называет модуль — иначе оператор ищет причину во всей доставке сразу.
type Census ¶
type Census struct {
// Manifests — доставленных манифестов прочитано.
Manifests int
// Seeding — из них объявили раздел `seed`. Ноль здесь законен и означает
// «модули посева не объявили», а НЕ «применять не стали».
Seeding int
// Reports — по модулю на каждый объявивший.
Reports []Report
}
Census — перепись всего применения.
type Report ¶
type Report struct {
// Module — модуль манифеста.
Module string
// Declared — объявлено разделом `seed` по каждому подразделу.
DeclaredAccounts, DeclaredGroups, DeclaredJoins, DeclaredGrants int
// Written — строк заведено либо приведено.
WrittenAccounts, WrittenGroups, WrittenJoins, WrittenGrants int
}
Report — перепись применения одного модуля. Числами, потому что «применено» без чисел неотличимо от «прошло мимо»: применитель, не нашедший ни одной своей строки, молчит ровно так же уверенно, как записавший все.
type Subject ¶
type Subject struct {
// Kind — форма субъекта из закрытого перечня выше.
Kind string
// Account — аккаунт, в котором живёт субъект.
Account string
// Name — имя субъекта в этом аккаунте.
Name string
}
Subject — получатель выдачи, адресованный ПАРОЙ (аккаунт, имя): так он уникален в продукте. Идентификатор сюда не входит — его производит хранилище, и требовать от манифеста воспроизвести его значило бы требовать воспроизвести частность записи.
type TxRunner ¶
type TxRunner interface {
RunInWriteTx(ctx context.Context, fn func(context.Context, Writer) error) error
}
TxRunner — «исполни это под одной писательской транзакцией».
Транзакция берётся НА МОДУЛЬ, а не на строку: личность модуля и её членства ложатся вместе либо не ложатся вовсе. Членство, доехавшее без личности, нарушило бы ссылочный триггер; личность без членства выглядела бы исправной и молча не давала бы модулю читать пределы.
type Writer ¶
type Writer interface {
// UpsertServiceAccount заводит личность модуля либо приводит её назначение.
UpsertServiceAccount(ctx context.Context, account, name, description string) (changed bool, err error)
// UpsertGroup заводит группу модуля.
UpsertGroup(ctx context.Context, account, name, description string) (changed bool, err error)
// JoinGroup вводит служебную запись в группу.
JoinGroup(ctx context.Context, saAccount, saName, groupAccount, groupName string) (changed bool, err error)
// GrantRelation выдаёт субъекту ИМЕНОВАННОЕ ОТНОШЕНИЕ на якоре области.
GrantRelation(ctx context.Context, subject Subject, relation, scopeKind, scopeID string) (changed bool, err error)
// GrantRole выдаёт субъекту РОЛЬ на якоре области.
GrantRole(ctx context.Context, subject Subject, roleID, scopeKind, scopeID string) (changed bool, err error)
}
Writer — то, что применителю нужно от хранилища, и ничего сверх.
Каждый глагол отвечает ИЗМЕНИЛОСЬ ЛИ состояние: «применено шесть манифестов, изменений ноль» и «применено ноль манифестов» — разные утверждения о платформе, а молчание у них одно и то же.