Skip to content

Commit 2c6cbf1

Browse files
authored
feat(coderd): use task data model for send/logs (coder#20381)
Updates coder/internal#976
1 parent 1cb2ac6 commit 2c6cbf1

File tree

4 files changed

+220
-279
lines changed

4 files changed

+220
-279
lines changed

cli/exp_task_logs_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import (
2323
func Test_TaskLogs(t *testing.T) {
2424
t.Parallel()
2525

26-
t.Skip("TODO(mafredri): Remove, fixed down-stack!")
26+
t.Skip("TODO(mafredri): Remove, fixed up-stack!")
2727

2828
testMessages := []agentapisdk.Message{
2929
{

cli/exp_task_send_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import (
2222
func Test_TaskSend(t *testing.T) {
2323
t.Parallel()
2424

25-
t.Skip("TODO(mafredri): Remove, fixed down-stack!")
25+
t.Skip("TODO(mafredri): Remove, fixed up-stack!")
2626

2727
t.Run("ByWorkspaceName_WithArgument", func(t *testing.T) {
2828
t.Parallel()

coderd/aitasks.go

Lines changed: 35 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"strings"
1212
"time"
1313

14-
"github.com/go-chi/chi/v5"
1514
"github.com/google/uuid"
1615

1716
"cdr.dev/slog"
@@ -755,15 +754,7 @@ func (api *API) taskDelete(rw http.ResponseWriter, r *http.Request) {
755754
// workspace and validate the sidebar app health.
756755
func (api *API) taskSend(rw http.ResponseWriter, r *http.Request) {
757756
ctx := r.Context()
758-
759-
idStr := chi.URLParam(r, "task")
760-
taskID, err := uuid.Parse(idStr)
761-
if err != nil {
762-
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
763-
Message: fmt.Sprintf("Invalid UUID %q for task ID.", idStr),
764-
})
765-
return
766-
}
757+
task := httpmw.TaskParam(r)
767758

768759
var req codersdk.TaskSendRequest
769760
if !httpapi.Read(ctx, rw, r, &req) {
@@ -776,7 +767,7 @@ func (api *API) taskSend(rw http.ResponseWriter, r *http.Request) {
776767
return
777768
}
778769

779-
if err = api.authAndDoWithTaskSidebarAppClient(r, taskID, func(ctx context.Context, client *http.Client, appURL *url.URL) error {
770+
if err := api.authAndDoWithTaskAppClient(r, task, func(ctx context.Context, client *http.Client, appURL *url.URL) error {
780771
agentAPIClient, err := aiagentapi.NewClient(appURL.String(), aiagentapi.WithHTTPClient(client))
781772
if err != nil {
782773
return httperror.NewResponseError(http.StatusBadGateway, codersdk.Response{
@@ -835,18 +826,10 @@ func (api *API) taskSend(rw http.ResponseWriter, r *http.Request) {
835826
// We enforce ApplicationConnect RBAC on the workspace and validate the sidebar app health.
836827
func (api *API) taskLogs(rw http.ResponseWriter, r *http.Request) {
837828
ctx := r.Context()
838-
839-
idStr := chi.URLParam(r, "task")
840-
taskID, err := uuid.Parse(idStr)
841-
if err != nil {
842-
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
843-
Message: fmt.Sprintf("Invalid UUID %q for task ID.", idStr),
844-
})
845-
return
846-
}
829+
task := httpmw.TaskParam(r)
847830

848831
var out codersdk.TaskLogsResponse
849-
if err := api.authAndDoWithTaskSidebarAppClient(r, taskID, func(ctx context.Context, client *http.Client, appURL *url.URL) error {
832+
if err := api.authAndDoWithTaskAppClient(r, task, func(ctx context.Context, client *http.Client, appURL *url.URL) error {
850833
agentAPIClient, err := aiagentapi.NewClient(appURL.String(), aiagentapi.WithHTTPClient(client))
851834
if err != nil {
852835
return httperror.NewResponseError(http.StatusBadGateway, codersdk.Response{
@@ -894,7 +877,7 @@ func (api *API) taskLogs(rw http.ResponseWriter, r *http.Request) {
894877
httpapi.Write(ctx, rw, http.StatusOK, out)
895878
}
896879

897-
// authAndDoWithTaskSidebarAppClient centralizes the shared logic to:
880+
// authAndDoWithTaskAppClient centralizes the shared logic to:
898881
//
899882
// - Fetch the task workspace
900883
// - Authorize ApplicationConnect on the workspace
@@ -903,15 +886,31 @@ func (api *API) taskLogs(rw http.ResponseWriter, r *http.Request) {
903886
//
904887
// The provided callback receives the context, an HTTP client that dials via the
905888
// agent, and the base app URL (as a value URL) to perform any request.
906-
func (api *API) authAndDoWithTaskSidebarAppClient(
889+
func (api *API) authAndDoWithTaskAppClient(
907890
r *http.Request,
908-
taskID uuid.UUID,
891+
task database.Task,
909892
do func(ctx context.Context, client *http.Client, appURL *url.URL) error,
910893
) error {
911894
ctx := r.Context()
912895

913-
workspaceID := taskID
914-
workspace, err := api.Database.GetWorkspaceByID(ctx, workspaceID)
896+
if task.Status != database.TaskStatusActive {
897+
return httperror.NewResponseError(http.StatusBadRequest, codersdk.Response{
898+
Message: "Task status must be active.",
899+
Detail: fmt.Sprintf("Task status is %q, it must be %q to interact with the task.", task.Status, codersdk.TaskStatusActive),
900+
})
901+
}
902+
if !task.WorkspaceID.Valid {
903+
return httperror.NewResponseError(http.StatusBadRequest, codersdk.Response{
904+
Message: "Task does not have a workspace.",
905+
})
906+
}
907+
if !task.WorkspaceAppID.Valid {
908+
return httperror.NewResponseError(http.StatusBadRequest, codersdk.Response{
909+
Message: "Task does not have a workspace app.",
910+
})
911+
}
912+
913+
workspace, err := api.Database.GetWorkspaceByID(ctx, task.WorkspaceID.UUID)
915914
if err != nil {
916915
if httpapi.Is404Error(err) {
917916
return httperror.ErrResourceNotFound
@@ -927,65 +926,30 @@ func (api *API) authAndDoWithTaskSidebarAppClient(
927926
return httperror.ErrResourceNotFound
928927
}
929928

930-
data, err := api.workspaceData(ctx, []database.Workspace{workspace})
929+
apps, err := api.Database.GetWorkspaceAppsByAgentID(ctx, task.WorkspaceAgentID.UUID)
931930
if err != nil {
932931
return httperror.NewResponseError(http.StatusInternalServerError, codersdk.Response{
933932
Message: "Internal error fetching workspace resources.",
934933
Detail: err.Error(),
935934
})
936935
}
937-
if len(data.builds) == 0 || len(data.templates) == 0 {
938-
return httperror.ErrResourceNotFound
939-
}
940-
build := data.builds[0]
941-
if build.HasAITask == nil || !*build.HasAITask || build.AITaskSidebarAppID == nil || *build.AITaskSidebarAppID == uuid.Nil {
942-
return httperror.NewResponseError(http.StatusBadRequest, codersdk.Response{
943-
Message: "Task is not configured with a sidebar app.",
944-
})
945-
}
946936

947-
// Find the sidebar app details to get the URL and validate app health.
948-
sidebarAppID := *build.AITaskSidebarAppID
949-
agentID, sidebarApp, ok := func() (uuid.UUID, codersdk.WorkspaceApp, bool) {
950-
for _, res := range build.Resources {
951-
for _, agent := range res.Agents {
952-
for _, app := range agent.Apps {
953-
if app.ID == sidebarAppID {
954-
return agent.ID, app, true
955-
}
956-
}
957-
}
937+
var app *database.WorkspaceApp
938+
for _, a := range apps {
939+
if a.ID == task.WorkspaceAppID.UUID {
940+
app = &a
941+
break
958942
}
959-
return uuid.Nil, codersdk.WorkspaceApp{}, false
960-
}()
961-
if !ok {
962-
return httperror.NewResponseError(http.StatusBadRequest, codersdk.Response{
963-
Message: "Task sidebar app not found in latest build.",
964-
})
965-
}
966-
967-
// Return an informative error if the app isn't healthy rather than trying
968-
// and failing.
969-
switch sidebarApp.Health {
970-
case codersdk.WorkspaceAppHealthDisabled:
971-
// No health check, pass through.
972-
case codersdk.WorkspaceAppHealthInitializing:
973-
return httperror.NewResponseError(http.StatusServiceUnavailable, codersdk.Response{
974-
Message: "Task sidebar app is initializing. Try again shortly.",
975-
})
976-
case codersdk.WorkspaceAppHealthUnhealthy:
977-
return httperror.NewResponseError(http.StatusServiceUnavailable, codersdk.Response{
978-
Message: "Task sidebar app is unhealthy.",
979-
})
980943
}
981944

982945
// Build the direct app URL and dial the agent.
983-
if sidebarApp.URL == "" {
946+
appURL := app.Url.String
947+
if appURL == "" {
984948
return httperror.NewResponseError(http.StatusInternalServerError, codersdk.Response{
985949
Message: "Task sidebar app URL is not configured.",
986950
})
987951
}
988-
parsedURL, err := url.Parse(sidebarApp.URL)
952+
parsedURL, err := url.Parse(appURL)
989953
if err != nil {
990954
return httperror.NewResponseError(http.StatusInternalServerError, codersdk.Response{
991955
Message: "Internal error parsing task app URL.",
@@ -1000,7 +964,7 @@ func (api *API) authAndDoWithTaskSidebarAppClient(
1000964

1001965
dialCtx, dialCancel := context.WithTimeout(ctx, time.Second*30)
1002966
defer dialCancel()
1003-
agentConn, release, err := api.agentProvider.AgentConn(dialCtx, agentID)
967+
agentConn, release, err := api.agentProvider.AgentConn(dialCtx, task.WorkspaceAgentID.UUID)
1004968
if err != nil {
1005969
return httperror.NewResponseError(http.StatusBadGateway, codersdk.Response{
1006970
Message: "Failed to reach task app endpoint.",

0 commit comments

Comments
 (0)