mirror of https://github.com/coder/coder.git
chore: add tests for app ID copy in app healths (#12088)
This commit is contained in:
parent
06254a167f
commit
2fc3064653
|
@ -197,9 +197,10 @@ type FakeAgentAPI struct {
|
||||||
t testing.TB
|
t testing.TB
|
||||||
logger slog.Logger
|
logger slog.Logger
|
||||||
|
|
||||||
manifest *agentproto.Manifest
|
manifest *agentproto.Manifest
|
||||||
startupCh chan *agentproto.Startup
|
startupCh chan *agentproto.Startup
|
||||||
statsCh chan *agentproto.Stats
|
statsCh chan *agentproto.Stats
|
||||||
|
appHealthCh chan *agentproto.BatchUpdateAppHealthRequest
|
||||||
|
|
||||||
getServiceBannerFunc func() (codersdk.ServiceBannerConfig, error)
|
getServiceBannerFunc func() (codersdk.ServiceBannerConfig, error)
|
||||||
}
|
}
|
||||||
|
@ -244,9 +245,14 @@ func (*FakeAgentAPI) UpdateLifecycle(context.Context, *agentproto.UpdateLifecycl
|
||||||
|
|
||||||
func (f *FakeAgentAPI) BatchUpdateAppHealths(ctx context.Context, req *agentproto.BatchUpdateAppHealthRequest) (*agentproto.BatchUpdateAppHealthResponse, error) {
|
func (f *FakeAgentAPI) BatchUpdateAppHealths(ctx context.Context, req *agentproto.BatchUpdateAppHealthRequest) (*agentproto.BatchUpdateAppHealthResponse, error) {
|
||||||
f.logger.Debug(ctx, "batch update app health", slog.F("req", req))
|
f.logger.Debug(ctx, "batch update app health", slog.F("req", req))
|
||||||
|
f.appHealthCh <- req
|
||||||
return &agentproto.BatchUpdateAppHealthResponse{}, nil
|
return &agentproto.BatchUpdateAppHealthResponse{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *FakeAgentAPI) AppHealthCh() <-chan *agentproto.BatchUpdateAppHealthRequest {
|
||||||
|
return f.appHealthCh
|
||||||
|
}
|
||||||
|
|
||||||
func (f *FakeAgentAPI) UpdateStartup(_ context.Context, req *agentproto.UpdateStartupRequest) (*agentproto.Startup, error) {
|
func (f *FakeAgentAPI) UpdateStartup(_ context.Context, req *agentproto.UpdateStartupRequest) (*agentproto.Startup, error) {
|
||||||
f.startupCh <- req.GetStartup()
|
f.startupCh <- req.GetStartup()
|
||||||
return req.GetStartup(), nil
|
return req.GetStartup(), nil
|
||||||
|
@ -264,10 +270,11 @@ func (*FakeAgentAPI) BatchCreateLogs(context.Context, *agentproto.BatchCreateLog
|
||||||
|
|
||||||
func NewFakeAgentAPI(t testing.TB, logger slog.Logger, manifest *agentproto.Manifest, statsCh chan *agentproto.Stats) *FakeAgentAPI {
|
func NewFakeAgentAPI(t testing.TB, logger slog.Logger, manifest *agentproto.Manifest, statsCh chan *agentproto.Stats) *FakeAgentAPI {
|
||||||
return &FakeAgentAPI{
|
return &FakeAgentAPI{
|
||||||
t: t,
|
t: t,
|
||||||
logger: logger.Named("FakeAgentAPI"),
|
logger: logger.Named("FakeAgentAPI"),
|
||||||
manifest: manifest,
|
manifest: manifest,
|
||||||
statsCh: statsCh,
|
statsCh: statsCh,
|
||||||
startupCh: make(chan *agentproto.Startup, 100),
|
startupCh: make(chan *agentproto.Startup, 100),
|
||||||
|
appHealthCh: make(chan *agentproto.BatchUpdateAppHealthRequest, 100),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,16 +4,21 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"cdr.dev/slog"
|
"cdr.dev/slog"
|
||||||
"cdr.dev/slog/sloggers/slogtest"
|
"cdr.dev/slog/sloggers/slogtest"
|
||||||
"github.com/coder/coder/v2/agent"
|
"github.com/coder/coder/v2/agent"
|
||||||
|
"github.com/coder/coder/v2/agent/agenttest"
|
||||||
|
"github.com/coder/coder/v2/agent/proto"
|
||||||
"github.com/coder/coder/v2/coderd/httpapi"
|
"github.com/coder/coder/v2/coderd/httpapi"
|
||||||
"github.com/coder/coder/v2/codersdk"
|
"github.com/coder/coder/v2/codersdk"
|
||||||
"github.com/coder/coder/v2/codersdk/agentsdk"
|
"github.com/coder/coder/v2/codersdk/agentsdk"
|
||||||
|
@ -40,12 +45,23 @@ func TestAppHealth_Healthy(t *testing.T) {
|
||||||
},
|
},
|
||||||
Health: codersdk.WorkspaceAppHealthInitializing,
|
Health: codersdk.WorkspaceAppHealthInitializing,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Slug: "app3",
|
||||||
|
Healthcheck: codersdk.Healthcheck{
|
||||||
|
Interval: 2,
|
||||||
|
Threshold: 1,
|
||||||
|
},
|
||||||
|
Health: codersdk.WorkspaceAppHealthInitializing,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
handlers := []http.Handler{
|
handlers := []http.Handler{
|
||||||
nil,
|
nil,
|
||||||
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
httpapi.Write(r.Context(), w, http.StatusOK, nil)
|
httpapi.Write(r.Context(), w, http.StatusOK, nil)
|
||||||
}),
|
}),
|
||||||
|
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
httpapi.Write(r.Context(), w, http.StatusOK, nil)
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
getApps, closeFn := setupAppReporter(ctx, t, apps, handlers)
|
getApps, closeFn := setupAppReporter(ctx, t, apps, handlers)
|
||||||
defer closeFn()
|
defer closeFn()
|
||||||
|
@ -58,7 +74,7 @@ func TestAppHealth_Healthy(t *testing.T) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return apps[1].Health == codersdk.WorkspaceAppHealthHealthy
|
return apps[1].Health == codersdk.WorkspaceAppHealthHealthy && apps[2].Health == codersdk.WorkspaceAppHealthHealthy
|
||||||
}, testutil.WaitLong, testutil.IntervalSlow)
|
}, testutil.WaitLong, testutil.IntervalSlow)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,6 +179,12 @@ func TestAppHealth_NotSpamming(t *testing.T) {
|
||||||
|
|
||||||
func setupAppReporter(ctx context.Context, t *testing.T, apps []codersdk.WorkspaceApp, handlers []http.Handler) (agent.WorkspaceAgentApps, func()) {
|
func setupAppReporter(ctx context.Context, t *testing.T, apps []codersdk.WorkspaceApp, handlers []http.Handler) (agent.WorkspaceAgentApps, func()) {
|
||||||
closers := []func(){}
|
closers := []func(){}
|
||||||
|
for i, app := range apps {
|
||||||
|
if app.ID == uuid.Nil {
|
||||||
|
app.ID = uuid.New()
|
||||||
|
apps[i] = app
|
||||||
|
}
|
||||||
|
}
|
||||||
for i, handler := range handlers {
|
for i, handler := range handlers {
|
||||||
if handler == nil {
|
if handler == nil {
|
||||||
continue
|
continue
|
||||||
|
@ -181,23 +203,43 @@ func setupAppReporter(ctx context.Context, t *testing.T, apps []codersdk.Workspa
|
||||||
var newApps []codersdk.WorkspaceApp
|
var newApps []codersdk.WorkspaceApp
|
||||||
return append(newApps, apps...), nil
|
return append(newApps, apps...), nil
|
||||||
}
|
}
|
||||||
postWorkspaceAgentAppHealth := func(_ context.Context, req agentsdk.PostAppHealthsRequest) error {
|
|
||||||
mu.Lock()
|
// We don't care about manifest or stats in this test since it's not using
|
||||||
for id, health := range req.Healths {
|
// a full agent and these RPCs won't get called.
|
||||||
for i, app := range apps {
|
//
|
||||||
if app.ID != id {
|
// We use a proper fake agent API so we can test the conversion code and the
|
||||||
continue
|
// request code as well. Before we were bypassing these by using a custom
|
||||||
|
// post function.
|
||||||
|
fakeAAPI := agenttest.NewFakeAgentAPI(t, slogtest.Make(t, nil), nil, nil)
|
||||||
|
|
||||||
|
// Process events from the channel and update the health of the apps.
|
||||||
|
go func() {
|
||||||
|
appHealthCh := fakeAAPI.AppHealthCh()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case req := <-appHealthCh:
|
||||||
|
mu.Lock()
|
||||||
|
for _, update := range req.Updates {
|
||||||
|
updateID, err := uuid.FromBytes(update.Id)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
updateHealth := codersdk.WorkspaceAppHealth(strings.ToLower(proto.AppHealth_name[int32(update.Health)]))
|
||||||
|
|
||||||
|
for i, app := range apps {
|
||||||
|
if app.ID != updateID {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
app.Health = updateHealth
|
||||||
|
apps[i] = app
|
||||||
|
}
|
||||||
}
|
}
|
||||||
app.Health = health
|
mu.Unlock()
|
||||||
apps[i] = app
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mu.Unlock()
|
}()
|
||||||
|
|
||||||
return nil
|
go agent.NewWorkspaceAppHealthReporter(slogtest.Make(t, nil).Leveled(slog.LevelDebug), apps, agentsdk.AppHealthPoster(fakeAAPI))(ctx)
|
||||||
}
|
|
||||||
|
|
||||||
go agent.NewWorkspaceAppHealthReporter(slogtest.Make(t, nil).Leveled(slog.LevelDebug), apps, postWorkspaceAgentAppHealth)(ctx)
|
|
||||||
|
|
||||||
return workspaceAgentApps, func() {
|
return workspaceAgentApps, func() {
|
||||||
for _, closeFn := range closers {
|
for _, closeFn := range closers {
|
||||||
|
|
|
@ -226,7 +226,12 @@ type PostAppHealthsRequest struct {
|
||||||
Healths map[uuid.UUID]codersdk.WorkspaceAppHealth
|
Healths map[uuid.UUID]codersdk.WorkspaceAppHealth
|
||||||
}
|
}
|
||||||
|
|
||||||
func AppHealthPoster(aAPI proto.DRPCAgentClient) func(ctx context.Context, req PostAppHealthsRequest) error {
|
// BatchUpdateAppHealthsClient is a partial interface of proto.DRPCAgentClient.
|
||||||
|
type BatchUpdateAppHealthsClient interface {
|
||||||
|
BatchUpdateAppHealths(ctx context.Context, req *proto.BatchUpdateAppHealthRequest) (*proto.BatchUpdateAppHealthResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func AppHealthPoster(aAPI BatchUpdateAppHealthsClient) func(ctx context.Context, req PostAppHealthsRequest) error {
|
||||||
return func(ctx context.Context, req PostAppHealthsRequest) error {
|
return func(ctx context.Context, req PostAppHealthsRequest) error {
|
||||||
pReq, err := ProtoFromAppHealthsRequest(req)
|
pReq, err := ProtoFromAppHealthsRequest(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -287,6 +287,8 @@ func ProtoFromAppHealthsRequest(req PostAppHealthsRequest) (*proto.BatchUpdateAp
|
||||||
return nil, xerrors.Errorf("unknown app health: %s", h)
|
return nil, xerrors.Errorf("unknown app health: %s", h)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy the ID, otherwise all updates will have the same ID (the last
|
||||||
|
// one in the list).
|
||||||
var idCopy uuid.UUID
|
var idCopy uuid.UUID
|
||||||
copy(idCopy[:], id[:])
|
copy(idCopy[:], id[:])
|
||||||
pReq.Updates = append(pReq.Updates, &proto.BatchUpdateAppHealthRequest_HealthUpdate{
|
pReq.Updates = append(pReq.Updates, &proto.BatchUpdateAppHealthRequest_HealthUpdate{
|
||||||
|
|
Loading…
Reference in New Issue