Documentation
¶
Overview ¶
Package handler 实现 Manager 的 HTTP API 与 WebUI 逻辑。 Manager 职责:配置读写、Runner 注册编排(安装/注册脚本)、容器启停调度、状态聚合;不直接承载 Runner 进程。
添加 Runner 前的冲突预检:在界面还停留在表单上时就把「这个名字会出问题」说清楚, 而不是等到写配置、装 runner、建容器时才以各种形态失败。
检查的是几类真实会失败或产生意外结果的冲突:
- 配置里已有同名 Runner(此前会被静默加上随机后缀,用户并不知情)
- 名称规范化后与已有 Runner 撞容器名(github-runner-a.b 与 a-b 会映射到同一个)
- 安装目录与已有 Runner 相同(path 指到别人的目录)
- 磁盘上已有注册过的 runner 目录(带 token 会被 config.sh 拒绝,不带 token 则是接管)
- 宿主机上已有同名容器(上一次删除没删干净,启动时会被直接复用)
Index ¶
- Constants
- Variables
- func AddRunner(c echo.Context) error
- func BuildInfo() *version.Info
- func GetRunner(c echo.Context) error
- func Health(c echo.Context) error
- func Index(c echo.Context) error
- func ListRunners(c echo.Context) error
- func PrecheckRunner(c echo.Context) error
- func Ready(c echo.Context) error
- func RecreateRunner(c echo.Context) error
- func RemoveRunnerByName(c echo.Context) error
- func ResetI18nCache()
- func RunnerRows(c echo.Context) error
- func StartRegistrationWorker()
- func StartRunner(c echo.Context) error
- func StopRunner(c echo.Context) error
- func Trf(c echo.Context, key string, args ...any) string
- func UpdateRunner(c echo.Context) error
- func VersionInfo(c echo.Context) error
- type AddRunnerRequest
- type PrecheckResponse
- type RunnerConflict
- type UpdateRunnerRequest
Constants ¶
const ( ConflictNameTaken = "name_taken" ConflictContainerName = "container_name" ConflictInstallDir = "install_dir" ConflictDirRegistered = "dir_registered" ConflictDirAdopt = "dir_adopt" ConflictDirExists = "dir_exists" ConflictContainerExists = "container_exists" )
冲突类型,前端据此做本地化文案,未知类型回落到 message
const ( ConflictLevelError = "error" ConflictLevelWarn = "warn" )
冲突级别:error 表示继续下去会失败或覆盖已有 Runner,warn 表示能继续但需要知情
Variables ¶
var ( // Version 语义化版本号,未注入时 /version 与 -version 都报 dev Version string // Commit 构建所用的 git 提交 Commit string // BuildDate 构建时间(RFC3339) BuildDate string // AssetVersion 内嵌静态资源的内容指纹,拼进页面里 /static/*.css|js 的 ?v=。 // 由 main 在启动时算好赋进来。 AssetVersion string )
三个都由构建时 -ldflags "-X main.Xxx=..." 注入,再由 main 赋进来。 Commit 与 BuildDate 是本次新加的:在此之前,拿到一个 runner-manager 二进制 是没办法知道它出自哪个提交的,线上排查只能靠版本号猜。
注入的是 main 的变量,不是 version-kit 的包级变量,所以 kit 升大版本 (模块路径随之变化)不必跟着改 ldflags——那正是 kit 自己的升级说明反复 警告、漏改时不报错、只会让二进制静默报 dev 的那一步。
var ConfigPath string
ConfigPath 配置文件路径,由 main 注入
var I18nLoader func(lang string) (map[string]string, error)
I18nLoader loads translations for a language code (e.g. "en", "zh"). Set by main from embed.
var Secrets *secrets.Store
Secrets Manager 侧凭据(可选 PAT)的存放位置,由 main 注入,与 ConfigPath 同理。
Functions ¶
func BuildInfo ¶
BuildInfo 按当前的 Version/Commit/BuildDate 组装一份版本信息。 GoVersion、Platform、Compiler 由 version-kit 从 runtime 填。
每次调用重新组装,而不是在包初始化时算好一份:version-kit 的 HandlerConfig 会在构造时把响应体固化下来,而这里的 Version 是允许运行时改的(main 在 flag.Parse 之后才赋值,用例也会改它)。/version 不是热路径,重算无所谓。
func Health ¶
Health 健康检查,供负载均衡或 K8s 探针使用。
响应体由 {"status":"ok"} 变成 {"status":"ok","service":"runner-fleet"}—— status 取值没变(health-kit 的 StatusHealthy 就是 "ok"),只多了一个字段。 状态码同样没变:进程活着恒为 200。
这个端点**始终免鉴权**(basicAuthMiddleware 里有 Skipper 放行),所以用 DefaultConfig:IncludeDetails 与 IncludeChecks 都是 false,不吐任何内部细节。
func ListRunners ¶
ListRunners 列出所有 runner;容器模式下用容器内 Agent 状态覆盖 Running/Status
func PrecheckRunner ¶
PrecheckRunner 预检名称与路径(GET /api/runner-precheck?name=&path=)。 只读,不改配置、不碰目录;供界面在表单上实时提示。
func Ready ¶
Ready 就绪检查:配置能不能读、runner 根目录在不在且可写。
与 /health 分开是因为两者失败时该做的事不一样:/health 失败 K8s 会重启容器, 而配置坏了重启治不好;/ready 失败只是把这个实例摘出负载,正是想要的效果。
同样免鉴权(探针拿不到 Basic Auth 凭据),所以也用 DefaultConfig—— 失败时只说 "status":"unhealthy",不说是哪一项、更不说路径。
func RecreateRunner ¶
RecreateRunner 按当前配置重建 Runner 容器(POST /api/runners/:name/recreate)。
启动已停止的容器时,发现创建参数与配置不一致会自动重建;但正在运行的容器不会—— 上面很可能正跑着 Job。这个接口就是那扇「我知道会中断,现在就换」的门。
func RemoveRunnerByName ¶
RemoveRunnerByName 从路径参数获取 name 并移除(DELETE /api/runners/:name) 会先停止 runner 进程,再删除其安装目录,最后从配置中移除。
func ResetI18nCache ¶ added in v1.8.0
func ResetI18nCache()
ResetI18nCache 丢掉已缓存的翻译表,供测试在替换 I18nLoader 之后调用。
导出是因为 cmd/runner-manager 的用例也会换掉 I18nLoader:不清缓存的话, 先跑的那个用例装进去的表会被后面的用例读到,而那种串扰只在改了测试顺序时才暴露。
func RunnerRows ¶
RunnerRows 只渲染列表的 <tbody> 片段,供界面定时刷新时整体替换。 走服务端片段而不是让 JS 拿 /api/runners 的 JSON 自己拼行,是因为行里那几处 三态判断(GitHubYes/GitHubNo、GitHubBusyYes)指针为 false 时也非 nil, 在 JS 里重写一遍正是最容易把「空闲」显示成「忙碌中」的地方。
func StartRegistrationWorker ¶
func StartRegistrationWorker()
StartRegistrationWorker 启动后台 worker,应在 main 中调用一次
func StartRunner ¶
StartRunner 启动指定 runner(POST /api/runners/:name/start);容器模式下启动 Runner 容器并调 Agent /start
func StopRunner ¶
StopRunner 停止指定 runner(POST /api/runners/:name/stop);容器模式下停止 Runner 容器
func Trf ¶ added in v1.8.0
Trf 把 trf 暴露给 handler 包外的中间件。
CSRF 守卫拦在路由之前,代码在 cmd/runner-manager,但它返回的 403 和 handler 里的错误走同一条路进到界面的 toast。界面说法语、这一条说中文,读到的人不会 认为是「有一条没翻」,只会认为撞上了另一个服务。
func UpdateRunner ¶
UpdateRunner 更新 runner 配置(PUT /api/runners/:name);名称不可改,与目录一致
func VersionInfo ¶
VersionInfo 返回版本信息(未注入时返回 dev)。
用 version-kit 的默认 HandlerConfig,也就是 Public() 那份精简取值——只有 version(Branch 我们没注入)。刻意**不**开 IncludeBuildDetails:Basic Auth 没配置时这个端点是完全公开的,而 go_version 能让任何人把一条已公布的 Go 运行时 CVE 对到确切的运行时版本上。kit 自己的注释写的就是这个理由。
于是响应体与改动前逐字节相同:{"version":"..."}。commit 与构建时间只走 -version 命令行,那是本机执行、不对外。
自己拿 ResolveConfig(...).JSONResponse() 写响应,而不是用 kit 的 HTTP handler: v4 把 net/http 那一套搬进了 httpadapter 子包,根包只剩「决定 serve 什么」的配置 API,也就是 Echo、Gin、chi 这类框架该用的那一半。这个文件的代码因此在升 v4 时 一行没动,只换了 import。
Types ¶
type AddRunnerRequest ¶
type AddRunnerRequest struct {
Name string `json:"name"`
Path string `json:"path"`
TargetType string `json:"target_type"`
Target string `json:"target"`
Labels []string `json:"labels"`
RegistrationToken string `json:"registration_token"`
// AutoRename 为 true 时沿用旧行为:名称冲突自动改名。默认改为返回 409 并给出建议名,
// 免得界面上填了 foo 却静默建出 foo-ab12cd,用户以为自己在操作 foo。
AutoRename bool `json:"auto_rename"`
}
AddRunnerRequest 添加 runner 请求。
刻意只有 json tag,没有 form tag:跨站 form 提交是 CORS 意义上的「简单请求」, 不触发预检就能带着已缓存的 Basic Auth 凭据发出去。少了 form tag,这种请求即便 绕过了 csrfGuardMiddleware 也只能绑出一个空结构体,随即被必填校验挡掉。 界面本来就用 JSON 提交(FormData 只用于就地读取表单字段),不受影响。
type PrecheckResponse ¶
type PrecheckResponse struct {
Name string `json:"name"`
InstallDir string `json:"install_dir"`
ContainerName string `json:"container_name,omitempty"` // 仅容器模式
Available bool `json:"available"` // 无 error 级冲突
SuggestedName string `json:"suggested_name,omitempty"` // 有冲突时给出的可用名称
Conflicts []RunnerConflict `json:"conflicts"`
}
PrecheckResponse GET /api/runner-precheck 的返回
type RunnerConflict ¶
type RunnerConflict struct {
Type string `json:"type"`
Level string `json:"level"`
Message string `json:"message"`
Detail string `json:"detail,omitempty"` // 与之冲突的对象:已有 Runner 名、目录或容器名
Suggestion string `json:"suggestion,omitempty"` // 人话建议
FixCommand string `json:"fix_command,omitempty"` // 可直接执行的命令,与语言无关
}
RunnerConflict 一条冲突。字段与容器探测的 ProbeInfo 对齐,便于前端统一渲染。
type UpdateRunnerRequest ¶
type UpdateRunnerRequest struct {
Name string `json:"name"`
Path string `json:"path"`
TargetType string `json:"target_type"`
Target string `json:"target"`
Labels []string `json:"labels"`
}
UpdateRunnerRequest 更新 runner 请求(名称不可改,以 URL 路径参数为准)。 与 AddRunnerRequest 同理,只接受 JSON。