exportcodec

package
v0.2.4 Latest Latest
Warning

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

Go to latest
Published: Nov 12, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package exportcodec 定义「导出文件格式版本」的策略(Codec)与注册表(Registry)。

背景:SaveScript/SaveWorkflow 会把数据库记录导出为 script.json / workflow.json 落盘 (只落盘、不提交 git),SaveScriptFiles/SaveWorkflowFiles 与 PublishScript/PublishWorkflow 再落盘并提交为一个 git commit,发布路径随后把它推送到 store 裸仓库; InstallScript/InstallWorkflow 则从 store 同步回目录后反向读取这两个文件导入数据库。

一旦文件格式演进(字段增删、脚本快照的目录布局变化),新旧产物必须能被区分处理, 因此导出文件顶层新增 version 字段标识文件格式版本:

  • 写侧:调用方从 Registry 取当前版本(CurrentVersion)的 Codec,由 Codec 决定文件内容 与目录布局,生成的 payload 顶层 Version 必须等于 Codec.Version();
  • 读侧(InstallScript/InstallWorkflow):调用方先用 PeekVersion 读出文件里的 version, 从 Registry 取对应 Codec,再用 DecodeScript/DecodeWorkflow 解析元数据, 并用同一 Codec 解释该版本的目录布局(如脚本快照目录); 脚本文件本身仍随 git 从 store 同步/还原;
  • 新增版本 = 新增一个 Codec 实现 + 在 internal/container/container.go 多 Register 一行, 调用方代码不变;
  • Codec 实现必须无状态且并发安全:Registry 在启动期装配,之后只读。

Index

Constants

View Source
const (
	// VersionV1 是第一版格式:
	//   - script.json   = script_id + script + container_template_specs + container_template_definitions;
	//   - workflow.json = workflow_id + workflow + scripts + container_template_specs
	//     + container_template_definitions;
	//     (两个容器资产列表均为按主键去重后的列表,绑定行通过 spec_id 引用运行配置,
	//     镜像名与拉取策略直接挂在绑定行上)
	//   - workflow 目录同时把引用的脚本目录快照到 <workflowDir>/script/<scriptID>
	//     (排除脚本目录自身的 .git)。
	VersionV1 = "v1"

	// CurrentVersion 是写侧使用的版本:写侧总是从 Registry 取这个版本的 Codec 落盘,
	// 因此装配时(internal/container/container.go)必须注册该版本。
	CurrentVersion = VersionV1
)

导出文件格式版本(写入 script.json / workflow.json 顶层 version 字段的取值)。

View Source
const (
	// ScriptJSONFileName 是脚本导出文件名。
	ScriptJSONFileName = "script.json"
	// WorkflowJSONFileName 是工作流导出文件名。
	WorkflowJSONFileName = "workflow.json"
)

导出文件名:写侧落盘(Codec.WriteXxxFiles)与读侧读取(InstallXxx)共用, 避免各版本实现各自定义导致读写不一致。

Variables

View Source
var (
	// ErrScriptIDRequired 表示 script.json 里缺少 script_id。
	ErrScriptIDRequired = errors.New("script_id is required in script.json")
	// ErrWorkflowIDRequired 表示 workflow.json 里缺少 workflow_id。
	ErrWorkflowIDRequired = errors.New("workflow_id is required in workflow.json")
)

安装侧(InstallScript / InstallWorkflow)在解析导出内容后发现关键引用键缺失时返回的错误。

调用方(handler)据此转 400:这类问题来自导出文件内容本身,属于请求错误, 不是服务端故障(与 ErrUnsupportedVersion 同一处理路径)。

View Source
var ErrUnsupportedVersion = errors.New("unsupported export file version")

ErrUnsupportedVersion 表示导出文件(script.json / workflow.json)顶层的 version 没有注册对应的 Codec,调用方(InstallScript/InstallWorkflow)应转成 400 而不是 500。

Functions

func ExportContainerAssets added in v0.2.0

func ExportContainerAssets(ctx context.Context, svc ContainerAssetService, definitionID int64) (*types.ContainerTemplateBundle, error)

