func (cmd) Execute(arguments map[string]interface{}) bool { // Read arguments imageFile := arguments["<image>"].(string) command := arguments["<command>"].([]string) vnc := arguments["--vnc"].(bool) // Create temporary storage and environment storage, err := runtime.NewTemporaryStorage(os.TempDir()) if err != nil { panic("Failed to create TemporaryStorage") } environment := &runtime.Environment{ TemporaryStorage: storage, } // Create a temporary folder tempFolder := filepath.Join("/tmp", slugid.Nice()) if err = os.Mkdir(tempFolder, 0777); err != nil { log.Fatal("Failed to create temporary folder in /tmp, error: ", err) } // Create the necessary runtime setup gc := &gc.GarbageCollector{} logger, _ := runtime.CreateLogger("info") log := logger.WithField("component", "qemu-run") // Create image manager log.Info("Creating image manager") manager, err := image.NewManager(filepath.Join(tempFolder, "/images/"), gc, logger.WithField("component", "image-manager"), nil) if err != nil { log.Fatal("Failed to create image manager", err) } // Get an instance of the image log.Info("Creating instance of image") image, err := manager.Instance("image", func(target string) error { return cp.CopyFile(target, imageFile) }) if err != nil { log.Fatal("Failed to create instance of image, error: ", err) } // Setup a user-space network log.Info("Creating user-space network") net, err := network.NewUserNetwork(tempFolder) if err != nil { log.Fatal("Failed to create user-space network, error: ", err) } // Create virtual machine log.Info("Creating virtual machine") vm, err := vm.NewVirtualMachine( image.Machine().Options(), image, net, tempFolder, "", "", logger.WithField("component", "vm"), ) if err != nil { log.Fatal("Failed to create virtual-machine, error: ", err) } // Create meta-data service log.Info("Creating meta-data service") var shellServer *interactive.ShellServer var displayServer *interactive.DisplayServer ms := metaservice.New(command, make(map[string]string), os.Stdout, func(result bool) { fmt.Println("### Task Completed, result = ", result) shellServer.WaitAndClose() displayServer.Abort() vm.Kill() }, environment) // Setup http handler for network vm.SetHTTPHandler(ms) // Create ShellServer shellServer = interactive.NewShellServer( ms.ExecShell, log.WithField("component", "shell-server"), ) // Create displayServer displayServer = interactive.NewDisplayServer( &socketDisplayProvider{socket: vm.VNCSocket()}, log.WithField("component", "display-server"), ) interactiveHandler := http.NewServeMux() interactiveHandler.Handle("/shell/", shellServer) interactiveHandler.Handle("/display/", displayServer) interactiveServer := graceful.Server{ Timeout: 30 * time.Second, Server: &http.Server{ Addr: "localhost:8080", Handler: interactiveHandler, }, NoSignalHandling: true, } go interactiveServer.ListenAndServe() // Start the virtual machine log.Info("Start the virtual machine") vm.Start() // Start vncviewer done := make(chan struct{}) if vnc { go StartVNCViewer(vm.VNCSocket(), done) } // Wait for SIGINT/SIGKILL or vm.Done c := make(chan os.Signal, 2) signal.Notify(c, os.Interrupt, os.Kill) // This pattern leaks, acceptable here select { case <-c: signal.Stop(c) fmt.Println("### Terminating QEMU") vm.Kill() case <-vm.Done: fmt.Println("### QEMU terminated") } close(done) // Ensure that QEMU has terminated before we continue <-vm.Done interactiveServer.Stop(100 * time.Millisecond) // Clean up anything left in the garbage collector gc.CollectAll() return true }
func buildImage( log *logrus.Entry, inputFile, outputFile string, fromImage, novnc bool, boot, cdrom string, size int, ) error { // Find absolute outputFile outputFile, err := filepath.Abs(outputFile) if err != nil { log.Error("Failed to resolve output file, error: ", err) return err } // Create temp folder for the image tempFolder, err := ioutil.TempDir("", "taskcluster-worker-build-image-") if err != nil { log.Error("Failed to create temporary folder, error: ", err) return err } defer os.RemoveAll(tempFolder) var img *image.MutableImage if !fromImage { // Read machine definition machine, err2 := vm.LoadMachine(inputFile) if err2 != nil { log.Error("Failed to load machine file from ", inputFile, " error: ", err2) return err2 } // Construct MutableImage log.Info("Creating MutableImage") img, err2 = image.NewMutableImage(tempFolder, int(size), machine) if err2 != nil { log.Error("Failed to create image, error: ", err2) return err2 } } else { img, err = image.NewMutableImageFromFile(inputFile, tempFolder) if err != nil { log.Error("Failed to load image, error: ", err) return err } } // Create temp folder for sockets socketFolder, err := ioutil.TempDir("", "taskcluster-worker-sockets-") if err != nil { log.Error("Failed to create temporary folder, error: ", err) return err } defer os.RemoveAll(socketFolder) // Setup a user-space network log.Info("Creating user-space network") net, err := network.NewUserNetwork(tempFolder) if err != nil { log.Error("Failed to create user-space network, error: ", err) return err } // Setup logService so that logs can be posted to meta-service at: // http://169.254.169.254/v1/log net.SetHandler(&logService{Destination: os.Stdout}) // Create virtual machine log.Info("Creating virtual machine") vm, err := vm.NewVirtualMachine(img.Machine().Options(), img, net, socketFolder, boot, cdrom, log.WithField("component", "vm")) if err != nil { log.Error("Failed to recreated virtual-machine, error: ", err) return err } // Start the virtual machine log.Info("Starting virtual machine") vm.Start() // Open VNC display if !novnc { go qemurun.StartVNCViewer(vm.VNCSocket(), vm.Done) } // Wait for interrupt to gracefully kill everything interrupted := make(chan os.Signal, 1) signal.Notify(interrupted, os.Interrupt) // Wait for virtual machine to be done, or we get interrupted select { case <-interrupted: vm.Kill() err = errors.New("SIGINT received, aborting virtual machine") case <-vm.Done: err = vm.Error } <-vm.Done signal.Stop(interrupted) defer img.Dispose() if err != nil { if e, ok := err.(*exec.ExitError); ok { log.Error("QEMU error: ", string(e.Stderr)) } log.Info("Error running virtual machine: ", err) return err } // Package up the finished image log.Info("Package virtual machine image") err = img.Package(outputFile) if err != nil { log.Error("Failed to package finished image, error: ", err) return err } return nil }