Skip to content

Commit

Permalink
Address PR feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
bduffany committed Feb 10, 2021
1 parent 68f23ca commit 22f5504
Show file tree
Hide file tree
Showing 9 changed files with 245 additions and 180 deletions.
6 changes: 0 additions & 6 deletions deps.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -1562,9 +1562,3 @@ def install_buildbuddy_dependencies():
sum = "h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=",
version = "v0.1.2",
)
go_repository(
name = "com_github_phayes_freeport",
importpath = "github.com/phayes/freeport",
sum = "h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc=",
version = "v0.0.0-20180830031419-95f893ade6f2",
)
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ require (
github.com/opencontainers/image-spec v1.0.1 // indirect
github.com/opencontainers/runc v0.1.1 // indirect
github.com/opencontainers/selinux v1.8.0 // indirect
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 // indirect
github.com/pkg/sftp v1.11.0 // indirect
github.com/pquerna/cachecontrol v0.0.0-20201205024021-ac21108117ac // indirect
github.com/prometheus/client_golang v1.6.0
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -363,8 +363,6 @@ github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/
github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo=
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc=
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
Expand Down
5 changes: 0 additions & 5 deletions server/test/integration/build_event_protocol/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_test")
go_test(
name = "go_default_test",
srcs = ["build_event_protocol_test.go"],
# TODO: Make this test automatic instead of manual. We can either use a
# cross-platform build of the dependent bazel binary, or upgrade to
# Bazel 4.0.0 and set "target_compatible_with" on this test, so that it
# only gets run on Linux when running `bazel test //...`
tags = ["manual"],
deps = [
"//server/testutil/bazel:go_default_library",
"//server/testutil/buildbuddy:go_default_library",
Expand Down
12 changes: 12 additions & 0 deletions server/testutil/app/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")

go_library(
name = "go_default_library",
testonly = 1,
srcs = ["app.go"],
importpath = "github.com/buildbuddy-io/buildbuddy/server/testutil/app",
visibility = ["//visibility:public"],
deps = [
"@io_bazel_rules_go//go/tools/bazel:go_default_library",
],
)
220 changes: 220 additions & 0 deletions server/testutil/app/app.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
package app

import (
"bytes"
"fmt"
"io/ioutil"
"net"
"net/http"
"os"
"os/exec"
"path/filepath"
"sync"
"testing"
"time"

bazelgo "github.com/bazelbuild/rules_go/go/tools/bazel"
)

const (
// readyCheckPollInterval determines how often to poll BuildBuddy server to check
// whether it's up and running.
readyCheckPollInterval = 500 * time.Millisecond
// readyCheckTimeout determines how long to wait until giving up on waiting for
// BuildBuddy server to become ready. If this timeout is reached, the test case
// running the server will fail with a timeout error.
readyCheckTimeout = 30 * time.Second
)

type App struct {
httpPort int
gRPCPort int
monitoringPort int
mu sync.Mutex
stdout bytes.Buffer
stderr bytes.Buffer
exited bool
// err is the error returned by `cmd.Wait()`.
err error
}

// appParams are configurable via `RunOpt`s.
type appParams struct {
cmdPath string
configPath string
cmdArgs []string
}

// Run a local BuildBuddy server for the scope of the given test case.
//
// Options may be given to override the default server options.
func Run(t *testing.T, opts ...RunOpt) *App {
p := &appParams{cmdArgs: []string{}}
for _, opt := range opts {
if err := opt(p); err != nil {
t.Fatal(err)
}
}
if p.cmdPath == "" {
t.Fatalf("missing `WithBuildBuddyCmdPath` opt")
}
if p.configPath == "" {
t.Fatalf("missing `WithConfigPath` opt")
}
dataDir, err := ioutil.TempDir("/tmp", "buildbuddy-test-*")
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
os.RemoveAll(dataDir)
})
// NOTE: No SSL ports are required since the server doesn't have an SSL config by default.
app := &App{
httpPort: freePort(t),
gRPCPort: freePort(t),
monitoringPort: freePort(t),
}
cmdArgs := []string{
fmt.Sprintf("--config_file=%s", p.configPath),
fmt.Sprintf("--port=%d", app.httpPort),
fmt.Sprintf("--grpc_port=%d", app.gRPCPort),
fmt.Sprintf("--monitoring_port=%d", app.monitoringPort),
fmt.Sprintf("--app.build_buddy_url=http://localhost:%d", app.httpPort),
"--database.data_source=sqlite3://:memory:",
fmt.Sprintf("--storage.disk.root_directory=%s", filepath.Join(dataDir, "storage")),
fmt.Sprintf("--cache.disk.root_directory=%s", filepath.Join(dataDir, "cache")),
}
cmdArgs = append(cmdArgs, p.cmdArgs...)
cmd := exec.Command(p.cmdPath, cmdArgs...)
cmd.Stdout = &app.stdout
cmd.Stderr = &app.stderr
if err := cmd.Start(); err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
cmd.Process.Kill() // ignore errors
})
go func() {
err := cmd.Wait()
app.mu.Lock()
defer app.mu.Unlock()
app.exited = true
app.err = err
}()
err = app.waitForReady()
if err != nil {
t.Fatal(app.fmtErrorWithLogs(err))
}
return app
}

