mirror of
https://gitea.com/gitea/act_runner
synced 2026-09-21 19:37:07 +02:00
fix: IPv6 URLs, cache-server SIGTERM and atomic task admission (#1217)
Three independent fixes, one commit each. 1. Hosts and ports were interpolated directly when building URLs, so a literal IPv6 address produced an unbracketed authority, making `ACTIONS_CACHE_URL`, `ACTIONS_RUNTIME_URL` and the artifact server listener unusable. Hosts are expected bare, as documented for `cache.host`, so an address that already carries brackets is no longer accepted. 2. `cache-server` waited on its own `os.Interrupt` channel and ignored SIGTERM, so service managers and container runtimes had to kill it. 3. Task admission used a separate `Load` and `Store`, so two concurrent dispatches of the same task id could both be admitted. Assisted-by: Claude Code:Opus 5 Co-authored-by: bircni <bircni@icloud.com> Reviewed-on: https://gitea.com/gitea/runner/pulls/1217 Reviewed-by: bircni <bircni@icloud.com> Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
committed by
bircni
co-authored by
bircni
parent
2ed8cdb76e
commit
498282caaa
@@ -234,7 +234,7 @@ func StartHandler(opts Options) (*Handler, error) {
|
||||
|
||||
func (h *Handler) ExternalURL() string {
|
||||
// TODO: make the external url configurable if necessary
|
||||
return fmt.Sprintf("http://%s:%d", h.outboundIP, h.port)
|
||||
return "http://" + net.JoinHostPort(h.outboundIP, strconv.Itoa(h.port))
|
||||
}
|
||||
|
||||
func (h *Handler) baseURL(cred JobCredential) string {
|
||||
|
||||
@@ -52,6 +52,10 @@ func signArtifactURL(h *Handler, id int64) string {
|
||||
return h.signedArtifactURL(JobCredential{}, uint64(id), time.Now().Add(artifactURLTTL))
|
||||
}
|
||||
|
||||
func TestHandler_ExternalURL(t *testing.T) {
|
||||
assert.Equal(t, "http://[2001:db8::1]:8080", (&Handler{outboundIP: "2001:db8::1", port: 8080}).ExternalURL())
|
||||
}
|
||||
|
||||
func TestHandler(t *testing.T) {
|
||||
dir := filepath.Join(t.TempDir(), "artifactcache")
|
||||
handler, err := StartHandler(Options{Dir: dir})
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -220,14 +221,14 @@ func Serve(ctx context.Context, artifactPath, addr, port string) context.CancelF
|
||||
downloads(router, artifactPath)
|
||||
|
||||
server := &http.Server{
|
||||
Addr: fmt.Sprintf("%s:%s", addr, port),
|
||||
Addr: net.JoinHostPort(addr, port),
|
||||
ReadHeaderTimeout: 2 * time.Second,
|
||||
Handler: router,
|
||||
}
|
||||
|
||||
// run server
|
||||
go func() {
|
||||
logger.Infof("Start server on http://%s:%s", addr, port)
|
||||
logger.Infof("Start server on http://%s", server.Addr)
|
||||
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
maps0 "maps"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
@@ -1646,7 +1647,7 @@ func imageOSFromImage(image string) string {
|
||||
func setActionRuntimeVars(rc *RunContext, env map[string]string) {
|
||||
actionsRuntimeURL := os.Getenv("ACTIONS_RUNTIME_URL")
|
||||
if actionsRuntimeURL == "" {
|
||||
actionsRuntimeURL = fmt.Sprintf("http://%s:%s/", rc.Config.ArtifactServerAddr, rc.Config.ArtifactServerPort)
|
||||
actionsRuntimeURL = "http://" + net.JoinHostPort(rc.Config.ArtifactServerAddr, rc.Config.ArtifactServerPort) + "/"
|
||||
}
|
||||
env["ACTIONS_RUNTIME_URL"] = actionsRuntimeURL
|
||||
|
||||
|
||||
@@ -1505,6 +1505,10 @@ func TestRunContextWithGithubEnvRunnerValues(t *testing.T) {
|
||||
rc := createRunsOnRunContext(t, "ubuntu-latest")
|
||||
rc.Config.RunnerName = "runner-1"
|
||||
rc.Config.Secrets = map[string]string{"ACTIONS_STEP_DEBUG": "true"}
|
||||
t.Setenv("ACTIONS_RUNTIME_URL", "")
|
||||
rc.Config.ArtifactServerPath = "artifacts"
|
||||
rc.Config.ArtifactServerAddr = "2001:db8::1"
|
||||
rc.Config.ArtifactServerPort = "8080"
|
||||
|
||||
env := map[string]string{}
|
||||
rc.withGithubEnv(ctx, &model.GithubContext{Workspace: "/workspace/owner/repo"}, env)
|
||||
@@ -1513,4 +1517,5 @@ func TestRunContextWithGithubEnvRunnerValues(t *testing.T) {
|
||||
assert.Equal(t, "self-hosted", env["RUNNER_ENVIRONMENT"])
|
||||
assert.Equal(t, "/workspace/owner", env["RUNNER_WORKSPACE"])
|
||||
assert.Equal(t, "1", env["RUNNER_DEBUG"])
|
||||
assert.Equal(t, "http://[2001:db8::1]:8080/", env["ACTIONS_RUNTIME_URL"])
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ func StartGitea(ctx context.Context, cli mobyclient.APIClient) (*GiteaFixture, e
|
||||
sharedNet, _ = selfNetwork(ctx, cli)
|
||||
)
|
||||
if sharedNet != "" {
|
||||
baseURL = fmt.Sprintf("http://%s:3000", name)
|
||||
baseURL = "http://" + net.JoinHostPort(name, "3000")
|
||||
netConfig = &network.NetworkingConfig{
|
||||
EndpointsConfig: map[string]*network.EndpointSettings{sharedNet: {}},
|
||||
}
|
||||
@@ -170,7 +170,7 @@ func StartGitea(ctx context.Context, cli mobyclient.APIClient) (*GiteaFixture, e
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("find a free host port: %w", err)
|
||||
}
|
||||
baseURL = fmt.Sprintf("http://%s:%d", host, port)
|
||||
baseURL = "http://" + net.JoinHostPort(host.String(), strconv.Itoa(port))
|
||||
hostConfig.PortBindings = network.PortMap{
|
||||
containerPort: []network.PortBinding{{HostIP: host, HostPort: strconv.Itoa(port)}},
|
||||
}
|
||||
|
||||
@@ -6,8 +6,6 @@ package cmd
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
|
||||
"gitea.com/gitea/runner/act/artifactcache"
|
||||
"gitea.com/gitea/runner/internal/app/run"
|
||||
@@ -67,10 +65,8 @@ func runCacheServer(configFile *string, cacheArgs *cacheServerArgs) func(cmd *co
|
||||
|
||||
log.Infof("cache server is listening on %v", cacheHandler.ExternalURL())
|
||||
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt)
|
||||
<-c
|
||||
<-cmd.Context().Done()
|
||||
|
||||
return nil
|
||||
return cacheHandler.Close()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ func Execute(ctx context.Context) {
|
||||
// hide completion command
|
||||
rootCmd.CompletionOptions.HiddenDefaultCmd = true
|
||||
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
if err := rootCmd.ExecuteContext(ctx); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,10 +281,9 @@ func (r *Runner) SetCapabilitiesFromDeclare(resp *connect.Response[runnerv1.Decl
|
||||
}
|
||||
|
||||
func (r *Runner) Run(ctx context.Context, task *runnerv1.Task) error {
|
||||
if _, ok := r.runningTasks.Load(task.Id); ok {
|
||||
if _, ok := r.runningTasks.LoadOrStore(task.Id, struct{}{}); ok {
|
||||
return fmt.Errorf("task %d is already running", task.Id)
|
||||
}
|
||||
r.runningTasks.Store(task.Id, struct{}{})
|
||||
defer r.runningTasks.Delete(task.Id)
|
||||
|
||||
r.runningCount.Add(1)
|
||||
|
||||
Reference in New Issue
Block a user