linsk/imgbuilder/imgbuilder.go

145 lines
3.4 KiB
Go
Raw Normal View History

2023-08-30 12:39:38 +01:00
package imgbuilder
2023-08-27 15:30:51 +01:00
import (
2023-08-27 15:53:44 +01:00
"bytes"
2023-08-27 15:30:51 +01:00
"context"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"log/slog"
2023-09-02 11:47:58 +01:00
"github.com/AlexSSD7/linsk/cmd/runvm"
2023-09-02 11:28:23 +01:00
"github.com/AlexSSD7/linsk/osspecifics"
2023-09-02 11:47:58 +01:00
"github.com/AlexSSD7/linsk/share"
2023-08-27 15:53:44 +01:00
"github.com/AlexSSD7/linsk/utils"
2023-08-27 15:30:51 +01:00
"github.com/AlexSSD7/linsk/vm"
"github.com/alessio/shellescape"
"github.com/pkg/errors"
"golang.org/x/crypto/ssh"
)
type BuildContext struct {
logger *slog.Logger
vi *vm.VM
}
2023-08-30 13:13:08 +01:00
func NewBuildContext(logger *slog.Logger, baseISOPath string, outPath string, showVMDisplay bool, biosPath string) (*BuildContext, error) {
2023-08-27 15:30:51 +01:00
baseISOPath = filepath.Clean(baseISOPath)
outPath = filepath.Clean(outPath)
_, err := os.Stat(outPath)
if err != nil {
if !errors.Is(err, os.ErrNotExist) {
return nil, errors.Wrap(err, "stat output file")
}
2023-08-29 14:24:18 +01:00
// File doesn't exist. Going forward with creating a new .qcow2 image.
2023-08-27 15:30:51 +01:00
} else {
return nil, fmt.Errorf("output file already exists")
}
err = createQEMUImg(outPath)
if err != nil {
return nil, errors.Wrap(err, "create temporary qemu image")
}
vi, err := vm.NewVM(logger.With("subcaller", "vm"), vm.Config{
2023-08-27 15:30:51 +01:00
CdromImagePath: baseISOPath,
2023-08-30 13:13:08 +01:00
BIOSPath: biosPath,
2023-08-27 15:30:51 +01:00
Drives: []vm.DriveConfig{{
Path: outPath,
}},
2023-08-30 13:13:08 +01:00
MemoryAlloc: 512,
2023-08-27 15:30:51 +01:00
UnrestrictedNetworking: true,
ShowDisplay: showVMDisplay,
InstallBaseUtilities: true,
})
if err != nil {
return nil, errors.Wrap(err, "create vm instance")
}
return &BuildContext{
logger: logger,
vi: vi,
}, nil
}
func createQEMUImg(outPath string) error {
outPath = filepath.Clean(outPath)
2023-08-28 11:35:57 +02:00
baseCmd := "qemu-img"
2023-08-27 15:30:51 +01:00
2023-09-02 11:28:23 +01:00
if osspecifics.IsWindows() {
2023-08-28 11:35:57 +02:00
baseCmd += ".exe"
}
err := exec.Command(baseCmd, "create", "-f", "qcow2", outPath, "1G").Run()
2023-08-27 15:30:51 +01:00
if err != nil {
return errors.Wrap(err, "run qemu-img create cmd")
}
return nil
}
2023-09-02 11:47:58 +01:00
func (bc *BuildContext) RunCLIBuild() int {
return runvm.RunVM(bc.vi, false, nil, func(ctx context.Context, v *vm.VM, fm *vm.FileManager, ntrc *share.NetTapRuntimeContext) int {
sc, err := bc.vi.DialSSH()
2023-08-27 15:30:51 +01:00
if err != nil {
2023-09-02 11:47:58 +01:00
bc.logger.Error("Failed to dial VM SSH", "error", err.Error())
return 1
2023-08-27 15:30:51 +01:00
}
2023-09-02 11:47:58 +01:00
defer func() { _ = sc.Close() }()
2023-08-27 15:30:51 +01:00
2023-09-02 11:47:58 +01:00
bc.logger.Info("VM OS installation in progress")
2023-08-27 15:30:51 +01:00
2023-09-02 11:47:58 +01:00
err = runAlpineSetup(sc, []string{"openssh", "lvm2", "util-linux", "cryptsetup", "vsftpd", "samba", "netatalk"})
if err != nil {
bc.logger.Error("Failed to set up Alpine Linux", "error", err.Error())
return 1
2023-08-27 15:30:51 +01:00
}
2023-09-02 11:47:58 +01:00
return 0
})
2023-08-27 15:30:51 +01:00
}
2023-09-02 11:47:58 +01:00
func runAlpineSetup(sc *ssh.Client, pkgs []string) error {
2023-08-27 15:30:51 +01:00
sess, err := sc.NewSession()
if err != nil {
return errors.Wrap(err, "new session")
}
2023-08-27 15:53:44 +01:00
stderr := bytes.NewBuffer(nil)
sess.Stderr = stderr
2023-08-27 15:30:51 +01:00
defer func() {
_ = sess.Close()
}()
2023-08-29 10:00:12 +01:00
cmd := "ifconfig eth0 up && ifconfig lo up && udhcpc && true > /etc/apk/repositories && setup-apkrepos -c -1 && printf 'y' | setup-disk -m sys /dev/vda"
2023-08-27 15:30:51 +01:00
if len(pkgs) != 0 {
pkgsQuoted := make([]string, len(pkgs))
for i, rawPkg := range pkgs {
pkgsQuoted[i] = shellescape.Quote(rawPkg)
}
cmd += " && mount /dev/vda3 /mnt && chroot /mnt apk add " + strings.Join(pkgsQuoted, " ")
}
2023-09-02 11:47:58 +01:00
//nolint:dupword
2023-08-30 15:24:25 +01:00
cmd += `&& chroot /mnt ash -c 'echo "PasswordAuthentication no" >> /etc/ssh/sshd_config && addgroup -g 1000 linsk && adduser -D -h /mnt -G linsk linsk -u 1000 && touch /etc/network/interfaces'`
2023-08-27 15:53:44 +01:00
2023-08-27 15:30:51 +01:00
err = sess.Run(cmd)
if err != nil {
2023-08-27 15:53:44 +01:00
return utils.WrapErrWithLog(err, "run setup cmd", stderr.String())
2023-08-27 15:30:51 +01:00
}
return nil
}