-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add FileActionSymlink and llb.Symlink
#5519
base: master
Are you sure you want to change the base?
Add FileActionSymlink and llb.Symlink
#5519
Conversation
client/llb/fileop.go
Outdated
SetSymlinkOption(*SymlinkInfo) | ||
} | ||
|
||
func Symlink(target, linkpath string) *FileAction { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like the function needs to be modified to accept ...SymlinkOption
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey nice, I've also wanted something like this 🎉
// FileActionSymlink creates a symlink | ||
FileActionSymlink symlink = 8; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should also add a capability https://github.com/moby/buildkit/blob/master/solver/pb/caps.go.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you be more specific? What exactly needs to be added and why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is done
I checked with @cpuguy83 but you may want to double-check the changes to make sure everything is there
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are certain file attributes that can be set separately from the target file and should be configurable (as they are with regular files).
/tmp/a # ls -l
total 12
-rw-r--r-- 1 root root 21 Nov 14 01:28 dummy_file.txt
lrwxrwxrwx 1 root nobody 14 Nov 14 02:28 dummy_symlink.txt -> dummy_file.txt
solver/pb/ops.proto
Outdated
// destination path for the new file representing the link | ||
string target = 1; | ||
// source path for the link | ||
string linkPath = 2; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just name it src
.
Alternatively, in Go&unix these are newpath
and oldpath
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just pushed up some updates, but I think I'll modify these to oldpath
and newpath
, I think it gives the clearest indication of which is which.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
solver/llbsolver/vertex.go
Outdated
@@ -391,6 +391,8 @@ func fileOpName(actions []*pb.FileAction) string { | |||
names = append(names, fmt.Sprintf("mkdir %s", a.Mkdir.Path)) | |||
case *pb.FileAction_Mkfile: | |||
names = append(names, fmt.Sprintf("mkfile %s", a.Mkfile.Path)) | |||
case *pb.FileAction_Symlink: | |||
names = append(names, fmt.Sprintf("symlink %s -> %s", a.Symlink.LinkPath, a.Symlink.Target)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this arrow makes more sense with arguments in reverse order. Eg. how it is in ls -l
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clarifying, you mean you'd like to see symlink newpath -> oldpath
(the version displayed by ls -l
), rather than symlink oldpath -> newpath
(the order of the arguments to ln -s
and the symlink
syscall)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the above is correct, this is done
solver/llbsolver/ops/file_test.go
Outdated
@@ -643,6 +644,13 @@ func (b *testFileBackend) Mkfile(_ context.Context, m, user, group fileoptypes.M | |||
return nil | |||
} | |||
|
|||
func (b *testFileBackend) Symlink(_ context.Context, m fileoptypes.Mount, a *pb.FileActionSymlink) error { | |||
mm := m.(*testMount) | |||
mm.id += "-mkfile" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-symlink
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, missed this one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
44cbc45
to
6b18ff2
Compare
TIL about symlink ownership and timestamps. Thanks! I just pushed up some updates with symlink timestamps and ownership added -- feel free to review while I make the rest of the changes. I've tested them manually, but I'll need to add some tests that verify they both work. |
6b18ff2
to
61b3374
Compare
@@ -336,6 +347,51 @@ func Mkfile(p string, m os.FileMode, dt []byte, opts ...MkfileOption) *FileActio | |||
} | |||
} | |||
|
|||
type SymlinkInfo struct { | |||
ChownOpt *ChownOpt |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mode
as well
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to the man pages, symlink permissions are irrelevant. Is the mode for the SUID, SGID, and sticky bits? What effect do those have on symlinks?
solver/llbsolver/file/backend.go
Outdated
return errors.WithStack(err) | ||
} | ||
|
||
if err := copy.Chown(src, nil, ch); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@profnandaa What's the wcow implications of this. Not sure which of these functions work correctly there (or if they work on symlink or the target).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me check and revert.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My preliminary findings
When I run the test on Windows (commenting out // requiresLinux(t)
), I get this:
=== NAME TestIntegration/TestFileOpSymlink/worker=containerd
client_test.go:2482:
Error Trace: C:/dev/buildkit/client/client_test.go:2482
C:/dev/buildkit/util/testutil/integration/run.go:97
C:/dev/buildkit/util/testutil/integration/run.go:211
Error: Not equal:
expected: 0
actual : 7777
Then the platform time differences:
=== NAME TestIntegration/TestFileOpSymlink/worker=containerd
client_test.go:2495:
Error Trace: C:/dev/buildkit/client/client_test.go:2495
C:/dev/buildkit/util/testutil/integration/run.go:97
C:/dev/buildkit/util/testutil/integration/run.go:211
Error: Not equal:
expected: time.Date(2024, time.November, 19, 23, 58, 26, 0, time.Local)
actual : time.Date(1969, time.December, 31, 16, 0, 42, 0, time.Local)
Diff:
--- Expected
+++ Actual
@@ -1,2 +1,2 @@
-(time.Time) 2024-11-19 23:58:26 -0800 PST
+(time.Time) 1969-12-31 16:00:42 -0800 PST
Passes when I comment out those:
diff --git a/client/client_test.go b/client/client_test.go
index 840135094..667c4de01 100644
--- a/client/client_test.go
+++ b/client/client_test.go
@@ -2411,7 +2411,7 @@ func testOCILayoutPlatformSource(t *testing.T, sb integration.Sandbox) {
}
func testFileOpSymlink(t *testing.T, sb integration.Sandbox) {
- requiresLinux(t)
+ // requiresLinux(t)
const (
fileOwner = 7777
@@ -2479,8 +2479,8 @@ func testFileOpSymlink(t *testing.T, sb integration.Sandbox) {
require.NoError(t, err)
require.Equal(t, []byte("contents"), dt)
- require.Equal(t, header.Uid, fileOwner)
- require.Equal(t, header.Gid, fileGroup)
+ // require.Equal(t, header.Uid, fileOwner)
+ // require.Equal(t, header.Gid, fileGroup)
entry, ok = m["baz"]
dt = entry.Data
@@ -2489,10 +2489,10 @@ func testFileOpSymlink(t *testing.T, sb integration.Sandbox) {
// make sure it was chowned properly
require.Equal(t, true, ok)
- require.Equal(t, header.Uid, linkOwner)
- require.Equal(t, header.Gid, linkGroup)
+ // require.Equal(t, header.Uid, linkOwner)
+ // require.Equal(t, header.Gid, linkGroup)
- require.Equal(t, header.ModTime, dummyTime)
+ // require.Equal(t, header.ModTime, dummyTime)
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@profnandaa What's the wcow implications of this. Not sure which of these functions work correctly there (or if they work on symlink or the target).
For context, what's WCOW?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, Windows Containers on Windows (WCOW).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, with my diff, I didn't mean that you change your tests. Please go ahead and return the requiresLinux
and leave your previous tests as-is. I was just testing on how it could run on Windows ... we'll still need to figure out permission stuff on Windows, that's a separate issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll revert and open an issue
client/llb/fileop.go
Outdated
SetSymlinkOption(*SymlinkInfo) | ||
} | ||
|
||
func Symlink(target, src string, opts ...SymlinkOption) *FileAction { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
re: #5534 -- will be nice to add some inline documentation for all the exports. We're working with @castrombithisamm to address the other missing ones.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is done
952a185
to
35fb128
Compare
f2575bb
to
ac0ccbf
Compare
* Add file.symlink.create capability and wire it up * Run codegen for new FileActionSymlink Message * Add Symlink test * Add user/group ownership and timestamps to symlink ** Symlinks have user/group ownership that are independent of those of the target file; in linux, the ownership of the symlink itself is only checked when the link resides in a directory with the sticky bit set and the link is the subject of removal or renaming. The sticky bit prevents files in the directory from being deleted or renamed by non-owners (members of the group that owns the file may not delete the file; the user must own the file). In addition to user/group restrictions, linux symlinks have timestamps that are independent of the timestamps on the target file. * Expose symlink options to `llb` package * Add symlink integration test * Use tar exporter for tests ** Using the local exporter causes the files to be exported with the permissions of the user who does the exporting, instead of retaining their file permissions from within the container. Using the tar exporter instead preserves the permissions until they can be checked. * Change symlink fields to `oldpath` and `newpath` ** Also run `make generated-files` * Fix typo * Add doc strings to exported `llb` identifiers * Remove `requiresLinux` from integration test * Revert "Remove `requiresLinux` from integration test" * Add fixes to please the linter Signed-off-by: Peter Engelbert <[email protected]>
ac0ccbf
to
45eab8c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copilot reviewed 7 out of 13 changed files in this pull request and generated no suggestions.
Files not reviewed (6)
- client/client_test.go: Evaluated as low risk
- client/llb/fileop.go: Evaluated as low risk
- client/llb/fileop_test.go: Evaluated as low risk
- cmd/buildctl/debug/dumpllb.go: Evaluated as low risk
- solver/llbsolver/file/backend.go: Evaluated as low risk
- solver/llbsolver/ops/file.go: Evaluated as low risk
Comments skipped due to low confidence (1)
solver/llbsolver/ops/file_test.go:647
- [nitpick] The parameter names
oldpath
andnewpath
might be confusing. Consider renaming them totarget
andlink
to avoid confusion.
func (b *testFileBackend) Symlink(_ context.Context, m, user, group fileoptypes.Mount, a *pb.FileActionSymlink) error {
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good. Comments about tests.
|
||
st := Scratch(). | ||
File(Mkfile("/foo", 0700, []byte("data"))). | ||
File(Symlink("foo", "/bar")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we have some tests that chain symlink together with other actions. Atm it is unclear what the point of Mkfile
is in this test as it is in a different FileOp
that is never checked after marshaling.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably the result of copying and pasting from another test. To be clear about your intent, you want to be sure that llb.Symlink
plays nicely with other file actions when chained together, i.e. they happen in the right order?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct. The actions are all linked together so that the input points to either the input of the fileop or the offset of another action that needs to run before it
require.Equal(t, fileGroup, header.Gid) | ||
|
||
entry, ok = m["baz"] | ||
header = entry.Header |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where does this check that it is a symlink and that it points to the correct location?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Noted, will fix this by checking header.Linkname
and verifying its destination.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
^
Signed-off-by: Peter Engelbert <[email protected]>
Currently, there is no way to create a symlink directly using llb. This PR adds a new
FileAction
which handles creating symlinks. Without this change, users have to run a command withllb.State.Run(...)
, which requires a shell or some other program to create the link.This PR also adds a test for the new feature.
@cpuguy83