Skip to content

Commit 8d1743a

Browse files
committed
Add gradual extension upload endpoint
1 parent 6207032 commit 8d1743a

5 files changed

Lines changed: 705 additions & 397 deletions

File tree

server/cmd/api/api/chromium.go

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,31 @@ const (
3434
extensionsBaseDir = "/home/kernel/extensions"
3535
)
3636

37-
// UploadExtensionsAndRestart handles multipart upload of one or more extension zips and extracts
38-
// them under /home/kernel/extensions/<name>. Unpacked extensions are loaded immediately over CDP;
39-
// extensions that require enterprise policy restart Chromium after their policy is installed.
37+
// UploadExtensionsAndRestart uploads extensions and always restarts Chromium.
4038
func (s *ApiService) UploadExtensionsAndRestart(ctx context.Context, request oapi.UploadExtensionsAndRestartRequestObject) (oapi.UploadExtensionsAndRestartResponseObject, error) {
39+
return s.uploadExtensions(ctx, request, true)
40+
}
41+
42+
// UploadExtensions uploads extensions and activates ordinary unpacked extensions over CDP.
43+
func (s *ApiService) UploadExtensions(ctx context.Context, request oapi.UploadExtensionsRequestObject) (oapi.UploadExtensionsResponseObject, error) {
44+
response, err := s.uploadExtensions(ctx, oapi.UploadExtensionsAndRestartRequestObject{Body: request.Body}, false)
45+
if err != nil {
46+
return nil, err
47+
}
48+
49+
switch response := response.(type) {
50+
case oapi.UploadExtensionsAndRestart201Response:
51+
return oapi.UploadExtensions201Response{}, nil
52+
case oapi.UploadExtensionsAndRestart400JSONResponse:
53+
return oapi.UploadExtensions400JSONResponse{BadRequestErrorJSONResponse: response.BadRequestErrorJSONResponse}, nil
54+
case oapi.UploadExtensionsAndRestart500JSONResponse:
55+
return oapi.UploadExtensions500JSONResponse{InternalErrorJSONResponse: response.InternalErrorJSONResponse}, nil
56+
default:
57+
return oapi.UploadExtensions500JSONResponse{InternalErrorJSONResponse: oapi.InternalErrorJSONResponse{Message: "internal error"}}, nil
58+
}
59+
}
60+
61+
func (s *ApiService) uploadExtensions(ctx context.Context, request oapi.UploadExtensionsAndRestartRequestObject, forceRestart bool) (oapi.UploadExtensionsAndRestartResponseObject, error) {
4162
log := logger.FromContext(ctx)
4263
start := time.Now()
4364
log.Info("upload extensions: begin")
@@ -159,7 +180,8 @@ func (s *ApiService) UploadExtensionsAndRestart(ctx context.Context, request oap
159180
return oapi.UploadExtensionsAndRestart500JSONResponse{InternalErrorJSONResponse: oapi.InternalErrorJSONResponse{Message: err.Error()}}, nil
160181
}
161182

162-
if requiresRestart {
183+
restarted := forceRestart || requiresRestart
184+
if restarted {
163185
if err := s.restartChromiumAndWait(ctx, "extension upload"); err != nil {
164186
return oapi.UploadExtensionsAndRestart500JSONResponse{
165187
InternalErrorJSONResponse: oapi.InternalErrorJSONResponse{Message: err.Error()},
@@ -174,10 +196,10 @@ func (s *ApiService) UploadExtensionsAndRestart(ctx context.Context, request oap
174196
},
175197
}, nil
176198
}
177-
requiresRestart = true
199+
restarted = true
178200
}
179201

180-
log.Info("extensions ready", "restarted", requiresRestart, "elapsed", time.Since(start).String())
202+
log.Info("extensions ready", "restarted", restarted, "elapsed", time.Since(start).String())
181203
return oapi.UploadExtensionsAndRestart201Response{}, nil
182204
}
183205

server/e2e/e2e_chromium_test.go

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -278,11 +278,11 @@ func TestExtensionUploadAndActivation(t *testing.T) {
278278
err = w.Close()
279279
require.NoError(t, err)
280280
start := time.Now()
281-
rsp, err := client.UploadExtensionsAndRestartWithBodyWithResponse(ctx, w.FormDataContentType(), &body)
281+
rsp, err := client.UploadExtensionsWithBodyWithResponse(ctx, w.FormDataContentType(), &body)
282282
elapsed := time.Since(start)
283-
require.NoError(t, err, "uploadExtensionsAndRestart request error")
283+
require.NoError(t, err, "uploadExtensions request error")
284284
require.Equal(t, http.StatusCreated, rsp.StatusCode(), "unexpected status: %s body=%s", rsp.Status(), string(rsp.Body))
285-
t.Logf("/chromium/upload-extensions-and-restart completed in %s (%d ms)", elapsed.String(), elapsed.Milliseconds())
285+
t.Logf("/chromium/upload-extensions completed in %s (%d ms)", elapsed.String(), elapsed.Milliseconds())
286286
}
287287

288288
browserWebSocketAfter, err := cdpclient.BrowserWebSocketURL(ctx, versionURL)
@@ -302,6 +302,28 @@ func TestExtensionUploadAndActivation(t *testing.T) {
302302
out, err := cmd.CombinedOutput()
303303
require.NoError(t, err, "title verify failed: %v output=%s", err, string(out))
304304
}
305+
306+
// The legacy endpoint retains its unconditional restart behavior.
307+
{
308+
client, err := c.APIClient()
309+
require.NoError(t, err)
310+
var body bytes.Buffer
311+
w := multipart.NewWriter(&body)
312+
fw, err := w.CreateFormFile("extensions.zip_file", "ext.zip")
313+
require.NoError(t, err)
314+
_, err = io.Copy(fw, bytes.NewReader(extZip))
315+
require.NoError(t, err)
316+
require.NoError(t, w.WriteField("extensions.name", "restart-testext"))
317+
require.NoError(t, w.Close())
318+
319+
rsp, err := client.UploadExtensionsAndRestartWithBodyWithResponse(ctx, w.FormDataContentType(), &body)
320+
require.NoError(t, err, "uploadExtensionsAndRestart request error")
321+
require.Equal(t, http.StatusCreated, rsp.StatusCode(), "unexpected status: %s body=%s", rsp.Status(), string(rsp.Body))
322+
}
323+
324+
browserWebSocketAfterRestart, err := cdpclient.BrowserWebSocketURL(ctx, versionURL)
325+
require.NoError(t, err, "get browser WebSocket URL after legacy extension upload")
326+
require.NotEqual(t, browserWebSocketAfter, browserWebSocketAfterRestart, "legacy endpoint did not restart Chromium")
305327
}
306328

307329
func TestScreenshotHeadless(t *testing.T) {

server/lib/events/category_gen.go

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)