Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions cmd/virtwork/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -530,9 +530,18 @@ func runE(cmd *cobra.Command, args []string) error {
})
}
if err := g.Wait(); err != nil {
for _, wlID := range auditWorkloadIDs {
_ = auditor.UpdateWorkloadStatus(ctx, wlID, "failed")
}
return fmt.Errorf("creating VMs: %w", err)
}

// Build VM-name → workload-component mapping for readiness tracking
vmToComponent := make(map[string]string, len(plans))
for _, p := range plans {
vmToComponent[p.vmName] = p.component
}

// Wait for readiness
if cfg.WaitForReady {
timeout := time.Duration(cfg.ReadyTimeoutSeconds) * time.Second
Expand All @@ -543,6 +552,7 @@ func runE(cmd *cobra.Command, args []string) error {
timeout, constants.DefaultPollInterval)

failures := 0
failedWorkloads := make(map[string]bool)
for name, err := range results {
if err != nil {
logger.Error("vm readiness check failed",
Expand All @@ -554,6 +564,9 @@ func runE(cmd *cobra.Command, args []string) error {
Message: fmt.Sprintf("VM %s failed readiness check", name),
ErrorDetail: err.Error(),
})
if comp, ok := vmToComponent[name]; ok {
failedWorkloads[comp] = true
}
} else {
_ = auditor.RecordEvent(ctx, execID, audit.EventRecord{
EventType: "vm_ready",
Expand All @@ -562,6 +575,13 @@ func runE(cmd *cobra.Command, args []string) error {
}
}
if failures > 0 {
for comp, wlID := range auditWorkloadIDs {
if failedWorkloads[comp] {
_ = auditor.UpdateWorkloadStatus(ctx, wlID, "failed")
} else {
_ = auditor.UpdateWorkloadStatus(ctx, wlID, "created")
}
}
return fmt.Errorf("%d of %d VMs failed; %w", failures, len(vmNames), ErrReadinessCheck)
}
logger.Info("all VMs ready", slog.Int("vm_count", len(vmNames)))
Expand Down
2 changes: 1 addition & 1 deletion docs/audit-schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ Indexes: `started_at`, `namespace`, `status`, `run_id`.
| `has_data_disk` | INTEGER NOT NULL | 1 when `DataVolumeTemplates()` is non-empty |
| `data_disk_size` | TEXT | NULL when `has_data_disk = 0` |
| `requires_service` | INTEGER NOT NULL | 1 for multi-VM workloads (network, tps) |
| `status` | TEXT NOT NULL | `pending` → `created` (after VM create succeeds) → `failed` |
| `status` | TEXT NOT NULL | `pending` → `created` (after all VMs succeed) or `failed` (on VM creation or readiness error) |

Indexes: `audit_id`, `workload_type`.

Expand Down
57 changes: 57 additions & 0 deletions internal/audit/audit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -562,3 +562,60 @@ var _ = Describe("NoOpAuditor", func() {
Expect(a.Close()).To(Succeed())
})
})

var _ = Describe("Workload status transitions", func() {
var (
auditor *audit.SQLiteAuditor
ctx context.Context
execID int64
wlID int64
)

BeforeEach(func() {
ctx = context.Background()
var err error
auditor, err = audit.NewSQLiteAuditor(":memory:")
Expect(err).NotTo(HaveOccurred())

cfg := &config.Config{Namespace: "test-ns"}
execID, _, err = auditor.StartExecution(ctx, "run", cfg)
Expect(err).NotTo(HaveOccurred())

wlID, err = auditor.RecordWorkload(ctx, execID, audit.WorkloadRecord{
WorkloadType: "cpu", Enabled: true, VMCount: 1, CPUCores: 2, Memory: "2Gi",
})
Expect(err).NotTo(HaveOccurred())
})

AfterEach(func() {
Expect(auditor.Close()).To(Succeed())
})

It("defaults to pending on insert", func() {
db := auditor.DB()
var status string
err := db.QueryRow(`SELECT status FROM workload_details WHERE id = ?`, wlID).Scan(&status)
Expect(err).NotTo(HaveOccurred())
Expect(status).To(Equal("pending"))
})

It("transitions to created on success", func() {
Expect(auditor.UpdateWorkloadStatus(ctx, wlID, "created")).To(Succeed())

db := auditor.DB()
var status string
err := db.QueryRow(`SELECT status FROM workload_details WHERE id = ?`, wlID).Scan(&status)
Expect(err).NotTo(HaveOccurred())
Expect(status).To(Equal("created"))
})

It("transitions to failed on orchestration error", func() {
Expect(auditor.UpdateWorkloadStatus(ctx, wlID, "failed")).To(Succeed())

db := auditor.DB()
var status string
err := db.QueryRow(`SELECT status FROM workload_details WHERE id = ?`, wlID).Scan(&status)
Expect(err).NotTo(HaveOccurred())
Expect(status).To(Equal("failed"))
})
})
Loading