ExportContainerAssets 按绑定行导出整套容器资产,返回统一平铺格式。

与 containerService.ExportContainerTemplate 同一逻辑:definitionID 就是绑定行主键 (脚本的 container_template_id),按「绑定行 → 运行配置」各取一条; 取不到(绑定行已删除、spec 缺失)时只跳过,不当作错误。 返回的绑定行固定 is_default=false 且不含时间字段,可原样写入 script.json / workflow.json。

func Merge added in v0.2.0

func Merge(b, other *types.ContainerTemplateBundle)

Merge 把 other 里尚未出现的资产按主键追加到 b(用于多脚本导出时去重汇总)。

func PeekVersion

func PeekVersion(raw []byte) (string, error)

PeekVersion 只解析导出文件(script.json / workflow.json)顶层的 version 字段, 其余字段一律忽略,供调用方在从 Registry 取 Codec 之前先窥探文件版本。

version 缺失或为空时返回空字符串(是否接受该文件由调用方决定,例如按 CurrentVersion 读回, 或直接判为不支持);raw 不是合法 JSON 时返回解析错误。

Types

type Codec

type Codec interface {
	// Version 返回该策略对应的文件格式版本,等于产物顶层 version 字段的取值,
	// 同时是它在 Registry 里的键(Registry.Get(Version()) 必须命中自己)。
	//
	// 同一个版本号必须既能写出、也能读回,因此 WriteXxxFiles 生成的 payload
	// 的 Version 字段必须由 Version() 赋值。
	Version() string

	// WriteScriptFiles 按该版本格式生成脚本导出内容并落盘到 req.ScriptDir
	// (文件名、是否附带 container_templates 等由版本决定),返回写入的 payload,
	// 其 Version 必须等于 Version()。
	//
	// 只落盘、不提交 git:SaveScript 用它把导出文件与数据库保持一致,写完目录即处于
	// dirty 状态;需要产出带提交的版本产物时,由调用方再调 WriteCommitScriptFiles。
	//
	// 实现需保证 req.ScriptDir 存在(不存在则创建)。
	WriteScriptFiles(ctx context.Context, req ScriptWriteRequest) (*types.ScriptJSONExportResponse, error)

	// WriteWorkflowFiles 按该版本格式生成工作流导出内容并落盘到 req.WorkflowDir,
	// 同时按该版本的目录布局把工作流引用的脚本快照到 req.WorkflowDir 下,
	// 返回写入的 payload,其 Version 必须等于 Version()。
	//
	// 与 WriteScriptFiles 一样只落盘、不提交 git(SaveWorkflow 使用它)。
	//
	// 实现需保证 req.WorkflowDir 存在(不存在则创建)。
	WriteWorkflowFiles(ctx context.Context, req WorkflowWriteRequest) (*types.WorkflowJSONExportResponse, error)

	// WriteCommitScriptFiles 在 WriteScriptFiles 的基础上,把 req.ScriptDir 的目录改动
	// 提交为一个 git commit(见 utils.CommitDirChanges,提交身份由装配时注入)。
	//
	// 调用方:SaveScriptFiles / PublishScript(保存或发布必须产出带提交的完整产物)。
	WriteCommitScriptFiles(ctx context.Context, req ScriptWriteRequest) (*types.ScriptJSONExportResponse, error)

	// WriteCommitWorkflowFiles 在 WriteWorkflowFiles 的基础上,把 req.WorkflowDir 的目录改动
	// 提交为一个 git commit(见 utils.CommitDirChanges,提交身份由装配时注入)。
	//
	// 调用方:SaveWorkflowFiles / PublishWorkflow。
	WriteCommitWorkflowFiles(ctx context.Context, req WorkflowWriteRequest) (*types.WorkflowJSONExportResponse, error)

	// DecodeScript 解析 script.json 的内容(raw 为文件字节),返回带 Version 的 payload。
	//
	// 只做解析与该版本特有的字段规整,不负责业务字段覆盖
	// (ID/ProjectID/StoreID/URL/Message/创建更新时间由 handler 决定)。
	// ScriptID 非空等校验同样由 handler 负责,便于统一转 400。
	DecodeScript(raw []byte) (*types.ScriptJSONExportResponse, error)

	// DecodeWorkflow 解析 workflow.json 的内容(raw 为文件字节),返回带 Version 的 payload。
	//
	// 该方法的职责边界与 DecodeScript 一致;v1 会在这里把 workflow.dag_definition
	// 从对象规整成字符串(等价原先 handler 内的 normalizeInstalledWorkflowMap),
	// 因为 dag_definition 落库是字符串列。
	DecodeWorkflow(raw []byte) (*types.WorkflowJSONExportResponse, error)

	// InstallScript 解析 req.Raw(内部调用本 Codec 的 DecodeScript)并把导出内容持久化:
	// 先按主键 upsert 容器镜像 / 运行配置 / 绑定行,再按 script_id upsert script 行。
	//
	// 与 DecodeScript 的分工:DecodeScript 只负责解析,本方法负责落库与安装特有的校验
	// (script.json 缺少 script_id 时返回 ErrScriptIDRequired,调用方据此转 400)。
	InstallScript(ctx context.Context, req ScriptInstallRequest) (*ScriptInstallResult, error)

	// InstallWorkflow 解析 req.Raw(内部调用本 Codec 的 DecodeWorkflow)并把导出内容持久化:
	// 先按主键 upsert 容器资产,再 upsert workflow 行与它引用的 script 行,
	// 最后把 workflow 目录里的脚本快照(见 ScriptSnapshotDir)还原到本地脚本目录。
	//
	// workflow.json 缺少 workflow_id 时返回 ErrWorkflowIDRequired,调用方据此转 400。
	InstallWorkflow(ctx context.Context, req WorkflowInstallRequest) (*WorkflowInstallResult, error)

	// ScriptSnapshotDir 返回该版本布局下 workflow 目录内承载某个脚本快照的目录。
	//
	// 写侧(WriteWorkflowFiles)把脚本快照进去,安装侧(InstallWorkflow)从同一位置
	// 还原到脚本目录,两边都通过本方法取路径,避免各自写死字面量。
	ScriptSnapshotDir(workflowDir, scriptID string) string

	// ScriptIDFromExportScript 从 workflow.json 的脚本条目里取脚本 ID(component_id)。
	//
	// 条目在 payload 里是 map(不是强类型),字段名属于该版本的格式知识,
	// 因此由 Codec 解释;取不到时返回空字符串,由调用方决定是跳过还是报错。
	ScriptIDFromExportScript(item map[string]any) string
}

