Перейти к содержимому

core.file

← Каталог модулей

soul-sidesoulstack.filesfilesroot требуется

Управление файлами и каталогами: содержимое/права/владелец, рендер из шаблона, создание каталога, удаление.

core.file приводит файл или каталог к нужному виду четырьмя состояниями: present (содержимое inline через content или копией с хоста через src), rendered (рендер text/template-шаблона), directory (каталог с атрибутами) и absent. Идемпотентно по содержимому (сверка SHA-256) и атрибутам mode/owner/group — совпало всё, changed=false. Заменяет отдельные core.copy и core.template (ADR-015). Soul-side: запись за пределы /var/lib/soul-stack/ требует fs_write_root, для системных путей на практике root.

Требования

  • Rootтребуется
  • Сторонаsoul-side
  • Коллекцияsoulstack.files
  • Категорияfiles
Capabilitiesfs_write_root

Состояния

core.file.present — Файл существует с заданным содержимым (inline content или копия src) плюс mode/owner/group.

Меняется, когда

Файла не было, либо отличается содержимое (сверка по SHA-256; для src — sha256 байт src), mode или владелец/группа.

Не меняется, когда

Содержимое, mode и владелец/группа уже совпадают.

Параметры

ПараметрТипОбяз. / дефолтОписание
pathstringrequiredЦелевой путь файла.
contentstringoptionalСодержимое файла (inline). Взаимоисключается с src; ни одного — пустой файл.
srcstringoptionalАбсолютный путь regular-файла на хосте, содержимое копируется в path (типично результат core.archive.extracted). Задаёт только содержимое, не атрибуты источника. Взаимоисключается с content.
modestringoptionalПрава в восьмеричной форме, напр. "0640".
ownerstringoptionalВладелец (имя пользователя).
groupstringoptionalГруппа-владелец (имя группы).

Пример — present с inline-content (роль core.copy)

- name: Drop a static marker file
module: core.file.present
params:
path: /etc/soul-stack/marker
content: "managed by soul-stack"
mode: "0644"
owner: root
group: root

Output

ПолеТипОписание
pathstring
sha256string
modestring
installedtrue

Справочник

Корень контекста шаблона (rendered)

Что видит text/template при рендере core.file.rendered. Отсутствие обращённой переменной — ошибка рендера (strict-mode missingkey=error), а не молчаливая пустая подстановка.

КлючЧто это
.vars.*производные переменные из vars: (вычислены CEL-фазой)
.input.*резолвнутый operator-input прохода (Вариант B — напрямую, без passthrough через vars:; Keeper кладёт ключ, только если шаблон реально обращается к .input.*)
.self.*проекция soulprint, snake_case: .self.os.pkg_mgr, .self.network.primary_ip
.roledeclared-роль хоста
.essence.*effective essence

Заметки

  • present задаёт содержимое ровно одним из content (inline) или src (копия regular-файла с хоста); оба вместе — ошибка (конфликт по присутствию ключа, даже content: "" вместе с src:). Ни того ни другого — пустой файл (legacy-поведение).
  • src задаёт только содержимое (типично результат core.archive.extracted); mode/owner/group берутся из явных params, атрибуты src не наследуются. Путь src обязан быть абсолютным, симлинк reject-ится через Lstat (источник не следуется по ссылке).
  • rendered: на wire Keeper обязан доставить и template_content, и render_context — без любого из них шаг падает (прод-инвариант golden-path, не optional).
  • Ключи .self.* — snake_case (.self.os.pkg_mgr, .self.os.init_system), как soulprint.self.*; camelCase не сработает. register на rendered-задаче типично служит якорем onchanges: для рестарта сервиса при изменении конфига.
  • present/absent/rendered/directory не запускают подпроцессов — рендер, запись, mkdir/chmod/chown идут in-process, без shell. recurse (рекурсивные права на содержимое каталога) в MVP не поддержан.

Безопасность и умолчания

  • Прямая запись в произвольный путь ФС, включая системные (/etc/...), — главный риск: path/content/mode/owner/group должны приходить от автора Destiny, а не из недоверенного ввода (иначе подмена /etc/passwd, /etc/sudoers, unit-файла).
  • Атомарность различается: rendered и src-ветка present пишут через temp + rename (наблюдатель не видит частично записанный конфиг), а content-ветка present — через os.WriteFile напрямую, без temp+rename: при сбое в момент записи файл может остаться усечённым. Для конфигов под работающим демоном предпочитайте rendered/src или рестарт потребителя через onchanges:.
  • src — копия regular-файла с самого хоста (симлинк/каталог/device/относительный путь reject-ятся), но источник не становится доверенным: src, как и path, задаёт автор Destiny, иначе содержимое произвольного файла хоста окажется скопировано в path.
  • mode/owner для секретов задавайте явно (0600/0640): без mode берётся дефолт os.WriteFile (зависит от umask), существующий mode не сверяется и не правится — секрет может оказаться world-readable.
  • rendered рендерит в песочнице (sprig-allowlist, нет доступа к FS/сети/окружению — три sandbox-барьера), но секреты (${ vault(...) }, пароли через vars:) попадают в render_context и в итоговый файл — отсюда обязательность явного mode для конфигов с секретами.
  • Привилегии: манифест объявляет fs_write_root (запись за пределы /var/lib/soul-stack/), но не run_as_root — модуль исполняется с привилегиями процесса soul-агента без повышения прав внутри; запись в системные пути на практике требует root.

См. также