From 53732699dfc4892c941ffae6f7ea33dbe57905f7 Mon Sep 17 00:00:00 2001 From: panxiao81 Date: Fri, 25 Sep 2026 19:03:13 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20=E8=A7=A3=E8=80=A6=20runner=20=E4=B8=8E?= =?UTF-8?q?=20placement=20=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/runnerbootstrap/bootstrap.go | 33 +++++++++++++--------- internal/runnerbootstrap/bootstrap_test.go | 6 ++-- internal/runnerbootstrap/executor.go | 15 +++++----- 3 files changed, 30 insertions(+), 24 deletions(-) diff --git a/internal/runnerbootstrap/bootstrap.go b/internal/runnerbootstrap/bootstrap.go index e04499a..5c8ae86 100644 --- a/internal/runnerbootstrap/bootstrap.go +++ b/internal/runnerbootstrap/bootstrap.go @@ -13,13 +13,12 @@ import ( ) const ( - EnvAssignmentID = "CI_ASSIGNMENT_ID" - EnvCapability = "CI_RUNNER_CAPABILITY" - EnvFacadeURL = "CI_RUNNER_FACADE_URL" - EnvFacadeID = "CI_RUNNER_FACADE_SPIFFE_ID" - EnvSPIFFEID = "CI_SPIFFE_ID" - EnvWorkloadClass = "CI_WORKLOAD_CLASS" - EnvDriver = "CI_WORKLOAD_DRIVER" + EnvAssignmentID = "CI_ASSIGNMENT_ID" + EnvCapability = "CI_RUNNER_CAPABILITY" + EnvFacadeURL = "CI_RUNNER_FACADE_URL" + EnvFacadeID = "CI_RUNNER_FACADE_SPIFFE_ID" + EnvSPIFFEID = "CI_SPIFFE_ID" + EnvRunnerLabels = "CI_RUNNER_LABELS_JSON" ) // Bootstrap emits assignment-scoped launch configuration. FacadeURL is the @@ -43,14 +42,17 @@ func (b Bootstrap) Environment(assignment taskassignment.Assignment) (map[string if capability == "" { return nil, errors.New("runner capability issuer is not configured") } + labels, err := json.Marshal([]string{"self-hosted", string(assignment.Placement.Class), string(assignment.Placement.Driver)}) + if err != nil { + return nil, fmt.Errorf("encode runner labels: %w", err) + } return map[string]string{ EnvAssignmentID: assignment.ID, EnvCapability: capability, EnvFacadeURL: b.FacadeURL, EnvFacadeID: b.FacadeSPIFFEID, EnvSPIFFEID: assignment.Identity.SPIFFEID, - EnvWorkloadClass: string(assignment.Placement.Class), - EnvDriver: string(assignment.Placement.Driver), + EnvRunnerLabels: string(labels), "SPIFFE_ENDPOINT_SOCKET": b.WorkloadAPIAddr, }, nil } @@ -69,7 +71,7 @@ type Registration struct { Ephemeral bool `json:"ephemeral"` } -func RegistrationJSON(assignmentID, capability, localProxyURL string, placement taskassignment.Placement) ([]byte, error) { +func RegistrationJSON(assignmentID, capability, localProxyURL string, labels []string) ([]byte, error) { if assignmentID == "" || capability == "" { return nil, errors.New("assignment ID and runner capability are required") } @@ -77,13 +79,18 @@ func RegistrationJSON(assignmentID, capability, localProxyURL string, placement if err != nil || parsed.Scheme != "http" || parsed.Host == "" { return nil, errors.New("local runner proxy URL must be an absolute http URL") } - if err := placement.Validate(); err != nil { - return nil, err + if len(labels) == 0 { + return nil, errors.New("runner labels are required") + } + for _, label := range labels { + if label == "" { + return nil, errors.New("runner labels must not be empty") + } } registration := Registration{ Warning: "Generated for one preassigned task by gitea-dynamic-runner.", UUID: assignmentID, Name: assignmentID, Token: capability, - Address: localProxyURL, Labels: []string{"self-hosted", string(placement.Class), string(placement.Driver)}, Ephemeral: true, + Address: localProxyURL, Labels: append([]string(nil), labels...), Ephemeral: true, } data, err := json.MarshalIndent(registration, "", " ") if err != nil { diff --git a/internal/runnerbootstrap/bootstrap_test.go b/internal/runnerbootstrap/bootstrap_test.go index 5f2aaa9..bcd79ee 100644 --- a/internal/runnerbootstrap/bootstrap_test.go +++ b/internal/runnerbootstrap/bootstrap_test.go @@ -46,7 +46,7 @@ func TestEnvironmentIsDeterministicAndAssignmentScoped(t *testing.T) { if first[EnvAssignmentID] != "gitea-task-42" || first[EnvSPIFFEID] != testAssignment().Identity.SPIFFEID { t.Fatalf("environment = %#v", first) } - if first[EnvWorkloadClass] != "container" || first[EnvDriver] != "kubernetes" || first[EnvFacadeID] == "" { + if first[EnvRunnerLabels] != `["self-hosted","container","kubernetes"]` || first[EnvFacadeID] == "" { t.Fatalf("environment = %#v", first) } if first["SPIFFE_ENDPOINT_SOCKET"] != "unix:///run/spire/agent-sockets/spire-agent.sock" { @@ -55,7 +55,7 @@ func TestEnvironmentIsDeterministicAndAssignmentScoped(t *testing.T) { } func TestRegistrationMatchesOfficialRunnerSchema(t *testing.T) { - data, err := RegistrationJSON("gitea-task-42", "capability", "http://127.0.0.1:8080", taskassignment.OpenSandboxVM) + data, err := RegistrationJSON("gitea-task-42", "capability", "http://127.0.0.1:8080", []string{"self-hosted", "vm", "opensandbox"}) if err != nil { t.Fatal(err) } @@ -72,7 +72,7 @@ func TestRegistrationMatchesOfficialRunnerSchema(t *testing.T) { } func TestRegistrationRejectsNonLocalTLSAddress(t *testing.T) { - if _, err := RegistrationJSON("id", "capability", "https://facade.example", taskassignment.KubernetesContainer); err == nil { + if _, err := RegistrationJSON("id", "capability", "https://facade.example", []string{"self-hosted"}); err == nil { t.Fatal("expected local proxy URL validation error") } } diff --git a/internal/runnerbootstrap/executor.go b/internal/runnerbootstrap/executor.go index c3415e0..608f471 100644 --- a/internal/runnerbootstrap/executor.go +++ b/internal/runnerbootstrap/executor.go @@ -2,6 +2,7 @@ package runnerbootstrap import ( "context" + "encoding/json" "errors" "fmt" "net" @@ -10,14 +11,12 @@ import ( "os/exec" "path/filepath" "time" - - "git.ddupan.top/panxiao81/gitea-dynamic-runner/internal/taskassignment" ) type ExecutorConfig struct { AssignmentID string Capability string - Placement taskassignment.Placement + RunnerLabels []string FacadeURL string FacadeSPIFFEID string WorkloadAPIAddr string @@ -71,7 +70,7 @@ func RunExecutor(ctx context.Context, config ExecutorConfig) error { defer os.RemoveAll(workDir) } registration, err := RegistrationJSON( - config.AssignmentID, config.Capability, "http://"+listener.Addr().String(), config.Placement, + config.AssignmentID, config.Capability, "http://"+listener.Addr().String(), config.RunnerLabels, ) if err != nil { return err @@ -144,13 +143,13 @@ func waitForFacade(ctx context.Context, endpoint string) error { // the assignment-scoped values injected by the backend. The Workload API // address follows SPIFFE_ENDPOINT_SOCKET through go-spiffe when not set here. func ExecutorConfigFromEnvironment() (ExecutorConfig, error) { - placement := taskassignment.Placement{Class: taskassignment.WorkloadClass(os.Getenv(EnvWorkloadClass)), Driver: taskassignment.Driver(os.Getenv(EnvDriver))} - if err := placement.Validate(); err != nil { - return ExecutorConfig{}, err + var labels []string + if err := json.Unmarshal([]byte(os.Getenv(EnvRunnerLabels)), &labels); err != nil || len(labels) == 0 { + return ExecutorConfig{}, errors.New("valid runner labels are required") } config := ExecutorConfig{ AssignmentID: os.Getenv(EnvAssignmentID), Capability: os.Getenv(EnvCapability), - Placement: placement, FacadeURL: os.Getenv(EnvFacadeURL), FacadeSPIFFEID: os.Getenv(EnvFacadeID), + RunnerLabels: labels, FacadeURL: os.Getenv(EnvFacadeURL), FacadeSPIFFEID: os.Getenv(EnvFacadeID), RunnerBinary: os.Getenv("GITEA_RUNNER_BINARY"), RunnerConfig: os.Getenv("GITEA_RUNNER_CONFIG_FILE"), ListenAddress: "127.0.0.1:0", Stdout: os.Stdout, Stderr: os.Stderr,