diff --git a/build/pkgs_list_amd64 b/build/pkgs_list_amd64 index 605344e..796d3a5 100644 --- a/build/pkgs_list_amd64 +++ b/build/pkgs_list_amd64 @@ -1,9 +1,9 @@ ca-certificates=20240203 -chromium=130.0.6723.91-2 -chromium-driver=130.0.6723.91-2 -chromium-sandbox=130.0.6723.91-2 +chromium=131.0.6778.204-1 +chromium-driver=131.0.6778.204-1 +chromium-sandbox=131.0.6778.204-1 ffmpeg=7:7.1-3 fonts-recommended=1 -pulseaudio=16.1+dfsg1-5.1+b1 +pulseaudio=17.0+dfsg1-1 wget=1.24.5-2+b1 -xvfb=2:21.1.14-1 +xvfb=2:21.1.15-2 diff --git a/build/pkgs_list_arm64 b/build/pkgs_list_arm64 index 23d8232..aa61263 100644 --- a/build/pkgs_list_arm64 +++ b/build/pkgs_list_arm64 @@ -1,9 +1,9 @@ ca-certificates=20240203 -chromium=130.0.6723.91-2 -chromium-driver=130.0.6723.91-2 -chromium-sandbox=130.0.6723.91-2 +chromium=131.0.6778.139-1 +chromium-driver=131.0.6778.139-1 +chromium-sandbox=131.0.6778.139-1 ffmpeg=7:7.1-3 fonts-recommended=1 -pulseaudio=16.1+dfsg1-5.1+b1 +pulseaudio=17.0+dfsg1-1 wget=1.24.5-2 -xvfb=2:21.1.14-1 +xvfb=2:21.1.15-1 diff --git a/cmd/recorder/job_test.go b/cmd/recorder/job_test.go index 5d725aa..fbec4a2 100644 --- a/cmd/recorder/job_test.go +++ b/cmd/recorder/job_test.go @@ -34,7 +34,7 @@ func TestReportJobFailure(t *testing.T) { AuthToken: "qj75unbsef83ik9p7ueypb6iyw", } cfg.SetDefaults() - rec, err := NewRecorder(cfg) + rec, err := NewRecorder(cfg, getDataDir("")) require.NoError(t, err) require.NotNil(t, rec) diff --git a/cmd/recorder/main.go b/cmd/recorder/main.go index 42142ec..6f2d932 100644 --- a/cmd/recorder/main.go +++ b/cmd/recorder/main.go @@ -2,20 +2,42 @@ package main import ( "fmt" + "io" "log/slog" "os" "os/signal" + "path/filepath" "syscall" "github.com/mattermost/calls-recorder/cmd/recorder/config" ) func main() { - logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{ + recID := os.Getenv("RECORDING_ID") + + // Create scoped (by jobID) data path + dataPath := getDataDir(recID) + err := os.MkdirAll(dataPath, 0700) + if err != nil { + slog.Error("failed to create data path", slog.String("err", err.Error())) + os.Exit(1) + } + + logFile, err := os.Create(filepath.Join(dataPath, "recorder.log")) + if err != nil { + slog.Error("failed to create log file", slog.String("err", err.Error())) + os.Exit(1) + } + defer logFile.Close() + + // This lets us write logs simultaneously to console and file. + logWriter := io.MultiWriter(os.Stdout, logFile) + + logger := slog.New(slog.NewTextHandler(logWriter, &slog.HandlerOptions{ AddSource: true, Level: slog.LevelDebug, ReplaceAttr: slogReplaceAttr, - })).With("recID", os.Getenv("RECORDING_ID")) + })).With("recID", recID) slog.SetDefault(logger) pid := os.Getpid() @@ -33,7 +55,7 @@ func main() { slog.SetDefault(logger.With("jobID", cfg.RecordingID)) - recorder, err := NewRecorder(cfg) + recorder, err := NewRecorder(cfg, dataPath) if err != nil { slog.Error("failed to create recorder", slog.String("err", err.Error())) os.Exit(1) diff --git a/cmd/recorder/recorder.go b/cmd/recorder/recorder.go index f010650..c64d345 100644 --- a/cmd/recorder/recorder.go +++ b/cmd/recorder/recorder.go @@ -47,6 +47,9 @@ const ( type Recorder struct { cfg config.RecorderConfig + // path to write recording file + dataPath string + // browser readyCh chan struct{} stopCh chan struct{} @@ -270,11 +273,15 @@ func runDisplayServer(width, height int) (*exec.Cmd, error) { return runCmd("Xvfb", args) } -func NewRecorder(cfg config.RecorderConfig) (*Recorder, error) { +func NewRecorder(cfg config.RecorderConfig, dataPath string) (*Recorder, error) { if err := cfg.IsValid(); err != nil { return nil, fmt.Errorf("invalid config: %w", err) } + if dataPath == "" { + return nil, fmt.Errorf("data path cannot be empty") + } + client := model.NewAPIv4Client(cfg.SiteURL) client.SetToken(cfg.AuthToken) client.HTTPClient = &http.Client{ @@ -285,6 +292,7 @@ func NewRecorder(cfg config.RecorderConfig) (*Recorder, error) { return &Recorder{ cfg: cfg, + dataPath: dataPath, readyCh: make(chan struct{}), stopCh: make(chan struct{}), stoppedCh: make(chan error), @@ -302,7 +310,7 @@ func (rec *Recorder) Start() error { if err != nil { return fmt.Errorf("failed to get filename for call: %w", err) } - rec.outPath = filepath.Join(getDataDir(), filename) + rec.outPath = filepath.Join(rec.dataPath, filename) rec.displayServer, err = runDisplayServer(rec.cfg.Width, rec.cfg.Height) if err != nil { diff --git a/cmd/recorder/recorder_test.go b/cmd/recorder/recorder_test.go index 44857cf..833ddc2 100644 --- a/cmd/recorder/recorder_test.go +++ b/cmd/recorder/recorder_test.go @@ -10,11 +10,26 @@ import ( func TestNewRecorder(t *testing.T) { t.Run("invalid config", func(t *testing.T) { - rec, err := NewRecorder(config.RecorderConfig{}) + rec, err := NewRecorder(config.RecorderConfig{}, getDataDir("")) require.EqualError(t, err, "invalid config: config cannot be empty") require.Nil(t, rec) }) + t.Run("invalid data path", func(t *testing.T) { + cfg := config.RecorderConfig{ + SiteURL: "http://localhost:8065", + CallID: "8w8jorhr7j83uqr6y1st894hqe", + PostID: "udzdsg7dwidbzcidx5khrf8nee", + RecordingID: "67t5u6cmtfbb7jug739d43xa9e", + AuthToken: "qj75unbsef83ik9p7ueypb6iyw", + } + cfg.SetDefaults() + + rec, err := NewRecorder(cfg, "") + require.EqualError(t, err, "data path cannot be empty") + require.Nil(t, rec) + }) + t.Run("valid config", func(t *testing.T) { cfg := config.RecorderConfig{ SiteURL: "http://localhost:8065", @@ -24,7 +39,7 @@ func TestNewRecorder(t *testing.T) { AuthToken: "qj75unbsef83ik9p7ueypb6iyw", } cfg.SetDefaults() - rec, err := NewRecorder(cfg) + rec, err := NewRecorder(cfg, getDataDir("")) require.NoError(t, err) require.NotNil(t, rec) }) diff --git a/cmd/recorder/upload_test.go b/cmd/recorder/upload_test.go index a5d673a..1f1f1da 100644 --- a/cmd/recorder/upload_test.go +++ b/cmd/recorder/upload_test.go @@ -38,7 +38,7 @@ func TestUploadRecording(t *testing.T) { AuthToken: "qj75unbsef83ik9p7ueypb6iyw", } cfg.SetDefaults() - rec, err := NewRecorder(cfg) + rec, err := NewRecorder(cfg, getDataDir("")) require.NoError(t, err) require.NotNil(t, rec) @@ -237,7 +237,7 @@ func TestPublishRecording(t *testing.T) { AuthToken: "qj75unbsef83ik9p7ueypb6iyw", } cfg.SetDefaults() - rec, err := NewRecorder(cfg) + rec, err := NewRecorder(cfg, getDataDir("")) require.NoError(t, err) require.NotNil(t, rec) diff --git a/cmd/recorder/utils.go b/cmd/recorder/utils.go index a901d7e..f9ba1bc 100644 --- a/cmd/recorder/utils.go +++ b/cmd/recorder/utils.go @@ -91,11 +91,11 @@ func pollBrowserEvaluateExpr(ctx context.Context, expr string, interval, timeout } } -func getDataDir() string { +func getDataDir(jobID string) string { if dir := os.Getenv("DATA_DIR"); dir != "" { - return dir + return filepath.Join(dir, jobID) } - return dataDir + return filepath.Join(dataDir, jobID) } func sanitizeFilename(name string) string {