diff --git a/pkg/cmd/agent-task/capi/client.go b/pkg/cmd/agent-task/capi/client.go index 45c214ea2..6d35f11c2 100644 --- a/pkg/cmd/agent-task/capi/client.go +++ b/pkg/cmd/agent-task/capi/client.go @@ -20,6 +20,7 @@ type CapiClient interface { CreateJob(ctx context.Context, owner, repo, problemStatement, baseBranch string) (*Job, error) GetJob(ctx context.Context, owner, repo, jobID string) (*Job, error) GetSession(ctx context.Context, id string) (*Session, error) + GetSessionLogs(ctx context.Context, id string) ([]byte, error) ListSessionsByResourceID(ctx context.Context, resourceType string, resourceID int64, limit int) ([]*Session, error) GetPullRequestDatabaseID(ctx context.Context, hostname string, owner string, repo string, number int) (int64, string, error) } diff --git a/pkg/cmd/agent-task/capi/client_mock.go b/pkg/cmd/agent-task/capi/client_mock.go index 85b757943..fa59ffab7 100644 --- a/pkg/cmd/agent-task/capi/client_mock.go +++ b/pkg/cmd/agent-task/capi/client_mock.go @@ -30,6 +30,9 @@ var _ CapiClient = &CapiClientMock{} // GetSessionFunc: func(ctx context.Context, id string) (*Session, error) { // panic("mock out the GetSession method") // }, +// GetSessionLogsFunc: func(ctx context.Context, id string) ([]byte, error) { +// panic("mock out the GetSessionLogs method") +// }, // ListSessionsByResourceIDFunc: func(ctx context.Context, resourceType string, resourceID int64, limit int) ([]*Session, error) { // panic("mock out the ListSessionsByResourceID method") // }, @@ -58,6 +61,9 @@ type CapiClientMock struct { // GetSessionFunc mocks the GetSession method. GetSessionFunc func(ctx context.Context, id string) (*Session, error) + // GetSessionLogsFunc mocks the GetSessionLogs method. + GetSessionLogsFunc func(ctx context.Context, id string) ([]byte, error) + // ListSessionsByResourceIDFunc mocks the ListSessionsByResourceID method. ListSessionsByResourceIDFunc func(ctx context.Context, resourceType string, resourceID int64, limit int) ([]*Session, error) @@ -113,6 +119,13 @@ type CapiClientMock struct { // ID is the id argument value. ID string } + // GetSessionLogs holds details about calls to the GetSessionLogs method. + GetSessionLogs []struct { + // Ctx is the ctx argument value. + Ctx context.Context + // ID is the id argument value. + ID string + } // ListSessionsByResourceID holds details about calls to the ListSessionsByResourceID method. ListSessionsByResourceID []struct { // Ctx is the ctx argument value. @@ -147,6 +160,7 @@ type CapiClientMock struct { lockGetJob sync.RWMutex lockGetPullRequestDatabaseID sync.RWMutex lockGetSession sync.RWMutex + lockGetSessionLogs sync.RWMutex lockListSessionsByResourceID sync.RWMutex lockListSessionsForRepo sync.RWMutex lockListSessionsForViewer sync.RWMutex @@ -328,6 +342,42 @@ func (mock *CapiClientMock) GetSessionCalls() []struct { return calls } +// GetSessionLogs calls GetSessionLogsFunc. +func (mock *CapiClientMock) GetSessionLogs(ctx context.Context, id string) ([]byte, error) { + if mock.GetSessionLogsFunc == nil { + panic("CapiClientMock.GetSessionLogsFunc: method is nil but CapiClient.GetSessionLogs was just called") + } + callInfo := struct { + Ctx context.Context + ID string + }{ + Ctx: ctx, + ID: id, + } + mock.lockGetSessionLogs.Lock() + mock.calls.GetSessionLogs = append(mock.calls.GetSessionLogs, callInfo) + mock.lockGetSessionLogs.Unlock() + return mock.GetSessionLogsFunc(ctx, id) +} + +// GetSessionLogsCalls gets all the calls that were made to GetSessionLogs. +// Check the length with: +// +// len(mockedCapiClient.GetSessionLogsCalls()) +func (mock *CapiClientMock) GetSessionLogsCalls() []struct { + Ctx context.Context + ID string +} { + var calls []struct { + Ctx context.Context + ID string + } + mock.lockGetSessionLogs.RLock() + calls = mock.calls.GetSessionLogs + mock.lockGetSessionLogs.RUnlock() + return calls +} + // ListSessionsByResourceID calls ListSessionsByResourceIDFunc. func (mock *CapiClientMock) ListSessionsByResourceID(ctx context.Context, resourceType string, resourceID int64, limit int) ([]*Session, error) { if mock.ListSessionsByResourceIDFunc == nil { diff --git a/pkg/cmd/agent-task/capi/sessions.go b/pkg/cmd/agent-task/capi/sessions.go index aa7deabaf..0c9de2722 100644 --- a/pkg/cmd/agent-task/capi/sessions.go +++ b/pkg/cmd/agent-task/capi/sessions.go @@ -7,6 +7,7 @@ import ( "encoding/json" "errors" "fmt" + "io" "net/http" "net/url" "slices" @@ -240,6 +241,35 @@ func (c *CAPIClient) GetSession(ctx context.Context, id string) (*Session, error return sessions[0], nil } +// GetSession retrieves logs of an agent session identified by ID. +func (c *CAPIClient) GetSessionLogs(ctx context.Context, id string) ([]byte, error) { + if id == "" { + return nil, fmt.Errorf("missing session ID") + } + + url := fmt.Sprintf("%s/agents/sessions/%s/logs", baseCAPIURL, url.PathEscape(id)) + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, http.NoBody) + if err != nil { + return nil, err + } + + res, err := c.httpClient.Do(req) + if err != nil { + return nil, err + } + + defer res.Body.Close() + if res.StatusCode != http.StatusOK { + if res.StatusCode == http.StatusNotFound { + return nil, ErrSessionNotFound + } + return nil, fmt.Errorf("failed to get session: %s", res.Status) + } + + return io.ReadAll(res.Body) +} + // ListSessionsByResourceID retrieves sessions associated with the given resource type and ID. func (c *CAPIClient) ListSessionsByResourceID(ctx context.Context, resourceType string, resourceID int64, limit int) ([]*Session, error) { if resourceType == "" || resourceID == 0 {