blob: 9a04a35619ca290724c5eeab1d19a72ea61f0dbb [file] [log] [blame]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package policy
import (
"context"
"time"
"github.com/golang/protobuf/ptypes/empty"
"go.chromium.org/tast-tests/cros/common/policy"
"go.chromium.org/tast-tests/cros/common/policy/reportingutil"
"go.chromium.org/tast-tests/cros/common/tape"
"go.chromium.org/tast-tests/cros/remote/policyutil"
"go.chromium.org/tast-tests/cros/services/cros/graphics"
ps "go.chromium.org/tast-tests/cros/services/cros/policy"
"go.chromium.org/tast/core/errors"
"go.chromium.org/tast/core/rpc"
"go.chromium.org/tast/core/testing"
)
const heartbeatReportingTimeout = 7 * time.Minute
type heartbeatTestParams struct {
IsUserEvent bool // If true, send user events from umanaged device, else send device events from managed device.
EnabledFeatures string
Autopush bool
}
func init() {
testing.AddTest(&testing.Test{
Func: HeartbeatReporting,
LacrosStatus: testing.LacrosVariantUnneeded,
Desc: "Verify heartbeat reporting functionality",
Contacts: []string{
"cros-reporting-alerts+tast@google.com",
"albertojuarez@google.com", // Test maintainer
"rzakarian@google.com",
},
BugComponent: "b:817866", // Chrome OS Server Projects > Enterprise Management > Reporting
Attr: []string{"group:golden_tier", "group:medium_low_tier", "group:hardware", "group:complementary", "group:enterprise-reporting", "group:hw_agnostic"},
SoftwareDeps: []string{"reboot", "chrome"},
ServiceDeps: []string{"tast.cros.policy.PolicyService", "tast.cros.hwsec.OwnershipService", "tast.cros.tape.Service", "tast.cros.graphics.ScreenshotService"},
Timeout: heartbeatReportingTimeout,
VarDeps: []string{
reportingutil.ManagedChromeCustomerIDPath,
reportingutil.EventsAPIKeyPath,
reportingutil.ProdEventsAPIKeyPath,
tape.ServiceAccountVar,
},
Params: []testing.Param{
{
Name: "autopush_unmanaged_device",
Val: heartbeatTestParams{
IsUserEvent: true,
// Enable the reporting pipeline, user heartbeat events, reporting from unmanaged device, and enable multigenerational storage for FAST_BATCH priority (i.e. exclude FAST_BATCH from legacy_storage_enabled list).
EnabledFeatures: "EncryptedReportingPipeline, EncryptedReportingManualTestUserHeartbeatEvent, EnableReportingFromUnmanagedDevices, ClientAutomatedTest, CrOSLateBootMissiveStorage:legacy_storage_enabled/IMMEDIATE,SLOW_BATCH,BACKGROUND_BATCH,MANUAL_BATCH,SECURITY,MANUAL_BATCH_LACROS",
Autopush: true,
},
},
{
Name: "autopush_managed_device",
Val: heartbeatTestParams{
IsUserEvent: false,
// Enable the reporting pipeline, device heartbeat events.
EnabledFeatures: "EncryptedReportingPipeline, EncryptedReportingManualTestHeartbeatEvent, ClientAutomatedTest",
Autopush: true,
},
},
{
Name: "autopush_managed_device_using_multigenerational_storage",
Val: heartbeatTestParams{
IsUserEvent: false,
// Enable the reporting pipeline, device heartbeat events, and multigenerational storage for FAST_BATCH priority (i.e. exclude FAST_BATCH from legacy_storage_enabled list).
EnabledFeatures: "EncryptedReportingPipeline, EncryptedReportingManualTestHeartbeatEvent, ClientAutomatedTest, CrOSLateBootMissiveStorage:legacy_storage_enabled/IMMEDIATE,SLOW_BATCH,BACKGROUND_BATCH,MANUAL_BATCH,SECURITY,MANUAL_BATCH_LACROS",
Autopush: true,
},
},
{
Name: "prod_managed_device",
Val: heartbeatTestParams{
IsUserEvent: false,
// Enable the reporting pipeline, device heartbeat events.
EnabledFeatures: "EncryptedReportingPipeline, EncryptedReportingManualTestHeartbeatEvent, ClientAutomatedTest",
Autopush: false,
},
},
},
})
}
// HeartbeatReporting tests that the ERP sends heartbeats to the server if enabled.
func HeartbeatReporting(ctx context.Context, s *testing.State) {
customerID := s.RequiredVar(reportingutil.ManagedChromeCustomerIDPath)
APIKey := s.RequiredVar(reportingutil.EventsAPIKeyPath)
ProdAPIKey := s.RequiredVar(reportingutil.ProdEventsAPIKeyPath)
sa := []byte(s.RequiredVar(tape.ServiceAccountVar))
params := s.Param().(heartbeatTestParams)
defer func(ctx context.Context) {
if err := policyutil.EnsureTPMAndSystemStateAreReset(ctx, s.DUT(), s.RPCHint()); err != nil {
s.Error("Failed to reset TPM after test: ", err)
}
}(ctx)
if err := policyutil.EnsureTPMAndSystemStateAreReset(ctx, s.DUT(), s.RPCHint()); err != nil {
s.Fatal("Failed to reset TPM: ", err)
}
cl, err := rpc.Dial(ctx, s.DUT(), s.RPCHint())
if err != nil {
s.Fatal("Failed to connect to the RPC service on the DUT: ", err)
}
defer cl.Close(ctx)
defer reportingutil.Deprovision(ctx, cl.Conn, sa)
screenshotService := graphics.NewScreenshotServiceClient(cl.Conn)
captureScreenshotOnError := func(ctx context.Context, hasError func() bool) {
if !hasError() {
return
}
screenshotService.CaptureScreenshot(ctx, &graphics.CaptureScreenshotRequest{FilePrefix: "reportingError"})
}
defer captureScreenshotOnError(ctx, s.HasError)
policyClient := ps.NewPolicyServiceClient(cl.Conn)
tapeClient, err := tape.NewClient(ctx, []byte(s.RequiredVar(tape.ServiceAccountVar)))
if err != nil {
s.Fatal("Failed to create tape client: ", err)
}
timeout := int32(heartbeatReportingTimeout.Seconds())
// Create an account manager and lease a test account for the duration of the test.
accManager, acc, err := tape.NewOwnedTestAccountManagerFromClient(ctx, tapeClient, true /*lock*/, tape.WithTimeout(timeout), tape.WithPoolID(tape.DefaultManaged))
if err != nil {
s.Fatal("Failed to create an account manager and lease an account: ", err)
}
defer accManager.CleanUp(ctx)
// Disable Asset ID screen on enrollment.
if err := reportingutil.DisableUpdatingDeviceAttribute(ctx, tapeClient, acc.RequestID); err != nil {
s.Fatal("Failed to set the asset policy: ", err)
}
// Set the URLs to use for the DM server and reporting server depending on the test params.
var dmServerURL string
var reportingServerURL string
if params.Autopush {
dmServerURL = policy.DMServerAlphaURL
reportingServerURL = reportingutil.ReportingServerURL
} else {
dmServerURL = policy.DMServerProdURL
reportingServerURL = reportingutil.ProdReportingServerURL
}
testStartTime := time.Now()
if params.IsUserEvent {
// This is a user event. Login with managed user, but don't enroll the device.
if _, err := policyClient.GAIALoginForReporting(ctx, &ps.GAIALoginForReportingRequest{
Username: acc.Username,
Password: acc.Password,
DmserverUrl: dmServerURL,
ReportingServerUrl: reportingServerURL,
// Enable user heart beat events, reporting from unmanaged devices, and legacy/non-multigenerational storage for all priorities except FAST_BATCH (the priority that heartbeat events use).
EnabledFeatures: params.EnabledFeatures,
}); err != nil {
s.Fatal("Failed to login to chrome with managed user: ", err)
}
} else {
// This is a device event. Enroll device and maybe login, depending on `SkipLogin` setting.
if _, err := policyClient.GAIAEnrollForReporting(ctx, &ps.GAIAEnrollForReportingRequest{
Username: acc.Username,
Password: acc.Password,
DmserverUrl: dmServerURL,
ReportingServerUrl: reportingServerURL,
EnabledFeatures: params.EnabledFeatures,
SkipLogin: true,
}); err != nil {
s.Fatal("Failed to enroll using chrome: ", err)
}
}
testTimeout := 2 * time.Minute
// Stop chrome when the test is finished.
defer policyClient.StopChrome(ctx, &empty.Empty{})
// Gather device info for device events.
var clientID string
// Verify that the server has received the events.
if err := testing.Poll(ctx, func(ctx context.Context) error {
var events []reportingutil.InputEvent
var err error
if !params.IsUserEvent {
// Look up device events using client id.
c, err := policyClient.ClientID(ctx, &empty.Empty{})
clientID = c.ClientId
if err != nil {
s.Fatalf("Failed to grab client ID from device: %v:", err)
}
// If the test uses the autopush server then query the autopush server for the events.
// Otherwise query the prod server.
if params.Autopush {
events, err = reportingutil.LookupEvents(ctx, customerID, clientID, APIKey, "HEARTBEAT_EVENTS", testStartTime)
} else {
events, err = reportingutil.LookupProdEvents(ctx, customerID, clientID, ProdAPIKey, "HEARTBEAT_EVENTS", testStartTime)
}
} else {
// Look up user events using user account info.
events, err = reportingutil.LookupUserEvents(ctx, customerID, APIKey, "HEARTBEAT_EVENTS", acc.Username, testStartTime)
}
if err != nil {
return errors.Wrap(err, "failed to look up events")
}
if err := reportingutil.SaveCrosboltEventCountMetric("heartbeat_events", len(events), s.OutDir()); err != nil {
s.Log("Failed to save heartbeat event count perf metric: ", err)
}
if len(events) < 1 {
return errors.New("no event found")
}
return nil
}, &testing.PollOptions{
Timeout: testTimeout,
Interval: 30 * time.Second,
}); err != nil {
s.Errorf("Reporting: Failed to validate heartbeat event: %v:", err)
}
}