Help us shape the future of Go! Take the 2026 Go Developer Survey

compose

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 20, 2026 License: Apache-2.0 Imports: 18 Imported by: 0

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

View Source
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 保留前缀)。

View Source
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 常量(无注册码提示的稳定标识)。

View Source
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

func DestructiveChanges(base, target *Spec) bool

DestructiveChanges 报告 base → target 的变更集是否含破坏性操作:基线中 存在而目标缺失的服务(删除)或卷(解绑)。这是破坏性判定的唯一真源 (MG-C3):Diff(→ plan artifact 的 requires_confirm_destructive)与 API 面 Deploy 入队门控共用本函数,消灭两处口径漂移。nil 参数视为空 Spec—— 空基线(首部署)无可删除对象,恒非破坏性。

func Load

func Load(ctx context.Context, path string) (*Spec, []Warning, error)

Load 读取并解析 compose 文件,执行受控子集校验后返回归一化 Spec 与 警告标注(警告不阻断校验:W_DEPLOY_NO_HEALTHCHECK、cron label 的 v0.2 生效提示等)。任何失败都经 apperr 携带注册表错误码(suggestion/docs 由 注册表默认带出)。

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

func Diff(base, target *Spec) (*Plan, error)

Diff 计算两份归一化 Spec 的变更集:base(如上一 revision 快照/基线 compose)→ target(本次目标)。nil 参数视为空 Spec(首部署语义)。 数据来源接口化:真实 DB 基线(revisions.compose_normalized)随引擎票接入, 本期 CLI 用 --baseline 另一份 compose 文件或空基线。

func (*Plan) RenderText

func (p *Plan) RenderText() string

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

func LoadEmpty(name string) *Spec

LoadEmpty 返回空基线 Spec(首部署语义:一切服务视为新增)。plan 在未 提供 --baseline 时使用;DB 基线(上一 revision 快照)随引擎票接入。

func (*Spec) CanonicalJSON

func (s *Spec) CanonicalJSON() ([]byte, error)

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 Volume

type Volume struct {
	Key    string `json:"key"`
	Driver string `json:"driver,omitempty"`
}

Volume 是顶层命名卷声明(卷数据生命周期归平台卷注册表,声明仅承载 driver 等卷级参数;external/name 拒绝)。

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 给出 稳定标识。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL