[infra][ftx] Checks for success in out/summary.json in CAS output.
Bug: b/258456267
Change-Id: I0da3f202a0803e9aa0ae48835a2738c0f351961c
Reviewed-on: https://fuchsia-review.googlesource.com/c/infra/infra/+/873653
Reviewed-by: Rahul Bangar <rahulbn@google.com>
Commit-Queue: Vinicius Felizardo <felizardo@google.com>
diff --git a/cmd/ftxtest/cas.go b/cmd/ftxtest/cas.go
new file mode 100644
index 0000000..e09fa8e
--- /dev/null
+++ b/cmd/ftxtest/cas.go
@@ -0,0 +1,51 @@
+// Copyright 2023 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 main
+
+import (
+ "context"
+ "fmt"
+ "os"
+
+ rbeClient "github.com/bazelbuild/remote-apis-sdks/go/pkg/client"
+ "github.com/bazelbuild/remote-apis-sdks/go/pkg/digest"
+ "github.com/bazelbuild/remote-apis-sdks/go/pkg/filemetadata"
+ "go.chromium.org/luci/auth"
+ "go.chromium.org/luci/client/casclient"
+)
+
+const (
+ casAddr = "remotebuildexecution.googleapis.com:443"
+)
+
+type CAS struct {
+ client *rbeClient.Client
+}
+
+func NewCAS(ctx context.Context, authOpts auth.Options, luciInstance string) (*CAS, error) {
+ casInstance := fmt.Sprintf("projects/%s/instances/default_instance", luciInstance)
+ client, err := casclient.NewLegacy(ctx, casAddr, casInstance, authOpts, true)
+ if err != nil {
+ return nil, fmt.Errorf("casclient.NewLegacy: %v", err)
+ }
+ return &CAS{
+ client: client,
+ }, nil
+}
+
+func (c *CAS) Download(ctx context.Context, hash string, size int64) (string, error) {
+ outDir, err := os.MkdirTemp("", "casOut")
+ if err != nil {
+ return "", fmt.Errorf("os.MkdirTemp: %v", err)
+ }
+ d := digest.Digest{
+ Hash: hash,
+ Size: size,
+ }
+ _, _, err = c.client.DownloadDirectory(ctx, d, outDir, filemetadata.NewNoopCache())
+ if err != nil {
+ return "", fmt.Errorf("cas DownloadDirectory: %v", err)
+ }
+ return outDir, nil
+}
diff --git a/cmd/ftxtest/main.go b/cmd/ftxtest/main.go
index bd19bd0..749f3c3 100644
--- a/cmd/ftxtest/main.go
+++ b/cmd/ftxtest/main.go
@@ -21,6 +21,7 @@
func getApplication(defaultAuthOpts auth.Options) *subcommands.DefaultApplication {
defaultAuthOpts.Scopes = []string{
+ "https://www.googleapis.com/auth/cloud-platform",
"https://www.googleapis.com/auth/userinfo.email",
}
return &subcommands.DefaultApplication{
diff --git a/cmd/ftxtest/run.go b/cmd/ftxtest/run.go
index ab4f82d..e095c18 100644
--- a/cmd/ftxtest/run.go
+++ b/cmd/ftxtest/run.go
@@ -73,6 +73,11 @@
state.SetSummaryMarkdown(fmt.Sprintf("%v: %v", buildInput.Name, err))
return err
}
+ if err := r.resultStep(ctx, bbCtx, swarming, taskId); err != nil {
+ err = fmt.Errorf("resultStep: %v", err)
+ state.SetSummaryMarkdown(fmt.Sprintf("%v: %v", buildInput.Name, err))
+ return err
+ }
return nil
})
}
@@ -86,8 +91,11 @@
fmt.Fprintf(os.Stderr, " luci-auth login -scopes %q\n", strings.Join(r.parsedAuthOpts.Scopes, " "))
return nil, errors.New("Not logged in.")
}
- instance := instance(buildInput)
- swarming, err := NewSwarming(httpClient, instance)
+ cas, err := NewCAS(ctx, r.parsedAuthOpts, instance(buildInput))
+ if err != nil {
+ return nil, fmt.Errorf("NewCAS: %v", err)
+ }
+ swarming, err := NewSwarming(httpClient, instance(buildInput), cas)
if err != nil {
step.End(err)
return nil, fmt.Errorf("NewSwarming: %v", err)
@@ -128,6 +136,16 @@
return nil
}
+func (r *runImpl) resultStep(ctx context.Context, bbCtx context.Context, swarming *Swarming, taskId string) error {
+ step, bbCtx := build.StartStep(bbCtx, "Result")
+ if err := swarming.CheckTestFailure(ctx, taskId); err != nil {
+ step.End(err)
+ return err
+ }
+ step.End(nil)
+ return nil
+}
+
func instance(buildInput *ftxproto.InputProperties) string {
if buildInput.External {
return "chromium-swarm"
diff --git a/cmd/ftxtest/swarming.go b/cmd/ftxtest/swarming.go
index 8fd6684..eebab54 100644
--- a/cmd/ftxtest/swarming.go
+++ b/cmd/ftxtest/swarming.go
@@ -4,8 +4,13 @@
package main
import (
+ "context"
+ "encoding/json"
+ "errors"
"fmt"
"net/http"
+ "os"
+ "path/filepath"
"strconv"
"strings"
"time"
@@ -17,6 +22,7 @@
type Swarming struct {
instance string
service *swarming.Service
+ cas *CAS
}
const (
@@ -26,7 +32,7 @@
poolTaskInterval = 10 * time.Second
)
-func NewSwarming(httpClient *http.Client, instance string) (*Swarming, error) {
+func NewSwarming(httpClient *http.Client, instance string, cas *CAS) (*Swarming, error) {
swarmingService, err := swarming.New(httpClient)
swarmingService.BasePath = fmt.Sprintf("https://%s.appspot.com/_ah/api/swarming/v1/", instance)
if err != nil {
@@ -35,6 +41,7 @@
swarming := &Swarming{
service: swarmingService,
instance: instance,
+ cas: cas,
}
return swarming, nil
}
@@ -132,3 +139,44 @@
return "turquoise:global.try"
}
}
+
+// testSummary determines the data for out/summary.json
+type testSummary struct {
+ Success bool `json:"success"`
+}
+
+func (s *Swarming) CheckTestFailure(ctx context.Context, taskId string) error {
+ task, err := s.service.Task.Result(taskId).Do()
+ if err != nil {
+ return fmt.Errorf("task.request: %v", err)
+ }
+ if task.CasOutputRoot == nil || task.CasOutputRoot.Digest == nil {
+ return errors.New("Swarming task did not produce CAS output")
+ }
+ d := task.CasOutputRoot.Digest
+ dir, err := s.cas.Download(ctx, d.Hash, d.SizeBytes)
+ if err != nil {
+ return fmt.Errorf("cas.Download: %v", err)
+ }
+ summary := testSummary{}
+ err = readJSON(filepath.Join(dir, "out", "summary.json"), &summary)
+ if err != nil {
+ return fmt.Errorf("readJSON: %v", err)
+ }
+ if !summary.Success {
+ return errors.New("Test failure")
+ }
+ return nil
+}
+
+func readJSON(filename string, out any) error {
+ rawData, err := os.ReadFile(filename)
+ if err != nil {
+ return fmt.Errorf("os.ReadFile: %v", err)
+ }
+ err = json.Unmarshal(rawData, out)
+ if err != nil {
+ return fmt.Errorf("json.Unmarshal: %v", err)
+ }
+ return nil
+}
diff --git a/cmd/ftxtest/test/build.json b/cmd/ftxtest/test/build.json
index 135a4c9..e35c2a4 100644
--- a/cmd/ftxtest/test/build.json
+++ b/cmd/ftxtest/test/build.json
@@ -3,7 +3,7 @@
"properties": {
"name": "hello_swarming_test",
"external": false,
- "input_artifacts_digest": "19a21c55f6790bc5e07f9e6c3be64dfd19fa696feaed629c6a9a8e15e5e469cb/83",
+ "input_artifacts_digest": "885f75a688c8cfd7fb8de820953f4949a9c6a1a5af7870fa2d059957f718c7af/83",
"target_dimensions": {
"pool": "fuchsia.dev.tests",
"device_type": "Vim3"