Skip to content

Fix lakectl fs rm -r deleting siblings that share the path prefix - #10605

Merged
nopcoder merged 3 commits into
treeverse:masterfrom
RyoKusnadi:fix/lakectl-rm-recursive-keep-siblings
Nov 10, 2026
Merged

nopcoder merged 3 commits into
treeverse:masterfrom
RyoKusnadi:fix/lakectl-rm-recursive-keep-siblings

Conversation

@RyoKusnadi

Copy link
Copy Markdown
Contributor

Closes #10582

Replaces #10600 (closed, and its fork branch is gone), with @nopcoder's review applied.

Change Description

Background

lakectl fs rm -r should delete a folder. fs download -r already adds a trailing / to the path, but fs rm -r didn't.

Bug Fix

  1. Problem - lakectl fs rm -r lakefs://repo/main/logs also deletes logs.txt and logs-archive/3.txt, not just logs/1.txt and logs/2.txt.
    How it happens: run the command with a path that has no trailing slash, while another object name starts with the same text.
    Steps: upload logs/1.txt, logs/2.txt, logs.txt, logs-archive/3.txt, then run lakectl fs rm -r lakefs://repo/main/logs.
    Expected: only logs/1.txt and logs/2.txt are deleted. Actual: all four are deleted.
  2. Root cause - cmd/lakectl/cmd/fs_rm.go passed the path unchanged as the list prefix, and the listing matches on a raw string prefix.
  3. Solution - new shared helper ensureTrailingPathDelimiter in common_helpers.go adds a trailing / to a non-empty path. An empty path (whole branch) is unchanged. fs rm -r now uses it, and so do the two places that already did this by hand: fs download -r and local init (no behavior change there).

Scope: fs ls -r is read-only and left as is, as the issue notes.

Testing Details

No new unit test, per review on #10600. Ran go build ./cmd/lakectl/..., go vet ./cmd/lakectl/cmd/, go test ./cmd/lakectl/cmd/ (77 passed), gofmt -l, and golangci-lint (no findings in the touched files).
Not run: esti system tests or a live lakeFS server, so the four-object steps above were not run end to end.
Environment: master @ e86d145, go1.27.1, darwin/arm64.

Breaking Change?

Behavior change: rm -r on a path without / no longer removes siblings that share the prefix. Anyone relying on that would now need to list the sibling paths themselves.

@github-actions github-actions Bot added the area/lakectl Issues related to lakeFS' command line interface (lakectl) label Nov 10, 2026
@RyoKusnadi

Copy link
Copy Markdown
Contributor Author

@nopcoder this is #10600 moved over with your review applied: helper renamed to ensureTrailingPathDelimiter in common_helpers.go, reused in fs download and local init, and the test dropped. Mind taking a look?

Comment thread cmd/lakectl/cmd/fs_download.go Outdated
Comment on lines +76 to +77
if remotePath != "" {
*remote.Path = ensureTrailingPathDelimiter(remotePath)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remotePath = ensureTrailingPathDelimiter(remotePath)
*remote.Path = remotePath

we will need the updated remotePath for later - check if it used after this line.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it is used later: strings.TrimPrefix(listItem.Path, remotePath) when building relPath. The result was the same thanks to the second TrimPrefix of /, but keeping the two in sync is cleaner. Updated in the latest commit. I kept the remotePath != "" guard, since remote.Path is nil for a URI with no path and the assignment would panic there.

@RyoKusnadi

Copy link
Copy Markdown
Contributor Author

@nopcoder updated in 2f5f63b, remotePath now carries the trailing delimiter too. Mind taking another look?

@RyoKusnadi

Copy link
Copy Markdown
Contributor Author

Heads up on the red "PR Requirements" check: the "Linked issue" step runs gh with TREEVERSE_CI_TOKEN, which is empty on fork PRs, so it fails before it checks anything. #10582 is linked (Closes #10582), so it should pass once it runs with the token.

@nopcoder nopcoder added the include-changelog PR description should be included in next release changelog label Nov 10, 2026

@nopcoder nopcoder left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM - left small comment related to a condition I think we can drop.

Comment thread cmd/lakectl/cmd/fs_download.go Outdated
ch := make(chan objectInfo, filesChanSize)
if remotePath != "" && !strings.HasSuffix(remotePath, uri.PathSeparator) {
*remote.Path += uri.PathSeparator
if remotePath != "" {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't think this if is needed

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, not needed. The path URI parser never leaves Path nil, and the helper already returns "" as is. Removed in 602e4c3.

@nopcoder
nopcoder merged commit 0a8a0ab into treeverse:master Nov 10, 2026
96 of 102 checks passed
@RyoKusnadi
RyoKusnadi deleted the fix/lakectl-rm-recursive-keep-siblings branch November 11, 2026 12:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/lakectl Issues related to lakeFS' command line interface (lakectl) include-changelog PR description should be included in next release changelog

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: lakectl fs rm -r on a path without a trailing slash also deletes sibling objects that share its name as a prefix

2 participants