Пример #1
0
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 TestImageManager(t *testing.T) {
	fmt.Println(" - Setup environment needed to test")
	gc := &gc.GarbageCollector{}
	log := logrus.StandardLogger()
	sentry, _ := raven.New("")
	imageFolder := filepath.Join("/tmp", slugid.Nice())

	fmt.Println(" - Create manager")
	manager, err := NewManager(imageFolder, gc, log.WithField("subsystem", "image-manager"), sentry)
	nilOrPanic(err, "Failed to create image manager")

	fmt.Println(" - Test parallel download")
	// Check that download can return and error, and we won't download twice
	// if we call before returning...
	downloadError := errors.New("test error")
	var err1 error
	wg := sync.WaitGroup{}
	wg.Add(2)
	go func() {
		_, err1 = manager.Instance("url:test-image-1", func(target string) error {
			time.Sleep(100 * time.Millisecond) // Sleep giving the second call time
			return downloadError
		})
		wg.Done()
	}()
	time.Sleep(50 * time.Millisecond) // Sleep giving the second call time
	instance, err2 := manager.Instance("url:test-image-1", func(target string) error {
		panic("We shouldn't get here, as the previous download haven't returned")
	})
	wg.Done()
	wg.Wait()
	assert(err1 == err2, "Expected the same errors: ", err1, err2)
	assert(downloadError == err1, "Expected the downloadError: ", err1)
	assert(instance == nil, "Expected instance to nil, when we have an error")

	fmt.Println(" - Test instantiation of image")
	instance, err = manager.Instance("url:test-image-1", func(target string) error {
		return copyFile(testImageFile, target)
	})
	nilOrPanic(err, "Failed to loadImage")
	assert(instance != nil, "Expected an instance")

	fmt.Println(" - Get the diskImage path so we can check it gets deleted")
	diskImage := instance.DiskFile()

	fmt.Println(" - Inspect file for sanity check: ", diskImage)
	info := inspectImageFile(diskImage, imageQCOW2Format)
	assert(info != nil, "Expected a qcow2 file")
	assert(info.Format == formatQCOW2)
	assert(!info.DirtyFlag)
	assert(info.BackingFile != "", "Missing backing file in qcow2")

	fmt.Println(" - Check that backing file exists")
	backingFile := filepath.Join(filepath.Dir(diskImage), info.BackingFile)
	_, err = os.Lstat(backingFile)
	nilOrPanic(err, "backingFile missing")

	fmt.Println(" - Garbage collect and test that image is still there")
	nilOrPanic(gc.CollectAll(), "gc.CollectAll() failed")
	_, err = os.Lstat(backingFile)
	nilOrPanic(err, "backingFile missing after GC")
	info = inspectImageFile(diskImage, imageQCOW2Format)
	assert(info != nil, "diskImage for instance deleted after GC")

	fmt.Println(" - Make a new instance")
	instance2, err := manager.Instance("url:test-image-1", func(target string) error {
		panic("We shouldn't get here, as it is currently in the cache")
	})
	nilOrPanic(err, "Failed to create new instance")
	diskImage2 := instance2.DiskFile()
	assert(diskImage2 != diskImage, "Expected a new disk image")
	info = inspectImageFile(diskImage2, imageQCOW2Format)
	assert(info != nil, "diskImage2 missing initially")

	fmt.Println(" - Release the first instance")
	instance.Release()
	_, err = os.Lstat(diskImage)
	assert(os.IsNotExist(err), "first instance diskImage shouldn't exist!")
	info = inspectImageFile(diskImage2, imageQCOW2Format)
	assert(info != nil, "diskImage2 missing after first instance release")

	fmt.Println(" - Garbage collect and test that image is still there")
	nilOrPanic(gc.CollectAll(), "gc.CollectAll() failed")
	_, err = os.Lstat(backingFile)
	nilOrPanic(err, "backingFile missing after second GC")
	_, err = os.Lstat(diskImage)
	assert(os.IsNotExist(err), "first instance diskImage shouldn't exist!")
	info = inspectImageFile(diskImage2, imageQCOW2Format)
	assert(info != nil, "diskImage2 missing after first instance release")

	fmt.Println(" - Release the second instance")
	instance2.Release()
	_, err = os.Lstat(diskImage2)
	assert(os.IsNotExist(err), "second instance diskImage shouldn't exist!")
	_, err = os.Lstat(backingFile)
	nilOrPanic(err, "backingFile missing after release, this shouldn't be...")

	fmt.Println(" - Garbage collect everything") // this should dispose the image
	nilOrPanic(gc.CollectAll(), "gc.CollectAll() failed")
	_, err = os.Lstat(backingFile)
	assert(os.IsNotExist(err), "Expected backingFile to be deleted after GC, file: ", backingFile)

	fmt.Println(" - Check that we can indeed reload the image")
	_, err = manager.Instance("url:test-image-1", func(target string) error {
		return downloadError
	})
	assert(err == downloadError, "Expected a downloadError", err)
}