Skip to content
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

Implement readUser on Windows #3518

Merged
merged 5 commits into from
Oct 20, 2023
Merged

Conversation

gabriel-samfira
Copy link
Collaborator

@gabriel-samfira gabriel-samfira commented Jan 18, 2023

This change adds the ability to resolve usernames to SIDs in the FileOpSolver. This enables WORKDIR, ADD and COPY to properly set permissions on Windows.

Depends on:

Signed-off-by: Gabriel Adrian Samfira [email protected]

@gabriel-samfira
Copy link
Collaborator Author

With the dependency merged, this PR is now ready for review. This is the last PR in this series. It enables useful definitions to be built natively on Windows.

CC @tonistiigi @AkihiroSuda

@@ -251,6 +212,7 @@ func docopy(ctx context.Context, src, dest string, action pb.FileActionCopy, u *
}

type Backend struct {
Executor executor.Executor
Copy link
Member

Choose a reason for hiding this comment

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

Wdyt if we put separate type definitions here for UNIX and windows? Logically the executor dependency really does not belong here, so would like to avoid it for UNIX case.

Another option would be to leave this type as-is and in solve() optionally pass in some option that does the user conversion. This would then only be passed in case of windows and linux can use the default implementation.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Logically the executor dependency really does not belong here

It felt a bit yucky to add the executor there. I changed it and added a new function NewFileOpBackend() that sets a readUserFn in the Backend{}. For Windows it wraps the readUser() function which uses the executor. For everything else we just return the usual readUser() function. This way we don't set an exported Executor field in the Backend{} struct.

Does that sound acceptable?

@@ -250,7 +211,16 @@ func docopy(ctx context.Context, src, dest string, action pb.FileActionCopy, u *
return nil
}

// NewFileOpBackend returns a new file operation backend. The executor is currently only used for Windows,
// and it is used to construct the readUserFn field set in the returned Backend.
func NewFileOpBackend(exec executor.Executor) *Backend {
Copy link
Member

Choose a reason for hiding this comment

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

I'd like to avoid passing this Executor in here (and import on buildkit/executor). And instead pass a function/interface only for readuser (ideally only on windows, in linux you can just pass nil).

Copy link
Collaborator Author

@gabriel-samfira gabriel-samfira Sep 5, 2023

Choose a reason for hiding this comment

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

This will mean having code that reads the user defined in more than one place (this package and somewhere else). Also, if we only pass in that function/interface for one OS, it creates a special case that will add cognitive burden for future readers of this code (one more thing to keep track of). I can do that if you wish, but it might be better to pass in that function/interface for all OSs, just to keep things consistent and in one place.

If we decide to go that route, we can define a new interface in solver/llbsolver/file, implement it in solver/llbsolver/ops package (the Windows version will call into the executor which is already imported in exec.go), and pass that implementation into Backend{}`.

How does that sound?

Copy link
Member

Choose a reason for hiding this comment

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

I can do that if you wish, but it might be better to pass in that function/interface for all OSs, just to keep things consistent and in one place.

Fine by me if you prefer that. I thought about it from the mindset that file can do its job without special configuration but if user wants to pass in custom readUser they can do this. This doesn't necessarily need to be just OS-based in the future but using /etc/passwd from some other snapshot or some other user database.

we can define a new interface

If it is just one function it can just be a callback as well.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I moved the readUser() code and defined a callback type. Have a look and let me know if you would prefer it done differently.

solver/llbsolver/file/backend.go Outdated Show resolved Hide resolved
solver/llbsolver/file/backend.go Outdated Show resolved Hide resolved
@@ -266,7 +237,8 @@ func (fb *Backend) Mkdir(ctx context.Context, m, user, group fileoptypes.Mount,
}
defer lm.Unmount()

u, err := readUser(action.Owner, user, group)
// u, err := readUser(action.Owner, user, group, fb.Executor)
u, err := fb.readUserFn(action.Owner, user, group)
Copy link
Member

Choose a reason for hiding this comment

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

This should not panic on nil.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

What do you think if we unexport Backend{} and have NewFileOpBackend() return an error if we pass in a nil callback. I think we should always pass in a callback even if it's a noop. This way we can avoid having to check if the read user function is nill whenever we want to use it, and simply check once in the "constructor".

Copy link
Member

Choose a reason for hiding this comment

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

Just a nil check should do I think. Or defaulting to noop function.

@@ -250,7 +210,18 @@ func docopy(ctx context.Context, src, dest string, action pb.FileActionCopy, u *
return nil
}

// NewFileOpBackend returns a new file operation backend. The executor is currently only used for Windows,
// and it is used to construct the readUserFn field set in the returned Backend.
func NewFileOpBackend(readUserFn ReadUserCallback) *Backend {
Copy link
Member

Choose a reason for hiding this comment

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

Maybe makes sense to put this into opt stuct.

solver/llbsolver/ops/file.go Outdated Show resolved Hide resolved
solver/llbsolver/ops/user_windows.go Outdated Show resolved Hide resolved
Copy link
Member

@tonistiigi tonistiigi left a comment

Choose a reason for hiding this comment

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

Looks like some comments are marked as resolved but I think you forgot to push your updates.

@gabriel-samfira
Copy link
Collaborator Author

Changes made. Apologies for the delay. I've been sidetracked for the past couple of weeks, and may be sparse for the next 2 weeks as well. I changed the readUser function to accept snapshot.Mountable and the conversion remains in the files package.

The Backend{} type has been un-exported and we now check that the readUser callback is not nil. From what I understand, that can't be a noop, as it will mean ignoring the USER/GROUP stanzas. It can be unsupported on a particular OS, but not nil. Correct me if I'm wrong on this 😄 .

@gabriel-samfira
Copy link
Collaborator Author

failures seem to be due to rate limits(?)

@slonopotamus
Copy link
Contributor

failures seem to be due to rate limits(?)

Uh?

https:/moby/buildkit/actions/runs/6428415521/job/17455566735?pr=3518#step:4:446

264.5 solver/llbsolver/file/backend.go:215:51: unexported-return: exported func NewFileOpBackend returns unexported type *file.backend, which can be annoying to use (revive)
264.5 func NewFileOpBackend(readUser ReadUserCallback) (*backend, error) {
264.5  

@gabriel-samfira
Copy link
Collaborator Author

failures seem to be due to rate limits(?)

Uh?

https:/moby/buildkit/actions/runs/6428415521/job/17455566735?pr=3518#step:4:446

264.5 solver/llbsolver/file/backend.go:215:51: unexported-return: exported func NewFileOpBackend returns unexported type *file.backend, which can be annoying to use (revive)
264.5 func NewFileOpBackend(readUser ReadUserCallback) (*backend, error) {
264.5  

Will fix later today. Apologies for the noise.

@gabriel-samfira
Copy link
Collaborator Author

@slonopotamus I had a quick look at one of the tests and stumbled across:

https:/moby/buildkit/actions/runs/6428415520/job/17455724114?pr=3518#step:8:1798

Which led me to believe it was a rate limit issue. I stopped there.

As for the linting issue, fixing that now.

Signed-off-by: Gabriel Adrian Samfira <[email protected]>
Signed-off-by: Gabriel Adrian Samfira <[email protected]>
@gabriel-samfira
Copy link
Collaborator Author

The failing test seems to be due to vagrant failing. @tonistiigi if you have a few minutes, let me know if the changes here are fine.

func (fb *Backend) readUserWrapper(owner *pb.ChownOpt, user, group fileoptypes.Mount) (*copy.User, error) {
var userMountable, groupMountable snapshot.Mountable
if user != nil {
usr, ok := user.(*Mount)
Copy link
Member

Choose a reason for hiding this comment

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

There is still something iffy about these type casts but looks like the previous code had a similar problem, so no update is needed with this PR.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I agree. There are a few architectural boundaries being broken here which could do with some refactor, but it will require touching a lot of code. This pattern is present in a number of places and should probably be tackled at some point as part of a broader cleanup.

Thanks for the review and the merge!

@tonistiigi tonistiigi merged commit 762b262 into moby:master Oct 20, 2023
54 of 55 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants