Skip to content

fix(cli): run the platform-credential import whatever hook cobra picks - #4175

Merged
pengpeng merged 1 commit into
mainfrom
cursor/cli-identity-every-subtree
Sep 20, 2026
Merged

pengpeng merged 1 commit into
mainfrom
cursor/cli-identity-every-subtree

Conversation

@pengpeng

Copy link
Copy Markdown
Member

The bug

In a container that app-service gave a credential to, the first command fails unless it happens to be one of a handful of verbs:

$ olares-cli market list -o json          # 78ms
no Olares profile is configured: the platform-issued credential for this
application is mounted but could not be loaded; reinstall or repair the
application that requested it

$ olares-cli profile list                 # 97ms
testip150@olares.cn  logged-in  platform(lares)

$ olares-cli market list -o json          # 405ms
… 211 apps

78ms does not fit a token exchange, so the failing run never attempted the import; profile list did it, and everything after it worked.

Why

credential.ImportManagedCredential hung on the root's PersistentPreRun, and cobra runs only the nearest persistent hook it finds walking up from the command that ran (command.go, break unless EnableTraverseRunHooks). Nineteen subtrees declared a hook of their own, so none of them ever reached the root's — market, settings, router, knowledge, download, chart, skills, preinstall, files nfs, and cluster with its nine children. profile declares none, which is why it was the one command that repaired the container.

Eighteen of those nineteen hooks existed only to set SilenceUsage, because setting the field on a group does nothing: cobra consults the root and the command that actually ran, never anything in between. A cosmetic need borrowed the one mechanism that also carried authentication, and that is where the bug came from.

The placement predates this; #4174 is what made it visible, because before it every command failed for a different reason.

The fix

Wrap, don't place. wireManagedIdentity walks the tree and wraps the import around every command that declares a hook, both PersistentPreRun and PersistentPreRunE. A command without one runs an ancestor's, and the root declares one, so every path ends at something wrapped. It runs before skipPreRunsForGroupHelp so <group> help still skips everything, and before each wrapped body so the files nfs version gate now has an identity when it checks — a latent bug of its own. viper.BindPFlags stays in the root's body; giving those subtrees viper bindings is a different blast radius.

Remove the cause. SilenceUsage: true on the root covers the whole tree, so the eighteen decorative hooks are deleted. The nineteenth (files nfs) does real work and stays.

Behavior changes

  • profile, dashboard, search, doctor, files and version no longer print full usage after an error, matching the other eighteen trees.
  • The import now runs for verbs that do not need an identity (chart, skills, preinstall), costing one exchange per invocation. It stays a no-op on host installs with no credential mount.

Left alone deliberately, against an earlier draft of this change: the SilenceUsage fields on group commands and leaves are not redundant. A group is the command that ran when it refuses an unknown verb, and a leaf is the case cobra checks. Removing them would change behavior, not just noise.

Tests

All fifteen existing importer tests call managedImporter.run directly, so none of them could tell whether cobra still called it. That is the layer this adds.

cmd/ctl/managed_identity_test.go stands up a credential mount and a cache directory under t.TempDir(), points the grant exchange at a local server that refuses it — which keeps the test off DNS and off the system keychain, while still creating the profile entry — then runs a real verb and reads config.json back. Only the importer ever marks a profile managed; resolving one never writes the file.

Four paths, one container each: market list (no hook below the root), files nfs history list (its own hook), profile list (the one that used to work), cluster pod list (a group two levels down). On main the first, second and fourth fail with the container's exact message and profile list passes — the session above, reproduced.

A second test asserts the root's SilenceUsage and that the only commands declaring hooks are the root, files nfs, and backups (which brings its own from backups-sdk). Adding a hook is fine — the walker covers it — but the failure points at the table above so a new subtree gets a case rather than silently going unchecked.

Also verified in a container with the cross-built linux binary, first command, nothing else run before it:

first market list config.json
main no Olares profile is configured: …mounted but could not be loaded not written
this branch /api/refresh: … no such host (the test credential is fake) managed profile present

go vet ./cmd/..., go test ./cmd/... ./pkg/credential/... ./pkg/cliconfig/..., and CGO_ENABLED=0 GOOS=linux go build ./cmd/ are green.

Made with Cursor

The import hung on the root's PersistentPreRun, and cobra runs only the
nearest one: any subtree declaring a hook of its own removed the identity
from every verb beneath it. Eighteen subtrees had one that did nothing but
set SilenceUsage, so in a fresh container `market list`, `settings`,
`router`, `knowledge` and all of `cluster` reported that no profile was
configured while the credential sat unread on its mount. `profile list`,
one of the few trees without a hook, repaired the container for whatever
ran after it.

Wrap the import around every declared hook instead of placing it in one,
and set SilenceUsage on the root, which covers the whole tree and replaces
the eighteen hooks that caused this. The nfs backend-version gate keeps its
hook and now runs with an identity available.

The existing importer tests all call the importer directly, so none of them
could see this. The new end-to-end cases drive the real tree the way a
container does and assert on config.json.

Co-authored-by: Cursor <cursoragent@cursor.com>
@vercel

vercel Bot commented Sep 20, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
olares Ready Ready Preview Sep 20, 2026 4:48pm UTC
1 Skipped Deployment
Project Deployment Actions Updated
olares-docs Ignored Ignored Sep 20, 2026 4:48pm UTC

@pengpeng
pengpeng merged commit a85a55e into main Sep 20, 2026
15 checks passed
pengpeng added a commit to beclab/lares that referenced this pull request Sep 20, 2026
…#30)

olares-cli imported the mounted credential only from a hook that most
subtrees shadowed, so a conversation opening with a market, cluster,
settings, router or knowledge question was told no profile was configured
and only recovered once some other command had run. 1.12.7-cli.10 runs the
import whatever hook cobra picks (beclab/Olares#4175).

Dockerfile.base changed, so image_base_tag moves with it.

Co-authored-by: Cursor <cursoragent@cursor.com>

This branch was successfully deployed

1 active deployment
Preview – olares 0a754c82 Deployed Sep 20, 2026 by vercel[bot]
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