Skip to content

Commit 0e9d4d3

Browse files
feat: make run logs recuperation call argo
with the run logs removed from loki, we now have to go through argo to get the active run logs in kubernetes and the finished run logs in seaweed
1 parent c610cdc commit 0e9d4d3

File tree

13 files changed

+175
-110
lines changed

13 files changed

+175
-110
lines changed

api/src/integrationTest/kotlin/com/cosmotech/api/home/run/RunControllerTests.kt

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ import org.springframework.test.context.ActiveProfiles
7676
import org.springframework.test.util.ReflectionTestUtils
7777
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*
7878
import org.springframework.test.web.servlet.result.MockMvcResultHandlers
79+
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.content
7980
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath
8081
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
8182

@@ -345,30 +346,20 @@ class RunControllerTests : ControllerTestBase() {
345346
@WithMockOauth2User
346347
fun get_run_logs() {
347348

348-
val lineOne = "This is a log entry"
349-
val lineTwo = "This is another log entry"
350-
val lineThree = "This is the last log entry"
349+
val logs =
350+
"""This is the first line of a log entry
351+
|This is the second line of a log entry
352+
|This is the third line of a log entry"""
353+
.trimMargin()
351354

352-
every { workflowService.getRunLogs(any()) } returns
353-
RunLogs(
354-
runId = runId,
355-
logs =
356-
mutableListOf(
357-
RunLogsEntry(lineOne),
358-
RunLogsEntry(lineTwo),
359-
RunLogsEntry(lineThree),
360-
))
355+
every { workflowService.getRunningLogs(any()) } returns logs
361356

362357
mvc.perform(
363358
get(
364359
"/organizations/$organizationId/workspaces/$workspaceId/runners/$runnerId}/runs/$runId/logs")
365-
.contentType(MediaType.APPLICATION_JSON)
366-
.accept(MediaType.APPLICATION_JSON))
360+
.accept(MediaType.TEXT_PLAIN))
367361
.andExpect(status().is2xxSuccessful)
368-
.andExpect(jsonPath("$.runId").value(runId))
369-
.andExpect(jsonPath("$.logs[0].line").value(lineOne))
370-
.andExpect(jsonPath("$.logs[1].line").value(lineTwo))
371-
.andExpect(jsonPath("$.logs[2].line").value(lineThree))
362+
.andExpect(content().string(logs))
372363
.andDo(MockMvcResultHandlers.print())
373364
.andDo(
374365
document(

doc/.openapi-generator/FILES

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,6 @@ Models/Run.md
4343
Models/RunContainer.md
4444
Models/RunData.md
4545
Models/RunDataQuery.md
46-
Models/RunLogs.md
47-
Models/RunLogsEntry.md
4846
Models/RunResourceRequested.md
4947
Models/RunState.md
5048
Models/RunStatus.md

doc/Apis/RunApi.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ Get the details of a run
7171

7272
<a name="getRunLogs"></a>
7373
# **getRunLogs**
74-
> RunLogs getRunLogs(organization\_id, workspace\_id, runner\_id, run\_id)
74+
> String getRunLogs(organization\_id, workspace\_id, runner\_id, run\_id)
7575
7676
get the logs for the Run
7777

@@ -86,7 +86,7 @@ get the logs for the Run
8686

8787
### Return type
8888

89-
[**RunLogs**](../Models/RunLogs.md)
89+
**String**
9090

9191
### Authorization
9292

@@ -95,7 +95,7 @@ get the logs for the Run
9595
### HTTP request headers
9696

9797
- **Content-Type**: Not defined
98-
- **Accept**: application/json, application/yaml
98+
- **Accept**: text/plain
9999

100100
<a name="getRunStatus"></a>
101101
# **getRunStatus**

doc/Models/RunLogs.md

Lines changed: 0 additions & 10 deletions
This file was deleted.

doc/Models/RunLogsEntry.md

Lines changed: 0 additions & 9 deletions
This file was deleted.

doc/README.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,6 @@ All URIs are relative to *http://localhost*
168168
- [RunContainer](./Models/RunContainer.md)
169169
- [RunData](./Models/RunData.md)
170170
- [RunDataQuery](./Models/RunDataQuery.md)
171-
- [RunLogs](./Models/RunLogs.md)
172-
- [RunLogsEntry](./Models/RunLogsEntry.md)
173171
- [RunResourceRequested](./Models/RunResourceRequested.md)
174172
- [RunState](./Models/RunState.md)
175173
- [RunStatus](./Models/RunStatus.md)

openapi/plantuml/schemas.plantuml

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -253,15 +253,6 @@ entity RunDataQuery {
253253
* query: String
254254
}
255255

256-
entity RunLogs {
257-
* runId: String
258-
* logs: List<RunLogsEntry>
259-
}
260-
261-
entity RunLogsEntry {
262-
* line: String
263-
}
264-
265256
entity RunResourceRequested {
266257
cpu: Long
267258
memory: Long
@@ -668,7 +659,6 @@ DatasetSecurity -- "0..*" DatasetAccessControl : accessControlList
668659
ContainerResourceSizing -- ContainerResourceSizeInfo : requests
669660
ContainerResourceSizing -- ContainerResourceSizeInfo : limits
670661
SolutionSecurity -- "0..*" SolutionAccessControl : accessControlList
671-
RunLogs -- "0..*" RunLogsEntry : logs
672662
WorkspaceSecurity -- "0..*" WorkspaceAccessControl : accessControlList
673663
WorkspaceCreateRequest -- WorkspaceSolution : solution
674664
WorkspaceCreateRequest -- WorkspaceWebApp : webApp

run/src/integrationTest/kotlin/com/cosmotech/run/service/RunServiceIntegrationTest.kt

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import com.cosmotech.run.config.existTable
2929
import com.cosmotech.run.config.toDataTableName
3030
import com.cosmotech.run.domain.Run
3131
import com.cosmotech.run.domain.RunDataQuery
32+
import com.cosmotech.run.domain.RunStatus
3233
import com.cosmotech.run.domain.SendRunDataRequest
3334
import com.cosmotech.run.workflow.WorkflowService
3435
import com.cosmotech.runner.RunnerApiServiceInterface
@@ -66,19 +67,22 @@ import org.json.JSONObject
6667
import org.junit.jupiter.api.BeforeEach
6768
import org.junit.jupiter.api.Nested
6869
import org.junit.jupiter.api.Test
70+
import org.junit.jupiter.api.assertDoesNotThrow
6971
import org.junit.jupiter.api.assertThrows
7072
import org.junit.jupiter.api.extension.ExtendWith
7173
import org.junit.runner.RunWith
7274
import org.postgresql.util.PSQLException
7375
import org.slf4j.LoggerFactory
7476
import org.springframework.beans.factory.annotation.Autowired
7577
import org.springframework.boot.test.context.SpringBootTest
78+
import org.springframework.http.HttpStatus
7679
import org.springframework.jdbc.core.JdbcTemplate
7780
import org.springframework.jdbc.datasource.DriverManagerDataSource
7881
import org.springframework.test.context.ActiveProfiles
7982
import org.springframework.test.context.junit.jupiter.SpringExtension
8083
import org.springframework.test.context.junit4.SpringRunner
8184
import org.springframework.test.util.ReflectionTestUtils
85+
import org.springframework.web.client.RestClientResponseException
8286

8387
@ActiveProfiles(profiles = ["run-test"])
8488
@ExtendWith(MockKExtension::class)
@@ -104,7 +108,8 @@ class RunServiceIntegrationTest : CsmRunTestBase() {
104108
@Autowired lateinit var solutionApiService: SolutionApiServiceInterface
105109
@Autowired lateinit var workspaceApiService: WorkspaceApiServiceInterface
106110
@SpykBean @Autowired lateinit var runnerApiService: RunnerApiServiceInterface
107-
@Autowired lateinit var runApiService: RunApiServiceInterface
111+
@SpykBean @Autowired lateinit var runServiceImpl: RunServiceImpl
112+
@SpykBean @Autowired lateinit var runApiService: RunApiServiceInterface
108113
@Autowired lateinit var eventPublisher: com.cosmotech.api.events.CsmEventPublisher
109114

110115
@Autowired lateinit var adminRunStorageTemplate: JdbcTemplate
@@ -397,6 +402,83 @@ class RunServiceIntegrationTest : CsmRunTestBase() {
397402
}
398403
}
399404

405+
@Test
406+
fun `test get running logs`() {
407+
runSavedId =
408+
mockStartRun(organizationSaved.id, workspaceSaved.id, runnerSaved.id, solutionSaved.id)
409+
val run = mockk<Run>()
410+
every { runApiService.getRun(any(), any(), any(), any()) } returns run
411+
every { run.id } returns "id"
412+
every { runServiceImpl.getRunStatus(any(), any(), any(), any()) } returns
413+
RunStatus(endTime = null)
414+
every { workflowService.getRunningLogs(any()) } returns
415+
"first line of result" + "|second line of result" + "|third line of result"
416+
every { workflowService.getArchivedLogs(any()) } throws Exception()
417+
418+
assertEquals(
419+
"first line of result" + "|second line of result" + "|third line of result",
420+
runApiService.getRunLogs(
421+
organizationSaved.id, workspaceSaved.id, runnerSaved.id, runSavedId))
422+
}
423+
424+
@Test
425+
fun `test get archived logs`() {
426+
runSavedId =
427+
mockStartRun(organizationSaved.id, workspaceSaved.id, runnerSaved.id, solutionSaved.id)
428+
val run = mockk<Run>()
429+
every { runApiService.getRun(any(), any(), any(), any()) } returns run
430+
every { run.id } returns "id"
431+
every { runServiceImpl.getRunStatus(any(), any(), any(), any()) } returns
432+
RunStatus(endTime = "endTime")
433+
logger.info("should get archived logs")
434+
every { workflowService.getRunningLogs(any()) } throws
435+
RestClientResponseException("message", HttpStatus.NOT_FOUND, "statusTest", null, null, null)
436+
every { workflowService.getArchivedLogs(any()) } returns
437+
"first line of result" + "|second line of result" + "|third line of result"
438+
assertEquals(
439+
"first line of result" + "|second line of result" + "|third line of result",
440+
runApiService.getRunLogs(
441+
organizationSaved.id, workspaceSaved.id, runnerSaved.id, runSavedId))
442+
}
443+
444+
@Test
445+
fun `test should get archived logs after a status change`() {
446+
runSavedId =
447+
mockStartRun(organizationSaved.id, workspaceSaved.id, runnerSaved.id, solutionSaved.id)
448+
val run = mockk<Run>()
449+
every { runApiService.getRun(any(), any(), any(), any()) } returns run
450+
every { run.id } returns "id"
451+
every { runServiceImpl.getRunStatus(any(), any(), any(), any()) } returns
452+
RunStatus(endTime = null)
453+
logger.info("should get logs even with a status change after assertion")
454+
every { workflowService.getRunningLogs(any()) } throws
455+
RestClientResponseException("message", HttpStatus.NOT_FOUND, "statusTest", null, null, null)
456+
every { workflowService.getArchivedLogs(any()) } returns
457+
"first line of result" + "|second line of result" + "|third line of result"
458+
assertDoesNotThrow {
459+
runApiService.getRunLogs(organizationSaved.id, workspaceSaved.id, runnerSaved.id, runSavedId)
460+
}
461+
assertEquals(
462+
"first line of result" + "|second line of result" + "|third line of result",
463+
runApiService.getRunLogs(
464+
organizationSaved.id, workspaceSaved.id, runnerSaved.id, runSavedId))
465+
}
466+
467+
@Test
468+
fun `test should fail after exception other than not_found`() {
469+
runSavedId =
470+
mockStartRun(organizationSaved.id, workspaceSaved.id, runnerSaved.id, solutionSaved.id)
471+
val run = mockk<Run>()
472+
every { runApiService.getRun(any(), any(), any(), any()) } returns run
473+
every { runServiceImpl.getRunStatus(any(), any(), any(), any()) } returns
474+
RunStatus(endTime = null)
475+
logger.info("should throw an error outside of status change error")
476+
every { workflowService.getRunningLogs(any()) } throws Exception()
477+
assertThrows<Exception> {
478+
runApiService.getRunLogs(organizationSaved.id, workspaceSaved.id, runnerSaved.id, runSavedId)
479+
}
480+
}
481+
400482
@Nested
401483
inner class RunServicePostgresIntegrationTest {
402484

run/src/main/kotlin/com/cosmotech/run/exceptions/RunExceptionHandling.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT license.
33
package com.cosmotech.run.exceptions
44

5+
import io.argoproj.workflow.ApiException
56
import java.net.URI
67
import java.sql.SQLException
78
import org.springframework.http.HttpStatus
@@ -24,4 +25,16 @@ class RunExceptionHandling {
2425

2526
return response
2627
}
28+
29+
@ExceptionHandler(ApiException::class)
30+
fun handleApiException(exception: ApiException): ProblemDetail {
31+
var response = ProblemDetail.forStatus(HttpStatus.INTERNAL_SERVER_ERROR)
32+
val internalServerError = HttpStatus.INTERNAL_SERVER_ERROR
33+
response.type = URI.create(httpStatusCodeTypePrefix + internalServerError.value())
34+
response.title =
35+
"Argo Workflows has encountered a problem. Please check Argo logs for more details."
36+
response.detail = exception.message
37+
38+
return response
39+
}
2740
}

run/src/main/kotlin/com/cosmotech/run/service/RunServiceImpl.kt

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import com.cosmotech.run.domain.QueryResult
2929
import com.cosmotech.run.domain.Run
3030
import com.cosmotech.run.domain.RunData
3131
import com.cosmotech.run.domain.RunDataQuery
32-
import com.cosmotech.run.domain.RunLogs
3332
import com.cosmotech.run.domain.RunState
3433
import com.cosmotech.run.domain.RunStatus
3534
import com.cosmotech.run.domain.RunTemplateParameterValue
@@ -53,9 +52,11 @@ import org.springframework.beans.factory.annotation.Value
5352
import org.springframework.context.event.EventListener
5453
import org.springframework.data.domain.PageRequest
5554
import org.springframework.data.domain.Pageable
55+
import org.springframework.http.HttpStatus
5656
import org.springframework.jdbc.core.JdbcTemplate
5757
import org.springframework.jdbc.datasource.DriverManagerDataSource
5858
import org.springframework.stereotype.Service
59+
import org.springframework.web.client.RestClientResponseException
5960

6061
internal const val WORKFLOW_TYPE_RUN = "container-run"
6162
internal const val WORKFLOW_TYPE_TWIN_GRAPH_IMPORT = "twin-graph-import"
@@ -473,8 +474,17 @@ class RunServiceImpl(
473474
workspaceId: String,
474475
runnerId: String,
475476
runId: String
476-
): RunLogs {
477-
return workflowService.getRunLogs(getRun(organizationId, workspaceId, runnerId, runId))
477+
): String {
478+
val run = getRun(organizationId, workspaceId, runnerId, runId)
479+
val status = getRunStatus(run)
480+
if (status.endTime.isNullOrEmpty()) {
481+
try {
482+
return workflowService.getRunningLogs(run)
483+
} catch (e: RestClientResponseException) {
484+
if (e.statusCode != HttpStatus.NOT_FOUND) throw e
485+
}
486+
}
487+
return workflowService.getArchivedLogs(run)
478488
}
479489

480490
override fun getRunStatus(

0 commit comments

Comments
 (0)