// BESBazelFlags returns the Bazel flags required to upload build logs to the App.
func (a *App) BESBazelFlags() []string {
return []string{
fmt.Sprintf("--bes_results_url=http://localhost:%d/invocation/", a.httpPort),
fmt.Sprintf("--bes_backend=grpc://localhost:%d", a.gRPCPort),
}
}

// RemoteCacheBazelFlags returns the Bazel flags required to use the App's remote cache.
func (a *App) RemoteCacheBazelFlags() []string {
return []string{
fmt.Sprintf("--remote_cache=grpc://localhost:%d", a.gRPCPort),
}
}

// Opt customizes the BuildBuddy app instance.
type RunOpt func(*appParams) error

// WithBuildBuddyCmdPath sets the path to the BuildBuddy binary.
func WithBuildBuddyCmdPath(path string) RunOpt {
return func(p *appParams) error {
cmdPath, err := bazelgo.Runfile(path)
if err != nil {
return err
}
p.cmdPath = cmdPath
return nil
}
}

// WithConfigPath sets the path to the BuildBuddy server config.
func WithConfigPath(path string) RunOpt {
return func(p *appParams) error {
configPath, err := bazelgo.Runfile(path)
if err != nil {
return err
}
p.configPath = configPath
return nil
}
}

// WithArgs adds extra flags to the BuildBuddy server.
func WithFlags(args ...string) RunOpt {
return func(p *appParams) error {
p.cmdArgs = append(p.cmdArgs, args...)
return nil
}
}

func freePort(t *testing.T) int {
addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
if err != nil {
t.Fatal(err)
}
l, err := net.ListenTCP("tcp", addr)
if err != nil {
t.Fatal(err)
}
defer l.Close()
return l.Addr().(*net.TCPAddr).Port
}

func (a *App) fmtErrorWithLogs(err error) error {
return fmt.Errorf(`%s
=== STDOUT ===
%s
=== STDERR ===
%s`, err, string(a.stdout.Bytes()), string(a.stderr.Bytes()))
}

func (a *App) waitForReady() error {
start := time.Now()
for {
a.mu.Lock()
exited := a.exited
err := a.err
a.mu.Unlock()
if exited {
return fmt.Errorf("app failed to start: %s", err)
}
resp, err := http.Get(fmt.Sprintf("http://localhost:%d/readyz?server-type=buildbuddy-server", a.httpPort))
ok := false
if err == nil {
ok, err = isOK(resp)
}
if ok {
return nil
}
if time.Since(start) > readyCheckTimeout {
errMsg := ""
if err == nil {
errMsg = fmt.Sprintf("/readyz status: %d", resp.StatusCode)
} else {
errMsg = fmt.Sprintf("/readyz err: %s", err)
}
return fmt.Errorf("app failed to start within %s: %s", readyCheckTimeout, errMsg)
}
time.Sleep(readyCheckPollInterval)
}
}

func isOK(resp *http.Response) (bool, error) {
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return false, err
}
return string(body) == "OK", nil
}
7 changes: 6 additions & 1 deletion server/testutil/bazel/bazel.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ import (
context "context"
)

const (
// Path to the bazel binary. Must match the path in the build rule.
bazelPath = "server/testutil/bazel/bazel-3.7.0"
)

var (
invocationIDRegexp = regexp.MustCompile("http://localhost:8080/invocation/([[:graph:]]+)")
)
Expand All @@ -28,7 +33,7 @@ type InvocationResult struct {

// Invoke the bazel CLI from within the given workspace dir.
func Invoke(ctx context.Context, t *testing.T, workspaceDir string, subCommand string, args ...string) *InvocationResult {
bazelBinaryPath, err := bazelgo.Runfile("server/testutil/bazel/bazel-3.7.0")
bazelBinaryPath, err := bazelgo.Runfile(bazelPath)
if err != nil {
return &InvocationResult{Error: err}
}
Expand Down
3 changes: 1 addition & 2 deletions server/testutil/buildbuddy/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ go_library(
importpath = "github.com/buildbuddy-io/buildbuddy/server/testutil/buildbuddy",
visibility = ["//visibility:public"],
deps = [
"@com_github_phayes_freeport//:go_default_library",
"@io_bazel_rules_go//go/tools/bazel:go_default_library",
"//server/testutil/app:go_default_library",
],
)
Loading

0 comments on commit 22f5504

Please sign in to comment.