feat: backend executor implement
This commit is contained in:
@@ -4,12 +4,135 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ncruces/go-sqlite3/driver"
|
||||
)
|
||||
|
||||
func TestStoreMigratesVersionOneAndCommitsBackendContainerDeployment(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
databasePath := filepath.Join(t.TempDir(), "transaction.db")
|
||||
dsn := (&url.URL{Scheme: "file", Path: databasePath}).String()
|
||||
database, err := driver.Open(dsn)
|
||||
if err != nil {
|
||||
t.Fatalf("open version one database: %v", err)
|
||||
}
|
||||
if _, err := database.ExecContext(ctx, schemaV1); err != nil {
|
||||
_ = database.Close()
|
||||
t.Fatalf("create version one database: %v", err)
|
||||
}
|
||||
if err := database.Close(); err != nil {
|
||||
t.Fatalf("close version one database: %v", err)
|
||||
}
|
||||
|
||||
store, err := OpenStore(ctx, databasePath)
|
||||
if err != nil {
|
||||
t.Fatalf("migrate transaction store: %v", err)
|
||||
}
|
||||
t.Cleanup(func() { _ = store.Close() })
|
||||
|
||||
if _, err := store.BackendContainerDeployment(ctx); !errors.Is(err, ErrNotFound) {
|
||||
t.Fatalf("unexpected deployment before commit: %v", err)
|
||||
}
|
||||
hasHistory, err := store.HasCommittedBackendContainerTransactionHistory(ctx)
|
||||
if err != nil || hasHistory {
|
||||
t.Fatalf("unexpected empty backend history: found=%t err=%v", hasHistory, err)
|
||||
}
|
||||
nativeRecord, _, err := store.CreateTransaction(ctx, CreateRequest{
|
||||
ID: "native-backend-transaction",
|
||||
IdempotencyKey: "backend:native-request",
|
||||
Source: "test",
|
||||
Service: "backend",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("create native backend transaction: %v", err)
|
||||
}
|
||||
if _, err := store.Transition(ctx, nativeRecord.ID, StateFailed, "finish native backend transaction"); err != nil {
|
||||
t.Fatalf("finish native backend transaction: %v", err)
|
||||
}
|
||||
hasHistory, err = store.HasCommittedBackendContainerTransactionHistory(ctx)
|
||||
if err != nil || hasHistory {
|
||||
t.Fatalf("native backend history was treated as container history: found=%t err=%v", hasHistory, err)
|
||||
}
|
||||
|
||||
record, created, err := store.CreateTransaction(ctx, CreateRequest{
|
||||
ID: "container-deployment-transaction",
|
||||
IdempotencyKey: "backend:container:deployment-request",
|
||||
Source: "test",
|
||||
Service: "backend",
|
||||
})
|
||||
if err != nil || !created {
|
||||
t.Fatalf("create backend container transaction: record=%+v created=%t err=%v", record, created, err)
|
||||
}
|
||||
for _, state := range []State{StateValidating, StatePrepared, StateStarting, StateSwitching, StateVerifying, StateDraining} {
|
||||
if _, err := store.Transition(ctx, record.ID, state, "test transition"); err != nil {
|
||||
t.Fatalf("transition backend container transaction to %s: %v", state, err)
|
||||
}
|
||||
}
|
||||
hasHistory, err = store.HasCommittedBackendContainerTransactionHistory(ctx)
|
||||
if err != nil || hasHistory {
|
||||
t.Fatalf("unfinished backend container transaction was treated as committed history: found=%t err=%v", hasHistory, err)
|
||||
}
|
||||
|
||||
committed, err := store.CommitBackendContainerDeployment(ctx, record.ID, BackendContainerDeployment{
|
||||
ActivePort: 8081,
|
||||
ContainerName: "backend-8081",
|
||||
ImageDigest: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
ContainerID: "container-backend-8081",
|
||||
}, "container deployment committed")
|
||||
if err != nil || committed.State != StateCommitted {
|
||||
t.Fatalf("commit backend container deployment: record=%+v err=%v", committed, err)
|
||||
}
|
||||
hasHistory, err = store.HasCommittedBackendContainerTransactionHistory(ctx)
|
||||
if err != nil || !hasHistory {
|
||||
t.Fatalf("committed backend container history was not recorded: found=%t err=%v", hasHistory, err)
|
||||
}
|
||||
deployment, err := store.BackendContainerDeployment(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("read backend container deployment: %v", err)
|
||||
}
|
||||
if deployment.ActivePort != 8081 || deployment.ContainerName != "backend-8081" || deployment.ContainerID != "container-backend-8081" || deployment.TransactionID != record.ID {
|
||||
t.Fatalf("unexpected backend container deployment: %+v", deployment)
|
||||
}
|
||||
var version int
|
||||
if err := store.db.QueryRowContext(ctx, "PRAGMA user_version").Scan(&version); err != nil || version != schemaVersion {
|
||||
t.Fatalf("unexpected migrated schema version: version=%d err=%v", version, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackendContainerDeploymentCommitRequiresDrainingTransaction(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
store := openTestStore(t)
|
||||
record, _, err := store.CreateTransaction(ctx, CreateRequest{
|
||||
ID: "container-deployment-wrong-state",
|
||||
IdempotencyKey: "container-deployment-wrong-state-request",
|
||||
Source: "test",
|
||||
Service: "backend",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("create backend transaction: %v", err)
|
||||
}
|
||||
_, err = store.CommitBackendContainerDeployment(ctx, record.ID, BackendContainerDeployment{
|
||||
ActivePort: 8080,
|
||||
ContainerName: "backend-8080",
|
||||
ImageDigest: "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||||
ContainerID: "container-backend-8080",
|
||||
}, "must fail")
|
||||
var transitionErr *TransitionError
|
||||
if !errors.As(err, &transitionErr) {
|
||||
t.Fatalf("expected deployment commit transition error, got %v", err)
|
||||
}
|
||||
if _, err := store.BackendContainerDeployment(ctx); !errors.Is(err, ErrNotFound) {
|
||||
t.Fatalf("failed commit wrote backend deployment: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStoreCreateIsIdempotentAndAllowsOnlyOneActiveTransaction(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
@@ -51,6 +174,26 @@ func TestStoreCreateIsIdempotentAndAllowsOnlyOneActiveTransaction(t *testing.T)
|
||||
if _, err := store.Transition(ctx, created.ID, StateFailed, "test terminal state"); err != nil {
|
||||
t.Fatalf("finish first transaction: %v", err)
|
||||
}
|
||||
retriedAfterFailure, isNew, err := store.CreateTransaction(ctx, CreateRequest{
|
||||
ID: "transaction-1-retry",
|
||||
IdempotencyKey: request.IdempotencyKey,
|
||||
Source: request.Source,
|
||||
Service: request.Service,
|
||||
Request: request.Request,
|
||||
})
|
||||
if err != nil || !isNew || retriedAfterFailure.ID != "transaction-1-retry" {
|
||||
t.Fatalf("retry failed transaction: record=%+v new=%v err=%v", retriedAfterFailure, isNew, err)
|
||||
}
|
||||
archived, err := store.Transaction(ctx, created.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("read archived failed transaction: %v", err)
|
||||
}
|
||||
if archived.IdempotencyKey != request.IdempotencyKey+":terminal:"+created.ID {
|
||||
t.Fatalf("unexpected archived idempotency key: %q", archived.IdempotencyKey)
|
||||
}
|
||||
if _, err := store.Transition(ctx, retriedAfterFailure.ID, StateFailed, "finish retried transaction"); err != nil {
|
||||
t.Fatalf("finish retried transaction: %v", err)
|
||||
}
|
||||
second, isNew, err := store.CreateTransaction(ctx, CreateRequest{
|
||||
ID: "transaction-2",
|
||||
IdempotencyKey: "request-2",
|
||||
|
||||
Reference in New Issue
Block a user