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

exportcodec

package
v0.1.5 Latest Latest
Warning

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

Go to latest
Published: Oct 14, 2026 License: MIT Imports: 6 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_templates + container_images;
	//   - workflow.json = workflow_id + workflow + scripts + container_templates + container_images;
	//     (container_templates / container_images 均为按主键去重后的列表,模板通过 image_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 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 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
	// ContainerImageCount / ContainerTemplateSpecCount / ContainerTemplateDefinitionCount
	// 分别是本次按主键 upsert 的镜像、运行配置与绑定行数量(导出文件没有对应列表时为 0)。
	ContainerImageCount              int
	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
	// ProjectCode 是 project.project_id(字符串),仅用于推导本地目录。
	ProjectCode string
	// 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 一致,额外多出:

  • ProjectCode 是 project.project_id(字符串),用于推导本地目录布局 (utils.GetScriptFileDir),注意与 ProjectID(int64 主键)区分;
  • 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
	// ContainerImageCount / ContainerTemplateSpecCount / ContainerTemplateDefinitionCount
	// 分别是本次按主键 upsert 的镜像、运行配置与绑定行数量。
	ContainerImageCount              int
	ContainerTemplateSpecCount       int
	ContainerTemplateDefinitionCount int
}

WorkflowInstallResult 是 InstallWorkflow 的结果。

type WorkflowWriteRequest

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

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

WorkflowPK 是 workflow 表主键(int64,不是 relation_id); ProjectID 是 project.project_id(字符串,注意与 workflow.ProjectID 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