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 (
cui "chromiumos/tast/local/crostini/ui"
// 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
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 {
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