Some checks failed
CI/CD / lint (pull_request) Failing after 13s
CI/CD / test (pull_request) Failing after 10s
CI/CD / typecheck (pull_request) Failing after 36s
CI/CD / build (pull_request) Has been skipped
CI/CD / publish-rpm (pull_request) Has been skipped
CI/CD / publish-deb (pull_request) Has been skipped
Kickstart installs on real hardware failed silently — no error reporting, only 3 progress callbacks, zero log streaming. This overhaul makes every install fully observable. Kickstart improvements: - Error trapping in %pre and %post (trap ERR sends failure details to bastion) - 12+ granular progress stages (was 3): SSH, hostname, k3s prep, EFI boot, metadata - Background log streamer: tails %post output and batch-sends to /api/log - bastion_log() function for explicit log lines from kickstart scripts Bastion API: - POST /api/log — receives raw log lines from kickstart (single or batch) - InstallLogBuffer — per-MAC ring buffer (2000 lines) + file persistence - GET /api/logs/:mac — now returns log_lines + log_total alongside stages - SSE /api/logs/:mac/follow — uses named events (event: stage vs event: log) - Progress events forwarded to labd via bastion-progress WebSocket message - Post-provision k3s logs routed through progressBus (was console-only) dnsmasq fixes found during VM testing: - HTTP Boot filename: ipxe-real.efi → ipxe.efi (leftover from old 2-stage approach) - pxe-service directives: only in proxy mode (breaks OVMF PXE in full mode) - PXEClient vendor class echo for UEFI firmware compatibility Integration tests: - PXE boot test: blank UEFI VM → dnsmasq → HTTP Boot → iPXE → bastion → install - ISO boot test: blank VM boots from bastion-generated ISO → same flow - Shared helpers: pxe-network (no DHCP, nftables fix), pxe-vm (UEFI + ISO boot) - test-provision.sh: runs both PXE + ISO tests with prerequisite checks - 250GB sparse QCOW2 disk (LVM layout needs ~204GB) 201 unit tests passing (11 new). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
157 lines
4.3 KiB
Plaintext
157 lines
4.3 KiB
Plaintext
generator client {
|
|
provider = "prisma-client-js"
|
|
}
|
|
|
|
datasource db {
|
|
provider = "cockroachdb"
|
|
url = env("DATABASE_URL")
|
|
}
|
|
|
|
model Server {
|
|
id String @id @default(uuid())
|
|
hostname String @unique
|
|
mac String? @unique
|
|
cloud String @default("baremetal")
|
|
environment String @default("default")
|
|
role String @default("worker")
|
|
labels Json @default("{}")
|
|
ip String?
|
|
agentVersion String?
|
|
status String @default("unknown") // unknown, online, offline, provisioning
|
|
lastHeartbeat DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
agent Agent?
|
|
auditLogs AuditLog[]
|
|
}
|
|
|
|
model Agent {
|
|
id String @id @default(uuid())
|
|
serverId String @unique
|
|
server Server @relation(fields: [serverId], references: [id], onDelete: Cascade)
|
|
certificatePem String?
|
|
enrolledAt DateTime @default(now())
|
|
lastSeen DateTime?
|
|
|
|
@@index([serverId])
|
|
}
|
|
|
|
model User {
|
|
id String @id @default(uuid())
|
|
username String @unique
|
|
displayName String?
|
|
certFingerprint String? @unique
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
roleBindings UserRole[]
|
|
auditLogs AuditLog[]
|
|
}
|
|
|
|
model Role {
|
|
id String @id @default(uuid())
|
|
name String @unique
|
|
description String?
|
|
createdAt DateTime @default(now())
|
|
|
|
permissions Permission[]
|
|
userBindings UserRole[]
|
|
}
|
|
|
|
model Permission {
|
|
id String @id @default(uuid())
|
|
roleId String
|
|
role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)
|
|
type String @default("allow") // allow or deny
|
|
action String // read, exec, apply, destroy, manage, admin, kubectl, *
|
|
cloud String @default("*")
|
|
environment String @default("*")
|
|
server String @default("*")
|
|
|
|
@@index([roleId])
|
|
}
|
|
|
|
model UserRole {
|
|
id String @id @default(uuid())
|
|
userId String
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
roleId String
|
|
role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([userId, roleId])
|
|
@@index([userId])
|
|
@@index([roleId])
|
|
}
|
|
|
|
model JoinToken {
|
|
id String @id @default(uuid())
|
|
token String @unique
|
|
type String @default("one-time") // one-time or reusable
|
|
label String?
|
|
usedBy String? // server hostname that used it
|
|
usedAt DateTime?
|
|
revokedAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
expiresAt DateTime?
|
|
}
|
|
|
|
model AuditLog {
|
|
id String @id @default(uuid())
|
|
userId String?
|
|
user User? @relation(fields: [userId], references: [id])
|
|
serverId String?
|
|
server Server? @relation(fields: [serverId], references: [id])
|
|
sessionId String?
|
|
action String // exec, kubectl, apply, login, rbac-denied, etc.
|
|
resourceType String? // server, cluster, role, app, etc.
|
|
resourceName String?
|
|
args String? // sanitized command args
|
|
result String @default("success") // success, denied, error
|
|
durationMs Int?
|
|
sourceIp String?
|
|
timestamp DateTime @default(now())
|
|
|
|
@@index([userId])
|
|
@@index([serverId])
|
|
@@index([sessionId])
|
|
@@index([timestamp])
|
|
@@index([action])
|
|
}
|
|
|
|
model PulumiRun {
|
|
id String @id @default(uuid())
|
|
userId String
|
|
stackName String
|
|
action String // up, preview, destroy
|
|
status String @default("pending") // pending, running, succeeded, failed
|
|
output String?
|
|
startedAt DateTime @default(now())
|
|
completedAt DateTime?
|
|
|
|
@@index([userId])
|
|
@@index([stackName])
|
|
}
|
|
|
|
model Bastion {
|
|
id String @id @default(uuid())
|
|
hostname String @unique
|
|
network String
|
|
serverIp String
|
|
status String @default("offline") // online, offline
|
|
lastHeartbeat DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
}
|
|
|
|
model Cluster {
|
|
id String @id @default(uuid())
|
|
name String @unique
|
|
cloud String @default("baremetal")
|
|
environment String @default("default")
|
|
kubeconfigEnc String? // encrypted kubeconfig
|
|
labels Json @default("{}")
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
}
|