Documentation
¶
Overview ¶
Package compose 实现 fleetly 的 Compose 应用描述处理:解析、受控子集 校验、稳定归一化与归一化差异(plan/diff 的地基)。
契约真源(本包行为逐条对齐,不发明清单外语义):
- docs/design/2026-09-17-architecture.md §2.4(受控子集支持/拒绝清单、 平台 label 约定、受管字段、变量插值关闭、plan/apply 语义)
- docs/design/2026-09-17-release-semantics.md §2.4(revisions 归一化 形态:env 按 key:sha256(value)+来源标注参与 spec_hash)与 §2.8 (受管字段政策:failure_action=pause、monitor≤5s、order 照用)
- docs/design/2026-09-17-stateful-placement.md §2.3(placement label 语法、constraints 命名空间、replicas×卷校验、label 一致性)
- docs/design/2026-09-17-state-model.md §2.4(fleetly.* 保留前缀 → E_LABEL_RESERVED)
解析器选型(关键决策):采用 github.com/compose-spec/compose-go/v2 官方 loader 作为 **schema 载体**(compose 语法合法性、类型换算:时长/字节/ 列表→映射 canonical 化),但 **受控子集校验完全自研**——受控子集是平台 契约而非 compose 全集,compose-go 的默认放行面远大于 §2.4 支持清单。 loader 选项与理由:
- SkipInterpolation:`${VAR}`/`.env` 插值关闭(§2.4),归一化按字面;
- SkipInclude / SkipExtends:include/extends 在拒绝清单内,必须保留 原始键让本包校验显式拒绝(默认 loader 会先解析消解掉这两个键);
- ResolvePaths=false:路径保持书写形态,保证 spec_hash 跨机稳定;
- SkipResolveEnvironment / SkipResolveLabels:env_file 与 label_file 不由 compose-go 合并——env 合并链(env_file < environment < 平台层) 需要保留 **来源标注**(release-semantics §2.4),本包自行解析 env_file(字面值、无插值,见 envfile.go);label_file 不在支持清单, 由白名单拒绝。
- schema 校验保持开启:语法非法(未知键/类型错)→ 包装为 E_COMPOSE_UNSUPPORTED;其上再跑本包白名单/拒绝清单/受管字段校验。
归一化产物 Spec 是后续 revisions.compose_normalized 快照与 desired-hash 的地基:字段排序确定(服务/卷/域名/env 按字典序)、env 以 key+sha256 参与哈希(值永不落盘、永不输出)、spec_hash = sha256(canonical_json)。
本包不做的事(留待后续票,勿在此扩权):
- apply 的真实执行(引擎层 T2.10);省略=删除的卷数据例外语义 (T2.10);平台 env_vars 合并层与 W_ENV_PLATFORM_OVERRIDE 告警 (env 票接入 DB 后);危险字段的 admin 显式旁路 + 审计(本期只 「拒绝+错误」,旁路留 TODO)。
Index ¶
- Constants
- func DestructiveChanges(base, target *Spec) bool
- func Load(ctx context.Context, path string) (*Spec, []Warning, error)
- type Build
- type Deploy
- type EnvVar
- type FieldChange
- type Healthcheck
- type Mount
- type Placement
- type Plan
- type ResourceLimits
- type Resources
- type RestartPolicy
- type Service
- type ServiceUpdate
- type ServicesPlan
- type Spec
- type UpdateConfig
- type Volume
- type VolumesPlan
- type Warning
Constants ¶
const ( LabelDomains = "fleetly.domains" LabelPlacementNode = "fleetly.placement.node" LabelCron = "fleetly.cron" LabelCronTimezone = "fleetly.cron.timezone" LabelCronTimeout = "fleetly.cron.timeout" // LabelNamespace 是平台保留 label 命名空间前缀:用户占用约定键之外的 // fleetly.* 键 → E_LABEL_RESERVED(422)。 LabelNamespace = "fleetly." )
平台 label 契约常量(架构 §2.4 平台约定表、state-model §2.4 保留前缀)。
const ( // WarningKindCronLabelPending:fleetly.cron* label 在 v0.1 不生效 //(定时任务为 v0.2 契约),服务按长驻部署。 WarningKindCronLabelPending = "cron_label_v02_pending" // WarningKindUserLabelNotPassed:非 fleetly.* 的服务 label 平台不透传 //(v0.1 受控子集只消费平台约定 label;S16-C2——静默丢弃改警告披露)。 WarningKindUserLabelNotPassed = "user_label_not_passed" )
警告 Kind 常量(无注册码提示的稳定标识)。
const ( EnvSourceEnvFile = "env_file" EnvSourceEnvironment = "environment" )
EnvSource 是归一化 env 条目的来源标注(release-semantics §2.4:三层合并 链 env_file < environment < 平台层;平台层随引擎/env 票接入,本包只产出 文件内两层)。值取稳定字符串,进快照与 plan/diff 输出。
Variables ¶
This section is empty.
Functions ¶
func DestructiveChanges ¶
DestructiveChanges 报告 base → target 的变更集是否含破坏性操作:基线中 存在而目标缺失的服务(删除)或卷(解绑)。这是破坏性判定的唯一真源 (MG-C3):Diff(→ plan artifact 的 requires_confirm_destructive)与 API 面 Deploy 入队门控共用本函数,消灭两处口径漂移。nil 参数视为空 Spec—— 空基线(首部署)无可删除对象,恒非破坏性。
Types ¶
type Build ¶
type Build struct {
Context string `json:"context"`
Dockerfile string `json:"dockerfile,omitempty"`
}
Build 是归一化后的构建声明(仅保留平台消费的子键;构建引擎参数不在 §2.4 支持清单,白名单拒绝)。
type Deploy ¶
type Deploy struct {
Mode string `json:"mode,omitempty"`
Replicas int64 `json:"replicas,omitempty"`
UpdateConfig *UpdateConfig `json:"update_config,omitempty"`
RestartPolicy *RestartPolicy `json:"restart_policy,omitempty"`
Resources *Resources `json:"resources,omitempty"`
Placement *Placement `json:"placement,omitempty"`
}
Deploy 是归一化后的 deploy 段(受管字段 failure_action/monitor 校验通过 后不进快照——值恒为 pause/5s 平台常量,写进快照只会放大噪音;order 及 其余字段照用)。
type EnvVar ¶
type EnvVar struct {
Key string `json:"key"`
Hash string `json:"hash"`
Source string `json:"source"`
}
EnvVar 是一条 env 合并结果:值只以 sha256 hex 表示(spec_hash 参与; 明文与明文长度都不泄露),Source 标注三层合并链中的文件内层级。
type FieldChange ¶
type FieldChange struct {
Path string `json:"path"`
From any `json:"from,omitempty"`
To any `json:"to,omitempty"`
}
FieldChange 是一条字段级变更:Path 为归一化形态内的稳定路径 (如 environment.FOO、deploy.replicas、healthcheck.test);From/To 为 该路径上的 JSON 值(env 路径只含 hash/source,无明文)。
type Healthcheck ¶
type Healthcheck struct {
Test []string `json:"test,omitempty"`
Interval string `json:"interval,omitempty"`
Timeout string `json:"timeout,omitempty"`
Retries uint64 `json:"retries,omitempty"`
StartPeriod string `json:"start_period,omitempty"`
}
Healthcheck 是按书写形态归一化的健康检查(平台默认 5s/3s/3/10s 只补 运行态缺省,不烘焙进快照——治理参数取当前平台配置,release-semantics §2.4)。时长为归一化字符串(time.Duration.String() 形态,如 10s)。
type Mount ¶
type Mount struct {
Volume string `json:"volume"`
Target string `json:"target"`
ReadOnly bool `json:"read_only,omitempty"`
}
Mount 是一次命名卷挂载。
type Placement ¶
type Placement struct {
Constraints []string `json:"constraints,omitempty"`
}
Placement 归一化放置约束(constraints 仅允许 node.labels.fleetly.* 命名空间,stateful-placement §2.3)。
type Plan ¶
type Plan struct {
// Name 是目标 Spec 的应用名(基线与目标名不一致时取目标侧)。
Name string `json:"name,omitempty"`
// SpecHash 是目标归一化形态哈希(= etag)。
SpecHash string `json:"spec_hash"`
// BaseSpecHash 是基线归一化形态哈希(空基线为空串)。
BaseSpecHash string `json:"base_spec_hash,omitempty"`
HasChanges bool `json:"has_changes"`
// Destructive 表示变更集含破坏性操作(服务删除/卷解绑——省略=删除;
// 卷数据不随声明删除的例外语义由执行层 T2.10 兑现,本期只解析标记)。
Destructive bool `json:"destructive"`
// RequiresConfirmDestructive 是执行侧 --confirm-destructive 的门控位
// (v0.1 恒等于 Destructive;独立成字段以便引擎层引入非破坏性降级后
// 二者分叉)。
RequiresConfirmDestructive bool `json:"requires_confirm_destructive"`
Services ServicesPlan `json:"services"`
Volumes VolumesPlan `json:"volumes,omitempty"`
Warnings []Warning `json:"warnings,omitempty"`
}
Plan 是 plan/diff 的产物:两份归一化 Spec 的变更集(架构 §2.4 plan/apply 语义——面向 AI Agent 的一等接口)。JSON 形态即 plan artifact:
- spec_hash 即 etag(apply 前校验防 stale 并发,落盘 artifact 的机制位);
- requires_confirm_destructive 置位时,执行侧(T2.10 引擎层)必须持有 --confirm-destructive 才允许继续;
- 脱敏结构性成立:env 只以 key+sha256+source 表示,明文值无从泄露。
func Diff ¶
Diff 计算两份归一化 Spec 的变更集:base(如上一 revision 快照/基线 compose)→ target(本次目标)。nil 参数视为空 Spec(首部署语义)。 数据来源接口化:真实 DB 基线(revisions.compose_normalized)随引擎票接入, 本期 CLI 用 --baseline 另一份 compose 文件或空基线。
func (*Plan) RenderText ¶
RenderText 渲染人读 plan/diff 报告(非 --json 形态;顺序确定)。
type ResourceLimits ¶
type ResourceLimits struct {
CPUS float64 `json:"cpus,omitempty"`
MemoryBytes int64 `json:"memory_bytes,omitempty"`
}
ResourceLimits 是 limits 的平台消费子集。
type Resources ¶
type Resources struct {
Limits *ResourceLimits `json:"limits,omitempty"`
}
Resources 归一化资源限额(仅 limits;reservations 不在支持清单)。
type RestartPolicy ¶
type RestartPolicy struct {
Condition string `json:"condition,omitempty"`
Delay string `json:"delay,omitempty"`
MaxAttempts uint64 `json:"max_attempts,omitempty"`
Window string `json:"window,omitempty"`
}
RestartPolicy 归一化重启策略(release-semantics §2.8:restart_policy 照用)。
type Service ¶
type Service struct {
Name string `json:"name"`
// Build/Image 互斥或同现均可(compose 允许 image+build;构建模式裁决
// 随构建票:无 dockerfile → Railpack,有 build.dockerfile → Dockerfile)。
Build *Build `json:"build,omitempty"`
Image string `json:"image,omitempty"`
Command []string `json:"command,omitempty"`
// Expose 保持原序(路由目标端口取首个,架构 §2.4)。
Expose []string `json:"expose,omitempty"`
// Domains 是 fleetly.domains label 解析后的域名列表(trim/小写/
// IDN→punycode 归一化后排序)。有该 label 的服务即入口。
Domains []string `json:"domains,omitempty"`
// PlacementNode 是 fleetly.placement.node label 的字面值(放置意图;
// 名或 n_<ULID> 的解析/绑定校验属放置层,语法校验见 validate.go)。
PlacementNode string `json:"placement_node,omitempty"`
Healthcheck *Healthcheck `json:"healthcheck,omitempty"`
// Environment 是 env 合并结果(env_file < environment),按 key 排序;
// 只含 key+hash+source,值永不进归一化形态(脱敏结构性成立)。
Environment []EnvVar `json:"environment,omitempty"`
Secrets []string `json:"secrets,omitempty"`
// Volumes 是命名卷挂载(v0.1 受控子集:仅命名卷;bind/tmpfs 拒绝),
// 保持声明顺序(挂载点集合有语义)。
Volumes []Mount `json:"volumes,omitempty"`
Networks []string `json:"networks,omitempty"`
Deploy *Deploy `json:"deploy,omitempty"`
StopSignal string `json:"stop_signal,omitempty"`
StopGracePeriod string `json:"stop_grace_period,omitempty"`
}
Service 是归一化后的服务定义。只承载受控子集字段;平台管理项(镜像 digest、secret 值、路由绑定、节点绑定)不进文件、不进快照。
type ServiceUpdate ¶
type ServiceUpdate struct {
Name string `json:"name"`
Fields []FieldChange `json:"fields"`
}
ServiceUpdate 是单个服务的字段级变更报告。
type ServicesPlan ¶
type ServicesPlan struct {
Added []string `json:"added,omitempty"`
Removed []string `json:"removed,omitempty"`
Updated []ServiceUpdate `json:"updated,omitempty"`
}
ServicesPlan 是服务维度的变更集。
type Spec ¶
type Spec struct {
Name string `json:"name,omitempty"`
Services []Service `json:"services"`
Volumes []Volume `json:"volumes,omitempty"`
Networks []string `json:"networks,omitempty"`
Secrets []string `json:"secrets,omitempty"`
// SpecHash 是 CanonicalJSON 的 sha256 hex(载入时填定)。
SpecHash string `json:"spec_hash"`
}
Spec 是 compose 文件经受控子集校验后的稳定归一化形态(架构 §2.4、 release-semantics §2.4):受控子集内的服务与卷定义;env 以 key:sha256(value)+来源标注表示(值本身不进归一化结果);无插值、字面值。 该形态即 revisions.compose_normalized 快照与 desired-hash 的地基。
稳定性纪律:所有切片输出前排序(服务/卷/网络/secret/域名/env 按名称或 字典序;expose/volumes 挂载顺序语义保留原序),canonical JSON 由 CanonicalJSON 确定性编码,spec_hash = sha256(canonical_json)。
func LoadEmpty ¶
LoadEmpty 返回空基线 Spec(首部署语义:一切服务视为新增)。plan 在未 提供 --baseline 时使用;DB 基线(上一 revision 快照)随引擎票接入。
func (*Spec) CanonicalJSON ¶
CanonicalJSON 输出确定性 JSON(键排序由 encoding/json 保证、HTML 转义 关闭、无尾部换行),作为 spec_hash 的哈希输入。
type UpdateConfig ¶
type UpdateConfig struct {
Order string `json:"order,omitempty"`
Parallelism uint64 `json:"parallelism,omitempty"`
Delay string `json:"delay,omitempty"`
}
UpdateConfig 归一化更新策略(order 照用;有卷服务显式 start-first 在 校验层拒绝 → E_COMPOSE_UNSAFE_STRATEGY)。
type VolumesPlan ¶
type VolumesPlan struct {
Added []string `json:"added,omitempty"`
Removed []string `json:"removed,omitempty"`
}
VolumesPlan 是顶层卷维度的变更集(新增=声明卷;移除=解绑标记,数据 不随声明删除)。
type Warning ¶
type Warning struct {
Code string `json:"code,omitempty"`
Kind string `json:"kind,omitempty"`
Service string `json:"service,omitempty"`
Message string `json:"message"`
}
Warning 是校验/计划产物上的非阻断标注。Code 为注册表 W_ 码(如 W_DEPLOY_NO_HEALTHCHECK);无注册码的提示(如 cron label 的 v0.2 生效 提示——v0.1 冻结注册表不含对应码,不发明新码)Code 留空、以 Kind 给出 稳定标识。