Codec 是一套导出文件格式版本对应的策略实现。

Codec 实现直接持有 interfaces.WorkflowService(写侧用它按主键生成导出 payload), 由 DI 容器在装配 Registry 时注入(见 internal/container/container.go)。

WriteXxxFiles 负责「按该版本的格式生成文件并落盘」,WriteCommitXxxFiles 在此基础上 把目录改动提交为一个 git commit;DecodeXxx / ScriptSnapshotDir 只负责解析内容与解释目录布局。 git 提交本身与格式版本无关,由 utils.CommitDirChanges 统一实现,各版本 Codec 直接复用, 因此调用方(handler)落盘后无需再单独提交。

方法分成三组,分别对应两个方向的调用方与目录布局:

  • 写侧:SaveScript/SaveWorkflow/PublishScript/PublishWorkflow;
  • 读侧:InstallScript/InstallWorkflow;
  • 布局:安装侧要按该版本约定找到 workflow 目录里的脚本快照。

type ContainerAssetInstallResult added in v0.2.0

type ContainerAssetInstallResult struct {
	SpecCount       int
	DefinitionCount int
}

ContainerAssetInstallResult 是安装容器资产的结果(按主键 upsert 的数量)。

func InstallContainerAssets added in v0.2.0

InstallContainerAssets 按主键 upsert 两张表,返回各类数量。

安装顺序固定为「运行配置 → 绑定行」:绑定行引用运行配置,id 为 0 视为导出文件损坏直接报错 (id 是唯一的引用键)。

