Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/workflows/chromium-launcher-test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Test chromium-launcher

on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
workflow_dispatch:

jobs:
test:
runs-on: ubuntu-latest
permissions:
contents: read

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: "server/go.mod"
cache: true

- name: Run chromium-launcher unit tests
run: go test ./cmd/chromium-launcher -v
working-directory: server
10 changes: 7 additions & 3 deletions images/chromium-headful/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,15 @@ COPY server/go.sum ./
RUN go mod download

COPY server/ .

# Build kernel-images API
RUN GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH:-amd64} \
go build -ldflags="-s -w" -o /out/kernel-images-api ./cmd/api

# Build chromium launcher
RUN GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH:-amd64} \
go build -ldflags="-s -w" -o /out/chromium-launcher ./cmd/chromium-launcher

# webrtc client
FROM node:22-bullseye-slim AS client
WORKDIR /src
Expand Down Expand Up @@ -89,7 +95,6 @@ RUN apt-get update && \
# Userland apps
sudo add-apt-repository ppa:mozillateam/ppa && \
sudo apt-get install -y --no-install-recommends \
chromium-browser \
libreoffice \
x11-apps \
xpdf \
Expand Down Expand Up @@ -169,14 +174,13 @@ COPY --from=xorg-deps /usr/local/lib/xorg/modules/drivers/dummy_drv.so /usr/lib/
COPY --from=xorg-deps /usr/local/lib/xorg/modules/input/neko_drv.so /usr/lib/xorg/modules/input/neko_drv.so

COPY images/chromium-headful/image-chromium/ /
COPY images/chromium-headful/start-chromium.sh /images/chromium-headful/start-chromium.sh
RUN chmod +x /images/chromium-headful/start-chromium.sh
COPY images/chromium-headful/wrapper.sh /wrapper.sh
COPY images/chromium-headful/supervisord.conf /etc/supervisor/supervisord.conf
COPY images/chromium-headful/supervisor/services/ /etc/supervisor/conf.d/services/

# copy the kernel-images API binary built in the builder stage
COPY --from=server-builder /out/kernel-images-api /usr/local/bin/kernel-images-api
COPY --from=server-builder /out/chromium-launcher /usr/local/bin/chromium-launcher

RUN useradd -m -s /bin/bash kernel

Expand Down
48 changes: 0 additions & 48 deletions images/chromium-headful/start-chromium.sh

This file was deleted.

2 changes: 1 addition & 1 deletion images/chromium-headful/supervisor/services/chromium.conf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[program:chromium]
command=/bin/bash -lc '/images/chromium-headful/start-chromium.sh'
command=/usr/local/bin/chromium-launcher
autostart=false
autorestart=true
startsecs=5
Expand Down
15 changes: 10 additions & 5 deletions images/chromium-headless/image/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,16 @@ COPY server/go.mod ./
COPY server/go.sum ./
RUN go mod download

# Copy the rest of the server source and build the binary
COPY server/ .

# Build kernel-images API
RUN GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH:-amd64} \
go build -ldflags="-s -w" -o /out/kernel-images-api ./cmd/api

# Build chromium launcher
RUN GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH:-amd64} \
go build -ldflags="-s -w" -o /out/chromium-launcher ./cmd/chromium-launcher

FROM docker.io/ubuntu:22.04

RUN set -xe; \
Expand Down Expand Up @@ -72,12 +77,11 @@ ENV WITHDOCKER=true
# Create a non-root user with a home directory
RUN useradd -m -s /bin/bash kernel

# Xvfb helper and supervisor-managed start scripts
COPY images/chromium-headless/image/start-chromium.sh /images/chromium-headless/image/start-chromium.sh
# supervisor start scripts
COPY images/chromium-headless/image/start-xvfb.sh /images/chromium-headless/image/start-xvfb.sh
RUN chmod +x /images/chromium-headless/image/start-chromium.sh /images/chromium-headless/image/start-xvfb.sh
RUN chmod +x /images/chromium-headless/image/start-xvfb.sh

# Wrapper script set environment
# Wrapper script to set environment
COPY images/chromium-headless/image/wrapper.sh /usr/bin/wrapper.sh

# Supervisord configuration
Expand All @@ -86,5 +90,6 @@ COPY images/chromium-headless/image/supervisor/services/ /etc/supervisor/conf.d/

