handler

package
v1.9.0 Latest Latest
Warning

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

Go to latest
Published: Sep 22, 2026 License: MIT Imports: 24 Imported by: 0

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

View Source
const (
	ConflictNameTaken       = "name_taken"
	ConflictContainerName   = "container_name"
	ConflictInstallDir      = "install_dir"
	ConflictDirRegistered   = "dir_registered"
	ConflictDirAdopt        = "dir_adopt"
	ConflictDirExists       = "dir_exists"
	ConflictContainerExists = "container_exists"
)

冲突类型,前端据此做本地化文案,未知类型回落到 message

View Source
const (
	ConflictLevelError = "error"
	ConflictLevelWarn  = "warn"
)

冲突级别:error 表示继续下去会失败或覆盖已有 Runner,warn 表示能继续但需要知情

Variables

View Source
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 的那一步。

View Source
var ConfigPath string

ConfigPath 配置文件路径,由 main 注入

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

View Source
var Secrets *secrets.Store

Secrets Manager 侧凭据(可选 PAT)的存放位置,由 main 注入,与 ConfigPath 同理。

Functions

func AddRunner

func AddRunner(c echo.Context) error

AddRunner 添加并可选注册新 runner

func BuildInfo

func BuildInfo() *version.Info

BuildInfo 按当前的 Version/Commit/BuildDate 组装一份版本信息。 GoVersion、Platform、Compiler 由 version-kit 从 runtime 填。

每次调用重新组装,而不是在包初始化时算好一份:version-kit 的 HandlerConfig 会在构造时把响应体固化下来,而这里的 Version 是允许运行时改的(main 在 flag.Parse 之后才赋值,用例也会改它)。/version 不是热路径,重算无所谓。

func GetRunner

func GetRunner(c echo.Context) error

GetRunner 查看单个 runner 配置与状态(GET /api/runners/:name);容器模式下用 Agent 状态覆盖

func Health

func Health(c echo.Context) error

Health 健康检查,供负载均衡或 K8s 探针使用。

响应体由 {"status":"ok"} 变成 {"status":"ok","service":"runner-fleet"}—— status 取值没变(health-kit 的 StatusHealthy 就是 "ok"),只多了一个字段。 状态码同样没变:进程活着恒为 200。

这个端点**始终免鉴权**(basicAuthMiddleware 里有 Skipper 放行),所以用 DefaultConfig:IncludeDetails 与 IncludeChecks 都是 false,不吐任何内部细节。

func Index

func Index(c echo.Context) error

Index 管理界面首页;容器模式下用容器内 Agent 状态覆盖

func ListRunners

func ListRunners(c echo.Context) error

ListRunners 列出所有 runner;容器模式下用容器内 Agent 状态覆盖 Running/Status

func PrecheckRunner

func PrecheckRunner(c echo.Context) error

PrecheckRunner 预检名称与路径(GET /api/runner-precheck?name=&path=)。 只读,不改配置、不碰目录;供界面在表单上实时提示。

func Ready

func Ready(c echo.Context) error

Ready 就绪检查:配置能不能读、runner 根目录在不在且可写。

与 /health 分开是因为两者失败时该做的事不一样:/health 失败 K8s 会重启容器, 而配置坏了重启治不好;/ready 失败只是把这个实例摘出负载,正是想要的效果。

同样免鉴权(探针拿不到 Basic Auth 凭据),所以也用 DefaultConfig—— 失败时只说 "status":"unhealthy",不说是哪一项、更不说路径。

func RecreateRunner

func RecreateRunner(c echo.Context) error

RecreateRunner 按当前配置重建 Runner 容器(POST /api/runners/:name/recreate)。

启动已停止的容器时,发现创建参数与配置不一致会自动重建;但正在运行的容器不会—— 上面很可能正跑着 Job。这个接口就是那扇「我知道会中断,现在就换」的门。

func RemoveRunnerByName

func RemoveRunnerByName(c echo.Context) error

RemoveRunnerByName 从路径参数获取 name 并移除(DELETE /api/runners/:name) 会先停止 runner 进程,再删除其安装目录,最后从配置中移除。

func ResetI18nCache added in v1.8.0

func ResetI18nCache()

ResetI18nCache 丢掉已缓存的翻译表,供测试在替换 I18nLoader 之后调用。

导出是因为 cmd/runner-manager 的用例也会换掉 I18nLoader:不清缓存的话, 先跑的那个用例装进去的表会被后面的用例读到,而那种串扰只在改了测试顺序时才暴露。

func RunnerRows

func RunnerRows(c echo.Context) error

RunnerRows 只渲染列表的 <tbody> 片段,供界面定时刷新时整体替换。 走服务端片段而不是让 JS 拿 /api/runners 的 JSON 自己拼行,是因为行里那几处 三态判断(GitHubYes/GitHubNo、GitHubBusyYes)指针为 false 时也非 nil, 在 JS 里重写一遍正是最容易把「空闲」显示成「忙碌中」的地方。

func StartRegistrationWorker

func StartRegistrationWorker()

StartRegistrationWorker 启动后台 worker,应在 main 中调用一次

func StartRunner

func StartRunner(c echo.Context) error

StartRunner 启动指定 runner(POST /api/runners/:name/start);容器模式下启动 Runner 容器并调 Agent /start

func StopRunner

func StopRunner(c echo.Context) error

StopRunner 停止指定 runner(POST /api/runners/:name/stop);容器模式下停止 Runner 容器

func Trf added in v1.8.0

func Trf(c echo.Context, key string, args ...any) string

Trf 把 trf 暴露给 handler 包外的中间件。

CSRF 守卫拦在路由之前,代码在 cmd/runner-manager,但它返回的 403 和 handler 里的错误走同一条路进到界面的 toast。界面说法语、这一条说中文,读到的人不会 认为是「有一条没翻」,只会认为撞上了另一个服务。

func UpdateRunner

func UpdateRunner(c echo.Context) error

UpdateRunner 更新 runner 配置(PUT /api/runners/:name);名称不可改,与目录一致

func VersionInfo

func VersionInfo(c echo.Context) error

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。

Jump to

Keyboard shortcuts

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