Repository navigation
feat(web): an Images section for every image slot in the chart - #137
Conversation
huskcass
left a comment
There was a problem hiding this comment.
No blockers.
Edits land on the form layer at the slot path (map → repository/tag, string → the string), upgrades seed them from the release's form layer, and clearing a row now really drops the override (before, clearing the engine field on upgrade kept the old one). The @sha256 split fix is right.
Medium
- Subchart images:
dumpHelmValuesishelm show values, which is the parentvalues.yamlonly, so a dependency's own image (the vendored cart chart, or any future subchart) only shows up if the parent pins it. That is why helm-charts#110 has to copy the cart defaults. Coalescing dependency defaults in swissChartValueswould make discovery complete and let #110 drop the copied pins. Fine as a follow-up. - Overdesign, the "set by " label:
provenanceLayersrebuilds the catalog/site layers on the client (variant image, mirror), andLAYER_ORDER/layerOrder/layerFor,imageOverrideLayerand edit-YAML parsing exist only to name a layer. That repeats swiss's composition and the plan's existing Provenance view. Cut it: the effective value as the placeholder is enough. This removes about 100 lines plus their tests, and theuseMemoin all three pages. - The engine is stored twice (
form.imageandform.images.image), and""vs. absent encodes "cleared" vs. "untouched".formFromPlanalready seeds every form-layer slot, so "cleared" can simply mean "in the release's form layer but empty in the form". Keep one map, and theimagePatchspecial case and theimages.imagecheck indropClearedImagesgo away.
Low
useImageSlotskeys on the whole request, so every keystroke anywhere (including in an image row) reruns compose plushelm show values. Key it on the request without image edits andedits. In DeploySheet, share the existing Override YAMLhelmValuesquery instead of adding a second one that also skips the recovery/adaptive wrapping.- The same wiring (
slotsRef,requestOf, layers memo, hook) is repeated in Deploy, Upgrade and DeploySheet. So areImageRows/ImageSource/imagePatchin DeployForm.tsx and DeploySettings.tsx. At least shareimagePatch. ReadingslotsRef.currentat call time to pass kinds intoplanRequestis fragile, and storing the shaped value when a row is edited would removeimageSlotsfromplanRequest. metricsMockis a hard-coded chart key in the UI. Drop the special case.- Over-defensive: the
try/catcharound discovery of server YAML, sorting of unknown layer names, ancestor pruning indropImage.dropImageandCHART_LAYERare exported only for tests.
Fine to merge once the label and the double engine store are trimmed. The subchart defaults can follow in swiss.
— Review Golden
huskcass
left a comment
There was a problem hiding this comment.
No blockers.
All of the previous points are addressed. The layer label and provenanceLayers are gone. The engine now lives only in images["image"], and a clear means "in the release's form layer but empty in the form", which matches what formFromPlan seeds. imageSlots no longer goes through planRequest. Discovery keys on the request without image edits and edits, and in DeploySheet it uses the same recovery and adaptive wrap. metricsMock, the try/catch and the ancestor pruning are removed, and ImageSource/imagePatch are shared.
I checked for regressions and found none. Clearing still drops only repository/tag and keeps pullPolicy. Upgrades seed and send back the existing overrides, and a form-layer image the chart doesn't list still shows as a row. The mirror/catalog dropdown round-trips through imageText.
Low
ImageEdit.draftexists only so a half-typed map ref keeps its trailing colon, because rows store the split shape. Storing{ kind, ref }text and splitting inapplyImageEditswould removedraft,imageValueand the round-trip check. This is optional.- Clearing an extra row (a path the chart doesn't list) makes the row disappear as soon as it's empty, so select-all-and-retype loses the field. This is minor.
Fine to merge.
— Review Golden
The deploy form had one engine image field, so the hang-watcher, PD router, CART, continuation gateway and mooncake master images could only be changed through the YAML editor. The swiss Deploy and Upgrade pages, and the console deploy sheet, now ask swiss for the merged helm values (compose dry run with dumpHelmValues) and list every image slot in them: a map with a string repository (plus its tag), or a string leaf named image. Slots under a section with enabled: false are hidden, and metricsMock is skipped. Each row shows the values path, the effective image and the layer that set it; an edit is written on the form layer at that path, and clearing a row on upgrade drops that override. The engine row keeps the site mirror / catalog original choice, now as a dropdown. splitImage keeps an @sha256: digest with the tag, so repo:pinned@sha256:... and host:port/repo refs split correctly. Refs modelsphere/modelsphere#57 Co-authored-by: aceforeverd <11715200+aceforeverd@users.noreply.github.com>
- Drop the "set by <layer>" label and the client-side layer rebuild behind it; a row shows its values path, with the effective image as the placeholder. - Keep the engine in the same images map as every other slot. On upgrade, a slot in the release's form layer that is empty in the form is cleared. - Store the shaped value when a row is edited, so planRequest no longer needs slot kinds. - Key the dry run on the request without image edits and edits YAML, still debounced, and move the shared Deploy/Upgrade/DeploySheet wiring into useImageSlots. - Drop the metricsMock special case, the try/catch around discovery and the empty-parent pruning in dropImage. Co-authored-by: aceforeverd <11715200+aceforeverd@users.noreply.github.com>
4e9abdb to
6a3bbc2
Compare
The Images section recomposed on every form change, and the Override YAML preview ran its own compose under another key, so a deploy made several /api/v2/compose calls. Both now share one query, keyed on the form without image edits or YAML edits, and it runs only while the Images section (now collapsed by default) or the YAML editor is open. Image edits and YAML overrides are applied on top of the returned values. A deploy that opens neither makes no compose call; opening both makes one per form state. Co-authored-by: aceforeverd <11715200+aceforeverd@users.noreply.github.com>
huskcass
left a comment
There was a problem hiding this comment.
No blockers.
Medium
- The 400 ms debounce on the dry-run key is gone. While Images or the YAML editor is open, every keystroke in a form text field changes the key and fires its own
/api/v2/compose, which works against what this commit is for. Debouncing the key inuseImageSlotsagain fixes it.
Low (cuts)
reloadrebuildsimageSlotsQueryOptionsjust to readenabled. Checking!!requestis enough, and once it is, thethrow new Error("nothing to compose")inqueryFncan't be reached.- The hook returns the same failure twice, as
error(string) andqueryError(Error). KeepqueryErrorand letImageRowsread.message. yamlFromFormandyamlFromButtoncan be oneyamlOpenstate, since only one of the two dialogs can be open at a time.
Verdict: on-demand compose with one shared query is the right simplification. Restore the debounce and this is good to merge.
— Review Golden
Typing in the form while Images or the YAML editor was open composed on every keystroke. The shared query key now waits 400ms for the request to hold still; opening a section still fetches the current request at once. Also: reload checks the request directly, the unreachable "nothing to compose" branch is gone, the hook returns one error, and the deploy sheet keeps a single open flag for its two YAML dialogs. Co-authored-by: aceforeverd <11715200+aceforeverd@users.noreply.github.com>
huskcass
left a comment
There was a problem hiding this comment.
No blockers.
Medium (cuts)
- The debounce is now three render-time refs (
wasEnabled,opening,settled) that change during render and hold a staleform/requestFor/prepare. PlainuseDebounced(requestKey)covers it: build the query from the debounced form. Opening right after an edit then waits at most 400 ms, which is fine and doesn't need its own path. useImageSlots.test.tsadds about 200 lines of hand-written fake DOM just to mount the hook. Once the refs are gone, the debounce isuseDebounced, which is already in use elsewhere. TestingimageSlotsQueryOptionswith theQueryObservercases the file already has is enough, so the fake DOM and the two mount tests can go.
Low
posted as PlanRequestwith the comment is fine. The three earlier Lows are addressed.
Verdict: the behaviour is right. Simplify the debounce and drop the fake-DOM test, then it's good to merge.
— Review Golden
Build the shared compose query from the debounced form and drop the refs that fetched at once on open; opening a section now waits out the same 400ms. Trim useImageSlots.test.ts to the QueryObserver tests of imageSlotsQueryOptions. Co-authored-by: aceforeverd <11715200+aceforeverd@users.noreply.github.com>
huskcass
left a comment
There was a problem hiding this comment.
No blockers.
The debounce is now plain useDebounced(form, 400), and the refs and fake-DOM test are gone. Nothing left to cut.
Verdict: good to merge.
— Review Golden
- site mirror applies on all detected images - correct the /api/v2/compose call, never call it on every keystroke, manual trigger is much clean
3118b09 to
713e9d6
Compare
huskcass
left a comment
There was a problem hiding this comment.
No blockers.
Manual-trigger discovery (target-keyed key + refresh button) and mirror choices on every slot read fine. KISS cuts:
- Medium:
useImageSlots.tslatestRequestRef(assigned during render) plus thelatestRequestparam are unnecessary:useQuerytakes the latest options each render, soqueryFn: () => deployApi.helmValues(requestFor(previewForm(form)))with the target-only key already posts the current form on open/refresh. Drop the ref and the extra param (tests shrink accordingly). - Medium:
DeploySheet.tsxvaluesQueryis auseQuerywithenabled: false,staleTime: Infinity,gcTime: 0and a key that changes on every edit, used only viarefetch(). It is just an async function; makevaluesSource.loadcall compose +helmValuesdirectly (or auseMutation) and drop the query plumbing. - Low:
OverrideDialogdraftRef+cancelledflag guard cases that don't matter (effect only reruns onsource.data; setState after unmount is a no-op). Revert to the plain effect. - Low:
ImageSourcenow has two inputs (choicesor legacyimage) and an optionallabels.defaultfallback; every caller passeschoices. Drop theimageprop, theimageChoicesfallback andimageChoicesitself (slotImageChoicesalready covers the engine row). - Low:
mirrorImagere-implements swissdProfile.MirrorImageon the client; fine for now, but keep the two in sync (or have the server return mirrored refs later).
Verdict: good to merge; the cuts above are optional simplifications.
— Review Golden
huskcass
left a comment
There was a problem hiding this comment.
No blockers.
Previous cuts are addressed: latestRequestRef/latestRequest gone, valuesQuery replaced by a mutation, draftRef/cancelled reverted, ImageSource takes only choices and imageChoices is deleted, mirrorImage points at Profile.MirrorImage.
- Low:
valuesKey(JSON of the request, rebuilt every render) passed as an otherwise-unused mutation variable just to detect staleness is a little clever; fine as is, a short comment on why would help.
Verdict: good to merge.
— Review Golden
Part of modelsphere/modelsphere#57 (task 3, generic image override). Chart side: modelsphere/helm-charts#110. That PR makes the CART and continuation gateway image keys real defaults, so they show up here.
Before this change, the deploy form had a single engine image field. The swiss Deploy and Upgrade pages and the console deploy sheet now show an Images section with one row per image slot in the chart.
swiss/lib/imageSlots.ts,discoverImageSlots) walks the merged values from the compose dry run (/api/v2/compose?dryRun=true&dumpHelmValues=true, which is chart defaults plus plan layers). A slot is either a map with a stringrepository(with itstag) or a string leaf namedimage. Slots under any section withenabled: false(pd,cart,hangWatcher, …) are hidden.<path>.repositoryand<path>.tag, and a string slot gets<path>. An empty row adds no override. Clearing a row on Upgrade drops that form-layer override, the same way clearing tolerations works.splitImagesplits off@sha256:…before looking for the tag, soquay.io/ascend/vllm-ascend:pinned@sha256:…keeps its digest on the tag, andhost:5000/repo:tagkeeps the port on the repository.No swiss or model-catalog changes.
Checks:
npm run typecheck,npm test(638 tests),npm run build,npm run build:swiss.