Skip to content
Merged
4 changes: 2 additions & 2 deletions server/cmd/api/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ type ApiService struct {

// DevTools upstream manager (Chromium supervisord log tailer)
upstreamMgr *devtoolsproxy.UpstreamManager
stz scaletozero.Controller
stz scaletozero.PinnedController

// inputMu serializes input-related operations (mouse, keyboard, screenshot)
inputMu sync.Mutex
Expand Down Expand Up @@ -96,7 +96,7 @@ func New(
recordManager recorder.RecordManager,
factory recorder.FFmpegRecorderFactory,
upstreamMgr *devtoolsproxy.UpstreamManager,
stz scaletozero.Controller,
stz scaletozero.PinnedController,
Comment thread
sjmiller609 marked this conversation as resolved.
nekoAuthClient *nekoclient.AuthClient,
captureSession *capturesession.CaptureSession,
eventStream *events.EventStream,
Expand Down
24 changes: 24 additions & 0 deletions server/cmd/api/api/standby.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package api

import (
"context"

"github.com/kernel/kernel-images/server/lib/logger"
oapi "github.com/kernel/kernel-images/server/lib/oapi"
)

func (s *ApiService) DisableStandby(ctx context.Context, _ oapi.DisableStandbyRequestObject) (oapi.DisableStandbyResponseObject, error) {
if err := s.stz.DisablePin(ctx); err != nil {
logger.FromContext(ctx).Error("failed to pin scale-to-zero disabled", "err", err)
return oapi.DisableStandby500JSONResponse{InternalErrorJSONResponse: oapi.InternalErrorJSONResponse{Message: "failed to disable standby"}}, nil
}
return oapi.DisableStandby204Response{}, nil
}

func (s *ApiService) EnableStandby(ctx context.Context, _ oapi.EnableStandbyRequestObject) (oapi.EnableStandbyResponseObject, error) {
if err := s.stz.EnablePin(ctx); err != nil {
logger.FromContext(ctx).Error("failed to release scale-to-zero pin", "err", err)
return oapi.EnableStandby500JSONResponse{InternalErrorJSONResponse: oapi.InternalErrorJSONResponse{Message: "failed to enable standby"}}, nil
}
return oapi.EnableStandby204Response{}, nil
}
60 changes: 60 additions & 0 deletions server/e2e/e2e_standby_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package e2e

import (
"context"
"net/http"
"os/exec"
"testing"
"time"

"github.com/stretchr/testify/require"
)

// TestStandbyDisableEnable exercises POST /system/standby/{disable,enable}
// against the real built image. The unikraft control file does not exist
// inside the docker test container, so the underlying scale-to-zero write
// is a no-op — this test validates HTTP wiring, idempotency, and that the
// scale-to-zero middleware coexists with the pin handlers.
func TestStandbyDisableEnable(t *testing.T) {
t.Parallel()

if _, err := exec.LookPath("docker"); err != nil {
t.Skipf("docker not available: %v", err)
}

ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute)
defer cancel()

c := NewTestContainer(t, headlessImage)
require.NoError(t, c.Start(ctx, ContainerConfig{}), "failed to start container")
defer c.Stop(ctx)

require.NoError(t, c.WaitReady(ctx), "api not ready")

client, err := c.APIClient()
require.NoError(t, err, "failed to create API client")

// Idempotent disable.
r1, err := client.DisableStandbyWithResponse(ctx)
require.NoError(t, err, "DisableStandby request failed")
require.Equal(t, http.StatusNoContent, r1.StatusCode(), "unexpected status: %s body=%s", r1.Status(), string(r1.Body))

r2, err := client.DisableStandbyWithResponse(ctx)
require.NoError(t, err, "second DisableStandby request failed")
require.Equal(t, http.StatusNoContent, r2.StatusCode(), "unexpected status: %s body=%s", r2.Status(), string(r2.Body))

// Normal request must still flow while pinned (scaletozero middleware
// runs on every request — the pin must not deadlock or break it).
readResp, err := client.ReadClipboardWithResponse(ctx)
require.NoError(t, err, "ReadClipboard request failed while pinned")
require.Equal(t, http.StatusOK, readResp.StatusCode(), "unexpected read status while pinned: %s body=%s", readResp.Status(), string(readResp.Body))

// Idempotent enable.
r3, err := client.EnableStandbyWithResponse(ctx)
require.NoError(t, err, "EnableStandby request failed")
require.Equal(t, http.StatusNoContent, r3.StatusCode(), "unexpected status: %s body=%s", r3.Status(), string(r3.Body))

r4, err := client.EnableStandbyWithResponse(ctx)
require.NoError(t, err, "second EnableStandby request failed")
require.Equal(t, http.StatusNoContent, r4.StatusCode(), "unexpected status: %s body=%s", r4.Status(), string(r4.Body))
}
Loading
Loading