feat: bootstrap official runner through SPIFFE facade

This commit is contained in:
2026-09-20 20:00:34 +00:00
parent 5fdd39f7ff
commit 8b77b4be63
16 changed files with 470 additions and 28 deletions
+41 -9
View File
@@ -34,11 +34,26 @@ type Metadata struct {
Annotations map[string]string
}
// LaunchSpec contains the durable resource metadata and the short-lived
// executor environment. Environment values configure the one-shot runner but
// are deliberately excluded from labels and annotations.
type LaunchSpec struct {
Metadata Metadata
Environment map[string]string
}
// Bootstrap produces assignment-scoped executor configuration. Implementations
// must be deterministic so a redelivery after controller restart creates the
// same credentials without storing another lifecycle record.
type Bootstrap interface {
Environment(taskassignment.Assignment) (map[string]string, error)
}
// Backend is implemented by the native Pod and OpenSandbox adapters.
// Every method must be idempotent.
type Backend interface {
Find(context.Context, string) (*Executor, error)
Create(context.Context, taskassignment.Assignment, Metadata) (*Executor, error)
Create(context.Context, taskassignment.Assignment, LaunchSpec) (*Executor, error)
BindIdentity(context.Context, *Executor, taskidentity.Identity) error
Delete(context.Context, *Executor) error
}
@@ -53,16 +68,17 @@ type TaskState interface {
// Worker has no correctness-critical in-memory state. Handle may be called
// again for the same assignment after any operation.
type Worker struct {
Backend Backend
Tasks TaskState
Backend Backend
Tasks TaskState
Bootstrap Bootstrap
}
// Accept completes the durable handoff from JetStream to the backend. Once it
// returns true, all recovery information exists in Kubernetes/OpenSandbox and
// the assignment message can be acknowledged immediately.
func (w Worker) Accept(ctx context.Context, assignment taskassignment.Assignment) (bool, error) {
if w.Backend == nil || w.Tasks == nil {
return false, errors.New("backend and Gitea task state are required")
if w.Backend == nil || w.Tasks == nil || w.Bootstrap == nil {
return false, errors.New("backend, Gitea task state, and runner bootstrap are required")
}
if assignment.ID == "" || assignment.Task == nil {
return false, errors.New("valid assignment is required")
@@ -84,7 +100,11 @@ func (w Worker) Accept(ctx context.Context, assignment taskassignment.Assignment
return true, nil
}
if executor == nil {
executor, err = w.Backend.Create(ctx, assignment, BackendMetadata(assignment))
launch, launchErr := w.launchSpec(assignment)
if launchErr != nil {
return false, launchErr
}
executor, err = w.Backend.Create(ctx, assignment, launch)
if err != nil {
return false, err
}
@@ -101,8 +121,8 @@ func (w Worker) Accept(ctx context.Context, assignment taskassignment.Assignment
// Handle performs one reconciliation. Done means the queue message may be
// acknowledged. A false result should remain pending and be reconciled again.
func (w Worker) Handle(ctx context.Context, assignment taskassignment.Assignment) (done bool, err error) {
if w.Backend == nil || w.Tasks == nil {
return false, errors.New("backend and Gitea task state are required")
if w.Backend == nil || w.Tasks == nil || w.Bootstrap == nil {
return false, errors.New("backend, Gitea task state, and runner bootstrap are required")
}
if assignment.ID == "" || assignment.Task == nil {
return false, errors.New("valid assignment is required")
@@ -126,7 +146,11 @@ func (w Worker) Handle(ctx context.Context, assignment taskassignment.Assignment
}
if executor == nil {
executor, err = w.Backend.Create(ctx, assignment, BackendMetadata(assignment))
launch, launchErr := w.launchSpec(assignment)
if launchErr != nil {
return false, launchErr
}
executor, err = w.Backend.Create(ctx, assignment, launch)
if err != nil {
return false, err
}
@@ -154,6 +178,14 @@ func (w Worker) Handle(ctx context.Context, assignment taskassignment.Assignment
}
}
func (w Worker) launchSpec(assignment taskassignment.Assignment) (LaunchSpec, error) {
environment, err := w.Bootstrap.Environment(assignment)
if err != nil {
return LaunchSpec{}, err
}
return LaunchSpec{Metadata: BackendMetadata(assignment), Environment: environment}, nil
}
// BackendMetadata is the shared metadata contract for Pods and OpenSandbox.
func BackendMetadata(assignment taskassignment.Assignment) Metadata {
return Metadata{
+11 -5
View File
@@ -17,8 +17,14 @@ type fakeBackend struct {
deleted int
}
type fakeBootstrap struct{}
func (fakeBootstrap) Environment(taskassignment.Assignment) (map[string]string, error) {
return map[string]string{"CI_RUNNER_CAPABILITY": "capability"}, nil
}
func (b *fakeBackend) Find(context.Context, string) (*Executor, error) { return b.executor, nil }
func (b *fakeBackend) Create(_ context.Context, _ taskassignment.Assignment, _ Metadata) (*Executor, error) {
func (b *fakeBackend) Create(_ context.Context, _ taskassignment.Assignment, _ LaunchSpec) (*Executor, error) {
b.created++
b.executor = &Executor{Name: "executor", IdentityTarget: "pod-uid", Phase: PhaseRunning}
return b.executor, nil
@@ -60,7 +66,7 @@ func assignment() taskassignment.Assignment {
func TestHandleRecoversExistingExecutorWithoutCreatingAnother(t *testing.T) {
backend := &fakeBackend{executor: &Executor{Name: "existing", IdentityTarget: "uid", Phase: PhaseRunning}}
worker := Worker{Backend: backend, Tasks: &fakeTasks{}}
worker := Worker{Backend: backend, Tasks: &fakeTasks{}, Bootstrap: fakeBootstrap{}}
done, err := worker.Handle(context.Background(), assignment())
if err != nil || done {
@@ -73,7 +79,7 @@ func TestHandleRecoversExistingExecutorWithoutCreatingAnother(t *testing.T) {
func TestAcceptAcknowledgesAfterBackendAndIdentityAreDurable(t *testing.T) {
backend := &fakeBackend{}
worker := Worker{Backend: backend, Tasks: &fakeTasks{}}
worker := Worker{Backend: backend, Tasks: &fakeTasks{}, Bootstrap: fakeBootstrap{}}
accepted, err := worker.Accept(context.Background(), assignment())
if err != nil || !accepted {
@@ -86,7 +92,7 @@ func TestAcceptAcknowledgesAfterBackendAndIdentityAreDurable(t *testing.T) {
func TestAcceptRetriesWhileBackendIdentityTargetIsUnavailable(t *testing.T) {
backend := &fakeBackend{executor: &Executor{Name: "pending", Phase: PhasePending}}
worker := Worker{Backend: backend, Tasks: &fakeTasks{}}
worker := Worker{Backend: backend, Tasks: &fakeTasks{}, Bootstrap: fakeBootstrap{}}
accepted, err := worker.Accept(context.Background(), assignment())
if err != nil || accepted {
@@ -100,7 +106,7 @@ func TestAcceptRetriesWhileBackendIdentityTargetIsUnavailable(t *testing.T) {
func TestHandleReportsBeforeCleanupAndBecomesRecoverable(t *testing.T) {
backend := &fakeBackend{executor: &Executor{Name: "finished", IdentityTarget: "uid", Phase: PhaseSucceeded}}
tasks := &fakeTasks{}
worker := Worker{Backend: backend, Tasks: tasks}
worker := Worker{Backend: backend, Tasks: tasks, Bootstrap: fakeBootstrap{}}
done, err := worker.Handle(context.Background(), assignment())
if err != nil || !done {