Преглед изворни кода

fix: persist blocked executions to saveLogs files (#1111)

James Read пре 3 дана
родитељ
комит
ec1aa3952f

+ 2 - 0
docs/modules/ROOT/pages/logs/saving.adoc

@@ -3,6 +3,8 @@
 
 By default, OliveTin only keeps logs in memory, meaning that if you restart OliveTin your logs will be lost. For some use cases this is acceptable, but you can configure OliveTin to save logs for you.
 
+When `saveLogs` is enabled, OliveTin writes a file for each configured directory when an execution finishes — a results YAML when `resultsDirectory` is set, and an output log when `outputDirectory` is set. If only one directory is configured, only that file type is written. This includes blocked runs (for example when concurrency or rate limits prevent the action from starting).
+
 You can configure the global setting for saving logs, or override it on a per-action basis;
 
 [source,yaml]

+ 1 - 1
service/internal/executor/executor.go

@@ -184,7 +184,6 @@ func DefaultExecutor(cfg *config.Config) *Executor {
 		stepExec,
 		stepExecAfter,
 		stepLogFinish,
-		stepSaveLog,
 		stepTrigger,
 	}
 
@@ -688,6 +687,7 @@ func (e *Executor) finishExecChain(req *ExecutionRequest) {
 	recordExecutionMetrics(req.logEntry)
 
 	notifyListenersFinished(req)
+	stepSaveLog(req)
 	e.drainGroupQueue()
 }
 

+ 78 - 0
service/internal/executor/executor_test.go

@@ -1042,6 +1042,84 @@ func TestStepSaveLogSanitizesNULInTitle(t *testing.T) {
 	assert.Equal(t, "nul ok", string(output))
 }
 
+func TestBlockedExecutionPersistsSaveLogs(t *testing.T) {
+	t.Parallel()
+
+	resultsDir := t.TempDir()
+	outputDir := t.TempDir()
+
+	action := &config.Action{
+		Title:         "Blocked report",
+		Shell:         "sleep 1",
+		MaxConcurrent: 1,
+		SaveLogs: config.SaveLogsConfig{
+			ResultsDirectory: resultsDir,
+			OutputDirectory:  outputDir,
+		},
+	}
+
+	e, cfg := testGroupExecutor([]*config.Action{action}, nil)
+	binding := e.FindBindingWithNoEntity(action)
+
+	wg1, tracking1 := e.ExecRequest(&ExecutionRequest{
+		Binding:           binding,
+		Cfg:               cfg,
+		AuthenticatedUser: auth.UserFromSystem(cfg, "testuser"),
+	})
+
+	waitUntilExecutionStarted(t, e, tracking1)
+
+	wg2, tracking2 := e.ExecRequest(&ExecutionRequest{
+		Binding:           binding,
+		Cfg:               cfg,
+		AuthenticatedUser: auth.UserFromSystem(cfg, "testuser"),
+	})
+
+	wg1.Wait()
+	wg2.Wait()
+
+	snapshot, ok := e.SnapshotLog(tracking2)
+	require.True(t, ok)
+	require.True(t, snapshot.Blocked)
+
+	resultsEntries, err := os.ReadDir(resultsDir)
+	require.NoError(t, err)
+
+	var resultsPath string
+
+	for _, entry := range resultsEntries {
+		if strings.Contains(entry.Name(), tracking2) {
+			resultsPath = filepath.Join(resultsDir, entry.Name())
+			break
+		}
+	}
+
+	require.NotEmpty(t, resultsPath)
+
+	outputEntries, err := os.ReadDir(outputDir)
+	require.NoError(t, err)
+
+	var outputPath string
+
+	for _, entry := range outputEntries {
+		if strings.Contains(entry.Name(), tracking2) {
+			outputPath = filepath.Join(outputDir, entry.Name())
+			break
+		}
+	}
+
+	require.NotEmpty(t, outputPath)
+
+	resultsData, err := os.ReadFile(resultsPath)
+	require.NoError(t, err)
+	assert.Contains(t, string(resultsData), "blocked: true")
+	assert.Contains(t, string(resultsData), tracking2)
+
+	outputData, err := os.ReadFile(outputPath)
+	require.NoError(t, err)
+	assert.Contains(t, string(outputData), "Blocked from executing due to concurrency limit")
+}
+
 func TestStepSaveLogReturnsFalseWhenDependenciesMissing(t *testing.T) {
 	started := time.Unix(1714333384, 0)
 	valid := &ExecutionRequest{