mirror of
https://gitea.com/gitea/act_runner.git
synced 2026-08-06 08:54:21 +02:00
fix: serve the whole results service from the cache server (#1141)
`ACTIONS_RESULTS_URL` names one origin serving every `github.actions.results.api.v1` service. Gitea serves the artifact half and this runner the cache half, so announcing `ACTIONS_CACHE_SERVICE_V2` while that URL pointed at Gitea was a promise the environment could not keep, and `docker buildx` posted its cache calls at Gitea and got a 404. The cache server now forwards the artifact half to the instance each job registers with, so it is the whole results service and jobs are pointed at it. The announcement follows, and the bundle patch follows the cache URL instead. Also fixes three things no JavaScript client reached: camelCase in the v2 responses where the Go clients read proto names, the missing `x-ms-request-id` on blob uploads that panics buildkit, and `cache.external_server` passed through without the trailing slash the v1 client concatenates onto. Tests run the real actions against the services they look for: `actions/cache` over both API versions, the artifact actions up and back down through the forwarding, and `setup-node`. The regression itself is covered by asserting that whatever a job is handed as `ACTIONS_RESULTS_URL` answers a cache service call. Fixes https://gitea.com/gitea/runner/issues/1139 Reviewed-on: https://gitea.com/gitea/runner/pulls/1141 Reviewed-by: bircni <bircni@icloud.com> Co-authored-by: silverwind <2021+silverwind@noreply.gitea.com>
This commit is contained in:
@@ -52,7 +52,13 @@ type credKey struct{}
|
||||
// poison another repo's cache, even from inside a container that reaches the
|
||||
// cache server over the docker bridge network.
|
||||
type JobCredential struct {
|
||||
Repo string
|
||||
Repo string `json:"repo"`
|
||||
|
||||
// Results is the instance whose artifact service this server forwards for the job, and
|
||||
// InsecureTLS how the runner reaches it; see results.go. The tags are the wire format a
|
||||
// remote runner registers with.
|
||||
Results string `json:"results"`
|
||||
InsecureTLS bool `json:"insecure_tls"`
|
||||
}
|
||||
|
||||
// credEntry holds a registered job's credential along with an active
|
||||
@@ -165,6 +171,7 @@ func StartHandler(dir, outboundIP string, port uint16, internalSecret string, lo
|
||||
router.POST(internalPath+"/register", h.internalAuth(h.internalRegister))
|
||||
router.POST(internalPath+"/revoke", h.internalAuth(h.internalRevoke))
|
||||
h.registerV2Routes(router)
|
||||
router.NotFound = http.HandlerFunc(h.forwardOrNotFound)
|
||||
|
||||
h.router = router
|
||||
|
||||
@@ -211,10 +218,11 @@ func (h *Handler) ExternalURL() string {
|
||||
// is only accepted while the job is running.
|
||||
//
|
||||
// Registrations are reference-counted: if a token is already registered, the
|
||||
// existing repo is kept and the refcount is incremented. The entry is
|
||||
// removed only when every revoker returned by RegisterJob has been called.
|
||||
// credential it was registered with is kept and the refcount is incremented.
|
||||
// The entry is removed only when every revoker returned by RegisterJob has
|
||||
// been called.
|
||||
// This keeps a stray re-registration from silently revoking a live job.
|
||||
func (h *Handler) RegisterJob(token, repo string) func() {
|
||||
func (h *Handler) RegisterJob(token string, cred JobCredential) func() {
|
||||
if h == nil || token == "" {
|
||||
return func() {}
|
||||
}
|
||||
@@ -223,7 +231,7 @@ func (h *Handler) RegisterJob(token, repo string) func() {
|
||||
existing.refs++
|
||||
} else {
|
||||
h.creds[token] = &credEntry{
|
||||
cred: JobCredential{Repo: repo},
|
||||
cred: cred,
|
||||
refs: 1,
|
||||
}
|
||||
}
|
||||
@@ -619,7 +627,7 @@ func (h *Handler) internalAuth(handler httprouter.Handle) httprouter.Handle {
|
||||
|
||||
type internalRegisterBody struct {
|
||||
Token string `json:"token"`
|
||||
Repo string `json:"repo"`
|
||||
JobCredential
|
||||
}
|
||||
|
||||
type internalRevokeBody struct {
|
||||
@@ -627,6 +635,15 @@ type internalRevokeBody struct {
|
||||
}
|
||||
|
||||
// POST /_internal/register
|
||||
// ResultsURL is what a job registered with cred should be given as ACTIONS_RESULTS_URL, or "" when
|
||||
// the credential names no instance to forward the artifact half to.
|
||||
func (h *Handler) ResultsURL(cred JobCredential) string {
|
||||
if h == nil || cred.Results == "" {
|
||||
return ""
|
||||
}
|
||||
return h.ExternalURL()
|
||||
}
|
||||
|
||||
func (h *Handler) internalRegister(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
var body internalRegisterBody
|
||||
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
||||
@@ -637,8 +654,9 @@ func (h *Handler) internalRegister(w http.ResponseWriter, r *http.Request, _ htt
|
||||
h.responseJSON(w, r, http.StatusBadRequest, errors.New("token is required"))
|
||||
return
|
||||
}
|
||||
h.RegisterJob(body.Token, body.Repo)
|
||||
h.responseJSON(w, r, http.StatusOK)
|
||||
h.RegisterJob(body.Token, body.JobCredential)
|
||||
// A server too old to forward answers without this, which is how the caller knows.
|
||||
h.responseJSON(w, r, http.StatusOK, map[string]any{"results_url": h.ResultsURL(body.JobCredential)})
|
||||
}
|
||||
|
||||
// POST /_internal/revoke
|
||||
|
||||
Reference in New Issue
Block a user