blob: 982f910aa4e5ac2b6f0a76d0ab481d274a2ae41b [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 wilco
import (
"context"
"io/ioutil"
"os"
"path/filepath"
"time"
"chromiumos/tast/common/fixture"
"chromiumos/tast/common/policy"
"chromiumos/tast/common/policy/fakedms"
"chromiumos/tast/errors"
"chromiumos/tast/local/bundles/cros/wilco/wilcoextension"
"chromiumos/tast/local/chrome"
"chromiumos/tast/local/policyutil"
"chromiumos/tast/local/policyutil/fixtures"
"chromiumos/tast/local/vm"
"chromiumos/tast/local/wilco"
"chromiumos/tast/testing"
dtcpb "chromiumos/wilco_dtc"
)
func init() {
testing.AddFixture(&testing.Fixture{
Name: "wilcoDTCAllowed",
Desc: "Wilco DTC fixture with support for DTC VM, Supportd daemon and Wilco Chrome Extension",
Contacts: []string{
"lamzin@google.com", // wilco_dtc_supportd maintainer
"chromeos-wilco@google.com",
"bisakhmondal00@gmail.com",
},
Impl: NewWilcoFixture(false),
Parent: fixture.FakeDMSEnrolled,
SetUpTimeout: chrome.ManagedUserLoginTimeout + 30*time.Second,
PostTestTimeout: 15 * time.Second,
ResetTimeout: chrome.ResetTimeout,
TearDownTimeout: chrome.ResetTimeout + 20*time.Second,
})
testing.AddFixture(&testing.Fixture{
Name: "wilcoDTCAllowedVMTestMode",
Desc: "Wilco DTC fixture with support for DTC VM (Test Mode Configuration), Supportd daemon and Wilco Chrome Extension",
Contacts: []string{
"lamzin@google.com", // wilco_dtc_supportd maintainer
"chromeos-wilco@google.com",
"bisakhmondal00@gmail.com",
},
Impl: NewWilcoFixture(true),
Parent: fixture.FakeDMSEnrolled,
SetUpTimeout: chrome.ManagedUserLoginTimeout + time.Minute,
PostTestTimeout: 15 * time.Second,
ResetTimeout: chrome.ResetTimeout,
TearDownTimeout: chrome.ResetTimeout + 20*time.Second,
})
}
// NewWilcoFixture returns a wilcoDTCFixture object reference that implements FixtureImpl interface.
func NewWilcoFixture(vmTestMode bool) *wilcoDTCFixture {
return &wilcoDTCFixture{
launchVMInTestMode: vmTestMode,
}
}
// wilcoDTCFixture implements testing.FixtureImpl.
type wilcoDTCFixture struct {
// launchVMInTestMode, if enabled, relaunches VM in test configuration.
launchVMInTestMode bool
// To enforce the checking of vm and supportd instances don't get changed by the test code.
wilcoDTCVMPID int
wilcoDTCSupportdPID int
// Local chrome & fdms state.
cr *chrome.Chrome
fdms *fakedms.FakeDMS
// Extension directory where assets are stored temporarily.
extensionDir string
}
func (w *wilcoDTCFixture) SetUp(ctx context.Context, s *testing.FixtState) interface{} {
fdms, ok := s.ParentValue().(*fakedms.FakeDMS)
if !ok {
s.Fatal("Parent is not a FakeDMS fixture")
}
// Load wilco extension alongside chrome.
extDir, err := ioutil.TempDir("", "tast.ChromeExtension.")
if err != nil {
s.Fatal("Failed to create temp dir: ", err)
}
w.extensionDir = extDir
s.Log("Writing unpacked extension to ", extDir)
if err := ioutil.WriteFile(filepath.Join(extDir, "manifest.json"), []byte(wilcoextension.Manifest), 0644); err != nil {
s.Fatal("Failed to write manifest.json: ", err)
}
if err := ioutil.WriteFile(filepath.Join(extDir, "background.js"), []byte{}, 0644); err != nil {
s.Fatal("Failed to write background.js: ", err)
}
if extID, err := chrome.ComputeExtensionID(extDir); err != nil {
s.Fatalf("Failed to compute extension ID for %v: %v", extDir, err)
} else if extID != wilcoextension.ID {
s.Fatalf("Unexpected extension id: got %s; want %s", extID, wilcoextension.ID)
}
cr, err := chrome.New(ctx, chrome.KeepEnrollment(),
chrome.FakeLogin(chrome.Creds{User: fixtures.Username, Pass: fixtures.Password}),
chrome.DMSPolicy(fdms.URL),
chrome.CustomLoginTimeout(chrome.ManagedUserLoginTimeout),
chrome.UnpackedExtension(extDir))
if err != nil {
s.Fatal("Chrome startup failed: ", err)
}
w.cr = cr
w.fdms = fdms
pb := policy.NewBlob()
// wilco_dtc and wilco_dtc_supportd only run for affiliated users.
pb.DeviceAffiliationIds = []string{"default_affiliation_id"}
pb.UserAffiliationIds = []string{"default_affiliation_id"}
// After this point, IsUserAffiliated flag should be updated.
if err := policyutil.ServeBlobAndRefresh(ctx, w.fdms, w.cr, pb); err != nil {
s.Fatal("Failed to serve and refresh: ", err)
}
// We should add policy value in the middle of 2 ServeBlobAndRefresh calls to be sure
// that IsUserAffiliated flag is updated and policy handler is triggered.
pb.AddPolicy(&policy.DeviceWilcoDtcAllowed{Val: true})
// After this point, the policy handler should be triggered.
if err := policyutil.ServeBlobAndRefresh(ctx, w.fdms, w.cr, pb); err != nil {
s.Fatal("Failed to serve and refresh: ", err)
}
// Ensuring the VM is ready to run commands over vsh.
if err := testing.Poll(ctx, func(ctx context.Context) error {
return vm.CreateVSHCommand(ctx, wilco.WilcoVMCID, "true").Run()
}, &testing.PollOptions{Timeout: 30 * time.Second}); err != nil {
s.Fatal("Failed to wait for DTC VM to be ready: ", err)
}
// Verify that wilco_dtc_supportd daemon was started by policy.
if err := testing.Poll(ctx, func(ctx context.Context) error {
if _, err := wilco.SupportdPID(ctx); err != nil {
return errors.Wrap(err, "failed to get Wilco DTC Support daemon PID")
}
return nil
}, &testing.PollOptions{Timeout: 10 * time.Second}); err != nil {
s.Fatal("Failed to wait for Wilco DTC Support Daemon to start: ", err)
}
// Restart wilco_dtc_supportd daemon in a test mode to collect more verbose logs.
if err := wilco.StartSupportd(ctx); err != nil {
s.Fatal("Failed to restart Wilco DTC Support Daemon: ", err)
}
w.wilcoDTCSupportdPID, err = wilco.SupportdPID(ctx)
if err != nil {
s.Fatal("Failed to get Wilco DTC Support Daemon PID after daemon restart: ", err)
}
// If wilco VM needs to be in test mode. Restarting wilco DTC vm with updated config.
if w.launchVMInTestMode {
if err := wilco.StopVM(ctx); err != nil {
s.Fatal("Failed to stop DTC VM: ", err)
}
if err := wilco.StartVM(ctx, &wilco.VMConfig{
StartProcesses: false,
TestDBusConfig: false,
}); err != nil {
s.Fatal("Failed to start the Wilco DTC VM: ", err)
}
}
w.wilcoDTCVMPID, err = wilco.VMPID(ctx)
if err != nil {
s.Fatal("Failed to get Wilco DTC VM PID: ", err)
}
// Wait until wilco_dtc_supportd bootstrapped the Mojo connection to Chrome.
if err := testing.Poll(ctx, func(ctx context.Context) error {
resp := dtcpb.GetStatefulPartitionAvailableCapacityResponse{}
if err := wilco.DPSLSendMessage(ctx, "GetStatefulPartitionAvailableCapacity",
&dtcpb.GetStatefulPartitionAvailableCapacityRequest{}, &resp); err != nil {
return errors.Wrap(err, "failed to get stateful partition available capacity")
}
if want := dtcpb.GetStatefulPartitionAvailableCapacityResponse_STATUS_OK; resp.Status != want {
return errors.Errorf("unexpected status received from vsh rpc method call = got %v, want %v", resp.Status, want)
}
return nil
}, &testing.PollOptions{Timeout: 10 * time.Second}); err != nil {
s.Fatal("Failed to wait wilco_dtc_supportd to bootstrap the Mojo connection to Chrome: ", err)
}
return fixtures.NewFixtData(w.cr, w.fdms)
}
func (w *wilcoDTCFixture) PreTest(ctx context.Context, s *testing.FixtTestState) {}
func (w *wilcoDTCFixture) PostTest(ctx context.Context, s *testing.FixtTestState) {
// Ensures that test doesn't interfere with the Wilco DTC VM and Daemon.
pid, err := wilco.VMPID(ctx)
if err != nil {
s.Fatal("Failed to get Wilco DTC VM PID: ", err)
}
if w.wilcoDTCVMPID != pid {
s.Error("The Wilco DTC VM PID changed while testing")
}
pid, err = wilco.SupportdPID(ctx)
if err != nil {
s.Fatal("Failed to get Wilco DTC Support daemon PID: ", err)
}
if w.wilcoDTCSupportdPID != pid {
s.Error("The Wilco DTC Support Daemon PID changed while testing")
}
}
func (w *wilcoDTCFixture) TearDown(ctx context.Context, s *testing.FixtState) {
// Stopping the the Wilco DTC VM and Daemon.
if err := wilco.StopSupportd(ctx); err != nil {
s.Error("Failed to stop the Wilco DTC Support Daemon: ", err)
}
if err := wilco.StopVM(ctx); err != nil {
s.Error("Failed to stop the Wilco DTC VM: ", err)
}
if err := w.cr.Close(ctx); err != nil {
s.Error("Failed to close Chrome connection: ", err)
}
if err := os.RemoveAll(w.extensionDir); err != nil {
s.Error("Failed to remove Chrome extension dir: ", err)
}
}
func (w *wilcoDTCFixture) Reset(ctx context.Context) error {
// Check the connection to Chrome.
if err := w.cr.Responded(ctx); err != nil {
return errors.Wrap(err, "existing Chrome connection is unusable")
}
// Reset Chrome state.
if err := w.cr.ResetState(ctx); err != nil {
return errors.Wrap(err, "failed resetting existing Chrome session")
}
return nil
}