143 lines
3.8 KiB
Go
143 lines
3.8 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"mkvm/volumes"
|
|
"mkvm/volumes/pools"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"entanglement.garden/common/cloudinit"
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
var (
|
|
debianRelease string
|
|
debianPackages []string
|
|
|
|
debianFilenameSuffix = map[pools.ImageFormat]string{
|
|
pools.ImageFormatRaw: "-generic-amd64-daily.raw",
|
|
pools.ImageFormatQcow2: "-generic-amd64-daily.qcow2",
|
|
}
|
|
)
|
|
|
|
var debianCmd = cobra.Command{
|
|
Use: "debian vm-name",
|
|
Args: cobra.ExactArgs(1),
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
name := args[0]
|
|
|
|
for _, u := range argSSHKeyURLs {
|
|
if err := downloadSSHKeys(u); err != nil {
|
|
logrus.WithError(err).WithField("url", u).Fatal("error downloading SSH keys")
|
|
}
|
|
}
|
|
|
|
conn, pool, err := getLibvirtAndPool()
|
|
if err != nil {
|
|
logrus.WithError(err).Fatal("error connecting to libvirt")
|
|
return
|
|
}
|
|
defer conn.Close()
|
|
|
|
diskImageURL, err := debianGetImageURL(debianRelease, pool.ImageFormat())
|
|
if err != nil {
|
|
logrus.WithError(err).Fatal("error getting disk image URL")
|
|
}
|
|
|
|
// download disk image
|
|
err = volumes.Create(conn, pool, argDiskSizeGB, diskImageURL, name)
|
|
if err != nil {
|
|
logrus.WithError(err).Fatal("error creating VM disk")
|
|
}
|
|
|
|
// prepare cloudconfig and start the http server
|
|
bind, err := getServerListenAddress(conn)
|
|
if err != nil {
|
|
logrus.WithError(err).Fatal("error getting server bind address")
|
|
}
|
|
|
|
serverURL := fmt.Sprintf("http://%s", bind)
|
|
|
|
cloudconfig := cloudinit.UserData{
|
|
Packages: argPackages,
|
|
SSHAuthorizedKeys: argSSHKeys,
|
|
}
|
|
err = buildCloudConfig(cloudconfig, name, fmt.Sprintf("%s/phone-home", serverURL))
|
|
if err != nil {
|
|
logrus.WithError(err).Fatal("error building cloud config")
|
|
}
|
|
|
|
go runHTTPServer(bind)
|
|
|
|
smbios := map[int]map[string]string{1: {"serial": fmt.Sprintf("ds=nocloud-net;s=%s/", serverURL)}}
|
|
|
|
// create domain
|
|
err = createDomain(conn, pool, name, setSMBIOS(smbios))
|
|
if err != nil {
|
|
logrus.WithError(err).Fatal("error creating domain")
|
|
}
|
|
|
|
wg.Add(1)
|
|
|
|
logrus.Info("waiting for VM to finish provisioning")
|
|
wg.Wait()
|
|
},
|
|
}
|
|
|
|
func init() {
|
|
debianCmd.Flags().StringVarP(&debianRelease, "release", "r", "bookworm", "debian release to install. Options: bookworm (default), trixie, sid")
|
|
debianCmd.Flags().StringArrayVarP(&debianPackages, "packages", "p", nil, "apt packages to install")
|
|
debianCmd.Flags().StringArrayVar(&argSSHKeys, "ssh-keys", nil, "SSH key(s) authorzed to access the VM")
|
|
debianCmd.Flags().StringArrayVarP(&argSSHKeyURLs, "ssh-key-urls", "s", nil, "URL(s) to SSH key(s) authorzed to access the VM. Expected in authorized_keys format.")
|
|
registerGlobalFlags(debianCmd)
|
|
|
|
rootCmd.AddCommand(&debianCmd)
|
|
}
|
|
|
|
func debianGetImageURL(release string, format pools.ImageFormat) (string, error) {
|
|
imageSuffix, ok := debianFilenameSuffix[format]
|
|
if !ok {
|
|
return "", fmt.Errorf("unexpected image format %s from storage pool", format)
|
|
}
|
|
|
|
diskImageURLPrefix := fmt.Sprintf("https://cloud.debian.org/images/cloud/%s/daily/latest", release)
|
|
|
|
// find image URL + hash
|
|
shaURL := fmt.Sprintf("%s/SHA512SUMS", diskImageURLPrefix)
|
|
shaResp, err := http.Get(shaURL)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer shaResp.Body.Close()
|
|
|
|
shas, err := io.ReadAll(shaResp.Body)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if shaResp.StatusCode != http.StatusOK {
|
|
logrus.WithFields(logrus.Fields{
|
|
"status": shaResp.Status,
|
|
"url": shaURL,
|
|
"resp": string(shas),
|
|
}).Fatal("failed to get image hash")
|
|
}
|
|
|
|
for _, line := range strings.Split(string(shas), "\n") {
|
|
hash, filename, ok := strings.Cut(strings.TrimSpace(line), " ")
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
if !strings.HasSuffix(filename, imageSuffix) {
|
|
continue
|
|
}
|
|
|
|
return fmt.Sprintf("%s/%s#hash=sha512:%s", diskImageURLPrefix, filename, hash), nil
|
|
}
|
|
|
|
return "", fmt.Errorf("unable to find hash of image in %s", shaURL)
|
|
}
|