# Copy the kernel-images API binary built in the builder stage
COPY --from=server-builder /out/kernel-images-api /usr/local/bin/kernel-images-api
COPY --from=server-builder /out/chromium-launcher /usr/local/bin/chromium-launcher

ENTRYPOINT [ "/usr/bin/wrapper.sh" ]
48 changes: 0 additions & 48 deletions images/chromium-headless/image/start-chromium.sh

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[program:chromium]
command=/bin/bash -lc '/images/chromium-headless/image/start-chromium.sh'
command=/usr/local/bin/chromium-launcher --headless
autostart=false
autorestart=true
startsecs=5
Expand Down
13 changes: 12 additions & 1 deletion server/cmd/api/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import (
"sync"
"time"

"github.com/onkernel/kernel-images/server/lib/devtoolsproxy"
"github.com/onkernel/kernel-images/server/lib/logger"
oapi "github.com/onkernel/kernel-images/server/lib/oapi"
"github.com/onkernel/kernel-images/server/lib/recorder"
"github.com/onkernel/kernel-images/server/lib/scaletozero"
)

type ApiService struct {
Expand All @@ -26,16 +28,23 @@ type ApiService struct {
// Process management
procMu sync.RWMutex
procs map[string]*processHandle

// DevTools upstream manager (Chromium supervisord log tailer)
upstreamMgr *devtoolsproxy.UpstreamManager

stz scaletozero.Controller
}

var _ oapi.StrictServerInterface = (*ApiService)(nil)

func New(recordManager recorder.RecordManager, factory recorder.FFmpegRecorderFactory) (*ApiService, error) {
func New(recordManager recorder.RecordManager, factory recorder.FFmpegRecorderFactory, upstreamMgr *devtoolsproxy.UpstreamManager, stz scaletozero.Controller) (*ApiService, error) {
switch {
case recordManager == nil:
return nil, fmt.Errorf("recordManager cannot be nil")
case factory == nil:
return nil, fmt.Errorf("factory cannot be nil")
case upstreamMgr == nil:
return nil, fmt.Errorf("upstreamMgr cannot be nil")
}

return &ApiService{
Expand All @@ -44,6 +53,8 @@ func New(recordManager recorder.RecordManager, factory recorder.FFmpegRecorderFa
defaultRecorderID: "default",
watches: make(map[string]*fsWatch),
procs: make(map[string]*processHandle),
upstreamMgr: upstreamMgr,
stz: stz,
}, nil
}

Expand Down
29 changes: 19 additions & 10 deletions server/cmd/api/api/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@ import (
"os"
"testing"

"log/slog"

"github.com/onkernel/kernel-images/server/lib/devtoolsproxy"
oapi "github.com/onkernel/kernel-images/server/lib/oapi"
"github.com/onkernel/kernel-images/server/lib/recorder"
"github.com/onkernel/kernel-images/server/lib/scaletozero"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
Expand All @@ -20,7 +24,7 @@ func TestApiService_StartRecording(t *testing.T) {

t.Run("success", func(t *testing.T) {
mgr := recorder.NewFFmpegManager()
svc, err := New(mgr, newMockFactory())
svc, err := New(mgr, newMockFactory(), newTestUpstreamManager(), scaletozero.NewNoopController())
require.NoError(t, err)

resp, err := svc.StartRecording(ctx, oapi.StartRecordingRequestObject{})
Expand All @@ -34,7 +38,7 @@ func TestApiService_StartRecording(t *testing.T) {

t.Run("already recording", func(t *testing.T) {
mgr := recorder.NewFFmpegManager()
svc, err := New(mgr, newMockFactory())
svc, err := New(mgr, newMockFactory(), newTestUpstreamManager(), scaletozero.NewNoopController())
require.NoError(t, err)

// First start should succeed
Expand All @@ -49,7 +53,7 @@ func TestApiService_StartRecording(t *testing.T) {

t.Run("custom ids don't collide", func(t *testing.T) {
mgr := recorder.NewFFmpegManager()
svc, err := New(mgr, newMockFactory())
svc, err := New(mgr, newMockFactory(), newTestUpstreamManager(), scaletozero.NewNoopController())
require.NoError(t, err)

for i := 0; i < 5; i++ {
Expand Down Expand Up @@ -82,7 +86,7 @@ func TestApiService_StopRecording(t *testing.T) {

t.Run("no active recording", func(t *testing.T) {
mgr := recorder.NewFFmpegManager()
svc, err := New(mgr, newMockFactory())
svc, err := New(mgr, newMockFactory(), newTestUpstreamManager(), scaletozero.NewNoopController())
require.NoError(t, err)

resp, err := svc.StopRecording(ctx, oapi.StopRecordingRequestObject{})
Expand All @@ -95,7 +99,7 @@ func TestApiService_StopRecording(t *testing.T) {
rec := &mockRecorder{id: "default", isRecordingFlag: true}
require.NoError(t, mgr.RegisterRecorder(ctx, rec), "failed to register recorder")

svc, err := New(mgr, newMockFactory())
svc, err := New(mgr, newMockFactory(), newTestUpstreamManager(), scaletozero.NewNoopController())
require.NoError(t, err)
resp, err := svc.StopRecording(ctx, oapi.StopRecordingRequestObject{})
require.NoError(t, err)
Expand All @@ -110,7 +114,7 @@ func TestApiService_StopRecording(t *testing.T) {

force := true
req := oapi.StopRecordingRequestObject{Body: &oapi.StopRecordingJSONRequestBody{ForceStop: &force}}
svc, err := New(mgr, newMockFactory())
svc, err := New(mgr, newMockFactory(), newTestUpstreamManager(), scaletozero.NewNoopController())
require.NoError(t, err)
resp, err := svc.StopRecording(ctx, req)
require.NoError(t, err)
Expand All @@ -124,7 +128,7 @@ func TestApiService_DownloadRecording(t *testing.T) {

t.Run("not found", func(t *testing.T) {
mgr := recorder.NewFFmpegManager()
svc, err := New(mgr, newMockFactory())
svc, err := New(mgr, newMockFactory(), newTestUpstreamManager(), scaletozero.NewNoopController())
require.NoError(t, err)
resp, err := svc.DownloadRecording(ctx, oapi.DownloadRecordingRequestObject{})
require.NoError(t, err)
Expand All @@ -144,7 +148,7 @@ func TestApiService_DownloadRecording(t *testing.T) {
rec := &mockRecorder{id: "default", isRecordingFlag: true, recordingData: randomBytes(minRecordingSizeInBytes - 1)}
require.NoError(t, mgr.RegisterRecorder(ctx, rec), "failed to register recorder")

svc, err := New(mgr, newMockFactory())
svc, err := New(mgr, newMockFactory(), newTestUpstreamManager(), scaletozero.NewNoopController())
require.NoError(t, err)
// will return a 202 when the recording is too small
resp, err := svc.DownloadRecording(ctx, oapi.DownloadRecordingRequestObject{})
Expand Down Expand Up @@ -174,7 +178,7 @@ func TestApiService_DownloadRecording(t *testing.T) {
rec := &mockRecorder{id: "default", recordingData: data}
require.NoError(t, mgr.RegisterRecorder(ctx, rec), "failed to register recorder")

svc, err := New(mgr, newMockFactory())
svc, err := New(mgr, newMockFactory(), newTestUpstreamManager(), scaletozero.NewNoopController())
require.NoError(t, err)
resp, err := svc.DownloadRecording(ctx, oapi.DownloadRecordingRequestObject{})
require.NoError(t, err)
Expand All @@ -194,7 +198,7 @@ func TestApiService_Shutdown(t *testing.T) {
rec := &mockRecorder{id: "default", isRecordingFlag: true}
require.NoError(t, mgr.RegisterRecorder(ctx, rec), "failed to register recorder")

svc, err := New(mgr, newMockFactory())
svc, err := New(mgr, newMockFactory(), newTestUpstreamManager(), scaletozero.NewNoopController())
require.NoError(t, err)

require.NoError(t, svc.Shutdown(ctx))
Expand Down Expand Up @@ -284,3 +288,8 @@ func newMockFactory() recorder.FFmpegRecorderFactory {
return rec, nil
}
}

func newTestUpstreamManager() *devtoolsproxy.UpstreamManager {
logger := slog.New(slog.NewTextHandler(io.Discard, nil))
return devtoolsproxy.NewUpstreamManager("", logger)
}
Loading
Loading