| // Copyright 2020 The Fuchsia 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 check |
| |
| import ( |
| "context" |
| "fmt" |
| |
| "go.fuchsia.dev/fuchsia/src/testing/host-target-testing/device" |
| "go.fuchsia.dev/fuchsia/src/testing/host-target-testing/sl4f" |
| |
| "go.fuchsia.dev/fuchsia/tools/lib/logger" |
| ) |
| |
| // IsDeviceUpToDate checks if the device's /system/meta matches the expected |
| // system image merkle. |
| func IsDeviceUpToDate( |
| ctx context.Context, |
| device *device.Client, |
| expectedSystemImageMerkle string, |
| ) (bool, error) { |
| remoteSystemImageMerkle, err := device.GetSystemImageMerkle(ctx) |
| if err != nil { |
| return false, fmt.Errorf("failed to get system image merkle while checking if device is up to date: %w", err) |
| } |
| |
| logger.Infof(ctx, "current system image merkle: %q", remoteSystemImageMerkle) |
| logger.Infof(ctx, "expected system image merkle: %q", expectedSystemImageMerkle) |
| |
| return expectedSystemImageMerkle == remoteSystemImageMerkle, nil |
| } |
| |
| func determineActiveABRConfig( |
| ctx context.Context, |
| rpcClient *sl4f.Client, |
| ) (*sl4f.Configuration, error) { |
| if rpcClient == nil { |
| logger.Infof(ctx, "sl4f not running, cannot determine active partition") |
| return nil, nil |
| } |
| |
| currentConfig, err := rpcClient.PaverQueryActiveConfiguration(ctx) |
| if err == sl4f.ErrNotSupported { |
| logger.Infof(ctx, "device does not support querying the active configuration") |
| return nil, nil |
| } else if err != nil { |
| return nil, err |
| } |
| |
| logger.Infof(ctx, "device booted to slot %s", currentConfig) |
| |
| return ¤tConfig, nil |
| } |
| |
| func DetermineCurrentABRConfig( |
| ctx context.Context, |
| rpcClient *sl4f.Client, |
| ) (*sl4f.Configuration, error) { |
| if rpcClient == nil { |
| logger.Infof(ctx, "sl4f not running, cannot determine current partition") |
| return nil, nil |
| } |
| |
| currentConfig, err := rpcClient.PaverQueryCurrentConfiguration(ctx) |
| if err == sl4f.ErrNotSupported { |
| logger.Infof(ctx, "device does not support querying the current configuration") |
| return nil, nil |
| } else if err != nil { |
| return nil, err |
| } |
| |
| logger.Infof(ctx, "device booted to slot %s", currentConfig) |
| |
| return ¤tConfig, nil |
| } |
| |
| func DetermineTargetABRConfig( |
| ctx context.Context, |
| rpcClient *sl4f.Client, |
| ) (*sl4f.Configuration, error) { |
| currentConfig, err := DetermineCurrentABRConfig(ctx, rpcClient) |
| if err != nil { |
| return nil, fmt.Errorf("could not determine target config when querying active config: %w", err) |
| } |
| if currentConfig == nil { |
| return nil, nil |
| } |
| |
| var targetConfig sl4f.Configuration |
| if *currentConfig == sl4f.ConfigurationA { |
| targetConfig = sl4f.ConfigurationB |
| } else { |
| targetConfig = sl4f.ConfigurationA |
| } |
| |
| return &targetConfig, nil |
| } |
| |
| func CheckABRConfig( |
| ctx context.Context, |
| rpcClient *sl4f.Client, |
| expectedConfig *sl4f.Configuration, |
| ) error { |
| if expectedConfig == nil { |
| logger.Infof(ctx, "no configuration expected, so not checking ABR configuration") |
| return nil |
| } |
| |
| if rpcClient == nil { |
| logger.Infof(ctx, "not connected to sl4f, cannot check ABR configuration") |
| return nil |
| } |
| |
| // Ensure the device is booting from the expected boot slot. |
| currentConfig, err := DetermineCurrentABRConfig(ctx, rpcClient) |
| if err != nil { |
| return fmt.Errorf("unable to determine current boot configuration: %w", err) |
| } |
| |
| if currentConfig == nil { |
| return fmt.Errorf("expected device to boot from slot %q, got <nil>", *expectedConfig) |
| } else if *currentConfig != *expectedConfig { |
| return fmt.Errorf("expected device to boot from slot %q, got %q", *expectedConfig, *currentConfig) |
| } |
| |
| return nil |
| } |
| |
| func ValidateDevice( |
| ctx context.Context, |
| device *device.Client, |
| rpcClient *sl4f.Client, |
| expectedSystemImageMerkle string, |
| expectedConfig *sl4f.Configuration, |
| warnOnABR bool, |
| ) error { |
| // At the this point the system should have been updated to the target |
| // system version. Confirm the update by fetching the device's current |
| // /system/meta, and making sure it is the correct version. |
| if expectedSystemImageMerkle != "" { |
| upToDate, err := IsDeviceUpToDate(ctx, device, expectedSystemImageMerkle) |
| if err != nil { |
| return fmt.Errorf("failed to check if device is up to date: %w", err) |
| } |
| if !upToDate { |
| return fmt.Errorf("system version failed to update to %q", expectedSystemImageMerkle) |
| } |
| } |
| |
| // Make sure the device doesn't have any broken static packages. |
| if err := device.ValidateStaticPackages(ctx); err != nil { |
| return fmt.Errorf("failed to validate static packages without sl4f: %w", err) |
| } |
| if rpcClient != nil { |
| if err := CheckABRConfig(ctx, rpcClient, expectedConfig); err != nil { |
| // FIXME(43336): during the rollout of ABR, the N-1 build might |
| // not be writing to the inactive partition, so don't |
| // err out during that phase. This will be removed once |
| // ABR has rolled through GI. |
| if warnOnABR { |
| logger.Infof(ctx, "ignoring error during ABR rollout: %v", err) |
| } else { |
| return fmt.Errorf("failed to validate device: %w", err) |
| } |
| } |
| } |
| |
| return nil |
| } |