blob: 19a361fe2265acedf37e1c0df74e7772cbdd8ec9 [file] [log] [blame]
// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package multivm
import (
"context"
"time"
"chromiumos/tast/errors"
"chromiumos/tast/local/chrome"
"chromiumos/tast/local/crostini"
cui "chromiumos/tast/local/crostini/ui"
"chromiumos/tast/local/vm"
"chromiumos/tast/testing"
)
// CrostiniName is a stable name for the Crostini VM.
const CrostiniName = "Crostini"
// CrostiniOptions describe how to start Crostini.
type CrostiniOptions struct {
DebianVersion vm.ContainerDebianVersion // OS version of the container image.
MinDiskSize uint64 // The minimum size of the VM image in bytes. 0 to use default disk size.
LargeContainer bool
}
// crostiniActivation represents an instance of the Crostini VM used in a
// multi-VM test.
type crostiniActivation struct {
container *vm.Container
}
// Name returns a stable name for the Crostini VM.
func (o CrostiniOptions) Name() string {
return CrostiniName
}
// ChromeOpts returns the Chrome option(s) that should be passed to
// chrome.New().
func (o CrostiniOptions) ChromeOpts() []chrome.Option {
return []chrome.Option{chrome.ExtraArgs("--vmodule=crostini*=1")}
}
// ActivateTimeout returns the time needed to setup the Crostini VM.
func (o CrostiniOptions) ActivateTimeout() time.Duration {
return 7 * time.Minute
}
// Activate spins up the Crostini VM.
func (o CrostiniOptions) Activate(ctx context.Context, cr *chrome.Chrome, tconn *chrome.TestConn, st StateManagerTestingState) (VMActivation, error) {
testing.ContextLog(ctx, "Creating Crostini")
iOptions := crostini.GetInstallerOptions(st, o.DebianVersion, o.LargeContainer, cr.NormalizedUser())
iOptions.UserName = cr.NormalizedUser()
iOptions.MinDiskSize = o.MinDiskSize
if _, err := cui.InstallCrostini(ctx, tconn, cr, iOptions); err != nil {
return nil, errors.Wrap(err, "installing Crostini")
}
var container *vm.Container
success := false
defer func() {
if !success {
if container != nil {
cleanupCrostini(ctx, container)
}
}
}()
// Container may be set even when an error is returned. This must be cleaned
// up if there was an error.
container, err := vm.DefaultContainer(ctx, cr.NormalizedUser())
if err != nil {
return nil, errors.Wrap(err, "connecting to running container")
}
success = true
vm.Lock()
return &crostiniActivation{container: container}, nil
}
// CheckAndReset checks the Crostini VM between tests.
func (c *crostiniActivation) CheckAndReset(ctx context.Context, st StateManagerTestingState) error {
if err := crostini.BasicCommandWorks(ctx, c.container); err != nil {
return errors.Wrap(err, "checking Crostini")
}
return nil
}
// Deactivate stops the Crostini VM.
func (c *crostiniActivation) Deactivate(ctx context.Context) error {
vm.Unlock()
return cleanupCrostini(ctx, c.container)
}
func cleanupCrostini(ctx context.Context, container *vm.Container) error {
if err := container.VM.Stop(ctx); err != nil {
return errors.Wrap(err, "stopping Crostini")
}
return nil
}
// VM returns the underlying crostini container.
func (c *crostiniActivation) VM() interface{} {
return c.container
}
// CrostiniFromPre returns the Crostini instance setup by the multi-vm
// precondition, if available, and nil otherwise.
func CrostiniFromPre(pre *PreData) *vm.Container {
if container, ok := pre.VMs[CrostiniName]; ok {
return container.(*vm.Container)
}
return nil
}