Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
40ef3fe50e
|
|||
d973179557
|
|||
49b8790032
|
|||
355fb314a1
|
|||
e037770c38
|
|||
57e15fa0a0
|
|||
f1fd2e1505
|
|||
02dda8bcf9
|
83
appvm.go
83
appvm.go
@ -13,6 +13,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
@ -165,23 +166,21 @@ func isRunning(l *libvirt.Libvirt, name string) bool {
|
|||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateAppVM(l *libvirt.Libvirt, appvmPath, name string, verbose,
|
func generateAppVM(l *libvirt.Libvirt,
|
||||||
online bool) (err error) {
|
nixName, vmName, appvmPath, sharedDir string,
|
||||||
|
verbose, online bool) (err error) {
|
||||||
|
|
||||||
err = os.Chdir(appvmPath)
|
err = os.Chdir(appvmPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
realpath, reginfo, qcow2, err := generateVM(name, verbose)
|
realpath, reginfo, qcow2, err := generateVM(nixName, verbose)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
sharedDir := fmt.Sprintf(os.Getenv("HOME") + "/appvm/" + name)
|
xml := generateXML(vmName, online, realpath, reginfo, qcow2, sharedDir)
|
||||||
os.MkdirAll(sharedDir, 0700)
|
|
||||||
|
|
||||||
xml := generateXML(name, online, realpath, reginfo, qcow2, sharedDir)
|
|
||||||
_, err = l.DomainCreateXML(xml, libvirt.DomainStartValidate)
|
_, err = l.DomainCreateXML(xml, libvirt.DomainStartValidate)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -210,14 +209,31 @@ func isAppvmConfigurationExists(appvmPath, name string) bool {
|
|||||||
return fileExists(appvmPath + "/nix/" + name + ".nix")
|
return fileExists(appvmPath + "/nix/" + name + ".nix")
|
||||||
}
|
}
|
||||||
|
|
||||||
func start(l *libvirt.Libvirt, name string, verbose, online bool,
|
func start(l *libvirt.Libvirt, name string, verbose, online, stateless bool,
|
||||||
args, open string) {
|
args, open string) {
|
||||||
|
|
||||||
appvmPath := configDir
|
appvmPath := configDir
|
||||||
vmHomePath := os.Getenv("HOME") + "/appvm/" + name + "/"
|
|
||||||
|
statelessName := fmt.Sprintf("tmp_%d_%s", rand.Int(), name)
|
||||||
|
|
||||||
|
sharedDir := os.Getenv("HOME") + "/appvm/"
|
||||||
|
if stateless {
|
||||||
|
sharedDir += statelessName
|
||||||
|
} else {
|
||||||
|
sharedDir += name
|
||||||
|
}
|
||||||
|
|
||||||
|
os.MkdirAll(sharedDir, 0700)
|
||||||
|
|
||||||
|
vmName := "appvm_"
|
||||||
|
if stateless {
|
||||||
|
vmName += statelessName
|
||||||
|
} else {
|
||||||
|
vmName += name
|
||||||
|
}
|
||||||
|
|
||||||
if open != "" {
|
if open != "" {
|
||||||
filename := vmHomePath + filepath.Base(open)
|
filename := sharedDir + "/" + filepath.Base(open)
|
||||||
err := copyFile(open, filename)
|
err := copyFile(open, filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Can't copy file")
|
log.Println("Can't copy file")
|
||||||
@ -228,7 +244,7 @@ func start(l *libvirt.Libvirt, name string, verbose, online bool,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if args != "" {
|
if args != "" {
|
||||||
err := ioutil.WriteFile(vmHomePath+".args", []byte(args), 0700)
|
err := ioutil.WriteFile(sharedDir+"/"+".args", []byte(args), 0700)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Can't write args")
|
log.Println("Can't write args")
|
||||||
return
|
return
|
||||||
@ -251,17 +267,19 @@ func start(l *libvirt.Libvirt, name string, verbose, online bool,
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isRunning(l, name) {
|
if !isRunning(l, vmName) {
|
||||||
if !verbose {
|
if !verbose {
|
||||||
go stupidProgressBar()
|
go stupidProgressBar()
|
||||||
}
|
}
|
||||||
err = generateAppVM(l, appvmPath, name, verbose, online)
|
|
||||||
|
err = generateAppVM(l, name, vmName, appvmPath, sharedDir,
|
||||||
|
verbose, online)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := exec.Command("virt-viewer", "-c", "qemu:///system", "appvm_"+name)
|
cmd := exec.Command("virt-viewer", "-c", "qemu:///system", vmName)
|
||||||
cmd.Start()
|
cmd.Start()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -365,9 +383,40 @@ func sync() {
|
|||||||
log.Println("Done")
|
log.Println("Done")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cleanupStatelessVMs(l *libvirt.Libvirt) {
|
||||||
|
domains, err := l.Domains()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dirs, err := ioutil.ReadDir(appvmHomesDir)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range dirs {
|
||||||
|
if !strings.HasPrefix(f.Name(), "tmp_") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
alive := false
|
||||||
|
for _, d := range domains {
|
||||||
|
if d.Name == "appvm_"+f.Name() {
|
||||||
|
alive = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !alive {
|
||||||
|
os.RemoveAll(appvmHomesDir + f.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var configDir = os.Getenv("HOME") + "/.config/appvm/"
|
var configDir = os.Getenv("HOME") + "/.config/appvm/"
|
||||||
|
var appvmHomesDir = os.Getenv("HOME") + "/appvm/"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
|
||||||
os.Mkdir(os.Getenv("HOME")+"/appvm", 0700)
|
os.Mkdir(os.Getenv("HOME")+"/appvm", 0700)
|
||||||
|
|
||||||
os.MkdirAll(configDir+"/nix", 0700)
|
os.MkdirAll(configDir+"/nix", 0700)
|
||||||
@ -393,6 +442,8 @@ func main() {
|
|||||||
}
|
}
|
||||||
defer l.Disconnect()
|
defer l.Disconnect()
|
||||||
|
|
||||||
|
cleanupStatelessVMs(l)
|
||||||
|
|
||||||
kingpin.Command("list", "List applications")
|
kingpin.Command("list", "List applications")
|
||||||
autoballonCommand := kingpin.Command("autoballoon", "Automatically adjust/reduce app vm memory")
|
autoballonCommand := kingpin.Command("autoballoon", "Automatically adjust/reduce app vm memory")
|
||||||
minMemory := autoballonCommand.Flag("min-memory", "Set minimal memory (megabytes)").Default("1024").Uint64()
|
minMemory := autoballonCommand.Flag("min-memory", "Set minimal memory (megabytes)").Default("1024").Uint64()
|
||||||
@ -404,6 +455,7 @@ func main() {
|
|||||||
startArgs := startCommand.Flag("args", "Command line arguments").String()
|
startArgs := startCommand.Flag("args", "Command line arguments").String()
|
||||||
startOpen := startCommand.Flag("open", "Pass file to application").String()
|
startOpen := startCommand.Flag("open", "Pass file to application").String()
|
||||||
startOffline := startCommand.Flag("offline", "Disconnect").Bool()
|
startOffline := startCommand.Flag("offline", "Disconnect").Bool()
|
||||||
|
startStateless := startCommand.Flag("stateless", "Do not use default state directory").Bool()
|
||||||
|
|
||||||
stopName := kingpin.Command("stop", "Stop application").Arg("name", "Application name").Required().String()
|
stopName := kingpin.Command("stop", "Stop application").Arg("name", "Application name").Required().String()
|
||||||
dropName := kingpin.Command("drop", "Remove application data").Arg("name", "Application name").Required().String()
|
dropName := kingpin.Command("drop", "Remove application data").Arg("name", "Application name").Required().String()
|
||||||
@ -426,7 +478,8 @@ func main() {
|
|||||||
case "generate":
|
case "generate":
|
||||||
generate(l, *generateName, *generateBin, *generateVMName)
|
generate(l, *generateName, *generateBin, *generateVMName)
|
||||||
case "start":
|
case "start":
|
||||||
start(l, *startName, !*startQuiet, !*startOffline,
|
start(l, *startName,
|
||||||
|
!*startQuiet, !*startOffline, *startStateless,
|
||||||
*startArgs, *startOpen)
|
*startArgs, *startOpen)
|
||||||
case "stop":
|
case "stop":
|
||||||
stop(l, *stopName)
|
stop(l, *stopName)
|
||||||
|
@ -37,7 +37,7 @@ main = xmonad defaultConfig
|
|||||||
|
|
||||||
startup :: X ()
|
startup :: X ()
|
||||||
startup = do
|
startup = do
|
||||||
spawn "${pkgs.spice-vdagent}/bin/spice-vdagent"
|
spawn "while [ 1 ]; do ${pkgs.spice-vdagent}/bin/spice-vdagent -x; done &"
|
||||||
'';
|
'';
|
||||||
|
|
||||||
systemd.services.home-user-build-xmonad = {
|
systemd.services.home-user-build-xmonad = {
|
||||||
|
49
generate.go
49
generate.go
@ -34,12 +34,8 @@ in {
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
func isPackageExists(name string) bool {
|
func isPackageExists(channel, name string) bool {
|
||||||
cmd := exec.Command("nix-env", "-iA", name)
|
return nil == exec.Command("nix-build", "<"+channel+">", "-A", name).Run()
|
||||||
cmd.Stdout = os.Stdout
|
|
||||||
cmd.Stderr = os.Stderr
|
|
||||||
err := cmd.Run()
|
|
||||||
return err == nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func nixPath(name string) (path string, err error) {
|
func nixPath(name string) (path string, err error) {
|
||||||
@ -62,8 +58,10 @@ func guessChannel() (channel string, err error) {
|
|||||||
for _, line := range channels {
|
for _, line := range channels {
|
||||||
fields := strings.Fields(line)
|
fields := strings.Fields(line)
|
||||||
if len(fields) == 2 {
|
if len(fields) == 2 {
|
||||||
channel = fields[0]
|
if strings.Contains(fields[1], "nixos.org/channels") {
|
||||||
return
|
channel = fields[0]
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,33 +79,37 @@ func filterDotfiles(files []os.FileInfo) (notHiddenFiles []os.FileInfo) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func generate(l *libvirt.Libvirt, pkg, bin, vmname string) (err error) {
|
func generate(l *libvirt.Libvirt, pkg, bin, vmname string) (err error) {
|
||||||
var name string
|
// TODO refactor
|
||||||
|
var name, channel string
|
||||||
|
|
||||||
if strings.Contains(pkg, ".") {
|
if strings.Contains(pkg, ".") {
|
||||||
name = pkg
|
channel = strings.Split(pkg, ".")[0]
|
||||||
|
name = strings.Join(strings.Split(pkg, ".")[1:], ".")
|
||||||
} else {
|
} else {
|
||||||
log.Println("Package name does not contains channel")
|
log.Println("Package name does not contains channel")
|
||||||
log.Println("Trying to guess")
|
log.Println("Trying to guess")
|
||||||
var channel string
|
|
||||||
channel, err = guessChannel()
|
channel, err = guessChannel()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Cannot guess channel")
|
log.Println("Cannot guess channel")
|
||||||
log.Println("Check nix-channel --list")
|
log.Println("Check nix-channel --list")
|
||||||
return
|
log.Println("Will try <nixpkgs>")
|
||||||
|
channel = "nixpkgs"
|
||||||
|
err = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
name = channel + "." + pkg
|
name = pkg
|
||||||
log.Println("Use", name)
|
log.Println("Use", channel+"."+pkg)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isPackageExists(name) {
|
if !isPackageExists(channel, name) {
|
||||||
s := "Package " + name + " does not exists"
|
s := "Package " + name + " does not exists"
|
||||||
err = errors.New(s)
|
err = errors.New(s)
|
||||||
log.Println(s)
|
log.Println(s)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
path, err := nixPath(name)
|
path, err := nixPath(channel + "." + name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Cannot find nix path")
|
log.Println("Cannot find nix path")
|
||||||
return
|
return
|
||||||
@ -171,25 +173,14 @@ func generate(l *libvirt.Libvirt, pkg, bin, vmname string) (err error) {
|
|||||||
bin = files[0].Name()
|
bin = files[0].Name()
|
||||||
}
|
}
|
||||||
|
|
||||||
var realName string
|
|
||||||
for i, s := range strings.Split(name, ".") {
|
|
||||||
if i == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if i != 1 {
|
|
||||||
realName += "."
|
|
||||||
}
|
|
||||||
realName += s
|
|
||||||
}
|
|
||||||
|
|
||||||
var appFilename string
|
var appFilename string
|
||||||
if vmname != "" {
|
if vmname != "" {
|
||||||
appFilename = configDir + "/nix/" + vmname + ".nix"
|
appFilename = configDir + "/nix/" + vmname + ".nix"
|
||||||
} else {
|
} else {
|
||||||
appFilename = configDir + "/nix/" + realName + ".nix"
|
appFilename = configDir + "/nix/" + name + ".nix"
|
||||||
}
|
}
|
||||||
|
|
||||||
appNixConfig := fmt.Sprintf(template, realName, bin)
|
appNixConfig := fmt.Sprintf(template, name, bin)
|
||||||
|
|
||||||
err = ioutil.WriteFile(appFilename, []byte(appNixConfig), 0600)
|
err = ioutil.WriteFile(appFilename, []byte(appNixConfig), 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
6
xml.go
6
xml.go
@ -5,7 +5,9 @@ import "fmt"
|
|||||||
// You may think that you want to rewrite to proper golang structures.
|
// You may think that you want to rewrite to proper golang structures.
|
||||||
// Believe me, you shouldn't.
|
// Believe me, you shouldn't.
|
||||||
|
|
||||||
func generateXML(name string, online bool, vmNixPath, reginfo, img, sharedDir string) string {
|
func generateXML(vmName string, online bool,
|
||||||
|
vmNixPath, reginfo, img, sharedDir string) string {
|
||||||
|
|
||||||
qemuParams := `
|
qemuParams := `
|
||||||
<qemu:commandline>
|
<qemu:commandline>
|
||||||
<qemu:arg value='-device'/>
|
<qemu:arg value='-device'/>
|
||||||
@ -24,7 +26,7 @@ func generateXML(name string, online bool, vmNixPath, reginfo, img, sharedDir st
|
|||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf(xmlTmpl, "appvm_"+name, vmNixPath, vmNixPath, vmNixPath,
|
return fmt.Sprintf(xmlTmpl, vmName, vmNixPath, vmNixPath, vmNixPath,
|
||||||
reginfo, img, sharedDir, sharedDir, sharedDir, qemuParams)
|
reginfo, img, sharedDir, sharedDir, sharedDir, qemuParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user