PMM-15205 Ship SEP embedded in PMM (Tech Preview) - #5884
Draft
yyyyyyyan wants to merge 27 commits into
Draft
Conversation
Vendors the SEP frontend into the PMM UI workspace under ui/packages/sep/* and mounts it on the /sep route, so the side-car's screens render inside PMM's shell instead of its own. Co-authored-by: Ignacio Durand <nachodurand@gmail.com> Signed-off-by: Yan Orestes <yan.orestes@percona.com> Claude-Session: https://claude.ai/code/session_01RFUiQUpHgyaK6ozEQJ8wmT
The embedded UI exchanges the PMM session for a SEP bearer rather than carrying an injected static token. Co-authored-by: Fábio Silva <ffjs1993@gmail.com> Co-authored-by: Ignacio Durand <nachodurand@gmail.com> Signed-off-by: Yan Orestes <yan.orestes@percona.com> Claude-Session: https://claude.ai/code/session_01RFUiQUpHgyaK6ozEQJ8wmT
Co-authored-by: Ignacio Durand <nachodurand@gmail.com> Signed-off-by: Yan Orestes <yan.orestes@percona.com> Claude-Session: https://claude.ai/code/session_01RFUiQUpHgyaK6ozEQJ8wmT
Co-authored-by: Ignacio Durand <nachodurand@gmail.com> Signed-off-by: Yan Orestes <yan.orestes@percona.com> Claude-Session: https://claude.ai/code/session_01RFUiQUpHgyaK6ozEQJ8wmT
Co-authored-by: Ignacio Durand <nachodurand@gmail.com> Signed-off-by: Yan Orestes <yan.orestes@percona.com> Claude-Session: https://claude.ai/code/session_01RFUiQUpHgyaK6ozEQJ8wmT
Co-authored-by: Fabio Silva <ffjs1993@gmail.com> Co-authored-by: Ignacio Durand <nachodurand@gmail.com> Signed-off-by: Yan Orestes <yan.orestes@percona.com> Claude-Session: https://claude.ai/code/session_01RFUiQUpHgyaK6ozEQJ8wmT
Signed-off-by: Yan Orestes <yan.orestes@percona.com> Claude-Session: https://claude.ai/code/session_01RFUiQUpHgyaK6ozEQJ8wmT
Co-authored-by: Fabio Silva <ffjs1993@gmail.com> Co-authored-by: Ignacio Durand <nachodurand@gmail.com> Signed-off-by: Yan Orestes <yan.orestes@percona.com> Claude-Session: https://claude.ai/code/session_01RFUiQUpHgyaK6ozEQJ8wmT
Co-authored-by: Ignacio Durand <nachodurand@gmail.com> Signed-off-by: Yan Orestes <yan.orestes@percona.com> Claude-Session: https://claude.ai/code/session_01RFUiQUpHgyaK6ozEQJ8wmT
changeOrigin rewrites Host and Referer but not Origin, so Grafana's CSRF check rejected state-changing requests through the dev proxy with a 403. Co-authored-by: Fabio Silva <ffjs1993@gmail.com> Co-authored-by: Ignacio Durand <nachodurand@gmail.com> Signed-off-by: Yan Orestes <yan.orestes@percona.com> Claude-Session: https://claude.ai/code/session_01RFUiQUpHgyaK6ozEQJ8wmT
Signed-off-by: Yan Orestes <yan.orestes@percona.com> Claude-Session: https://claude.ai/code/session_01RFUiQUpHgyaK6ozEQJ8wmT
Co-authored-by: Peter Addo <peter.addo@percona.com> Signed-off-by: Yan Orestes <yan.orestes@percona.com> Claude-Session: https://claude.ai/code/session_01RFUiQUpHgyaK6ozEQJ8wmT
Co-authored-by: Ignacio Durand <nachodurand@gmail.com> Signed-off-by: Yan Orestes <yan.orestes@percona.com> Claude-Session: https://claude.ai/code/session_01RFUiQUpHgyaK6ozEQJ8wmT
Co-authored-by: Ignacio Durand <nachodurand@gmail.com> Signed-off-by: Yan Orestes <yan.orestes@percona.com> Claude-Session: https://claude.ai/code/session_01RFUiQUpHgyaK6ozEQJ8wmT
Co-authored-by: Ignacio Durand <nachodurand@gmail.com> Signed-off-by: Yan Orestes <yan.orestes@percona.com> Claude-Session: https://claude.ai/code/session_01RFUiQUpHgyaK6ozEQJ8wmT
PMM-15281 made sepEnabled a required field of ReadonlySettings. The mock that has to satisfy that type, src/api/__mocks__/settings.ts, only reached main on 2026-09-02 with PMM-15302 (#5744) - after every UI checkpoint on this branch - so the two met for the first time when the branch last merged main. The merge was textually clean and left a type error behind: tsc fails with TS2741 on READONLY_SETTINGS_MOCK. Nothing caught it because the branch had no pull request of its own, so CI had never run on it. false matches the default the provider ships and the value testUtils.tsx already uses. Claude-Session: https://claude.ai/code/session_01RFUiQUpHgyaK6ozEQJ8wmT Signed-off-by: Yan Orestes <yan.orestes@percona.com>
Ships the /sep/ nginx reverse-proxy configuration as first-class shipped configuration, replacing the hand-maintained overlay in SEP's feature-build harness. Gated on PMM_ENABLE_SEP; with the flag unset the only shipped delta is one inert glob include. Adds PMM_SEP_ADDRESS (default sep:9000), validated before interpolation, with the resolver read from /etc/resolv.conf so the AMI and OVF images work under Podman's aardvark-dns. Claude-Session: https://claude.ai/code/session_01RFUiQUpHgyaK6ozEQJ8wmT
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #5884 +/- ##
==========================================
+ Coverage 43.59% 44.23% +0.63%
==========================================
Files 415 304 -111
Lines 43134 33119 -10015
==========================================
- Hits 18804 14649 -4155
+ Misses 22454 16950 -5504
+ Partials 1876 1520 -356 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…5762) With PMM_ENABLE_SEP set, pmm-server writes SEP's deployment secrets into /srv/sep as files named after the canonical SEP settings name each supplies: SECRET_KEY, SEP__DATABASE__PASSWORD, INVENTORY__DATABASE__PASSWORD and TASKS__DATABASE__PASSWORD. SECRET_KEY is generated once and persisted, so restarting PMM keeps SEP sessions valid; the database passwords are rewritten every start so rotation takes effect on the next restart. PMM_SEP_POSTGRES_PASSWORD is now optional - PMM generates and persists one when it is unset, so a bare docker compose up needs no secret chosen in advance. Also carries the /srv/sep mountpoint and the pmm-sep volume, which the closed #5755 used to supply. Claude-Session: https://claude.ai/code/session_01RFUiQUpHgyaK6ozEQJ8wmT
Refreshes the four vendored spec files from SEP main c16578dfe and regenerates
the TS client. The previous sync watermark was 249f6738a (2026-08-25); 19 SEP
commits have touched the mirrored packages since.
Copied wholesale and formatted rather than hand-spliced, per the porting notes.
main.json's raw diff was pure reflow and collapses to nothing once oxfmt runs.
Endpoint-shape audit over the drift, which is the check that matters here
rather than the commit count:
- All nine bare-array apiClient.get call sites still receive arrays:
/api/admin/apps/, /api/apps/, /api/sep/hosts/,
/api/sep/services/{id}/schemas, /api/sep/schemas/{id}/tables,
/api/apps/atw/, /execution-events/{id}, and the two that already
normalize either shape.
- Three endpoints were removed, all inventory writes (SEP-1856). PMM does
not vendor the inventory app and renders it generically from the schema
the side-car serves, which no longer declares those forms or actions, so
nothing in PMM reaches them.
- Sixteen endpoints were added; all are additive.
This commit alone leaves @sep/api failing check-types, because SettingClassEnum
is gone from the spec. PMM-15440 immediately following resolves it.
Claude-Session: https://claude.ai/code/session_01RFUiQUpHgyaK6ozEQJ8wmT
Signed-off-by: Yan Orestes <yan.orestes@percona.com>
SEP-1825 lets an app declare its own settings class without editing core or migrations, so the class travels as the Pydantic class __name__ - "SEPSettings", "AlertsSettings" - rather than a member of a closed enum, and SettingClassEnum is gone from the spec. Not optional: with the refreshed spec, aliasing the removed schema is a compile error. Claude-Session: https://claude.ai/code/session_01RFUiQUpHgyaK6ozEQJ8wmT Signed-off-by: Yan Orestes <yan.orestes@percona.com>
SEP-1779 lifted the duplicated search debounce into @sep/framework. It landed before the 249f6738a sync watermark, but the sync that set that watermark audited spec and endpoint shape rather than exports, so PMM never picked it up and the hook is absent from the vendored framework. Surfaced as a hard dependency: SEP-1956's Send dialog imports useDebouncedValue from @sep/framework, so the next commit does not compile without it. Ports the hook and its test only. PMM's own duplicated debounces - CollectPane's SNIPPET_SEARCH_DEBOUNCE_MS and ScriptPreviewField's DEBOUNCE_MS - are left alone; collapsing them onto this hook is SEP-1779's refactor and has no bearing on the drift being closed here. Claude-Session: https://claude.ai/code/session_01RFUiQUpHgyaK6ozEQJ8wmT Signed-off-by: Yan Orestes <yan.orestes@percona.com>
…ider Ports SEP-1956. The Send dialog resolves support case references against the delivery provider as the user types instead of requiring an exact reference, falling back to a plain text input when the deployment declares no case-search section or the provider errors. atw_config gained case_search_available, so useAtwConfig's error fallback carries it too - without that the fallback no longer satisfies AtwConfig and the regenerated client fails to compile. SendDialog.tsx was byte-identical to SEP's pre-change copy once run through PMM's formatter, so it is taken wholesale rather than hand-merged. The test's case-search helper is indexed rather than destructured with a tuple annotation: PMM typechecks the ATW tests where SEP does not, and mock.calls is not a fixed-length tuple. Claude-Session: https://claude.ai/code/session_01RFUiQUpHgyaK6ozEQJ8wmT Signed-off-by: Yan Orestes <yan.orestes@percona.com>
Ports SEP-1943's frontend arm. TaskHistoryStatusEnum gained UNLAUNCHABLE for an execution whose Nomad node could not launch the requested interpreter, so the history badge, the log-viewer badge and the ATW resend set all learn it. The compiler enforced the first of those against the regenerated union. The second half is a crash that predates SEP-1943 and is fixed on the way past. FinishStatus never listed 'stale' while the SSE finish event forwards the backend's status verbatim, so MAP['stale'] was undefined and reading .color off it threw - blanking the whole log viewer for any stale task whose logs were open. 'stale' joins the union and the map, and the badge now renders an unrecognised status as its raw text rather than indexing into nothing, so the next member added backend-side degrades instead of crashing. unlaunchable and executor-gone deliberately share one entry: to an operator both mean the node could not run this, not that the script failed. Claude-Session: https://claude.ai/code/session_01RFUiQUpHgyaK6ozEQJ8wmT Signed-off-by: Yan Orestes <yan.orestes@percona.com>
Ports SEP-1942's frontend arm. A send log's step entries gained a `kind` discriminator - resolution or upload - and `failed` joins the step status, so a failed send says which step ended it rather than only that it ended. `kind` is optional only for history: the backend writes it on every entry it records now, but rows persisted before it existed carry entries without it. Claude-Session: https://claude.ai/code/session_01RFUiQUpHgyaK6ozEQJ8wmT Signed-off-by: Yan Orestes <yan.orestes@percona.com>
Ports the frontend arm of SEP-1892. Scheduled tasks carry an optional display_name alongside name; PluginTask did not declare it, so the friendlier label the side-car sends was dropped on the floor. PMM's copy calls this PluginTask where SEP calls it AppTask - the pre-rename vocabulary the vendored packages still use. The rest of SEP-1892 is inventory tombstone collection: backend-only, shipped by the side-car. Claude-Session: https://claude.ai/code/session_01RFUiQUpHgyaK6ozEQJ8wmT Signed-off-by: Yan Orestes <yan.orestes@percona.com>
Ports SEP-1818. A host field may now name a target_service; when it does, the selector shows a non-blocking warning if the chosen executor's address differs from the address of the node the service runs on. Choosing a non-co-located executor stays legal - it is usually a mistake, and it used to fail late and unhelpfully. Co-location is compared on address alone: Nomad node names and inventory display names are independent namespaces, so two hosts sharing an address are one machine. An absent address is unknown rather than different, so the warning stays silent whenever co-location cannot be established. HostSelector.tsx had diverged too far from SEP's copy for a wholesale take - 272 lines apart after running SEP's baseline through PMM's formatter - so the change is applied by hand. isHostMismatch and its test are taken verbatim. Only HostField gains target_service; PMM vendors no MultiHostField. Claude-Session: https://claude.ai/code/session_01RFUiQUpHgyaK6ozEQJ8wmT Signed-off-by: Yan Orestes <yan.orestes@percona.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Ticket number: PMM-15205
Feature build:
perconalab/pmm-server-fb:PR-4500-2c43912— built byPercona-Lab/pmm-submodules#4500 from submodules commit
2c43912, pinning thisbranch at
53521917d(2026-09-03).How to tell whether a PR is in that build. The submodules pin follows this
branch's tip at build time, so the image tag names the submodules commit, not
the PMM one. The build's
ci.ymlrecords the PMM commit and date it was cutfrom, and the line above repeats it. For anything merged here since, ask git:
Re-cut the build after merging anything you need exercised, and refresh both
that
ci.ymlblock and this line.Integration branch for the PMM + SEP Integration (Tech Preview) epic. Every
child ticket of PMM-15205 lands here first, so
mainsees one reviewable changewith the whole Tech Preview in it rather than a half-wired feature spread over a
dozen merges.
This PR is a draft and stays that way until the epic closes. It is open now
so the accumulated diff is reviewable while the remaining children land, not
because it is ready.
How this branch is used
PRs for PMM-15205 children target
PMM-15205-sep-fb(or stack on a siblingwhose own base is this branch). They are squash-merged here, one commit per
ticket. This branch is then merged to
mainonce.The branch was previously used only to compile feature builds — it was
re-derived from scratch whenever a build was needed, so it carried duplicate and
superseded copies of work that was still in review. It has been rebuilt as a
linear history on current
main, one commit per ticket, and its tree verifiedbyte-identical to the last feature-build derivation plus the current heads of
#5759 and #5762.
Composition
ui/packages/sep/*and mounted on/sep(4 commits: the migration, a peak-ui alignment, form/layout fixes, and a dev-proxyOriginfix)/sep/nginx reverse proxy, productized out of the harness overlaySECRET_KEYand database credentials published as secret files; the database password generated when unsetmainc16578dfe— support-case autocomplete, theunlaunchablestatus, delivery-step naming, the executor co-location warning, per-task display names, and a plain-string setting classPMM-15238 (#5700) and PMM-15288 (#5728) are already on
mainand are not part ofthis diff. PMM-15280 (#5755) was closed — the Grafana service account moved to
the SEP side.
Everything here is behind
PMM_ENABLE_SEPWith the flag unset the shipped backend delta is one inert glob
includeinpmm.conf; nginx renders no drop-in, no secret files are written, and no SEPdatabase is provisioned. The SEP UI is additionally gated on the
sepEnabledsetting (PMM-15281), which defaults to false, so the nav entry and its routes
stay hidden.
Please merge with a merge commit, not a squash
The repo's default is squash-and-merge, which would collapse the whole epic —
13 tickets and several thousand reviewed lines — into a single
maincommit anddiscard the per-ticket attribution, including the
Co-authored-bytrailers thatcredit everyone who worked on the UI migration. A merge commit keeps the
per-ticket history bisectable on
main.Testing
Each child ticket carries its own testing evidence on its own PR; this
description does not restate it.
What is specific to the integration branch:
shellcheck -s bashandbash -nare clean onentrypoint.sh,sep-secretsand
postgres-sepat the branch tip.go test ./managed/utils/envvars/...passes. Inmanaged/services/server,TestSepConfigFilespasses;TestFilesfails outside a container because itreads
/etc/nginx/nginx.conf— it fails identically onmain, so it isneither covered by nor broken by this branch.
identical apart from the two backend PRs being at their current reviewed heads
rather than superseded copies.
Two defects were found and fixed on this branch, and they are the reason it
needs a PR at all.
The SEP log viewer was crashing on any task in
stalestatus whose logs wereopen:
FinishStatusnever listed it while the SSEfinishevent forwards thebackend's status verbatim, so the badge's map returned
undefinedand reading.coloroff it threw. Fixed in #5886, which also makes the badge render anunrecognised status as raw text rather than indexing into nothing.
The second was found by CI on this PR's first run. PMM-15281 made
sepEnableda required field ofReadonlySettings;src/api/__mocks__/settings.ts, which has to satisfy thattype, only reached
mainon 2026-09-02 with #5744, after every UI change onthis branch. They met for the first time when the branch last merged
main.The merge was textually clean and left
tscfailing with TS2741 — and nothingcaught it, because the branch had no pull request and so had never been built by
CI.
Before merge
/srv/sepmountpoint and the rendered nginx drop-in only exist in a real build.
https://claude.ai/code/session_01RFUiQUpHgyaK6ozEQJ8wmT