2024-12-09 03:17:56 +00:00
package main
import (
"fmt"
"io"
"mkvm/volumes"
"mkvm/volumes/pools"
"net/http"
"strings"
2024-12-09 06:59:06 +00:00
"entanglement.garden/common/cloudinit"
2024-12-09 03:17:56 +00:00
"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 ]
2024-12-09 06:59:06 +00:00
for _ , u := range argSSHKeyURLs {
if err := downloadSSHKeys ( u ) ; err != nil {
logrus . WithError ( err ) . WithField ( "url" , u ) . Fatal ( "error downloading SSH keys" )
}
}
2024-12-09 03:17:56 +00:00
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 )
2024-12-09 06:59:06 +00:00
cloudconfig := cloudinit . UserData {
Packages : argPackages ,
SSHAuthorizedKeys : argSSHKeys ,
}
err = buildCloudConfig ( cloudconfig , name , fmt . Sprintf ( "%s/phone-home" , serverURL ) )
2024-12-09 03:17:56 +00:00
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" )
2024-12-09 06:59:06 +00:00
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 )
2024-12-09 03:17:56 +00:00
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 )
}