| // 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) |
| } |
| } |