diff --git a/dev/build-tracker/BUILD.bazel b/dev/build-tracker/BUILD.bazel index 41a5793da3d..5421f7c0099 100644 --- a/dev/build-tracker/BUILD.bazel +++ b/dev/build-tracker/BUILD.bazel @@ -1,16 +1,27 @@ load("//dev:go_defs.bzl", "go_test") load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") +load("//dev:oci_defs.bzl", "image_repository", "oci_image", "oci_push", "oci_tarball") +load("@rules_pkg//:pkg.bzl", "pkg_tar") +load("@container_structure_test//:defs.bzl", "container_structure_test") +load("//dev:msp_delivery.bzl", "msp_delivery") go_library( name = "build-tracker_lib", - srcs = ["main.go"], + srcs = [ + "background.go", + "main.go", + ], importpath = "github.com/sourcegraph/sourcegraph/dev/build-tracker", visibility = ["//visibility:private"], deps = [ "//dev/build-tracker/build", "//dev/build-tracker/config", "//dev/build-tracker/notify", + "//internal/goroutine", + "//internal/version", + "//lib/background", "//lib/errors", + "//lib/managedservicesplatform/runtime", "@com_github_gorilla_mux//:mux", "@com_github_sourcegraph_log//:log", ], @@ -36,9 +47,59 @@ go_test( "//dev/build-tracker/config", "//dev/build-tracker/notify", "//dev/team", + "//internal/goroutine", "@com_github_buildkite_go_buildkite_v3//buildkite", "@com_github_gorilla_mux//:mux", "@com_github_sourcegraph_log//logtest", "@com_github_stretchr_testify//require", ], ) + +pkg_tar( + name = "tar_build-tracker", + srcs = [":build-tracker"], +) + +oci_image( + name = "image", + base = "@wolfi_base", + entrypoint = [ + "/sbin/tini", + "--", + "/build-tracker", + ], + tars = [":tar_build-tracker"], + user = "sourcegraph", +) + +oci_tarball( + name = "image_tarball", + image = ":image", + repo_tags = ["build-tracker:candidate"], +) + +container_structure_test( + name = "image_test", + timeout = "short", + configs = ["image_test.yaml"], + driver = "docker", + image = ":image", + tags = [ + "exclusive", + "requires-network", + ], +) + +oci_push( + name = "candidate_push", + image = ":image", + repository = image_repository("build-tracker"), +) + +# automatic deployment not enabled for initial testing +# msp_delivery( +# name = "msp_deploy", +# gcp_project = "build-tracker-prod-59bf", +# msp_service_id = "build-tracker", +# repository = "us.gcr.io/sourcegraph-dev/build-tracker", +# ) diff --git a/dev/build-tracker/Dockerfile b/dev/build-tracker/Dockerfile deleted file mode 100644 index 222fff62eb3..00000000000 --- a/dev/build-tracker/Dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -FROM golang:1.19.8-alpine@sha256:841c160ed35923d96c95c52403c4e6db5decd9cbce034aa851e412ade5d4b74f AS build-tracker-build - -ENV GO111MODULE on -ENV GOARCH amd64 -ENV GOOS linux - -COPY . /repo - -WORKDIR /repo/dev/build-tracker - -RUN go build -o /build-tracker . - -FROM sourcegraph/alpine-3.14:213466_2023-04-17_5.0-bdda34a71619@sha256:6354a4ff578b685e36c8fbde81f62125ae0011b047fb2cc22d1b0de616b3c59a AS build-tracker - -RUN apk --no-cache add tzdata -COPY --from=build-tracker-build /build-tracker /usr/local/bin/build-tracker -ENTRYPOINT ["build-tracker"] diff --git a/dev/build-tracker/background.go b/dev/build-tracker/background.go new file mode 100644 index 00000000000..856fe43f909 --- /dev/null +++ b/dev/build-tracker/background.go @@ -0,0 +1,29 @@ +package main + +import ( + "context" + "time" + + "github.com/sourcegraph/log" + + "github.com/sourcegraph/sourcegraph/dev/build-tracker/build" + "github.com/sourcegraph/sourcegraph/internal/goroutine" +) + +func deleteOldBuilds(logger log.Logger, store *build.Store, every, window time.Duration) goroutine.BackgroundRoutine { + return goroutine.NewPeriodicGoroutine(context.Background(), goroutine.HandlerFunc(func(ctx context.Context) error { + oldBuilds := make([]int, 0) + now := time.Now() + for _, b := range store.FinishedBuilds() { + finishedAt := *b.FinishedAt + delta := now.Sub(finishedAt.Time) + if delta >= window { + logger.Debug("build past age window", log.Int("buildNumber", *b.Number), log.Time("FinishedAt", finishedAt.Time), log.Duration("window", window)) + oldBuilds = append(oldBuilds, *b.Number) + } + } + logger.Info("deleting old builds", log.Int("oldBuildCount", len(oldBuilds))) + store.DelByBuildNumber(oldBuilds...) + return nil + }), goroutine.WithInterval(every)) +} diff --git a/dev/build-tracker/build.sh b/dev/build-tracker/build.sh deleted file mode 100755 index 12154ea1d55..00000000000 --- a/dev/build-tracker/build.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash - -# This script builds the build-tracker docker image. - -cd "$(dirname "${BASH_SOURCE[0]}")/../.." -set -eu - -IMAGE="us-central1-docker.pkg.dev/sourcegraph-ci/build-tracker/build-tracker" - -echo "--- docker build build-tracker $(pwd)" -docker build -f dev/build-tracker/Dockerfile -t "$IMAGE" "$(pwd)" \ - -#docker push $IMAGE diff --git a/dev/build-tracker/config/BUILD.bazel b/dev/build-tracker/config/BUILD.bazel index e8f88ec85fa..8c4add2c30f 100644 --- a/dev/build-tracker/config/BUILD.bazel +++ b/dev/build-tracker/config/BUILD.bazel @@ -5,5 +5,5 @@ go_library( srcs = ["config.go"], importpath = "github.com/sourcegraph/sourcegraph/dev/build-tracker/config", visibility = ["//visibility:public"], - deps = ["//lib/errors"], + deps = ["//lib/managedservicesplatform/runtime"], ) diff --git a/dev/build-tracker/config/config.go b/dev/build-tracker/config/config.go index 6683df7f7b6..d3435823f39 100644 --- a/dev/build-tracker/config/config.go +++ b/dev/build-tracker/config/config.go @@ -1,82 +1,24 @@ package config -import ( - "os" - "strconv" - - "github.com/sourcegraph/sourcegraph/lib/errors" -) +import "github.com/sourcegraph/sourcegraph/lib/managedservicesplatform/runtime" const DefaultChannel = "#william-buildchecker-webhook-test" type Config struct { BuildkiteToken string SlackToken string - GithubToken string SlackChannel string Production bool DebugPassword string } -func NewFromEnv() (*Config, error) { - var c Config - - err := envVar("BUILDKITE_WEBHOOK_TOKEN", &c.BuildkiteToken) - if err != nil { - return nil, err - } - err = envVar("SLACK_TOKEN", &c.SlackToken) - if err != nil { - return nil, err - } - err = envVar("GITHUB_TOKEN", &c.GithubToken) - if err != nil { - return nil, err - } - - err = envVar("SLACK_CHANNEL", &c.SlackChannel) - if err != nil { - c.SlackChannel = DefaultChannel - } - - err = envVar("BUILDTRACKER_PRODUCTION", &c.Production) - if err != nil { - c.Production = false - } +func (c *Config) Load(env *runtime.Env) { + c.BuildkiteToken = env.Get("BUILDKITE_WEBHOOK_TOKEN", "", "") + c.SlackToken = env.Get("SLACK_TOKEN", "", "") + c.SlackChannel = env.Get("SLACK_CHANNEL", DefaultChannel, "") + c.Production = env.GetBool("BUILDTRACKER_PRODUCTION", "false", "") if c.Production { - _ = envVar("BUILDTRACKER_DEBUG_PASSWORD", &c.DebugPassword) - if c.DebugPassword == "" { - return nil, errors.New("BUILDTRACKER_DEBUG_PASSWORD is required when BUILDTRACKER_PRODUCTION is true") - } + c.DebugPassword = env.Get("BUIDLTRACKER_DEBUG_PASSWORD", "", "") } - - return &c, nil -} - -func envVar[T any](name string, target *T) error { - value, exists := os.LookupEnv(name) - if !exists { - return errors.Newf("%s not found in environment", name) - } - - switch p := any(target).(type) { - case *bool: - { - v, err := strconv.ParseBool(value) - if err != nil { - return err - } - - *p = v - } - case *string: - { - *p = value - } - default: - panic(errors.Newf("unsuporrted target type %T", target)) - } - - return nil } diff --git a/dev/build-tracker/image_test.yaml b/dev/build-tracker/image_test.yaml new file mode 100644 index 00000000000..b440111b22e --- /dev/null +++ b/dev/build-tracker/image_test.yaml @@ -0,0 +1,15 @@ +schemaVersion: "2.0.0" + +commandTests: + - name: "binary is runnable" + command: "/build-tracker" + envVars: + - key: "SANITY_CHECK" + value: "true" + + - name: "not running as root" + command: "/usr/bin/id" + args: + - -u + excludedOutput: ["^0"] + exitCode: 0 diff --git a/dev/build-tracker/integration_test.go b/dev/build-tracker/integration_test.go index fbe59868b74..9b4802f339a 100644 --- a/dev/build-tracker/integration_test.go +++ b/dev/build-tracker/integration_test.go @@ -3,6 +3,7 @@ package main import ( "flag" "fmt" + "os" "testing" "github.com/buildkite/go-buildkite/v3/buildkite" @@ -15,8 +16,10 @@ import ( "github.com/sourcegraph/sourcegraph/dev/team" ) -var RunSlackIntegrationTest = flag.Bool("RunSlackIntegrationTest", false, "Run Slack integration tests") -var RunGitHubIntegrationTest = flag.Bool("RunGitHubIntegrationTest", false, "Run Github integration tests") +var ( + RunSlackIntegrationTest = flag.Bool("RunSlackIntegrationTest", false, "Run Slack integration tests") + RunGitHubIntegrationTest = flag.Bool("RunGitHubIntegrationTest", false, "Run Github integration tests") +) type TestJobLine struct { title string @@ -78,14 +81,9 @@ func TestLargeAmountOfFailures(t *testing.T) { } logger := logtest.NoOp(t) - conf, err := config.NewFromEnv() - if err != nil { - t.Fatal(err) - } + client := notify.NewClient(logger, os.Getenv("SLACK_TOKEN"), config.DefaultChannel) - client := notify.NewClient(logger, conf.SlackToken, conf.GithubToken, config.DefaultChannel) - - err = client.Send(info) + err := client.Send(info) if err != nil { t.Fatalf("failed to send build: %s", err) } @@ -120,14 +118,9 @@ func TestGetTeammateFromBuild(t *testing.T) { } logger := logtest.NoOp(t) - conf, err := config.NewFromEnv() - if err != nil { - t.Fatal(err) - } - conf.SlackChannel = config.DefaultChannel t.Run("with nil author, commit author is still retrieved", func(t *testing.T) { - client := notify.NewClient(logger, conf.SlackToken, conf.GithubToken, conf.SlackChannel) + client := notify.NewClient(logger, os.Getenv("SLACK_TOKEN"), config.DefaultChannel) num := 160000 commit := "ca7c44f79984ff8d645b580bfaaf08ce9a37a05d" @@ -141,7 +134,7 @@ func TestGetTeammateFromBuild(t *testing.T) { Number: &num, Commit: &commit, }, - Pipeline: &build.Pipeline{buildkite.Pipeline{ + Pipeline: &build.Pipeline{Pipeline: buildkite.Pipeline{ Name: &pipelineID, }}, Steps: map[string]*build.Step{}, @@ -153,7 +146,7 @@ func TestGetTeammateFromBuild(t *testing.T) { require.Equal(t, teammate.Name, "Leo Papaloizos") }) t.Run("commit author preferred over build author", func(t *testing.T) { - client := notify.NewClient(logger, conf.SlackToken, conf.GithubToken, conf.SlackChannel) + client := notify.NewClient(logger, os.Getenv("SLACK_TOKEN"), config.DefaultChannel) num := 160000 commit := "78926a5b3b836a8a104a5d5adf891e5626b1e405" @@ -171,7 +164,7 @@ func TestGetTeammateFromBuild(t *testing.T) { Email: "william.bezuidenhout@sourcegraph.com", }, }, - Pipeline: &build.Pipeline{buildkite.Pipeline{ + Pipeline: &build.Pipeline{Pipeline: buildkite.Pipeline{ Name: &pipelineID, }}, Steps: map[string]*build.Step{}, @@ -182,7 +175,7 @@ func TestGetTeammateFromBuild(t *testing.T) { require.Equal(t, teammate.Name, "Ryan Slade") }) t.Run("retrieving teammate for build populates cache", func(t *testing.T) { - client := notify.NewClient(logger, conf.SlackToken, conf.GithubToken, conf.SlackChannel) + client := notify.NewClient(logger, os.Getenv("SLACK_TOKEN"), config.DefaultChannel) num := 160000 commit := "78926a5b3b836a8a104a5d5adf891e5626b1e405" @@ -200,7 +193,7 @@ func TestGetTeammateFromBuild(t *testing.T) { Email: "william.bezuidenhout@sourcegraph.com", }, }, - Pipeline: &build.Pipeline{buildkite.Pipeline{ + Pipeline: &build.Pipeline{Pipeline: buildkite.Pipeline{ Name: &pipelineID, }}, Steps: map[string]*build.Step{}, @@ -219,12 +212,7 @@ func TestSlackNotification(t *testing.T) { } logger := logtest.NoOp(t) - conf, err := config.NewFromEnv() - if err != nil { - t.Fatal(err) - } - - client := notify.NewClient(logger, conf.SlackToken, conf.GithubToken, config.DefaultChannel) + client := notify.NewClient(logger, os.Getenv("SLACK_TOKEN"), config.DefaultChannel) // Each child test needs to increment this number, otherwise notifications will be overwritten buildNumber := 160000 @@ -442,12 +430,13 @@ func TestServerNotify(t *testing.T) { } logger := logtest.NoOp(t) - conf, err := config.NewFromEnv() - if err != nil { - t.Fatal(err) + conf := config.Config{ + BuildkiteToken: os.Getenv("BUILDKITE_WEBHOOK_TOKEN"), + SlackToken: os.Getenv("SLACK_TOKEN"), + SlackChannel: os.Getenv("SLACK_CHANNEL"), } - server := NewServer(logger, *conf) + server := NewServer(":8080", logger, conf) num := 160000 url := "http://www.google.com" @@ -474,7 +463,7 @@ func TestServerNotify(t *testing.T) { URL: &url, Commit: &commit, }, - Pipeline: &build.Pipeline{buildkite.Pipeline{ + Pipeline: &build.Pipeline{Pipeline: buildkite.Pipeline{ Name: &pipelineID, }}, Steps: map[string]*build.Step{ @@ -483,7 +472,7 @@ func TestServerNotify(t *testing.T) { } // post a new notification - err = server.notifyIfFailed(build) + err := server.notifyIfFailed(build) if err != nil { t.Fatalf("failed to send slack notification: %v", err) } diff --git a/dev/build-tracker/main.go b/dev/build-tracker/main.go index 05802644cd5..984284b15db 100644 --- a/dev/build-tracker/main.go +++ b/dev/build-tracker/main.go @@ -1,7 +1,9 @@ package main import ( + "context" "encoding/json" + "fmt" "io" "net/http" "strconv" @@ -14,12 +16,17 @@ import ( "github.com/sourcegraph/sourcegraph/dev/build-tracker/build" "github.com/sourcegraph/sourcegraph/dev/build-tracker/config" "github.com/sourcegraph/sourcegraph/dev/build-tracker/notify" + "github.com/sourcegraph/sourcegraph/internal/version" + "github.com/sourcegraph/sourcegraph/lib/background" "github.com/sourcegraph/sourcegraph/lib/errors" + "github.com/sourcegraph/sourcegraph/lib/managedservicesplatform/runtime" ) -var ErrInvalidToken = errors.New("buildkite token is invalid") -var ErrInvalidHeader = errors.New("Header of request is invalid") -var ErrUnwantedEvent = errors.New("Unwanted event received") +var ( + ErrInvalidToken = errors.New("buildkite token is invalid") + ErrInvalidHeader = errors.New("Header of request is invalid") + ErrUnwantedEvent = errors.New("Unwanted event received") +) var nowFunc = time.Now @@ -41,13 +48,13 @@ type Server struct { } // NewServer creatse a new server to listen for Buildkite webhook events. -func NewServer(logger log.Logger, c config.Config) *Server { +func NewServer(addr string, logger log.Logger, c config.Config) *Server { logger = logger.Scoped("server") server := &Server{ logger: logger, store: build.NewBuildStore(logger), config: &c, - notifyClient: notify.NewClient(logger, c.SlackToken, c.GithubToken, c.SlackChannel), + notifyClient: notify.NewClient(logger, c.SlackToken, c.SlackChannel), } // Register routes the the server will be responding too @@ -60,12 +67,28 @@ func NewServer(logger log.Logger, c config.Config) *Server { server.http = &http.Server{ Handler: r, - Addr: ":8080", + Addr: addr, } return server } +func (s *Server) Start() { + if err := s.http.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { + s.logger.Error("error stopping server", log.Error(err)) + } +} + +func (s *Server) Stop() { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + if err := s.http.Shutdown(ctx); err != nil { + s.logger.Error("error shutting down server", log.Error(err)) + } else { + s.logger.Info("server stopped") + } +} + func (s *Server) handleGetBuild(w http.ResponseWriter, req *http.Request) { if s.config.Production { user, pass, ok := req.BasicAuth() @@ -192,40 +215,6 @@ func (s *Server) notifyIfFailed(b *build.Build) error { return nil } -func (s *Server) deleteOldBuilds(window time.Duration) { - oldBuilds := make([]int, 0) - now := nowFunc() - for _, b := range s.store.FinishedBuilds() { - finishedAt := *b.FinishedAt - delta := now.Sub(finishedAt.Time) - if delta >= window { - s.logger.Debug("build past age window", log.Int("buildNumber", *b.Number), log.Time("FinishedAt", finishedAt.Time), log.Duration("window", window)) - oldBuilds = append(oldBuilds, *b.Number) - } - } - s.logger.Info("deleting old builds", log.Int("oldBuildCount", len(oldBuilds))) - s.store.DelByBuildNumber(oldBuilds...) -} - -func (s *Server) startCleaner(every, window time.Duration) func() { - ticker := time.NewTicker(every) - done := make(chan interface{}) - - go func() { - for { - select { - case <-ticker.C: - s.deleteOldBuilds(window) - case <-done: - ticker.Stop() - return - } - } - }() - - return func() { done <- nil } -} - // processEvent processes a BuildEvent received from Buildkite. If the event is for a `build.finished` event we get the // full build which includes all recorded jobs for the build and send a notification. // processEvent delegates the decision to actually send a notifcation @@ -285,33 +274,21 @@ func determineBuildStatusNotification(logger log.Logger, b *build.Build) *notify return &info } -// Serve starts the http server and listens for buildkite build events to be sent on the route "/buildkite" func main() { - sync := log.Init(log.Resource{ - Name: "BuildTracker", - Namespace: "CI", - }) - defer sync.Sync() - - logger := log.Scoped("BuildTracker") - - serverConf, err := config.NewFromEnv() - if err != nil { - logger.Fatal("failed to get config from env", log.Error(err)) - } - logger.Info("config loaded from environment", log.Object("config", log.String("SlackChannel", serverConf.SlackChannel), log.Bool("Production", serverConf.Production))) - server := NewServer(logger, *serverConf) - - stopFn := server.startCleaner(CleanUpInterval, BuildExpiryWindow) - defer stopFn() - - if server.config.Production { - server.logger.Info("server is in production mode!") - } else { - server.logger.Info("server is in development mode!") - } - - if err := server.http.ListenAndServe(); err != nil { - logger.Fatal("server exited with error", log.Error(err)) - } + runtime.Start[config.Config](Service{}) } + +type Service struct{} + +// Initialize implements runtime.Service. +func (s Service) Initialize(ctx context.Context, logger log.Logger, contract runtime.Contract, config config.Config) (background.Routine, error) { + logger.Info("config loaded from environment", log.Object("config", log.String("SlackChannel", config.SlackChannel), log.Bool("Production", config.Production))) + + return background.CombinedRoutine{ + NewServer(fmt.Sprintf(":%d", contract.Port), logger, config), + deleteOldBuilds(logger, nil, CleanUpInterval, BuildExpiryWindow), + }, nil +} + +func (s Service) Name() string { return "build-tracker" } +func (s Service) Version() string { return version.Version() } diff --git a/dev/build-tracker/notify/slack.go b/dev/build-tracker/notify/slack.go index f13cd5da0d7..f0e982c08e7 100644 --- a/dev/build-tracker/notify/slack.go +++ b/dev/build-tracker/notify/slack.go @@ -18,18 +18,6 @@ import ( const StepShowLimit = 5 -type cacheItem[T any] struct { - Value T - Timestamp time.Time -} - -func newCacheItem[T any](value T) *cacheItem[T] { - return &cacheItem[T]{ - Value: value, - Timestamp: time.Now(), - } -} - type NotificationClient interface { Send(info *BuildNotification) error GetNotification(buildNumber int) *SlackNotification @@ -101,7 +89,7 @@ func NewSlackNotification(id, channel string, info *BuildNotification, author st } } -func NewClient(logger log.Logger, slackToken, githubToken, channel string) *Client { +func NewClient(logger log.Logger, slackToken, channel string) *Client { debug := os.Getenv("BUILD_TRACKER_SLACK_DEBUG") == "1" slackClient := slack.New(slackToken, slack.OptionDebug(debug)) diff --git a/dev/build-tracker/server_test.go b/dev/build-tracker/server_test.go index a0e0934febe..b5f1646f0d2 100644 --- a/dev/build-tracker/server_test.go +++ b/dev/build-tracker/server_test.go @@ -1,6 +1,7 @@ package main import ( + "context" "encoding/json" "net/http" "net/http/httptest" @@ -15,6 +16,7 @@ import ( "github.com/sourcegraph/sourcegraph/dev/build-tracker/build" "github.com/sourcegraph/sourcegraph/dev/build-tracker/config" "github.com/sourcegraph/sourcegraph/dev/build-tracker/notify" + "github.com/sourcegraph/sourcegraph/internal/goroutine" ) func TestGetBuild(t *testing.T) { @@ -23,7 +25,7 @@ func TestGetBuild(t *testing.T) { req, _ := http.NewRequest(http.MethodGet, "/-/debug/1234", nil) req = mux.SetURLVars(req, map[string]string{"buildNumber": "1234"}) t.Run("401 Unauthorized when in production mode and incorrect credentials", func(t *testing.T) { - server := NewServer(logger, config.Config{Production: true, DebugPassword: "this is a test"}) + server := NewServer(":8080", logger, config.Config{Production: true, DebugPassword: "this is a test"}) rec := httptest.NewRecorder() server.handleGetBuild(rec, req) @@ -36,7 +38,7 @@ func TestGetBuild(t *testing.T) { }) t.Run("404 for build that does not exist", func(t *testing.T) { - server := NewServer(logger, config.Config{}) + server := NewServer(":8080", logger, config.Config{}) rec := httptest.NewRecorder() server.handleGetBuild(rec, req) @@ -44,7 +46,7 @@ func TestGetBuild(t *testing.T) { }) t.Run("get marshalled json for build", func(t *testing.T) { - server := NewServer(logger, config.Config{}) + server := NewServer(":8080", logger, config.Config{}) rec := httptest.NewRecorder() num := 1234 @@ -97,7 +99,7 @@ func TestGetBuild(t *testing.T) { }) t.Run("200 with valid credentials in production mode", func(t *testing.T) { - server := NewServer(logger, config.Config{Production: true, DebugPassword: "this is a test"}) + server := NewServer(":8080", logger, config.Config{Production: true, DebugPassword: "this is a test"}) rec := httptest.NewRecorder() req.SetBasicAuth("devx", server.config.DebugPassword) @@ -129,7 +131,7 @@ func TestOldBuildsGetDeleted(t *testing.T) { } t.Run("All old builds get removed", func(t *testing.T) { - server := NewServer(logger, config.Config{}) + server := NewServer(":8080", logger, config.Config{}) b := finishedBuild(1, "passed", time.Now().AddDate(-1, 0, 0)) server.store.Set(b) @@ -139,9 +141,10 @@ func TestOldBuildsGetDeleted(t *testing.T) { b = finishedBuild(3, "failed", time.Now().AddDate(0, 0, -1)) server.store.Set(b) - stopFunc := server.startCleaner(10*time.Millisecond, 24*time.Hour) + ctx, cancel := context.WithCancel(context.Background()) + go goroutine.MonitorBackgroundRoutines(ctx, deleteOldBuilds(logger, server.store, 10*time.Millisecond, 24*time.Hour)) time.Sleep(20 * time.Millisecond) - stopFunc() + cancel() builds := server.store.FinishedBuilds() @@ -150,7 +153,7 @@ func TestOldBuildsGetDeleted(t *testing.T) { } }) t.Run("1 build left after old builds are removed", func(t *testing.T) { - server := NewServer(logger, config.Config{}) + server := NewServer(":8080", logger, config.Config{}) b := finishedBuild(1, "canceled", time.Now().AddDate(-1, 0, 0)) server.store.Set(b) @@ -160,9 +163,10 @@ func TestOldBuildsGetDeleted(t *testing.T) { b = finishedBuild(3, "failed", time.Now()) server.store.Set(b) - stopFunc := server.startCleaner(10*time.Millisecond, 24*time.Hour) + ctx, cancel := context.WithCancel(context.Background()) + go goroutine.MonitorBackgroundRoutines(ctx, deleteOldBuilds(logger, server.store, 10*time.Millisecond, 24*time.Hour)) time.Sleep(20 * time.Millisecond) - stopFunc() + cancel() builds := server.store.FinishedBuilds() @@ -170,7 +174,6 @@ func TestOldBuildsGetDeleted(t *testing.T) { t.Errorf("Expected one build to be left over. Got %d, wanted %d", len(builds), 1) } }) - } type MockNotificationClient struct { @@ -231,7 +234,7 @@ func TestProcessEvent(t *testing.T) { return &build.Event{Name: build.EventBuildFinished, Build: buildkite.Build{State: &state, Number: &buildNumber, Pipeline: pipeline}, Job: job.Job} } t.Run("no send notification on unfinished builds", func(t *testing.T) { - server := NewServer(logger, config.Config{}) + server := NewServer(":8080", logger, config.Config{}) mockNotifyClient := &MockNotificationClient{} server.notifyClient = mockNotifyClient buildNumber := 1234 @@ -248,7 +251,7 @@ func TestProcessEvent(t *testing.T) { }) t.Run("failed build sends notification", func(t *testing.T) { - server := NewServer(logger, config.Config{}) + server := NewServer(":8080", logger, config.Config{}) mockNotifyClient := &MockNotificationClient{} server.notifyClient = mockNotifyClient buildNumber := 1234 @@ -264,7 +267,7 @@ func TestProcessEvent(t *testing.T) { }) t.Run("passed build sends notification", func(t *testing.T) { - server := NewServer(logger, config.Config{}) + server := NewServer(":8080", logger, config.Config{}) mockNotifyClient := &MockNotificationClient{} server.notifyClient = mockNotifyClient buildNumber := 1234 @@ -280,7 +283,7 @@ func TestProcessEvent(t *testing.T) { }) t.Run("failed build, then passed build sends fixed notification", func(t *testing.T) { - server := NewServer(logger, config.Config{}) + server := NewServer(":8080", logger, config.Config{}) mockNotifyClient := &MockNotificationClient{} server.notifyClient = mockNotifyClient buildNumber := 1234