type ContainerAssetService added in v0.2.0

type ContainerAssetService interface {
	CreateContainerTemplateSpec(ctx context.Context, item *types.ContainerTemplateSpec) error
	GetContainerTemplateSpecByID(ctx context.Context, id int64) (*types.ContainerTemplateSpec, error)
	UpdateContainerTemplateSpec(ctx context.Context, item *types.ContainerTemplateSpec) error

	CreateContainerTemplateDefinition(ctx context.Context, item *types.ContainerTemplateDefinition) error
	GetContainerTemplateDefinitionByID(ctx context.Context, id int64) (*types.ContainerTemplateDefinition, error)
	UpdateContainerTemplateDefinition(ctx context.Context, item *types.ContainerTemplateDefinition) error
}

ContainerAssetService 是导出 / 安装容器资产所需的最小服务能力。

定义成小接口而不是直接依赖 interfaces.ContainerRepository, 既避免 exportcodec 与 interfaces 互相引用,也让逻辑易于单测。

type Registry

type Registry struct {
	// contains filtered or unexported fields
}

Registry 是版本号到 Codec 的注册表,风格与 internal/container_runtime/factory.go 的 Registry 保持一致(NewRegistry / Register / Get / List)。

与 container_runtime.Registry 的区别只有键的含义:那里的键是 runtime 名字(docker/k8s), 这里的键是导出文件格式版本(codec.Version(),如 "v1")。

生命周期:由 DI 容器在启动期装配(internal/container/container.go), 之后只被读取,因此自身不加锁;Codec 实现必须无状态、并发安全。

func NewRegistry

func NewRegistry() *Registry

NewRegistry 创建空注册表。

func (*Registry) Get

func (r *Registry) Get(version string) Codec

Get 按版本号取 Codec,未注册时返回 nil(与 container_runtime.Registry.Get 一致: 由调用方判空决定如何报错,例如转 400)。nil receiver 安全。

func (*Registry) List

func (r *Registry) List() []Codec

List 返回已注册的全部 Codec(顺序不定),便于启动日志与装配自检。

func (*Registry) Register

func (r *Registry) Register(codec Codec)

Register 按 codec.Version() 注册。

只有启动期装配会调用;同版本重复注册以最后一次为准(便于测试里替换实现)。

func (*Registry) Versions

func (r *Registry) Versions() []string

Versions 返回已注册的版本号(已排序),便于启动日志与测试断言「版本 X 已装配」。

type ScriptInstallRequest

type ScriptInstallRequest struct {
	// Raw 是 script.json 文件内容(与 DecodeScript 的入参一致)。
	Raw []byte
	// ProjectID 是安装到的目标 project 主键(project.ID)。
	ProjectID int64
	// StoreID 是发布来源 store 主键(写入 script.store_id)。
	StoreID int64
	// StoreMessage 非空时覆盖 script.message。
	StoreMessage string
	// ScriptID 是安装后的 script_id:create=true 时调用方已生成新 uuid,
	// 否则沿用导出文件/发布方记录的 script_id。
	ScriptID string
	// CreateMode 为 true 时作为副本新增,而不是按 script_id upsert 原脚本。
	CreateMode bool
}

ScriptInstallRequest 是安装脚本(InstallScript)的入参。

Raw 是 script.json 的文件内容,与 DecodeScript 的入参一致;InstallScript 内部会调用 DecodeScript 解析它,因此调用方只需把文件原样读进来,不需要(也不应)再自行解析。

其余字段是「落库上下文」:导出文件只描述组件内容,安装到哪个 project、来自哪个 store 以及安装成什么 script_id 由调用方(handler)决定。

字段语义与 handler 原实现保持一致:

  • ProjectID 是 project 表主键(写入 script.project_id),不是 project.project_id;
  • StoreMessage 非空时覆盖 script 行的 message;
  • CreateMode 为 true 时作为副本新增(component_name 追加 _Copy 后缀、store_id 置 0), 否则按 ScriptID 在目标 project 内存在与否决定更新或新增。

