feat: execute durable client commands through supervisor
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
//go:build !windows
|
||||
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
rvboxv1 "github.com/rvbox/rvbox/gen/go/rvbox/v1"
|
||||
"github.com/rvbox/rvbox/internal/client/spool"
|
||||
clientwindows "github.com/rvbox/rvbox/internal/client/supervisor/windows"
|
||||
"github.com/rvbox/rvbox/internal/domain"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
func TestExecutorRunsDurableCommandAndPublishesTerminalEvents_HP_EXECUTOR_01(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
store, err := spool.Open(ctx, spool.Options{DataDir: filepath.Join(t.TempDir(), "spool"), BusyTimeout: time.Second})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer store.Close()
|
||||
issue, _ := domain.ParseUUIDv7("019c46f1-1d02-7000-8000-0000000000d1")
|
||||
spec := &rvboxv1.ExecutionSpec{ShellType: rvboxv1.ShellType_SHELL_SH, Source: &rvboxv1.ExecutionSpec_CommandText{CommandText: "printf executor-ok"}}
|
||||
encoded, err := proto.Marshal(spec)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
requestHash := sha256.Sum256([]byte("executor-request"))
|
||||
if _, err := store.AcceptCommand(ctx, spool.Command{IssueUUID: issue, ImmutableSHA256: requestHash, Revision: 1, Phase: uint32(rvboxv1.CommandLifecycle_COMMAND_ACCEPTED), ExecutionSpec: encoded}, time.Now().UTC()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
notified := make(chan domain.UUID, 16)
|
||||
supervised, err := clientwindows.NewSupervisor(clientwindows.NativeOptions{MaxOutputChunk: 64})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
executor, err := NewExecutor(ExecutorOptions{Store: store, Supervisor: supervised, WorkDir: t.TempDir(), Notify: func(value domain.UUID) { notified <- value }})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
dispatch := &rvboxv1.CommandDispatch{IssueUuid: issue.String(), CommandRevision: 1, TargetSessionGeneration: 1, IssueTime: timestamppb.Now(), Spec: spec, ImmutableRequestSha256: requestHash[:]}
|
||||
if err := executor.Dispatch(ctx, Session{Generation: 1}, dispatch); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
deadline := time.After(5 * time.Second)
|
||||
for {
|
||||
command, err := store.GetCommand(ctx, issue)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if command.Phase == uint32(rvboxv1.CommandLifecycle_COMMAND_SUCCEEDED) {
|
||||
break
|
||||
}
|
||||
select {
|
||||
case <-deadline:
|
||||
t.Fatalf("executor did not reach terminal phase: %d", command.Phase)
|
||||
case <-notified:
|
||||
}
|
||||
}
|
||||
if _, err := store.AssignSendWindow(ctx, issue, 8, 1<<20); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
events, err := store.PendingEvents(ctx, issue)
|
||||
if err != nil || len(events) < 3 {
|
||||
t.Fatalf("durable executor events = %#v, %v", events, err)
|
||||
}
|
||||
if _, err := store.Check(ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecutorWaitsForScriptCommitBeforeLaunch_HP_EXECUTOR_02(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
store, err := spool.Open(ctx, spool.Options{DataDir: filepath.Join(t.TempDir(), "spool"), BusyTimeout: time.Second})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer store.Close()
|
||||
issue, _ := domain.ParseUUIDv7("019c46f1-1d02-7000-8000-0000000000d2")
|
||||
body := []byte("printf script-ok")
|
||||
digest := sha256.Sum256(body)
|
||||
spec := &rvboxv1.ExecutionSpec{ShellType: rvboxv1.ShellType_SHELL_SH, Source: &rvboxv1.ExecutionSpec_Script{Script: &rvboxv1.ScriptDescriptor{Filename: "ignored.sh", SizeBytes: uint64(len(body)), Sha256: digest[:]}}}
|
||||
encoded, _ := proto.Marshal(spec)
|
||||
hash := sha256.Sum256([]byte("script-executor-request"))
|
||||
if _, err := store.AcceptCommand(ctx, spool.Command{IssueUUID: issue, ImmutableSHA256: hash, Revision: 1, Phase: uint32(rvboxv1.CommandLifecycle_COMMAND_ACCEPTED), ExecutionSpec: encoded, Script: &spool.ScriptDescriptor{SizeBytes: uint64(len(body)), SHA256: digest}}, time.Now().UTC()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
supervised, _ := clientwindows.NewSupervisor(clientwindows.NativeOptions{})
|
||||
executor, err := NewExecutor(ExecutorOptions{Store: store, Supervisor: supervised, WorkDir: t.TempDir()})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
dispatch := &rvboxv1.CommandDispatch{IssueUuid: issue.String(), CommandRevision: 1, TargetSessionGeneration: 1, Spec: spec, ImmutableRequestSha256: hash[:]}
|
||||
if err := executor.Dispatch(ctx, Session{Generation: 1}, dispatch); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
command, _ := store.GetCommand(ctx, issue)
|
||||
if command.Phase != uint32(rvboxv1.CommandLifecycle_COMMAND_ACCEPTED) {
|
||||
t.Fatalf("script launched before commit, phase=%d", command.Phase)
|
||||
}
|
||||
chunkHash := sha256.Sum256(body)
|
||||
if _, err := store.AppendScriptChunk(ctx, issue, 0, body, chunkHash); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := store.CommitScript(ctx, issue, spool.ScriptDescriptor{SizeBytes: uint64(len(body)), SHA256: digest}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := executor.ScriptReady(ctx, Session{Generation: 1}, issue); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
deadline := time.After(5 * time.Second)
|
||||
for {
|
||||
command, err = store.GetCommand(ctx, issue)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if command.Phase == uint32(rvboxv1.CommandLifecycle_COMMAND_SUCCEEDED) {
|
||||
return
|
||||
}
|
||||
select {
|
||||
case <-deadline:
|
||||
t.Fatal("committed script did not reach terminal phase")
|
||||
case <-time.After(10 * time.Millisecond):
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user