Skip to content

Surface T-shirt size elements in the backoffice admin - #2467

Draft
Elena Peña Tapia (ElePT) wants to merge 2 commits into
mainfrom
add-tshirt-admin-elements
Draft

Surface T-shirt size elements in the backoffice admin#2467
Elena Peña Tapia (ElePT) wants to merge 2 commits into
mainfrom
add-tshirt-admin-elements

Conversation

@ElePT

@ElePT Elena Peña Tapia (ElePT) commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

Summary

The T-shirt size models (ComputeProfile, FunctionSize, Program.default_size) shipped in 0.36.0, but a function's size catalog (its sizes map) and its default size weren't surfaced in the backoffice admin where operators work. This PR adds them, and — as a second step — makes a Fleets function created purely in the admin actually runnable.

Part 1 — Surface the size elements

On the Program (Function) page

  • FunctionSizeInline — a function's whole sizes map (each size key → compute profile) is now editable inline on its own page, mirroring the catalog the upload endpoint's sizes payload builds.
  • default_size field in a new "Sizes" fieldset. Its dropdown is restricted to the function's own sizes (Program.clean() rejects a default belonging to another function).
  • sizes_summary changelist column — N (default: s) or -.

On the ComputeProfile page

  • sizes_using column — reference count, so an operator sees a profile is in use before editing/deleting it (the FK is PROTECT).

Both changelist columns are annotated to avoid per-row queries.

Part 2 — Make a backoffice-created Fleets function runnable

A Program created purely in the admin previously got only model defaults, skipping two runnable-critical steps the upload use case performs. Replicated here, reusing upload.py logic (single source of truth):

  • CE project assignment. ProgramAdminForm.clean() assigns a Code Engine project to a Fleets function (provider's active project, or the deployment default) via CodeEngineProject.objects.assign_to_program, and blocks the save — with the upload endpoint's own message — when none is available. A Fleets function with no active CE project cannot run, so this fails loud rather than persisting a dead function.
  • Default-size seeding. ProgramAdmin.save_related() seeds the deployment default size (DEFAULT_FUNCTION_SIZEDEFAULT_FUNCTION_SIZE_PROFILE) for a size-less Fleets function. Runs after the inline FunctionSize formset, so an operator's declared sizes are respected; skipped when a default is already set. Non-fatal + warns when the profile is unregistered.

Refactor: extracted a module-level seed_default_size() (the use-case method now delegates) and promoted _no_ce_project_messageno_ce_project_message.

Scope

  • Admin-only. No model or migration changes.
  • Does not touch Job.compute_profile (the deprecated string) — that's a separate branch's concern.

Behavioral note (blocking tradeoff)

Because clean() blocks any Fleets save that can't get an active CE project, a legacy/misconfigured Fleets function whose provider has no active project (and no default configured) cannot be saved — even to disable it — until a project is assignable. This is the intended "fail-loud" behavior (mirrors the upload endpoint); the operator assigns a project in the same edit.

Testing

  • gateway/tests/test_tshirt_admin.py (21 tests): inline rendering, default_size dropdown scoping, sizes_using count; CE auto-assign (provider + default paths), blocking when unconfigured / no project / inactive project, operator-chosen project preserved, Ray no-ops; default-size seeding (present/absent profile, no-clobber, sizes-declared), plus two end-to-end admin client tests proving the real add form blocks a non-runnable function and creates a runnable one.
  • All admin + upload + size regression suites pass; manage.py check clean; pylint 10.00/10 on both source files; black clean.
  • (Pre-existing, unrelated: test_http_server / test_function_access_client fail in this env without Redis/a running server — untouched by this PR.)

Register a function's size catalog (its sizes map) and default size where
operators actually work:

- FunctionSizeInline on ProgramAdmin: edit each size -> compute profile
  inline on the function page, mirroring the upload endpoint's sizes payload.
- default_size field in a new Sizes fieldset, with its dropdown limited to
  the function's own sizes (Program.clean() rejects a default belonging to
  another function).
- sizes_summary column on the Program changelist (count + default).
- sizes_using column on ComputeProfile so operators see a profile is
  referenced before editing/deleting it (the FK is PROTECT).

Both changelist columns are annotated to avoid per-row queries.
A Program created purely in the Django admin previously got only model
defaults, skipping two runnable-critical steps the upload use case performs:

- CE project assignment. ProgramAdminForm.clean() now assigns a Code Engine
  project to a Fleets function (provider's active project, or the deployment
  default) via CodeEngineProject.objects.assign_to_program, and BLOCKS the save
  with the upload endpoint's own message when none is available — a Fleets
  function with no active project cannot run.
- Default-size seeding. ProgramAdmin.save_related() seeds the deployment
  default size (DEFAULT_FUNCTION_SIZE -> DEFAULT_FUNCTION_SIZE_PROFILE) for a
  size-less Fleets function, like upload does. Runs after the inline
  FunctionSize formset so an operator's declared sizes are respected; skipped
  when a default is already set. Non-fatal + warns when the profile is
  unregistered.

Reuses upload.py logic via a new module-level seed_default_size() (the use
case method now delegates) and a promoted no_ce_project_message(), keeping one
source of truth. Admin-only; no model or migration changes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant