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

feat: Multiple postinstall symlinks pointing to the same thing #495

Open
wants to merge 9 commits into
base: main
Choose a base branch
from

Conversation

pmengelbert
Copy link
Contributor

What this PR does / why we need it:
Currently, you can't create multiple symlink newpaths that point to the same oldpath. This is due to the fact that map keys have to be unique.

For more information, see #489

This should be merged before #494

@pmengelbert pmengelbert requested a review from a team as a code owner January 9, 2025 22:40
spec.go Outdated
@@ -130,7 +130,8 @@ type PostInstall struct {
// SymlinkTarget specifies the properties of a symlink
type SymlinkTarget struct {
// Path is the path where the symlink should be placed
Path string `yaml:"path" json:"path" jsonschema:"required"`
Path string `yaml:"path" json:"path" jsonschema:"oneof_required=path"`
Copy link
Member

@cpuguy83 cpuguy83 Jan 10, 2025

Choose a reason for hiding this comment

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

We should mark this is deprecated and have people use paths instead.
We can also make it so when we load the spec we migrate Path to Paths then there's only one field to deal with.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

How do we mark it as deprecated?

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done, but I'm going to add tests for it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

tests added.

target.go Outdated
@@ -42,6 +42,12 @@ func (t *Target) validate() error {
}
}

if t.Image != nil {
Copy link
Member

Choose a reason for hiding this comment

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

Let's add a validate to t.Image, which itself could return nil if *Image is nil (note: this is what I did in #492

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@pmengelbert
Copy link
Contributor Author

@cpuguy83 This should be ready for another round of review.

@pmengelbert pmengelbert requested a review from cpuguy83 January 13, 2025 18:27
load.go Outdated

var errs []error
if err := i.Post.validate(); err != nil {
errs = append(errs, errors.Wrap(err, "postinstall"))
Copy link
Member

Choose a reason for hiding this comment

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

Nit, just "post"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done.

load.go Outdated

// By this point, both the oldpath and the SymlinkTarget are
// well-formed. We still need to make sure each 'newpath' is unique.
if cfg.Paths == nil {
Copy link
Member

Choose a reason for hiding this comment

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

Validations should not be modifying anything.
We can also iterate on Paths without having to have it be non-nil.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

load.go Outdated
cfg.Paths = []string{}
}

newpaths := slices.Clone(cfg.Paths)
Copy link
Member

Choose a reason for hiding this comment

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

Why the clone? Should not need to modify anything here.
If we want to make sure that cfg.Path is not also somewhere in cfg.Paths you can check that directly.

for _, link := range symlinks {
  if link.Path == cfg.Path {
    // error
  }
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I could have sworn I put this in fillDefaults. I need to move this code there. The clone from moving the old code around. I'll take another pass

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Now I remember. Clone so we don't modify the original, but stick the cfg.Path field on the end so we can just loop through. It's weird, I'll change it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why the clone? Should not need to modify anything here. If we want to make sure that cfg.Path is not also somewhere in cfg.Paths you can check that directly.

for _, link := range symlinks {
  if link.Path == cfg.Path {
    // error
  }
}

And the goal is to make sure the there are no duplicate oldpaths in the whole map[string]SymlinkTarget. The desired behavior is unclear (follow the symlink that has already been created? Or overwrite the existing symlink?) Map iteration is not reliably ordered (and we sort the keys to avoid cache misses), so the user is unlikely to get what they want anyway. Better to just not allow it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done. This exhibits the desired behavior, doesn't modify the spec, is tested properly, and only allocates the map when needed.

load.go Outdated

func validateSymlinks(symlinks map[string]SymlinkTarget) error {
var errs []error
dests := make(map[string]string, len(symlinks)<<1)
Copy link
Member

Choose a reason for hiding this comment

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

Maybe lets call this seen and only allocate the map if its needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done.

@pmengelbert pmengelbert force-pushed the pmengelbert/multiple_postinstall_symlinks_pointing_to_the_same_thing/1 branch 2 times, most recently from e4a8974 to cf1cf76 Compare January 16, 2025 16:48
s = s.File(llb.Mkdir(path.Dir(dstPath), 0755, llb.WithParents(true)))
s = s.File(llb.Copy(s, srcPath, dstPath))

for oldpath, newpaths := range post.Symlinks {
Copy link
Member

Choose a reason for hiding this comment

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

I think we still need to sort this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

helpers.go Outdated
for src, tgt := range post.Symlinks {
fmt.Fprintf(buf, "mkdir -p %q\n", filepath.Join(rootfsPath, filepath.Dir(tgt.Path)))
fmt.Fprintf(buf, "ln -s %q %q\n", src, filepath.Join(rootfsPath, tgt.Path))
for oldpath, newpaths := range post.Symlinks {
Copy link
Member

Choose a reason for hiding this comment

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

Should also sort this

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

load_test.go Outdated
Post: &PostInstall{
Symlinks: map[string]SymlinkTarget{
"oldpath": {
Path: "/newpath3",
Copy link
Member

Choose a reason for hiding this comment

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

I don't think we need to test this since its not a valid case.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch. Fixed it so the test code will reject invalid tests.

target.go Outdated
cfg.Path = ""
}

sort.Strings(cfg.Paths)
Copy link
Member

Choose a reason for hiding this comment

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

I don't think this should be sorted since this should be left to the user.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done.

@cpuguy83 cpuguy83 added this to the v0.12.0 milestone Jan 29, 2025
@pmengelbert pmengelbert force-pushed the pmengelbert/multiple_postinstall_symlinks_pointing_to_the_same_thing/1 branch from a21b267 to 6fd5303 Compare February 5, 2025 16:57
Allow for multiple links to point to the same target.

Signed-off-by: Peter Engelbert <[email protected]>
Upon loading the spec, if `Path` is nonempty, move it to `Paths`. This
commit also reworks the tests.

Signed-off-by: Peter Engelbert <[email protected]>
* no longer modifies anything in `.validate()`
* only allocates map when it's actually needed
* stipulates in the test that `.Paths` should end up sorted (this is for
  llb caching)
* s/postinstall/post

Signed-off-by: Peter Engelbert <[email protected]>
Some of the `fillDefaults` tests were testing invalid situations. To
prevent this, validation is run on the test ins and outs before calling
`fillDefaults`. The invalid tests have been removed.

This commit also removes the sorting from `fillDefaults`, this will be
left to the user instead.

Signed-off-by: Peter Engelbert <[email protected]>
Instead of sorting the list of symlinks, leave it for the user to do.
This commit removes the sorting from `fillDefaults` and implements it in
the handlers.

The map keys of the symlinks are now sorted as well.

Signed-off-by: Peter Engelbert <[email protected]>
@pmengelbert pmengelbert force-pushed the pmengelbert/multiple_postinstall_symlinks_pointing_to_the_same_thing/1 branch from 6fd5303 to 7a63ab6 Compare February 6, 2025 15:14
@pmengelbert
Copy link
Contributor Author

This has been rebased and is ready for another round of review @cpuguy83

@pmengelbert pmengelbert requested a review from cpuguy83 February 6, 2025 15:16
Signed-off-by: Peter Engelbert <[email protected]>
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.

2 participants