forked from wrenn/wrenn
v0.2.0 (#50)
Co-authored-by: Tasnim Kabir Sadik <tksadik@omukk.dev> Reviewed-on: wrenn/wrenn#50
This commit is contained in:
@ -6,26 +6,28 @@ import (
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
|
||||
"git.omukk.dev/wrenn/wrenn/pkg/id"
|
||||
)
|
||||
|
||||
// IsMinimal reports whether the given team and template IDs represent the
|
||||
// built-in "minimal" template (both all-zeros).
|
||||
func IsMinimal(teamID, templateID pgtype.UUID) bool {
|
||||
return teamID.Bytes == id.PlatformTeamID.Bytes && templateID.Bytes == id.MinimalTemplateID.Bytes
|
||||
func timeNowNano() int64 { return time.Now().UnixNano() }
|
||||
|
||||
// IsSystemTemplate reports whether the given team and template IDs represent a
|
||||
// built-in system base template (minimal-ubuntu / -alpine / -arch / -fedora):
|
||||
// platform-owned with a template ID in the reserved range. System templates are
|
||||
// protected from deletion.
|
||||
func IsSystemTemplate(teamID, templateID pgtype.UUID) bool {
|
||||
return teamID.Bytes == id.PlatformTeamID.Bytes && id.IsReservedTemplateID(templateID)
|
||||
}
|
||||
|
||||
// TemplateDir returns the on-disk directory for a template.
|
||||
// TemplateDir returns the on-disk directory for a template. Every template —
|
||||
// including the built-in system base templates — lives under the teams tree:
|
||||
//
|
||||
// minimal (zeros, zeros): {wrennDir}/images/minimal
|
||||
// all others: {wrennDir}/images/teams/{base36(teamID)}/{base36(templateID)}
|
||||
// {wrennDir}/images/teams/{base36(teamID)}/{base36(templateID)}
|
||||
func TemplateDir(wrennDir string, teamID, templateID pgtype.UUID) string {
|
||||
if IsMinimal(teamID, templateID) {
|
||||
return filepath.Join(wrennDir, "images", "minimal")
|
||||
}
|
||||
return filepath.Join(wrennDir, "images", "teams",
|
||||
id.UUIDToBase36(teamID.Bytes),
|
||||
id.UUIDToBase36(templateID.Bytes))
|
||||
@ -36,17 +38,64 @@ func TemplateRootfs(wrennDir string, teamID, templateID pgtype.UUID) string {
|
||||
return filepath.Join(TemplateDir(wrennDir, teamID, templateID), "rootfs.ext4")
|
||||
}
|
||||
|
||||
// PauseSnapshotDir returns the directory for a paused sandbox's snapshot files.
|
||||
func PauseSnapshotDir(wrennDir, sandboxID string) string {
|
||||
return filepath.Join(wrennDir, "snapshots", sandboxID)
|
||||
// IsSnapshotTemplate reports whether dir contains a Cloud Hypervisor memory
|
||||
// snapshot (state.json + config.json + memory-ranges) alongside the flattened
|
||||
// rootfs.ext4. Used to distinguish snapshot templates (launch via CH restore)
|
||||
// from base/disk-only templates (launch via fresh boot).
|
||||
//
|
||||
// state.json is CH-authoritative — its presence indicates a complete snapshot.
|
||||
func IsSnapshotTemplate(dir string) bool {
|
||||
for _, name := range []string{"state.json", "config.json", "rootfs.ext4"} {
|
||||
if _, err := os.Stat(filepath.Join(dir, name)); err != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// SandboxesDir returns the directory for running sandbox CoW files.
|
||||
// SandboxCowName is the filename for a sandbox's CoW rootfs diff, kept inside
|
||||
// the per-sandbox directory alongside any pause snapshot files.
|
||||
const SandboxCowName = "rootfs.cow"
|
||||
|
||||
// SandboxDir returns the per-sandbox directory under sandboxes/. It holds
|
||||
// the CoW file and, if the sandbox is paused, the snapshot files.
|
||||
//
|
||||
// Layout:
|
||||
//
|
||||
// {wrennDir}/sandboxes/{id}/rootfs.cow CoW file (persistent across pause/resume)
|
||||
// {wrennDir}/sandboxes/{id}/ paused snapshot (config.json, state.json, memory-ranges, wrenn-snapshot.json)
|
||||
// {wrennDir}/sandboxes/{id}.staging-*/ in-flight Pause writes (cleaned up by swapDir or startup GC)
|
||||
// {wrennDir}/sandboxes/{id}.trash-*/ mid-swap previous generation (cleaned up by swapDir or startup GC)
|
||||
func SandboxDir(wrennDir, sandboxID string) string {
|
||||
return filepath.Join(wrennDir, "sandboxes", sandboxID)
|
||||
}
|
||||
|
||||
// SandboxCowPath returns the path to a sandbox's CoW rootfs diff file.
|
||||
func SandboxCowPath(wrennDir, sandboxID string) string {
|
||||
return filepath.Join(SandboxDir(wrennDir, sandboxID), SandboxCowName)
|
||||
}
|
||||
|
||||
// PauseSnapshotDir returns the directory for a paused sandbox's snapshot files.
|
||||
// Same path as SandboxDir — pause snapshot files live alongside the CoW.
|
||||
func PauseSnapshotDir(wrennDir, sandboxID string) string {
|
||||
return SandboxDir(wrennDir, sandboxID)
|
||||
}
|
||||
|
||||
// PauseStagingDir returns a fresh staging directory for an in-flight Pause.
|
||||
// Each call returns a unique path (timestamped) so concurrent retries do not
|
||||
// collide.
|
||||
func PauseStagingDir(wrennDir, sandboxID string) string {
|
||||
return filepath.Join(wrennDir, "sandboxes",
|
||||
fmt.Sprintf("%s.staging-%d", sandboxID, timeNowNano()))
|
||||
}
|
||||
|
||||
// SandboxesDir returns the directory for running sandbox CoW files and paused
|
||||
// snapshot directories.
|
||||
func SandboxesDir(wrennDir string) string {
|
||||
return filepath.Join(wrennDir, "sandboxes")
|
||||
}
|
||||
|
||||
// KernelPath returns the path to the Firecracker kernel.
|
||||
// KernelPath returns the path to the VM kernel.
|
||||
func KernelPath(wrennDir string) string {
|
||||
return filepath.Join(wrennDir, "kernels", "vmlinux")
|
||||
}
|
||||
|
||||
@ -9,7 +9,7 @@ import (
|
||||
"git.omukk.dev/wrenn/wrenn/pkg/id"
|
||||
)
|
||||
|
||||
func TestIsMinimal(t *testing.T) {
|
||||
func TestIsSystemTemplate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
teamID pgtype.UUID
|
||||
@ -17,35 +17,41 @@ func TestIsMinimal(t *testing.T) {
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "both zeros",
|
||||
name: "ubuntu (zeros, zeros)",
|
||||
teamID: id.PlatformTeamID,
|
||||
templateID: id.MinimalTemplateID,
|
||||
templateID: id.UbuntuTemplateID,
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "non-zero team",
|
||||
teamID: pgtype.UUID{Bytes: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, Valid: true},
|
||||
templateID: id.MinimalTemplateID,
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "non-zero template",
|
||||
name: "fedora (platform, id 3)",
|
||||
teamID: id.PlatformTeamID,
|
||||
templateID: pgtype.UUID{Bytes: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, Valid: true},
|
||||
templateID: id.FedoraTemplateID,
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "platform, max reserved id",
|
||||
teamID: id.PlatformTeamID,
|
||||
templateID: pgtype.UUID{Bytes: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x04, 0x00}, Valid: true}, // 1024
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "platform, above reserved range",
|
||||
teamID: id.PlatformTeamID,
|
||||
templateID: pgtype.UUID{Bytes: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x04, 0x01}, Valid: true}, // 1025
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "both non-zero",
|
||||
teamID: pgtype.UUID{Bytes: [16]byte{1}, Valid: true},
|
||||
templateID: pgtype.UUID{Bytes: [16]byte{2}, Valid: true},
|
||||
name: "non-platform team, reserved id",
|
||||
teamID: pgtype.UUID{Bytes: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, Valid: true},
|
||||
templateID: id.UbuntuTemplateID,
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := IsMinimal(tt.teamID, tt.templateID); got != tt.want {
|
||||
t.Errorf("IsMinimal() = %v, want %v", got, tt.want)
|
||||
if got := IsSystemTemplate(tt.teamID, tt.templateID); got != tt.want {
|
||||
t.Errorf("IsSystemTemplate() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -54,9 +60,11 @@ func TestIsMinimal(t *testing.T) {
|
||||
func TestTemplateDir(t *testing.T) {
|
||||
wrennDir := "/var/lib/wrenn"
|
||||
|
||||
t.Run("minimal", func(t *testing.T) {
|
||||
got := TemplateDir(wrennDir, id.PlatformTeamID, id.MinimalTemplateID)
|
||||
want := filepath.Join(wrennDir, "images", "minimal")
|
||||
t.Run("system base template (ubuntu) lives under teams", func(t *testing.T) {
|
||||
got := TemplateDir(wrennDir, id.PlatformTeamID, id.UbuntuTemplateID)
|
||||
want := filepath.Join(wrennDir, "images", "teams",
|
||||
id.UUIDToBase36(id.PlatformTeamID.Bytes),
|
||||
id.UUIDToBase36(id.UbuntuTemplateID.Bytes))
|
||||
if got != want {
|
||||
t.Errorf("TemplateDir() = %q, want %q", got, want)
|
||||
}
|
||||
@ -88,8 +96,11 @@ func TestTemplateDir(t *testing.T) {
|
||||
|
||||
func TestTemplateRootfs(t *testing.T) {
|
||||
wrennDir := "/var/lib/wrenn"
|
||||
got := TemplateRootfs(wrennDir, id.PlatformTeamID, id.MinimalTemplateID)
|
||||
want := filepath.Join(wrennDir, "images", "minimal", "rootfs.ext4")
|
||||
got := TemplateRootfs(wrennDir, id.PlatformTeamID, id.UbuntuTemplateID)
|
||||
want := filepath.Join(wrennDir, "images", "teams",
|
||||
id.UUIDToBase36(id.PlatformTeamID.Bytes),
|
||||
id.UUIDToBase36(id.UbuntuTemplateID.Bytes),
|
||||
"rootfs.ext4")
|
||||
if got != want {
|
||||
t.Errorf("TemplateRootfs() = %q, want %q", got, want)
|
||||
}
|
||||
@ -97,12 +108,20 @@ func TestTemplateRootfs(t *testing.T) {
|
||||
|
||||
func TestPauseSnapshotDir(t *testing.T) {
|
||||
got := PauseSnapshotDir("/var/lib/wrenn", "cl-abc123")
|
||||
want := "/var/lib/wrenn/snapshots/cl-abc123"
|
||||
want := "/var/lib/wrenn/sandboxes/cl-abc123"
|
||||
if got != want {
|
||||
t.Errorf("PauseSnapshotDir() = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPauseStagingDir(t *testing.T) {
|
||||
got := PauseStagingDir("/var/lib/wrenn", "cl-abc123")
|
||||
prefix := "/var/lib/wrenn/sandboxes/cl-abc123.staging-"
|
||||
if len(got) <= len(prefix) || got[:len(prefix)] != prefix {
|
||||
t.Errorf("PauseStagingDir() = %q, want prefix %q", got, prefix)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSandboxesDir(t *testing.T) {
|
||||
got := SandboxesDir("/var/lib/wrenn")
|
||||
want := "/var/lib/wrenn/sandboxes"
|
||||
|
||||
Reference in New Issue
Block a user