CI / changes (push) Successful in 9s
CI / commitlint (push) Has been skipped
CI / openapi (push) Successful in 26s
CI / web (push) Successful in 33s
CI / go (push) Successful in 56s
CI / bird2 (push) Successful in 14s
CI / release (push) Successful in 20s
Added new endpoints for estimating and executing runtime log auto-cleanup based on tenant settings. Introduced configuration options for auto-cleanup policies, including scheduling and file size limits. Updated the API documentation and UI components to reflect these changes, improving user interaction with runtime log management. Enhanced error handling and added new UI elements for better visibility of audit logs and cleanup actions.
192 lines
6.1 KiB
Go
192 lines
6.1 KiB
Go
package httpapi
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"evobgp/internal/runtimelogs"
|
|
)
|
|
|
|
func TestRuntimeLogsFSUnavailable503(t *testing.T) {
|
|
srv, err := New(Options{SeedDemo: true, InsecureDev: true})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer srv.Close()
|
|
handler := srv.Handler()
|
|
|
|
tests := []struct {
|
|
method string
|
|
path string
|
|
}{
|
|
{http.MethodGet, "/v1/runtime-logs/files"},
|
|
{http.MethodGet, "/v1/runtime-logs/files/evobgp-all.log"},
|
|
{http.MethodDelete, "/v1/runtime-logs/files/evobgp-all.log"},
|
|
}
|
|
for _, tc := range tests {
|
|
t.Run(tc.method+" "+tc.path, func(t *testing.T) {
|
|
req := httptest.NewRequest(tc.method, tc.path, nil)
|
|
req.Header.Set("Authorization", "Bearer dev")
|
|
rec := httptest.NewRecorder()
|
|
handler.ServeHTTP(rec, req)
|
|
if rec.Code != http.StatusServiceUnavailable {
|
|
t.Fatalf("status=%d body=%s", rec.Code, rec.Body.String())
|
|
}
|
|
if !strings.Contains(rec.Body.String(), "runtime_logs_unavailable") {
|
|
t.Fatalf("expected runtime_logs_unavailable detail, body=%s", rec.Body.String())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRuntimeLogsCleanupAuditWithoutFS(t *testing.T) {
|
|
srv, err := New(Options{SeedDemo: true, InsecureDev: true})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer srv.Close()
|
|
handler := srv.Handler()
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/v1/runtime-logs/cleanup-audit", nil)
|
|
req.Header.Set("Authorization", "Bearer dev")
|
|
rec := httptest.NewRecorder()
|
|
handler.ServeHTTP(rec, req)
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("status=%d body=%s", rec.Code, rec.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestRuntimeLogsHappyPath(t *testing.T) {
|
|
dir := t.TempDir()
|
|
logPath := filepath.Join(dir, "evobgp-all.log")
|
|
if err := os.WriteFile(logPath, []byte("line1\nline2\nline3\n"), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
srv, err := New(Options{SeedDemo: true, InsecureDev: true})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer srv.Close()
|
|
srv.runtimeLogs = runtimelogs.NewService(runtimelogs.Config{
|
|
RootDir: dir,
|
|
ServiceName: runtimelogs.ServiceNameAll,
|
|
})
|
|
handler := srv.Handler()
|
|
tenant, _, _, _, _ := srv.Store().DemoIDs()
|
|
mustSetTestAPIKeys(t, srv, "vwkey|"+tenant+"|viewer,opkey|"+tenant+"|operator")
|
|
|
|
t.Run("list", func(t *testing.T) {
|
|
req := httptest.NewRequest(http.MethodGet, "/v1/runtime-logs/files", nil)
|
|
req.Header.Set("Authorization", "Bearer vwkey")
|
|
rec := httptest.NewRecorder()
|
|
handler.ServeHTTP(rec, req)
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("status=%d body=%s", rec.Code, rec.Body.String())
|
|
}
|
|
if !strings.Contains(rec.Body.String(), "evobgp-all.log") {
|
|
t.Fatalf("expected file in list, body=%s", rec.Body.String())
|
|
}
|
|
})
|
|
|
|
t.Run("tail", func(t *testing.T) {
|
|
req := httptest.NewRequest(http.MethodGet, "/v1/runtime-logs/files/evobgp-all.log?lines=2", nil)
|
|
req.Header.Set("Authorization", "Bearer vwkey")
|
|
rec := httptest.NewRecorder()
|
|
handler.ServeHTTP(rec, req)
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("status=%d body=%s", rec.Code, rec.Body.String())
|
|
}
|
|
if !strings.Contains(rec.Body.String(), "line2") || !strings.Contains(rec.Body.String(), "line3") {
|
|
t.Fatalf("unexpected tail body=%s", rec.Body.String())
|
|
}
|
|
})
|
|
|
|
t.Run("viewer cannot cleanup", func(t *testing.T) {
|
|
req := httptest.NewRequest(http.MethodDelete, "/v1/runtime-logs/files/evobgp-all.log", nil)
|
|
req.Header.Set("Authorization", "Bearer vwkey")
|
|
rec := httptest.NewRecorder()
|
|
handler.ServeHTTP(rec, req)
|
|
if rec.Code != http.StatusForbidden {
|
|
t.Fatalf("status=%d body=%s", rec.Code, rec.Body.String())
|
|
}
|
|
})
|
|
|
|
t.Run("cleanup truncate and audit", func(t *testing.T) {
|
|
req := httptest.NewRequest(http.MethodDelete, "/v1/runtime-logs/files/evobgp-all.log?mode=truncate", nil)
|
|
req.Header.Set("Authorization", "Bearer opkey")
|
|
rec := httptest.NewRecorder()
|
|
handler.ServeHTTP(rec, req)
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("status=%d body=%s", rec.Code, rec.Body.String())
|
|
}
|
|
if !strings.Contains(rec.Body.String(), `"action":"truncate"`) {
|
|
t.Fatalf("unexpected cleanup body=%s", rec.Body.String())
|
|
}
|
|
st, err := os.Stat(logPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if st.Size() != 0 {
|
|
t.Fatalf("expected truncated file, size=%d", st.Size())
|
|
}
|
|
|
|
auditReq := httptest.NewRequest(http.MethodGet, "/v1/runtime-logs/cleanup-audit", nil)
|
|
auditReq.Header.Set("Authorization", "Bearer vwkey")
|
|
auditRec := httptest.NewRecorder()
|
|
handler.ServeHTTP(auditRec, auditReq)
|
|
if auditRec.Code != http.StatusOK {
|
|
t.Fatalf("audit status=%d body=%s", auditRec.Code, auditRec.Body.String())
|
|
}
|
|
if !strings.Contains(auditRec.Body.String(), "evobgp-all.log") {
|
|
t.Fatalf("expected audit entry, body=%s", auditRec.Body.String())
|
|
}
|
|
})
|
|
|
|
t.Run("auto estimate and run", func(t *testing.T) {
|
|
bigPath := filepath.Join(dir, "big.log")
|
|
if err := os.WriteFile(bigPath, make([]byte, 2*1024*1024), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := srv.store.PatchGlobalSettings(tenant, map[string]any{
|
|
runtimelogs.KeyMaxFileMB: 1,
|
|
runtimelogs.KeyAutoMode: "truncate",
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
estReq := httptest.NewRequest(http.MethodGet, "/v1/runtime-logs/auto-estimate", nil)
|
|
estReq.Header.Set("Authorization", "Bearer opkey")
|
|
estRec := httptest.NewRecorder()
|
|
handler.ServeHTTP(estRec, estReq)
|
|
if estRec.Code != http.StatusOK {
|
|
t.Fatalf("estimate status=%d body=%s", estRec.Code, estRec.Body.String())
|
|
}
|
|
if !strings.Contains(estRec.Body.String(), `"would_count":1`) {
|
|
t.Fatalf("expected would_count=1, body=%s", estRec.Body.String())
|
|
}
|
|
|
|
runReq := httptest.NewRequest(http.MethodPost, "/v1/runtime-logs/auto-run", nil)
|
|
runReq.Header.Set("Authorization", "Bearer opkey")
|
|
runRec := httptest.NewRecorder()
|
|
handler.ServeHTTP(runRec, runReq)
|
|
if runRec.Code != http.StatusOK {
|
|
t.Fatalf("auto-run status=%d body=%s", runRec.Code, runRec.Body.String())
|
|
}
|
|
if !strings.Contains(runRec.Body.String(), "auto:scheduler") && !strings.Contains(runRec.Body.String(), `"cleaned_count":1`) {
|
|
t.Fatalf("unexpected auto-run body=%s", runRec.Body.String())
|
|
}
|
|
st, err := os.Stat(bigPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if st.Size() != 0 {
|
|
t.Fatalf("expected truncated big.log, size=%d", st.Size())
|
|
}
|
|
})
|
|
}
|