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

Dereference symbolic links when copying files from ct data folder #2731

Merged
merged 4 commits into from
Jul 25, 2022

Conversation

gonzalobf
Copy link
Contributor

When running rebar3 ct, rebar_prv_common_test copies the data folders
to a different location. If {name}_SUITE_data folder contains relative
symbolic links, the symbolic links might stop working since the path
might be different.

A solution is to add the -L option to cp so it dereferences the
symbolic links and copies the original content in the target file.

This is only done for the data folder but and won't change any code that
previously used rebar_file_utils:cp_r.

I asked in erlangforum a few days ago because I thought it was common test
problem but today I realised that is rebar3 copying the files.

I'm not sure if this problem happens with Windows too since I don't have
any computer with it installed.

I'm not 100% sure about if this change will be wanted so I did a quick
implementation to discuss it.

Thank you

@gonzalobf gonzalobf force-pushed the gonzalo-dereference-symbolic-links branch from 094f1e4 to 07c8a6a Compare July 21, 2022 18:11
Copy link
Collaborator

@ferd ferd left a comment

Choose a reason for hiding this comment

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

That's a good fix! For the implementation though, instead of using a boolean as a third argument, we should pass in options like [{dereference, true|false}] so that instead of checking for true or false to add the -L, we check proplists:get_value(dereference, Opts, false), which future-proofs the function.

@gonzalobf
Copy link
Contributor Author

That's a good fix! For the implementation though, instead of using a boolean as a third argument, we should pass in options like [{dereference, true|false}] so that instead of checking for true or false to add the -L, we check proplists:get_value(dereference, Opts, false), which future-proofs the function.

Thank you :)

I added a commit changing the code to use a proplist. I'm not sure how should I add -L to the OptStr, so I used a case inside the cp_r function but I thought about these other possibilities:

  • Moving the code to another function:
get_options_str(Options) ->
    DefaultOptStr = "-Rp"
    case proplists:get_value(dereference, Options, false) of
        true -> DefaultOptStr ++ "L";
        false -> DefaultOptStr
     end.
  • Parse all the values in Options in a recursive way. It will be helpful for adding future options.
get_options_str(Options) ->
    get_options_str(Options, "-Rp").

get_options_str([], OptionsStr) ->
    OptionsStr;
get_options_str([{dereference, false} | Rest], OptionsStr) ->
    get_options_str(Rest, OptionsStr);
get_options_str([{dereference, true} | Rest], OptionsStr) ->
    get_options_str(Rest, OptionsStr ++ "L").

Let me know if you prefer any of these or if there is another way that I didn't think about

When running `rebar3 ct`, `rebar_prv_common_test` copies the data folders
to a different location. If `{name}_SUITE_data` folder contains relative
symbolic links, the symbolic links might stop working since the path
might be different.

A solution is to add the `-L` option to `cp` so it dereferences the
symbolic links and copies the original content in the target file.

This is only done for the data folder but and won't change any code that
previously used `rebar_file_utils:cp_r`.
The change will help reducing the changes needed if we want
to add more options in the future.
@gonzalobf gonzalobf force-pushed the gonzalo-dereference-symbolic-links branch from ef4a02d to 79a1aa0 Compare July 22, 2022 08:45
@ferd
Copy link
Collaborator

ferd commented Jul 22, 2022

I prefer the current (and first) option, because it has clearer semantics in situations where the option is passed multiple times in the proplists: [{dereference, true}, {dereference, false}, {dereference, true}] would yield -RpL in the first case and -RpLL in the recursive case.

@ferd
Copy link
Collaborator

ferd commented Jul 22, 2022

It would probably be worth it to write a quick test in https://github.com/erlang/rebar3/blob/main/apps/rebar/test/rebar_file_utils_SUITE.erl for this.

Since the code actually does nothing for windows, it would be alright to do a case os:type() of ... check that documents the act of not dereferencing anything. In fact, looking at https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/robocopy it appears that robocopy dereferences symlinks by default and we should use /sl to do a literal link copy in the default case.

@gonzalobf
Copy link
Contributor Author

I prefer the current (and first) option, because it has clearer semantics in situations where the option is passed multiple times in the proplists: [{dereference, true}, {dereference, false}, {dereference, true}] would yield -RpL in the first case and -RpLL in the recursive case.

I didn't think about that possibility. I'll add the test so we make sure this works and leave the rest as it is.

The tests checks that the `Sources` if properly copied to `Dest`. If the
`[{dereference, true}]` option is given, the symbolic links should be a
regular file after copying them.

Windows derefereces symbolic links by default and the code hasn't been
adapted yet, so the tests should fail for now.
@gonzalobf gonzalobf marked this pull request as draft July 23, 2022 14:53
@gonzalobf gonzalobf marked this pull request as ready for review July 23, 2022 14:54
@gonzalobf
Copy link
Contributor Author

In fact, looking at https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/robocopy it appears that robocopy dereferences symlinks by default and we should use /sl to do a literal link copy in the default case.

@ferd I added the tests for cp_r. I tried to be as compact and clear as possible but they might need a change. Let me know want do you think.

I expect the test cp_r_copies_files to for fail and cp_r_dereferences_symbolic_links to pass since it is the default behaviour in Windows as you said. Would you mind to approve the workflow so we confirm it is true? (I need to install a VM to run the test it locally)

Once we confirmed they fail, I'll will adapt the Windows code so it uses /sl by default and doesn't use it when we pass [{dereference, true}]

@gonzalobf
Copy link
Contributor Author

gonzalobf commented Jul 23, 2022

