blob: 94df6560e02f95d593101bc13b722d372587648b [file] [log] [blame]
// Copyright 2024 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package main
import (
"context"
"encoding/json"
"log"
"os"
"path/filepath"
conf "go.chromium.org/chromiumos/config/go"
"go.chromium.org/chromiumos/config/go/test/api"
labapi "go.chromium.org/chromiumos/config/go/test/lab/api"
libapi "go.chromium.org/chromiumos/test/ctpv2/common/dynamic_updates"
"go.chromium.org/chromiumos/test/ctpv2/common/dynamic_updates/builders"
"go.chromium.org/chromiumos/test/ctpv2/common/dynamic_updates/common"
"go.chromium.org/chromiumos/test/ctpv2/common/dynamic_updates/generators"
"go.chromium.org/chromiumos/test/ctpv2/common/dynamic_updates/interfaces"
server "go.chromium.org/chromiumos/test/ctpv2/common/server_template"
"go.chromium.org/chromiumos/test/publish/cmd/publishserver/storage"
"go.chromium.org/tast/core/errors"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/structpb"
)
const (
artifactsBucket = "e2e-coverage-artifacts"
versionPath = "active_version/brya"
versionFile = "current.json"
hptRoot = "/tmp/cros-hpt"
authTokenFile = "authToken.txt"
gsPrefix = "gs://"
archiveBucket = "chromeos-image-archive"
authTokenFilePath = "/tmp/cros-hpt/authToken.txt"
perfProject = "google.com:cr-code-cov-merger"
perfTopic = "perf-data"
perfBucket = "e2e_perf_data"
)
type ActiveVersion struct {
Board string `json:"board"`
SnapshotVersion string `json:"snapshot_version"`
Version string `json:"version"`
}
type GSClient interface {
DownloadFile(context.Context, string, string) error
}
func getActiveVersion(ctx context.Context, gsClient GSClient, localPath string) (*ActiveVersion, error) {
gsURL := gsPrefix + filepath.Join(artifactsBucket, versionPath, versionFile)
if err := gsClient.DownloadFile(ctx, gsURL, localPath); err != nil {
return nil, err
}
contents, err := os.ReadFile(localPath)
if err != nil {
return nil, err
}
v := &ActiveVersion{}
if err := json.Unmarshal(contents, v); err != nil {
log.Printf("Error getting active version: %v", err)
return nil, err
}
return v, nil
}
func hptGenerator(av *ActiveVersion) *generators.GenericTaskGenerator {
hptContainer := builders.NewContainerBuilder(
"cros-hpt",
"cros-hpt",
"",
"/tmp/cros-hpt",
"cros-hpt server -port 0",
)
// Insert cros-hpt after last provisioning and last test executed step.
// We need to start and run cros-hpt container after provisioning.
// Also, we need to stop it after tests have been executed.
insertInstructions := map[string]interfaces.InsertInstructionGetter{
"afterProvision": common.InsertActionWrapper(
api.UpdateAction_Insert_APPEND,
common.FindLast(api.FocalTaskFinder_PROVISION),
),
"afterTest": common.InsertActionWrapper(
api.UpdateAction_Insert_APPEND,
common.FindLast(api.FocalTaskFinder_TEST),
),
}
inputs := map[string]proto.Message{
"authTokenFilePath": structpb.NewStringValue(authTokenFilePath),
"board": structpb.NewStringValue(av.Board),
"version": structpb.NewStringValue(av.Version),
"snapshotVersion": structpb.NewStringValue(av.SnapshotVersion),
"projectId": structpb.NewStringValue(perfProject),
"topicId": structpb.NewStringValue(perfTopic),
"bucket": structpb.NewStringValue(perfBucket),
"dutIP": &labapi.IpEndpoint{},
}
// Start cros-hpt container after provisioning and stop after running the tests.
generator := generators.NewGenericTaskGenerator(
common.NewTaskIdentifier("hpt").Id,
"cros-hpt",
[]*builders.ContainerBuilder{hptContainer},
insertInstructions,
&interfaces.GenericTaskMessageRequest{
InsertionId: "afterProvision",
StaticInputs: inputs,
DynamicInputs: map[string]string{
"dutIP.value": common.NewPrimaryDeviceIdentifier().GetCrosDutServer(),
},
},
&interfaces.GenericTaskMessageRequest{
InsertionId: "afterProvision",
},
&interfaces.GenericTaskMessageRequest{
InsertionId: "afterTest",
},
)
return generator
}
func executor(req *api.InternalTestplan, log *log.Logger) (*api.InternalTestplan, error) {
ctx := context.Background()
gsClient, err := storage.NewGSClient(ctx, filepath.Join(hptRoot, authTokenFile))
if err != nil {
return nil, err
}
defer gsClient.Close()
localPath := filepath.Join(hptRoot, versionFile)
av, err := getActiveVersion(ctx, gsClient, localPath)
if err != nil {
return nil, err
}
for _, target := range req.GetSuiteInfo().GetSuiteMetadata().GetTargetRequirements() {
variant := target.GetSwRequirement().GetVariant()
for _, hwTarget := range target.HwRequirements.HwDefinition {
log.Printf("Build: %s\nImagePath: %s\nVariant: %s\nBoard: %s\n", av.Version, av.SnapshotVersion, variant, av.Board)
hwTarget.ProvisionInfo = generateProvisionInfo(hwTarget.GetProvisionInfo(), av.Version, av.SnapshotVersion, variant)
}
}
du := req.GetSuiteInfo().GetSuiteMetadata().GetDynamicUpdates()
if err := libapi.AppendUserDefinedDynamicUpdates(&du, hptGenerator(av).Generate); err != nil {
return nil, errors.Wrapf(err, "Error adding hpt container.")
}
return req, nil
}
// Currently this is simple... just append the new provision info onto the existing.
// We are appending it in case another provsion filter has run and populated info.
func generateProvisionInfo(current []*api.ProvisionInfo, build string, path string, variant string) []*api.ProvisionInfo {
installInfo := &api.InstallRequest{
ImagePath: &conf.StoragePath{
Path: path,
HostType: conf.StoragePath_GS,
},
}
info := &api.ProvisionInfo{
Type: api.ProvisionInfo_CROS,
InstallRequest: installInfo,
Identifier: variant,
}
return append(current, info)
}
func main() {
err := server.Server(executor, "hpt_filter")
if err != nil {
os.Exit(2)
}
os.Exit(0)
}