目录相关说明:脚本安装不涉及文件系统还原(脚本文件已随 git 同步),故不含 BaseDir。

由导出文件内容推导的脚本文件仍由 handler 用 git 从 store 同步/还原(与格式版本无关)。

type ScriptInstallResult

type ScriptInstallResult struct {
	// ScriptID 是落库后的 script_id;InstalledScriptID 是 script 表主键。
	ScriptID          string
	InstalledScriptID int64
	// ContainerTemplateSpecCount / ContainerTemplateDefinitionCount
	// 分别是本次按主键 upsert 的运行配置与绑定行数量(导出文件没有对应列表时为 0)。
	ContainerTemplateSpecCount       int
	ContainerTemplateDefinitionCount int
}

ScriptInstallResult 是 InstallScript 的结果。

type ScriptWriteRequest

type ScriptWriteRequest struct {
	ScriptPK      int64
	ScriptDir     string
	CommitMessage string
}

ScriptWriteRequest 是写脚本导出文件的入参。

ScriptPK 是 script 表主键(int64,不是 script_id),ScriptDir 是脚本目录绝对路径; CommitMessage 是落盘后 git 提交使用的 message(为空时使用默认文案)。

type WorkflowInstallRequest

type WorkflowInstallRequest struct {
	// Raw 是 workflow.json 文件内容(与 DecodeWorkflow 的入参一致)。
	Raw []byte
	// ProjectID 是安装到的目标 project 主键(project.ID),同时用于推导本地目录。
	ProjectID int64
	// StoreID 是发布来源 store 主键。
	StoreID int64
	// StoreMessage 非空时覆盖 workflow / script 行的 message。
	StoreMessage string
	// BaseDir 是 storage.base_dir。
	BaseDir string
	// WorkflowDir 是已同步出的 workflow 目录(脚本快照的父目录)。
	WorkflowDir string
}

WorkflowInstallRequest 是安装工作流(InstallWorkflow)的入参。

Raw 是 workflow.json 的文件内容,与 DecodeWorkflow 的入参一致;InstallWorkflow 内部会 调用 DecodeWorkflow 解析它。其余字段语义与 ScriptInstallRequest 一致,额外多出:

  • ProjectID 是安装到的目标 project 主键(project.ID),同时用于推导本地目录布局;
  • BaseDir 是 storage.base_dir;
  • WorkflowDir 是已从 store 同步出的 workflow 目录,脚本快照位于 <WorkflowDir>/script/<scriptID>(ScriptSnapshotDir),安装时还原到脚本目录。

type WorkflowInstallResult

type WorkflowInstallResult struct {
	// WorkflowID 是导出文件里的 workflow_id;InstalledWorkflowID 是 workflow 表主键。
	WorkflowID          string
	InstalledWorkflowID int64
	// InstalledScriptCount 是本次安装(新增或更新)的 script 行数。
	InstalledScriptCount int
	// ContainerTemplateSpecCount / ContainerTemplateDefinitionCount
	// 分别是本次按主键 upsert 的运行配置与绑定行数量。
	ContainerTemplateSpecCount       int
	ContainerTemplateDefinitionCount int
}

WorkflowInstallResult 是 InstallWorkflow 的结果。

type WorkflowWriteRequest

type WorkflowWriteRequest struct {
	WorkflowPK    int64
	ProjectID     int64
	BaseDir       string
	WorkflowDir   string
	CommitMessage string
}

WorkflowWriteRequest 是写工作流导出文件的入参。

WorkflowPK 是 workflow 表主键(int64,不是 relation_id); ProjectID 是 project 表主键(int64),同时用于推导本地目录布局; BaseDir 是 storage.base_dir;WorkflowDir 是 workflow 目录绝对路径; CommitMessage 是落盘后 git 提交使用的 message(为空时使用默认文案)。

Directories

Path Synopsis
Package v1 实现导出文件格式 v1(exportcodec.VersionV1)的 Codec 策略。
Package v1 实现导出文件格式 v1(exportcodec.VersionV1)的 Codec 策略。

Jump to

Keyboard shortcuts

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