136 lines
4.4 KiB
Go
136 lines
4.4 KiB
Go
package taskworker
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
runnerv1 "gitea.dev/actionslib/runner/v1"
|
|
|
|
"git.ddupan.top/panxiao81/gitea-dynamic-runner/internal/taskassignment"
|
|
"git.ddupan.top/panxiao81/gitea-dynamic-runner/internal/taskidentity"
|
|
)
|
|
|
|
type fakeBackend struct {
|
|
executor *Executor
|
|
created int
|
|
bound int
|
|
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, _ LaunchSpec) (*Executor, error) {
|
|
b.created++
|
|
b.executor = &Executor{Name: "executor", IdentityTarget: "pod-uid", Phase: PhaseRunning}
|
|
return b.executor, nil
|
|
}
|
|
func (b *fakeBackend) BindIdentity(context.Context, *Executor, taskidentity.Identity) error {
|
|
b.bound++
|
|
return nil
|
|
}
|
|
func (b *fakeBackend) Delete(context.Context, *Executor) error {
|
|
b.deleted++
|
|
b.executor = nil
|
|
return nil
|
|
}
|
|
|
|
type fakeTasks struct {
|
|
terminal bool
|
|
reported []Phase
|
|
}
|
|
|
|
func (t *fakeTasks) Terminal(context.Context, int64) (bool, error) { return t.terminal, nil }
|
|
func (t *fakeTasks) Report(_ context.Context, _ int64, phase Phase) error {
|
|
t.reported = append(t.reported, phase)
|
|
t.terminal = true
|
|
return nil
|
|
}
|
|
|
|
func assignment() taskassignment.Assignment {
|
|
return taskassignment.Assignment{
|
|
ID: "gitea-task-42",
|
|
Backend: taskassignment.BackendPod,
|
|
Task: &runnerv1.Task{Id: 42},
|
|
Identity: taskidentity.Identity{
|
|
Repository: "owner/repo",
|
|
Task: "publish",
|
|
SPIFFEID: "spiffe://ddupan.top/ci/owner/repo/publish",
|
|
},
|
|
}
|
|
}
|
|
|
|
func TestHandleRecoversExistingExecutorWithoutCreatingAnother(t *testing.T) {
|
|
backend := &fakeBackend{executor: &Executor{Name: "existing", IdentityTarget: "uid", Phase: PhaseRunning}}
|
|
worker := Worker{Backend: backend, Tasks: &fakeTasks{}, Bootstrap: fakeBootstrap{}}
|
|
|
|
done, err := worker.Handle(context.Background(), assignment())
|
|
if err != nil || done {
|
|
t.Fatalf("Handle() = (%v, %v), want pending", done, err)
|
|
}
|
|
if backend.created != 0 || backend.bound != 1 {
|
|
t.Fatalf("created=%d bound=%d", backend.created, backend.bound)
|
|
}
|
|
}
|
|
|
|
func TestAcceptAcknowledgesAfterBackendAndIdentityAreDurable(t *testing.T) {
|
|
backend := &fakeBackend{}
|
|
worker := Worker{Backend: backend, Bootstrap: fakeBootstrap{}}
|
|
|
|
accepted, err := worker.Accept(context.Background(), assignment())
|
|
if err != nil || !accepted {
|
|
t.Fatalf("Accept() = (%v, %v), want accepted", accepted, err)
|
|
}
|
|
if backend.created != 1 || backend.bound != 1 || backend.deleted != 0 {
|
|
t.Fatalf("created=%d bound=%d deleted=%d", backend.created, backend.bound, backend.deleted)
|
|
}
|
|
}
|
|
|
|
func TestAcceptRetriesWhileBackendIdentityTargetIsUnavailable(t *testing.T) {
|
|
backend := &fakeBackend{executor: &Executor{Name: "pending", Phase: PhasePending}}
|
|
worker := Worker{Backend: backend, Bootstrap: fakeBootstrap{}}
|
|
|
|
accepted, err := worker.Accept(context.Background(), assignment())
|
|
if err != nil || accepted {
|
|
t.Fatalf("Accept() = (%v, %v), want retry", accepted, err)
|
|
}
|
|
if backend.bound != 0 {
|
|
t.Fatalf("identity bindings = %d", backend.bound)
|
|
}
|
|
}
|
|
|
|
func TestHandleReportsBeforeCleanupAndBecomesRecoverable(t *testing.T) {
|
|
backend := &fakeBackend{executor: &Executor{Name: "finished", IdentityTarget: "uid", Phase: PhaseSucceeded}}
|
|
tasks := &fakeTasks{}
|
|
worker := Worker{Backend: backend, Tasks: tasks, Bootstrap: fakeBootstrap{}}
|
|
|
|
done, err := worker.Handle(context.Background(), assignment())
|
|
if err != nil || !done {
|
|
t.Fatalf("Handle() = (%v, %v), want done", done, err)
|
|
}
|
|
if len(tasks.reported) != 1 || backend.deleted != 1 {
|
|
t.Fatalf("reported=%v deleted=%d", tasks.reported, backend.deleted)
|
|
}
|
|
|
|
// Simulate redelivery after deletion but before the queue ACK. Gitea's
|
|
// terminal state prevents a duplicate executor from being created.
|
|
done, err = worker.Handle(context.Background(), assignment())
|
|
if err != nil || !done || backend.created != 0 {
|
|
t.Fatalf("recovery = (%v, %v), created=%d", done, err, backend.created)
|
|
}
|
|
}
|
|
|
|
func TestBackendMetadataContainsRecoveryKeys(t *testing.T) {
|
|
metadata := BackendMetadata(assignment())
|
|
if metadata.Labels["ci.ddupan.top/assignment-id"] != "gitea-task-42" {
|
|
t.Fatalf("labels = %#v", metadata.Labels)
|
|
}
|
|
if metadata.Annotations["ci.ddupan.top/spiffe-id"] != "spiffe://ddupan.top/ci/owner/repo/publish" {
|
|
t.Fatalf("annotations = %#v", metadata.Annotations)
|
|
}
|
|
}
|