Thank you, it failed as expected. I pushed another commit doing the changes for Windows

@gonzalobf gonzalobf force-pushed the gonzalo-dereference-symbolic-links branch 2 times, most recently from b54e64a to 3993362 Compare July 25, 2022 16:42
The current behaviour:
- When `Source` is a folder, `robocopy` is used. It follows symbolic
  links and copies the original data, creating a raw file

- When `Source` is a file/symbolic link, `file:copy` is used. It copies
  the raw data creating a raw file.

The change in this commit will use `/sl`  when copying files
from Source folder and `file:make_symlink` when Source is a
symbolic link on Windows.

If the `[{dereference, true}]` option is given, it will dereference the
files and copy the content instead of a symbolic link

This change has been added so the Windows copy behaves more unix one.
@gonzalobf gonzalobf force-pushed the gonzalo-dereference-symbolic-links branch from 3993362 to ffc7072 Compare July 25, 2022 16:43
@gonzalobf
Copy link
Contributor Author

@ferd I run the tests in a Windows VM and I think I fixed them.

I thought the files where copied only using robocopy but cp_r_win32 does also file:copy when the Source is a file and not a folder. I expanded my test so cp_r_win32 file:copy is called when copying src/symbolic and robocopy with sub_dir.

I hope I didn't miss any other copy case.

Copy link
Collaborator

@ferd ferd left a comment

Choose a reason for hiding this comment

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

Looks well-detailed, nice tests. As soon as CI passes, this is in.
Thanks for the contribution!

@ferd ferd merged commit dc3eca5 into erlang:main Jul 25, 2022
@gonzalobf
Copy link
Contributor Author

Thank you for your help!

gonzalobf added a commit to gonzalobf/rebar3 that referenced this pull request Aug 25, 2022
The `test_SUITE_data` folder when running common test is only derefence
when the folder is in the root app. When the folder is in
`apps/{app-name}/test`, the folder is copied in compilation time. The
first approach to fix it was added here [1].

[1] erlang#2731
gonzalobf added a commit to gonzalobf/rebar3 that referenced this pull request Aug 25, 2022
The `test_SUITE_data` folder when running common test is only
dereference when the folder is in the root app. When the folder
is in `apps/{app-name}/test`, the folder is copied in compilation time.
The first approach to fix it was added here [1].

[1] erlang#2731
gonzalobf added a commit to gonzalobf/rebar3 that referenced this pull request Aug 25, 2022
I found that the patch [1] I did for dereferencing symbolic link in
common tests not always works. The `copy_bare_suites` function is only
runs when the ct suite is placed in the root folder, but it doesn't run
when it is placed in the `test/` folder.

In this commit, I added `copy_bare_suites` to all the cases of
`maybe_inject_test_dir`. I also avoided copying the folder again
in `rebar_prv_compile` when it is a test directory. This last extra
copy was run just after the steps in `rebar_prv_common_test` so it was
rewriting everything done in `copy_bare_suites`, leaving the folder with
the broken symbolic links.

[1] erlang#2731
gonzalobf added a commit to gonzalobf/rebar3 that referenced this pull request Aug 25, 2022
I found that the patch [1] I did for dereferencing symbolic link in
common tests not always works. The `copy_bare_suites` function only
runs when the ct suite is placed in the root folder, but it doesn't run
when it is placed in the `test/` folder.

In this commit, I added `copy_bare_suites` to all the cases of
`maybe_inject_test_dir`. I also avoided copying the folder again
in `rebar_prv_compile` when it is a test directory. This last extra
copy was run just after the steps in `rebar_prv_common_test` so it was
rewriting everything done in `copy_bare_suites`, leaving the folder with
the broken symbolic links.

[1] erlang#2731
gonzalobf added a commit to gonzalobf/rebar3 that referenced this pull request Aug 25, 2022
I found that the patch [1] I did for dereferencing symbolic link in
common tests not always works. The `copy_bare_suites` function only
runs when the ct suite is placed in the root folder, but it doesn't run
when it is placed in the `test/` folder.

In this commit, I added `copy_bare_suites` to all the cases of
`maybe_inject_test_dir`. I also avoided copying the folder again
in `rebar_prv_compile` when it is a test directory. This last extra
copy was run just after the steps in `rebar_prv_common_test` so it was
rewriting everything done in `copy_bare_suites`, leaving the folder with
the broken symbolic links.

[1] erlang#2731
gonzalobf added a commit to gonzalobf/rebar3 that referenced this pull request Aug 25, 2022
I found that the patch [1] I did for dereferencing symbolic links in
common tests does not always work.

The `copy_bare_suites` function only runs when the ct suite is placed
in the root folder, but it doesn't run when it is placed in the `test/`
folder.

In this commit, I added `copy_bare_suites` to all the cases of
`maybe_inject_test_dir`. I also avoided copying the folder again
in `rebar_prv_compile` when it is a test directory. This last extra
copy was run just after the steps in `rebar_prv_common_test` so it was
rewriting everything done in `copy_bare_suites`, leaving the folder with
the broken symbolic links.

[1] erlang#2731
gonzalobf added a commit to gonzalobf/rebar3 that referenced this pull request Jan 27, 2023
The tests checks that the files and the symbolic files are copied
correctly to the _build folder.

In the case of symbolic links, the copy needs to dereference the
content. If not, the symbolic link can point to relative path that
doesn't exist in _build folder.

These tests try to prove that the feature added in [1] doesn't work
for all cases. When the `_SUITE_data` is inside an app test directory,
the content are copied without referencing.

[1] erlang#2731
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