diff --git a/.golangci.yml b/.golangci.yml index 49c80c1..2327dd9 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -9,9 +9,42 @@ linters: - staticcheck - whitespace - wrapcheck - exclusions: - presets: - - comments + - ineffassign + - unused + - unconvert + - bodyclose + - copyloopvar + - nilerr + - nilnil + - wastedassign + - maintidx + - nakedret + - predeclared + - gocritic + - prealloc + - noctx + - testifylint + - unparam + - nestif + - gocyclo + - cyclop + settings: + # TODO: is this disable for revive OK? what does it do? + revive: + rules: + - name: exported + disabled: true + # TODO: check complexity values + cyclop: + max-complexity: 70 + gocyclo: + min-complexity: 70 + nestif: + min-complexity: 17 + # TODO: is this value OK? What does it do? + maintidx: + under: 5 formatters: enable: - - gofmt + - gofumpt + - goimports diff --git a/backend.go b/backend.go index 7ca0966..28cdf73 100644 --- a/backend.go +++ b/backend.go @@ -1,6 +1,8 @@ //go:build linux // +build linux +// Package fakemachine provides a lightweight virtual machine abstraction for +// running commands in an isolated environment. package fakemachine import ( @@ -15,13 +17,14 @@ func implementedBackends(m *Machine) []backend { } } -/* A list of backends which are implemented - sorted in order in which the - * "auto" backend chooses them. - */ +// BackendNames returns the list of implemented backends sorted in order of +// preference for the "auto" selection algorithm, with "auto" prepended. func BackendNames() []string { - names := []string{"auto"} + backends := implementedBackends(nil) + names := make([]string, 0, 1+len(backends)) + names = append(names, "auto") - for _, backend := range implementedBackends(nil) { + for _, backend := range backends { names = append(names, backend.Name()) } diff --git a/backend_qemu.go b/backend_qemu.go index ccf5c90..b21d182 100644 --- a/backend_qemu.go +++ b/backend_qemu.go @@ -177,7 +177,7 @@ func (b qemuBackend) ModulePath() (string, error) { } func (b qemuBackend) UdevRules() []string { - udevRules := []string{} + udevRules := make([]string, 0, 2*len(b.machine.images)) // create symlink under /dev/disk/by-fakemachine-label/ for each virtual image for i, img := range b.machine.images { @@ -228,14 +228,16 @@ func (b qemuBackend) StartQemu(kvm bool) (bool, error) { } memory := fmt.Sprintf("%d", m.memory) numcpus := fmt.Sprintf("%d", m.numcpus) - qemuargs := []string{qemuMachine.binary, + qemuargs := []string{ + qemuMachine.binary, "-smp", numcpus, "-m", memory, "-kernel", kernelPath, "-initrd", m.initrdpath, "-display", "none", "-nic", "user,model=virtio-net-pci", - "-no-reboot"} + "-no-reboot", + } if kvm { qemuargs = append(qemuargs, @@ -247,9 +249,11 @@ func (b qemuBackend) StartQemu(kvm bool) (bool, error) { qemuargs = append(qemuargs, "-machine", qemuMachine.machine) console := fmt.Sprintf("console=%s", qemuMachine.console) - kernelargs := []string{console, "panic=-1", + kernelargs := []string{ + console, "panic=-1", "plymouth.enable=0", - "systemd.unit=fakemachine.service"} + "systemd.unit=fakemachine.service", + } if m.showBoot { // Create a character device representing our stdio diff --git a/cmd/fakemachine/main.go b/cmd/fakemachine/main.go index 0b9db1a..8533941 100644 --- a/cmd/fakemachine/main.go +++ b/cmd/fakemachine/main.go @@ -1,15 +1,18 @@ +// Package main is the entry point for the fakemachine command-line tool. package main import ( - "al.essio.dev/pkg/shellescape" "errors" "fmt" - "github.com/docker/go-units" - "github.com/go-debos/fakemachine" - "github.com/jessevdk/go-flags" "os" "runtime/debug" "strings" + + "al.essio.dev/pkg/shellescape" + + "github.com/docker/go-units" + "github.com/go-debos/fakemachine" + "github.com/jessevdk/go-flags" ) var Version string @@ -28,8 +31,10 @@ type Options struct { Version bool `long:"version" description:"Print fakemachine version"` } -var options Options -var parser = flags.NewParser(&options, flags.Default) +var ( + options Options + parser = flags.NewParser(&options, flags.Default) +) func determineVersionFromBuild() string { info, ok := debug.ReadBuildInfo() @@ -131,7 +136,7 @@ func SetupEnviron(m *fakemachine.Machine, options Options) { // These are the environment variables that will be detected on the // host and propagated to fakemachine. These are listed lower case, but // they are detected and configured in both lower case and upper case. - var environVars = [...]string{ + environVars := [...]string{ "http_proxy", "https_proxy", "ftp_proxy", @@ -166,7 +171,7 @@ func SetupEnviron(m *fakemachine.Machine, options Options) { } // Puts in a format that is compatible with output of os.Environ() - EnvironString := []string{} + EnvironString := make([]string, 0, len(EnvironVars)) for k, v := range EnvironVars { warnLocalhost(k, v) EnvironString = append(EnvironString, fmt.Sprintf("%s=%s", k, v)) diff --git a/cpio/writerhelper.go b/cpio/writerhelper.go index 72c2d05..38c9cbe 100644 --- a/cpio/writerhelper.go +++ b/cpio/writerhelper.go @@ -1,3 +1,4 @@ +// Package writerhelper provides helpers for writing cpio archives. package writerhelper import ( @@ -10,27 +11,33 @@ import ( "path/filepath" "strings" - "github.com/surma/gocpio" + cpio "github.com/surma/gocpio" ) +// WriterHelper wraps a cpio.Writer and tracks written paths to automatically +// create parent directories as needed. type WriterHelper struct { paths map[string]bool *cpio.Writer } +// WriteDirectory describes a directory entry to add to the archive. type WriteDirectory struct { Directory string Perm os.FileMode } +// WriteSymlink describes a symbolic link entry to add to the archive. type WriteSymlink struct { Target string Link string Perm os.FileMode } +// Transformer is a function that transforms data from src and writes it to dst. type Transformer func(dst io.Writer, src io.Reader) error +// NewWriterHelper creates a WriterHelper that writes a cpio archive to f. func NewWriterHelper(f io.Writer) *WriterHelper { return &WriterHelper{ paths: map[string]bool{"/": true}, @@ -54,7 +61,7 @@ func (w *WriterHelper) ensureBaseDirectory(directory string) error { continue } - err := w.WriteDirectory(collector, 0755) + err := w.WriteDirectory(collector, 0o755) if err != nil { return err } @@ -63,6 +70,7 @@ func (w *WriterHelper) ensureBaseDirectory(directory string) error { return nil } +// WriteDirectories writes multiple directory entries to the archive. func (w *WriterHelper) WriteDirectories(directories []WriteDirectory) error { for _, d := range directories { err := w.WriteDirectory(d.Directory, d.Perm) @@ -73,6 +81,7 @@ func (w *WriterHelper) WriteDirectories(directories []WriteDirectory) error { return nil } +// WriteDirectory writes a single directory entry, creating parent directories as needed. func (w *WriterHelper) WriteDirectory(directory string, perm os.FileMode) error { err := w.ensureBaseDirectory(path.Dir(directory)) if err != nil { @@ -94,10 +103,12 @@ func (w *WriterHelper) WriteDirectory(directory string, perm os.FileMode) error return nil } +// WriteFile writes a regular file with the given string content to the archive. func (w *WriterHelper) WriteFile(file, content string, perm os.FileMode) error { return w.WriteFileRaw(file, []byte(content), perm) } +// WriteFileRaw writes a regular file with the given byte content to the archive. func (w *WriterHelper) WriteFileRaw(file string, bytes []byte, perm os.FileMode) error { err := w.ensureBaseDirectory(path.Dir(file)) if err != nil { @@ -122,6 +133,7 @@ func (w *WriterHelper) WriteFileRaw(file string, bytes []byte, perm os.FileMode) return nil } +// WriteSymlinks writes multiple symbolic link entries to the archive. func (w *WriterHelper) WriteSymlinks(links []WriteSymlink) error { for _, l := range links { err := w.WriteSymlink(l.Target, l.Link, l.Perm) @@ -132,6 +144,7 @@ func (w *WriterHelper) WriteSymlinks(links []WriteSymlink) error { return nil } +// WriteSymlink writes a single symbolic link entry to the archive. func (w *WriterHelper) WriteSymlink(target, link string, perm os.FileMode) error { err := w.ensureBaseDirectory(path.Dir(link)) if err != nil { @@ -159,6 +172,7 @@ func (w *WriterHelper) WriteSymlink(target, link string, perm os.FileMode) error return nil } +// WriteCharDevice writes a character device entry to the archive. func (w *WriterHelper) WriteCharDevice(device string, major, minor int64, perm os.FileMode) error { err := w.ensureBaseDirectory(path.Dir(device)) if err != nil { @@ -179,16 +193,18 @@ func (w *WriterHelper) WriteCharDevice(device string, major, minor int64, perm o return nil } +// CopyTree recursively copies a host directory tree into the archive. func (w *WriterHelper) CopyTree(path string) error { walker := func(p string, info os.FileInfo, err error) error { if err != nil { return fmt.Errorf("error visiting %s: %w", p, err) } - if info.Mode().IsDir() { + switch { + case info.Mode().IsDir(): err = w.WriteDirectory(p, info.Mode() & ^os.ModeType) - } else if info.Mode().IsRegular() { + case info.Mode().IsRegular(): err = w.CopyFile(p) - } else { + default: err = fmt.Errorf("file type not handled for %s", p) } @@ -202,6 +218,7 @@ func (w *WriterHelper) CopyTree(path string) error { return nil } +// CopyFileTo copies a file from src on the host into the archive at dst. func (w *WriterHelper) CopyFileTo(src, dst string) (err error) { if err := w.ensureBaseDirectory(path.Dir(dst)); err != nil { return err @@ -242,6 +259,7 @@ func (w *WriterHelper) CopyFileTo(src, dst string) (err error) { return nil } +// TransformFileTo reads src from the host, transforms it with fn, and writes the result into the archive at dst. func (w *WriterHelper) TransformFileTo(src, dst string, fn Transformer) (err error) { if err := w.ensureBaseDirectory(path.Dir(dst)); err != nil { return err @@ -287,6 +305,7 @@ func (w *WriterHelper) TransformFileTo(src, dst string, fn Transformer) (err err return nil } +// CopyFile copies a file from the host into the archive at the same path. func (w *WriterHelper) CopyFile(in string) error { return w.CopyFileTo(in, in) } diff --git a/decompressors.go b/decompressors.go index b907c87..be33813 100644 --- a/decompressors.go +++ b/decompressors.go @@ -10,6 +10,7 @@ import ( "github.com/ulikunitz/xz" ) +// ZstdDecompressor decompresses zstd-compressed data from src into dst. func ZstdDecompressor(dst io.Writer, src io.Reader) error { decompressor, err := zstd.NewReader(src) if err != nil { @@ -24,13 +25,14 @@ func ZstdDecompressor(dst io.Writer, src io.Reader) error { return nil } +// XzDecompressor decompresses xz-compressed data from src into dst. func XzDecompressor(dst io.Writer, src io.Reader) error { decompressor, err := xz.NewReader(src) if err != nil { return fmt.Errorf("failed to create xz decompressor: %w", err) } // There is no Close() API. See: https://github.com/ulikunitz/xz/issues/45 - //defer decompressor.Close() + // defer decompressor.Close() _, err = io.Copy(dst, decompressor) if err != nil { @@ -39,6 +41,7 @@ func XzDecompressor(dst io.Writer, src io.Reader) error { return nil } +// GzipDecompressor decompresses gzip-compressed data from src into dst. func GzipDecompressor(dst io.Writer, src io.Reader) (err error) { decompressor, err := gzip.NewReader(src) if err != nil { @@ -57,6 +60,7 @@ func GzipDecompressor(dst io.Writer, src io.Reader) (err error) { return nil } +// NullDecompressor copies uncompressed data from src into dst unchanged. func NullDecompressor(dst io.Writer, src io.Reader) error { _, err := io.Copy(dst, src) if err != nil { diff --git a/decompressors_test.go b/decompressors_test.go index 22b2b44..c37df75 100644 --- a/decompressors_test.go +++ b/decompressors_test.go @@ -10,7 +10,7 @@ import ( "path" "testing" - "github.com/go-debos/fakemachine/cpio" + writerhelper "github.com/go-debos/fakemachine/cpio" "github.com/stretchr/testify/require" ) @@ -50,8 +50,8 @@ func checkStreamsMatch(output, check io.Reader) error { } } -func decompressorTest(file, suffix string, d writerhelper.Transformer) (err error) { - testFilePath := path.Join("testdata", file+suffix) +func decompressorTest(suffix string, d writerhelper.Transformer) (err error) { + testFilePath := path.Join("testdata", "test"+suffix) f, err := os.Open(testFilePath) if err != nil { return fmt.Errorf("open test file %s: %w", testFilePath, err) @@ -67,7 +67,7 @@ func decompressorTest(file, suffix string, d writerhelper.Transformer) (err erro return fmt.Errorf("decompress test file %s: %w", testFilePath, err) } - checkFilePath := path.Join("testdata", file) + checkFilePath := path.Join("testdata", "test") checkFile, err := os.Open(checkFilePath) if err != nil { return fmt.Errorf("open check file %s: %w", checkFilePath, err) @@ -86,21 +86,21 @@ func decompressorTest(file, suffix string, d writerhelper.Transformer) (err erro } func TestZstd(t *testing.T) { - err := decompressorTest("test", ".zst", ZstdDecompressor) + err := decompressorTest(".zst", ZstdDecompressor) require.NoError(t, err) } func TestXz(t *testing.T) { - err := decompressorTest("test", ".xz", XzDecompressor) + err := decompressorTest(".xz", XzDecompressor) require.NoError(t, err) } func TestGzip(t *testing.T) { - err := decompressorTest("test", ".gz", GzipDecompressor) + err := decompressorTest(".gz", GzipDecompressor) require.NoError(t, err) } func TestNull(t *testing.T) { - err := decompressorTest("test", "", NullDecompressor) + err := decompressorTest("", NullDecompressor) require.NoError(t, err) } diff --git a/machine.go b/machine.go index 4f3c4fc..9fd5d52 100644 --- a/machine.go +++ b/machine.go @@ -3,9 +3,9 @@ package fakemachine import ( - "al.essio.dev/pkg/shellescape" "bufio" "bytes" + "context" "errors" "fmt" "io" @@ -19,6 +19,8 @@ import ( "strings" "text/template" + "al.essio.dev/pkg/shellescape" + writerhelper "github.com/go-debos/fakemachine/cpio" ) @@ -35,7 +37,7 @@ func mergedUsrSystem() (bool, error) { // There may be multiple row with same fieldname so []string // is used to return all data. func getModData(modname string, fieldname string, kernelRelease string) ([]string, error) { - out, err := exec.Command("modinfo", "-k", kernelRelease, modname).Output() + out, err := exec.CommandContext(context.Background(), "modinfo", "-k", kernelRelease, modname).Output() if err != nil { return nil, fmt.Errorf("failed to call modinfo for module %q and kernel release %q: %w", modname, kernelRelease, err) } @@ -184,27 +186,24 @@ func realDir(path string) (string, error) { } // addVolumeIfExists adds volumePath as a machine volume if it exists on the host. -// -// It returns true if the volume was added. A missing path is not treated as an -// error and returns false, nil. If the path exists but is not a directory, or -// cannot be checked, it returns false and an error. -func (m *Machine) addVolumeIfExists(volumePath string) (bool, error) { +// A missing path is not treated as an error. If the path exists but is not a +// directory, or cannot be checked, it returns an error. +func (m *Machine) addVolumeIfExists(volumePath string) error { stat, err := os.Stat(volumePath) - if err != nil { if errors.Is(err, os.ErrNotExist) { - return false, nil + return nil } - return false, fmt.Errorf("failed to check %q: %w", volumePath, err) + return fmt.Errorf("failed to check %q: %w", volumePath, err) } if !stat.IsDir() { - return false, fmt.Errorf("failed to add volume %q: not a directory", volumePath) + return fmt.Errorf("failed to add volume %q: not a directory", volumePath) } m.AddVolume(volumePath) - return true, nil + return nil } func (m *Machine) addVolumesWithGlob(pattern string) error { @@ -214,7 +213,7 @@ func (m *Machine) addVolumesWithGlob(pattern string) error { } for _, volumePath := range matches { - if _, err := m.addVolumeIfExists(volumePath); err != nil { + if err := m.addVolumeIfExists(volumePath); err != nil { return err } } @@ -222,8 +221,10 @@ func (m *Machine) addVolumesWithGlob(pattern string) error { return nil } +// Arch represents the CPU architecture of the fake machine. type Arch string +// Supported architecture constants. const ( Amd64 Arch = "amd64" Arm64 Arch = "arm64" @@ -251,6 +252,8 @@ type image struct { label string } +// Machine represents a fake machine instance that runs commands in a virtual +// environment using one of the available backends. type Machine struct { arch Arch backend backend @@ -272,12 +275,12 @@ type Machine struct { initrdpath string } -// Create a new machine object with the auto backend +// NewMachine creates a new machine object using the auto-selected backend. func NewMachine() (*Machine, error) { return NewMachineWithBackend("auto") } -// Create a new machine object +// NewMachineWithBackend creates a new machine object using the named backend. func NewMachineWithBackend(backendName string) (*Machine, error) { var err error m := &Machine{memory: 2048, numcpus: runtime.NumCPU(), sectorSize: 512} @@ -308,10 +311,10 @@ func NewMachineWithBackend(backendName string) (*Machine, error) { } // Mounts for ssl certificates - if _, err := m.addVolumeIfExists("/etc/ca-certificates"); err != nil { + if err := m.addVolumeIfExists("/etc/ca-certificates"); err != nil { return nil, err } - if _, err := m.addVolumeIfExists("/etc/ssl"); err != nil { + if err := m.addVolumeIfExists("/etc/ssl"); err != nil { return nil, err } @@ -321,30 +324,31 @@ func NewMachineWithBackend(backendName string) (*Machine, error) { } // Dbus configuration - if _, err := m.addVolumeIfExists("/etc/dbus-1"); err != nil { + if err := m.addVolumeIfExists("/etc/dbus-1"); err != nil { return nil, err } // Debian alternative symlinks - if _, err := m.addVolumeIfExists("/etc/alternatives"); err != nil { + if err := m.addVolumeIfExists("/etc/alternatives"); err != nil { return nil, err } // Debian binfmt registry - if _, err := m.addVolumeIfExists("/var/lib/binfmts"); err != nil { + if err := m.addVolumeIfExists("/var/lib/binfmts"); err != nil { return nil, err } return m, nil } +// InMachine reports whether the current process is running inside a fake machine. func InMachine() (ret bool) { _, ret = os.LookupEnv("IN_FAKE_MACHINE") return } -// Check whether the auto backend is supported +// Supported reports whether the auto backend is supported on the current machine. func Supported() bool { _, err := newBackend("auto", nil) return err == nil @@ -367,6 +371,7 @@ busybox modprobe {{ $m }} exec /lib/systemd/systemd ` + const networkdTemplate = ` [Match] Type=ether @@ -494,7 +499,7 @@ func (m *Machine) AddVolumeAt(hostDirectory, machineDirectory string) { } } m.mounts = append(m.mounts, mountPoint{hostDirectory, machineDirectory, label, false}) - m.count = m.count + 1 + m.count++ } // AddVolume mounts directory from the host at the same location in the @@ -527,7 +532,7 @@ func (m *Machine) CreateImageWithLabel(path string, size int64, label string) (_ flags |= os.O_CREATE } - i, err := os.OpenFile(path, flags, 0666) + i, err := os.OpenFile(path, flags, 0o666) if err != nil { if size < 0 { return "", fmt.Errorf("failed to open existing image file %s: %w", path, err) @@ -625,7 +630,7 @@ func (m Machine) generateFstab(w *writerhelper.WriterHelper, backend backend) er } fstab = append(fstab, "") - err := w.WriteFile("/etc/fstab", strings.Join(fstab, "\n"), 0755) + err := w.WriteFile("/etc/fstab", strings.Join(fstab, "\n"), 0o755) if err != nil { return fmt.Errorf("failed to write fstab: %w", err) } @@ -680,12 +685,13 @@ func (m *Machine) generateModulesDep(w *writerhelper.WriterHelper, moddir string } path := path.Join(moddir, "modules.dep") - if err := w.WriteFile(path, strings.Join(output, "\n"), 0644); err != nil { + if err := w.WriteFile(path, strings.Join(output, "\n"), 0o644); err != nil { return fmt.Errorf("failed to write modules.dep: %w", err) } return nil } +// SetEnviron sets additional environment variables to pass into the fake machine. func (m *Machine) SetEnviron(environ []string) { m.Environ = environ } @@ -698,7 +704,8 @@ func (m *Machine) writerKernelModules(w *writerhelper.WriterHelper, moddir strin modfiles := []string{ "modules.builtin", "modules.alias", - "modules.symbols"} + "modules.symbols", + } for _, v := range modfiles { if err := w.CopyFile(moddir + "/" + v); err != nil { @@ -743,7 +750,7 @@ func (m *Machine) setupscratch() error { if err != nil { return err } - mkfs := exec.Command("mkfs.ext4", "-q", m.scratchfile) + mkfs := exec.CommandContext(context.Background(), "mkfs.ext4", "-q", m.scratchfile) err = mkfs.Run() if err != nil { return fmt.Errorf("failed to format scratch disk: %w", err) @@ -766,7 +773,7 @@ func (m *Machine) cleanup() error { } func (m *Machine) buildInitrd(command string, extracontent [][2]string) (err error) { - f, err := os.OpenFile(m.initrdpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755) + f, err := os.OpenFile(m.initrdpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o755) if err != nil { return fmt.Errorf("failed to create initrd file: %w", err) } @@ -790,41 +797,41 @@ func (m *Machine) buildInitrd(command string, extracontent [][2]string) (err err }() err = w.WriteDirectories([]writerhelper.WriteDirectory{ - {Directory: "/scratch", Perm: 01777}, - {Directory: "/var/tmp", Perm: 01777}, - {Directory: "/var/lib/dbus", Perm: 0755}, - {Directory: "/tmp", Perm: 01777}, - {Directory: "/sys", Perm: 0755}, - {Directory: "/proc", Perm: 0755}, - {Directory: "/run", Perm: 0755}, - {Directory: "/usr", Perm: 0755}, - {Directory: "/usr/bin", Perm: 0755}, - {Directory: "/lib64", Perm: 0755}, + {Directory: "/scratch", Perm: 0o1777}, + {Directory: "/var/tmp", Perm: 0o1777}, + {Directory: "/var/lib/dbus", Perm: 0o755}, + {Directory: "/tmp", Perm: 0o1777}, + {Directory: "/sys", Perm: 0o755}, + {Directory: "/proc", Perm: 0o755}, + {Directory: "/run", Perm: 0o755}, + {Directory: "/usr", Perm: 0o755}, + {Directory: "/usr/bin", Perm: 0o755}, + {Directory: "/lib64", Perm: 0o755}, }) if err != nil { return fmt.Errorf("failed to write directories: %w", err) } - err = w.WriteSymlink("/run", "/var/run", 0755) + err = w.WriteSymlink("/run", "/var/run", 0o755) if err != nil { return fmt.Errorf("failed to write /var/run symlink: %w", err) } if m.mergedUsr { err = w.WriteSymlinks([]writerhelper.WriteSymlink{ - {Target: "/usr/sbin", Link: "/sbin", Perm: 0755}, - {Target: "/usr/bin", Link: "/bin", Perm: 0755}, - {Target: "/usr/lib", Link: "/lib", Perm: 0755}, - {Target: "/usr/lib64", Link: "/lib64", Perm: 0755}, + {Target: "/usr/sbin", Link: "/sbin", Perm: 0o755}, + {Target: "/usr/bin", Link: "/bin", Perm: 0o755}, + {Target: "/usr/lib", Link: "/lib", Perm: 0o755}, + {Target: "/usr/lib64", Link: "/lib64", Perm: 0o755}, }) if err != nil { return fmt.Errorf("failed to write merged-usr symlinks: %w", err) } } else { err = w.WriteDirectories([]writerhelper.WriteDirectory{ - {Directory: "/sbin", Perm: 0744}, - {Directory: "/bin", Perm: 0755}, - {Directory: "/lib", Perm: 0755}, + {Directory: "/sbin", Perm: 0o744}, + {Directory: "/bin", Perm: 0o755}, + {Directory: "/lib", Perm: 0o755}, }) if err != nil { return fmt.Errorf("failed to write non-merged-usr directories: %w", err) @@ -871,7 +878,7 @@ func (m *Machine) buildInitrd(command string, extracontent [][2]string) (err err return fmt.Errorf("failed to copy libresolv.so.2: %w", err) } - err = w.WriteCharDevice("/dev/console", 5, 1, 0700) + err = w.WriteCharDevice("/dev/console", 5, 1, 0o700) if err != nil { return fmt.Errorf("failed to write /dev/console device: %w", err) } @@ -888,12 +895,12 @@ func (m *Machine) buildInitrd(command string, extracontent [][2]string) (err err } // Core system configuration - err = w.WriteFile("/etc/machine-id", "", 0444) + err = w.WriteFile("/etc/machine-id", "", 0o444) if err != nil { return fmt.Errorf("failed to write machine-id: %w", err) } - err = w.WriteFile("/etc/hostname", "fakemachine", 0444) + err = w.WriteFile("/etc/hostname", "fakemachine", 0o444) if err != nil { return fmt.Errorf("failed to write hostname: %w", err) } @@ -915,19 +922,19 @@ func (m *Machine) buildInitrd(command string, extracontent [][2]string) (err err // udev rules udevRules := strings.Join(m.backend.UdevRules(), "\n") + "\n" - err = w.WriteFile("/etc/udev/rules.d/61-fakemachine.rules", udevRules, 0444) + err = w.WriteFile("/etc/udev/rules.d/61-fakemachine.rules", udevRules, 0o444) if err != nil { return fmt.Errorf("failed to write udev rules: %w", err) } err = w.WriteFile("/etc/systemd/network/ethernet.network", - networkdTemplate, 0444) + networkdTemplate, 0o444) if err != nil { return fmt.Errorf("failed to write ethernet.network: %w", err) } err = w.WriteFile("/etc/systemd/network/10-ethernet.link", - networkdLinkTemplate, 0444) + networkdLinkTemplate, 0o444) if err != nil { return fmt.Errorf("failed to write ethernet.link: %w", err) } @@ -935,7 +942,7 @@ func (m *Machine) buildInitrd(command string, extracontent [][2]string) (err err err = w.WriteSymlink( "/lib/systemd/resolv.conf", "/etc/resolv.conf", - 0755) + 0o755) if err != nil { return fmt.Errorf("failed to write resolv.conf symlink: %w", err) } @@ -946,7 +953,7 @@ func (m *Machine) buildInitrd(command string, extracontent [][2]string) (err err } err = w.WriteFile("etc/systemd/system/fakemachine.service", - fmt.Sprintf(serviceTemplate, m.backend.JobOutputTTY(), strings.Join(m.Environ, " ")), 0644) + fmt.Sprintf(serviceTemplate, m.backend.JobOutputTTY(), strings.Join(m.Environ, " ")), 0o644) if err != nil { return fmt.Errorf("failed to write fakemachine.service: %w", err) } @@ -954,13 +961,13 @@ func (m *Machine) buildInitrd(command string, extracontent [][2]string) (err err err = w.WriteSymlink( "/lib/systemd/system/serial-getty@ttyS0.service", "/dev/null", - 0755) + 0o755) if err != nil { return fmt.Errorf("failed to write serial-getty symlink: %w", err) } err = w.WriteFile("/wrapper", - fmt.Sprintf(commandWrapper, command), 0755) + fmt.Sprintf(commandWrapper, command), 0o755) if err != nil { return fmt.Errorf("failed to write wrapper script: %w", err) } @@ -970,7 +977,7 @@ func (m *Machine) buildInitrd(command string, extracontent [][2]string) (err err return err } - err = w.WriteFileRaw("/init", init, 0755) + err = w.WriteFileRaw("/init", init, 0o755) if err != nil { return fmt.Errorf("failed to write init script: %w", err) } @@ -1053,7 +1060,7 @@ func (m *Machine) startup(command string, extracontent [][2]string) (code int, e // Set a default result of failure so that if the backend fails to start // we get a defined exit code instead of an error reading the result file. resultPath := path.Join(tmpdir, "result") - if err := os.WriteFile(resultPath, []byte("1"), 0644); err != nil { + if err := os.WriteFile(resultPath, []byte("1"), 0o644); err != nil { return -1, fmt.Errorf("failed to create result file: %w", err) } @@ -1080,7 +1087,6 @@ func (m *Machine) startup(command string, extracontent [][2]string) (code int, e return -1, fmt.Errorf("failed to read result file: %w", err) } exitcode, err := strconv.Atoi(strings.TrimSpace(string(exitstr))) - if err != nil { return -1, fmt.Errorf("failed to parse exit code: %w", err) } @@ -1102,7 +1108,6 @@ func (m *Machine) RunInMachineWithArgs(args []string) (int, error) { command := strings.Join([]string{name, quotedArgs}, " ") executable, err := exec.LookPath(os.Args[0]) - if err != nil { return -1, fmt.Errorf("failed to find executable: %w", err) } diff --git a/machine_test.go b/machine_test.go index 8423dd6..02df6ae 100644 --- a/machine_test.go +++ b/machine_test.go @@ -12,8 +12,10 @@ import ( "github.com/stretchr/testify/require" ) -var backendName string -var testArg string +var ( + backendName string + testArg string +) func init() { flag.StringVar(&backendName, "backend", "auto", "Fakemachine backend to use") @@ -197,7 +199,7 @@ func TestImageLabel(t *testing.T) { if InMachine() { t.Log("Running in the machine") devices := flag.Args() - require.Equal(t, 2, len(devices), "Only expected two devices") + require.Len(t, devices, 2, "Only expected two devices") autolabel := devices[0] labeled := devices[1] @@ -262,9 +264,15 @@ func TestDiskSuffix(t *testing.T) { i int want string }{ - {0, "a"}, {1, "b"}, {25, "z"}, - {26, "aa"}, {27, "ab"}, {51, "az"}, - {52, "ba"}, {701, "zz"}, {702, "aaa"}, + {0, "a"}, + {1, "b"}, + {25, "z"}, + {26, "aa"}, + {27, "ab"}, + {51, "az"}, + {52, "ba"}, + {701, "zz"}, + {702, "aaa"}, } for _, c := range cases { require.Equal(t, c.want, diskSuffix(c.i), "diskSuffix(%d)", c.i) @@ -293,7 +301,8 @@ func TestCommandEscaping(t *testing.T) { m := CreateMachine(t) exitcode, err := m.RunInMachineWithArgs([]string{ "-test.v", "-test.run", - "TestCommandEscaping", "-testarg", "$s'n\\akes"}) + "TestCommandEscaping", "-testarg", "$s'n\\akes", + }) require.NoError(t, err) require.Equal(t, 0, exitcode) }