Move avoidable daemon shell-outs into Go
Reduce the control plane's dependency on helper scripts while keeping the hard Linux integration points in the approved shell-out layer. Replace the bash-driven image build path with a native Go builder that clones and optionally resizes the rootfs, boots a temporary Firecracker VM, provisions the guest over SSH, installs packages and modules, and preserves the package-manifest sidecar. Also replace a few small convenience shell-outs with Go helpers: read process stats from /proc, use os.Truncate for ext4 image growth, add file-clone and normalized-line helpers, drop the sh -c work-disk flattening path, and launch Firecracker via a direct sudo command. Add tests for the new SSH/archive and system helpers, plus a policy test that keeps os/exec imports confined to cli/firecracker/system. Update the docs to describe customize.sh as a manual helper rather than the daemon's image-build backend. Validated with go mod tidy, go test ./..., and make build.
This commit is contained in:
parent
0a0b0b617b
commit
942d242c03
17 changed files with 936 additions and 145 deletions
|
|
@ -81,46 +81,64 @@ func TestResizeExt4ImageStopsAtFirstFailure(t *testing.T) {
|
|||
|
||||
tests := []struct {
|
||||
name string
|
||||
steps []systemStep
|
||||
setup func(t *testing.T) string
|
||||
steps func(path string) []systemStep
|
||||
wantErr string
|
||||
wantCalls int
|
||||
}{
|
||||
{
|
||||
name: "truncate failure",
|
||||
steps: []systemStep{
|
||||
{call: systemCall{name: "truncate", args: []string{"-s", "4096", "/tmp/root.ext4"}}, err: errors.New("truncate failed")},
|
||||
setup: func(t *testing.T) string {
|
||||
return t.TempDir()
|
||||
},
|
||||
wantErr: "truncate failed",
|
||||
wantCalls: 1,
|
||||
wantErr: "",
|
||||
wantCalls: 0,
|
||||
},
|
||||
{
|
||||
name: "e2fsck failure",
|
||||
steps: []systemStep{
|
||||
{call: systemCall{name: "truncate", args: []string{"-s", "4096", "/tmp/root.ext4"}}},
|
||||
{call: systemCall{name: "e2fsck", args: []string{"-p", "-f", "/tmp/root.ext4"}}, err: errors.New("e2fsck failed")},
|
||||
steps: func(path string) []systemStep {
|
||||
return []systemStep{
|
||||
{call: systemCall{name: "e2fsck", args: []string{"-p", "-f", path}}, err: errors.New("e2fsck failed")},
|
||||
}
|
||||
},
|
||||
wantErr: "e2fsck failed",
|
||||
wantCalls: 2,
|
||||
wantCalls: 1,
|
||||
},
|
||||
{
|
||||
name: "resize2fs failure",
|
||||
steps: []systemStep{
|
||||
{call: systemCall{name: "truncate", args: []string{"-s", "4096", "/tmp/root.ext4"}}},
|
||||
{call: systemCall{name: "e2fsck", args: []string{"-p", "-f", "/tmp/root.ext4"}}},
|
||||
{call: systemCall{name: "resize2fs", args: []string{"/tmp/root.ext4"}}, err: errors.New("resize2fs failed")},
|
||||
steps: func(path string) []systemStep {
|
||||
return []systemStep{
|
||||
{call: systemCall{name: "e2fsck", args: []string{"-p", "-f", path}}},
|
||||
{call: systemCall{name: "resize2fs", args: []string{path}}, err: errors.New("resize2fs failed")},
|
||||
}
|
||||
},
|
||||
wantErr: "resize2fs failed",
|
||||
wantCalls: 3,
|
||||
wantCalls: 2,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
runner := &scriptedRunner{t: t, steps: tt.steps}
|
||||
err := ResizeExt4Image(context.Background(), runner, "/tmp/root.ext4", 4096)
|
||||
if err == nil || !strings.Contains(err.Error(), tt.wantErr) {
|
||||
path := "/tmp/root.ext4"
|
||||
if tt.setup != nil {
|
||||
path = tt.setup(t)
|
||||
} else {
|
||||
path = filepath.Join(t.TempDir(), "root.ext4")
|
||||
if err := os.WriteFile(path, []byte("seed"), 0o644); err != nil {
|
||||
t.Fatalf("WriteFile(%s): %v", path, err)
|
||||
}
|
||||
}
|
||||
var steps []systemStep
|
||||
if tt.steps != nil {
|
||||
steps = tt.steps(path)
|
||||
}
|
||||
runner := &scriptedRunner{t: t, steps: steps}
|
||||
err := ResizeExt4Image(context.Background(), runner, path, 4096)
|
||||
if err == nil {
|
||||
t.Fatal("ResizeExt4Image() succeeded, want error")
|
||||
}
|
||||
if tt.wantErr != "" && !strings.Contains(err.Error(), tt.wantErr) {
|
||||
t.Fatalf("ResizeExt4Image() error = %v, want %q", err, tt.wantErr)
|
||||
}
|
||||
if len(runner.calls) != tt.wantCalls {
|
||||
|
|
@ -131,6 +149,24 @@ func TestResizeExt4ImageStopsAtFirstFailure(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestReadNormalizedLines(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
path := filepath.Join(t.TempDir(), "packages.apt")
|
||||
if err := os.WriteFile(path, []byte("\n# comment\n git \nless # trailing\n\r\ntmux\r\n"), 0o644); err != nil {
|
||||
t.Fatalf("WriteFile: %v", err)
|
||||
}
|
||||
|
||||
got, err := ReadNormalizedLines(path)
|
||||
if err != nil {
|
||||
t.Fatalf("ReadNormalizedLines: %v", err)
|
||||
}
|
||||
want := []string{"git", "less", "tmux"}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("lines = %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteExt4FileRemovesTempFileAndReturnsCopyError(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
@ -218,6 +254,34 @@ func TestMountTempDirUsesLoopForRegularFilesAndCleanupUsesBackgroundContext(t *t
|
|||
}
|
||||
}
|
||||
|
||||
func TestParseProcHelpers(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
stat, err := parseProcStat("1234 (firecracker) S 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22")
|
||||
if err != nil {
|
||||
t.Fatalf("parseProcStat: %v", err)
|
||||
}
|
||||
if stat.userTicks != 11 || stat.systemTicks != 12 || stat.startTicks != 19 {
|
||||
t.Fatalf("proc stat = %+v", stat)
|
||||
}
|
||||
|
||||
statm, err := parseProcStatm("200 50 0 0 0 0 0")
|
||||
if err != nil {
|
||||
t.Fatalf("parseProcStatm: %v", err)
|
||||
}
|
||||
if statm.sizePages != 200 || statm.residentPages != 50 {
|
||||
t.Fatalf("proc statm = %+v", statm)
|
||||
}
|
||||
|
||||
uptime, err := parseProcUptime("321.50 42.10")
|
||||
if err != nil {
|
||||
t.Fatalf("parseProcUptime: %v", err)
|
||||
}
|
||||
if uptime != 321.50 {
|
||||
t.Fatalf("uptime = %v, want 321.50", uptime)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMountTempDirRemovesTempDirWhenMountFails(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue