test: enable reindex readonly test on *BSD #28660

pull pinheadmz wants to merge 1 commits into bitcoin:master from pinheadmz:readonly-bsd changing 1 files +31 −12
  1. pinheadmz commented at 8:57 pm on October 16, 2023: member

    see #27850 (review)

    OpenBSD and FreeBSD don’t have chattr but they do have chflags, use that method to make the block file immutable for the reindex_readonly test.

    Written and tested on a VPS running FreeBSD:

    0FreeBSD freebsd-13-1 13.2-RELEASE-p4 FreeBSD 13.2-RELEASE-p4 GENERIC amd64
    
  2. DrahtBot commented at 8:57 pm on October 16, 2023: contributor

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    Code Coverage

    For detailed information about the code coverage, see the test coverage report.

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    ACK maflcko, theStack, jonatack

    If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #28116 (test: update tool_wallet coverage for unexpected writes to wallet by jonatack)

    If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

  3. DrahtBot added the label Tests on Oct 16, 2023
  4. in test/functional/feature_reindex_readonly.py:55 in 3709e2863f outdated
    51@@ -51,6 +52,20 @@ def reindex_readonly(self):
    52                     self.log.warning("This should only happen due to missing capabilities in a container.")
    53                     self.log.warning("Make sure to --cap-add LINUX_IMMUTABLE if you want to run this test.")
    54                     return
    55+        if platform.system() == "FreeBSD" or platform.system() == "OpenBSD":
    


    jonatack commented at 9:09 pm on October 16, 2023:
    I assume you checked what this returns locally: import platform; print(platform.system()), but how did you check for both platforms?

    pinheadmz commented at 2:28 am on October 17, 2023:
    I literally ran the command on both free and open bsd systems ;-) I already have an OpenBSD server for DNS and just spun up a FreeBSD on Google cloud for this task.
  5. jonatack commented at 9:12 pm on October 16, 2023: contributor
    Concept ACK. I’m extracting this to a test utility in #28116, if by any chance you prefer to build on that and I’ll pull in the commit there.
  6. in test/functional/feature_reindex_readonly.py:78 in 3709e2863f outdated
    73@@ -59,6 +74,8 @@ def reindex_readonly(self):
    74 
    75         if used_chattr:
    76             subprocess.check_call(['chattr', '-i', filename])
    77+        if used_chflags:
    78+            subprocess.check_call(['chflags', 'nouchg', filename])
    


    jonatack commented at 9:16 pm on October 16, 2023:
    Verified on https://man.openbsd.org/chflags.1 that uchg and nouchg appear to be the correct values for “set the user immutable flag (owner or superuser only)” and “clear the immutable bit”, respectively.
  7. theStack commented at 11:49 pm on October 16, 2023: contributor
    Concept ACK, will test on OpenBSD 7.4 in a bit.
  8. in test/functional/feature_reindex_readonly.py:38 in 3709e2863f outdated
    34@@ -35,6 +35,7 @@ def reindex_readonly(self):
    35         filename.chmod(stat.S_IREAD)
    36 
    37         used_chattr = False
    38+        used_chflags = False
    


    maflcko commented at 10:37 am on October 17, 2023:

    Nit: Could replace the two boolean with a single undo_immutable, which holds an optional lambda to run. (a no-op by default, and set to None if the test should terminate early, and the respective undo command if an util helper was used?)

    This should reduce the number of symbols, and reduce the mix of if-else vs early-return. The diff below would look like:

     0diff --git a/test/functional/feature_reindex_readonly.py b/test/functional/feature_reindex_readonly.py
     1index 26531f472b..77716b371e 100755
     2--- a/test/functional/feature_reindex_readonly.py
     3+++ b/test/functional/feature_reindex_readonly.py
     4@@ -53,12 +53,11 @@ class BlockstoreReindexTest(BitcoinTestFramework):
     5                     return
     6 
     7         self.log.debug("Attempt to restart and reindex the node with the unwritable block file")
     8-        with self.nodes[0].assert_debug_log(expected_msgs=['FlushStateToDisk', 'failed to open file'], unexpected_msgs=[]):
     9+        if undo_immutable:
    10+          with self.nodes[0].assert_debug_log(expected_msgs=['FlushStateToDisk', 'failed to open file'], unexpected_msgs=[]):
    11             self.nodes[0].assert_start_raises_init_error(extra_args=['-reindex', '-fastprune'],
    12                 expected_msg="Error: A fatal internal error occurred, see debug.log for details")
    13-
    14-        if used_chattr:
    15-            subprocess.check_call(['chattr', '-i', filename])
    16+          undo_immutable()
    17 
    18         filename.chmod(0o777)
    19 
    
  9. maflcko approved
  10. maflcko commented at 10:53 am on October 17, 2023: member
    lgtm ACK
  11. in test/functional/feature_reindex_readonly.py:76 in d72f863ae9 outdated
    79+            with self.nodes[0].assert_debug_log(expected_msgs=['FlushStateToDisk', 'failed to open file'], unexpected_msgs=[]):
    80+                self.nodes[0].assert_start_raises_init_error(extra_args=['-reindex', '-fastprune'],
    81+                    expected_msg="Error: A fatal internal error occurred, see debug.log for details")
    82+            undo_immutable()
    83+        else:
    84+            return
    


    maflcko commented at 2:54 pm on October 17, 2023:
    nit: I think you can now remove the early return, because it should be fine and clearer to run the next line unconditionally.

    pinheadmz commented at 2:56 pm on October 17, 2023:
    heh thanks. Having an issue on macos now…

    jonatack commented at 2:59 pm on October 17, 2023:

    heh thanks. Having an issue on macos now…

    The last push (57972250feb3ad61b91c8fbcb01eac1b4b57095f) seems to run fine on arm64 macOS for me.


    maflcko commented at 3:00 pm on October 17, 2023:
    macOS should be unaffected by this and execute lambda: None, no?

    pinheadmz commented at 3:00 pm on October 17, 2023:
    @jonatack as root?

    pinheadmz commented at 3:01 pm on October 17, 2023:
    OK I see even before this new commit macOS test would NOT work as root. Hm I thought we already went over that. I guess the macOS ci is not running as root?

    maflcko commented at 3:02 pm on October 17, 2023:
    Are you saying that macOS under root can run this test on master, but not on this pull request?

    jonatack commented at 3:03 pm on October 17, 2023:
    Ah. Yes, sudo test/functional/feature_reindex_readonly.py stalls.

    pinheadmz commented at 3:04 pm on October 17, 2023:

    yes, macos as root on master doesnt work. However, this will:

    if platform.system() == "FreeBSD" or platform.system() == "OpenBSD" or platform.system() == "Darwin":

    whats the best style for a triple - or in python?


    pinheadmz commented at 3:06 pm on October 17, 2023:
    if platform.system() in ["FreeBSD", "OpenBSD", "Darwin"]: there we go ;-)

    maflcko commented at 3:08 pm on October 17, 2023:
    This is very confusing. The last commit should be a refactor. And the first commit shouldn’t affect macOS. This is a bug on master already, right?

    jonatack commented at 3:08 pm on October 17, 2023:
    Latest push e7db19f42bb50953c6aaaf5997f8ee78eed9dab4 works for me as root.

    pinheadmz commented at 3:10 pm on October 17, 2023:
    @maflcko the first commit does not affect macos. the second commit is a refactor that also fixes a macos/root bug on master. Want me to separate the macos out to commit 3?

    jonatack commented at 3:10 pm on October 17, 2023:
    @maflcko yes, running this test as root blocks for me on master as well.
  12. pinheadmz force-pushed on Oct 17, 2023
  13. pinheadmz force-pushed on Oct 17, 2023
  14. in test/functional/feature_reindex_readonly.py:54 in e7db19f42b outdated
    55-        self.log.debug("Attempt to restart and reindex the node with the unwritable block file")
    56-        with self.nodes[0].assert_debug_log(expected_msgs=['FlushStateToDisk', 'failed to open file'], unexpected_msgs=[]):
    57-            self.nodes[0].assert_start_raises_init_error(extra_args=['-reindex', '-fastprune'],
    58-                expected_msg="Error: A fatal internal error occurred, see debug.log for details")
    59+                    undo_immutable = False
    60+        if platform.system() in ["FreeBSD", "OpenBSD", "Darwin"]:
    


    maflcko commented at 3:11 pm on October 17, 2023:
    What about netbsd? I think you can just run if chflags --version or similar here?
  15. maflcko approved
  16. maflcko commented at 3:12 pm on October 17, 2023: member
  17. pinheadmz force-pushed on Oct 17, 2023
  18. pinheadmz force-pushed on Oct 17, 2023
  19. pinheadmz commented at 3:24 pm on October 17, 2023: member
    squashed and replaced all the platform queries with try/catch tests for the commands themselves.
  20. pinheadmz force-pushed on Oct 17, 2023
  21. DrahtBot added the label CI failed on Oct 17, 2023
  22. pinheadmz force-pushed on Oct 17, 2023
  23. DrahtBot removed the label CI failed on Oct 17, 2023
  24. maflcko commented at 9:07 am on October 20, 2023: member
    lgtm code ACK cbd5bf07d251900b944056bebc7635a9ce9fee6b
  25. DrahtBot requested review from jonatack on Oct 20, 2023
  26. DrahtBot requested review from theStack on Oct 20, 2023
  27. maflcko added this to the milestone 26.0 on Oct 20, 2023
  28. theStack commented at 11:39 am on October 20, 2023: contributor
    Is there any good reason to abuse try/catch for regular logic control flow, in this case the OS detection (previously done with platform.system(), which makes much more sense IMHO)? For example, I’m running OpenBSD 7.4 with the package e2fsprogs installed, which has a binary /usr/local/bin/chattr installed, so the Linux branch is executed. That’s a bit unexpected, though on the other hand one could argue it’s fine, as the test still succeeds and serves its purpose. At least it works as long as we only need the binaries and there is no other OS-specific tests going on inside the branches.
  29. pinheadmz commented at 11:58 am on October 20, 2023: member
    @theStack the issue was hard-coding all the OSs. Every flavor of *BSD plus macOS has chattr, Linux has chflags and Windows, well… chmod seems to work against root on Windows. The test isn’t trying to assert that certain behaviors are expected on certain OSes, we just want that file to be immutable “by any means necessary”
  30. in test/functional/feature_reindex_readonly.py:54 in cbd5bf07d2 outdated
    55-
    56-        self.log.debug("Attempt to restart and reindex the node with the unwritable block file")
    57-        with self.nodes[0].assert_debug_log(expected_msgs=['FlushStateToDisk', 'failed to open file'], unexpected_msgs=[]):
    58-            self.nodes[0].assert_start_raises_init_error(extra_args=['-reindex', '-fastprune'],
    59-                expected_msg="Error: A fatal internal error occurred, see debug.log for details")
    60+                    undo_immutable = False
    


    theStack commented at 12:02 pm on October 20, 2023:

    nit, might be good not to mix data types (probably a leftover from an earlier version of the PR?) :

    0                    undo_immutable = lambda: None
    

    (also in the macOS/BSD branch below)


    maflcko commented at 12:54 pm on October 20, 2023:

    I don’t think this works. This will break running the test in a Linux container without --cap-add LINUX_IMMUTABLE, no?

    See also the log line immediately above this line.


    theStack commented at 1:00 pm on October 20, 2023:
    Oops, indeed. In my mind lambda: None was falsy and thought the if condition below wouldn’t be executed, my bad.

    maflcko commented at 1:09 pm on October 20, 2023:
    It may be possible to type lambda: pass, which should be equal to lambda: None?

    theStack commented at 4:31 pm on October 20, 2023:

    It may be possible to type lambda: pass, which should be equal to lambda: None?

    Unfortunately not:

    0$ python3
    1Python 3.10.11 (main, Apr 13 2023, 01:52:20) [Clang 13.0.0 ] on openbsd7
    2Type "help", "copyright", "credits" or "license" for more information.
    3>>> x = lambda: pass
    4  File "<stdin>", line 1
    5    x = lambda: pass
    6                ^^^^
    7SyntaxError: invalid syntax
    

    I wonder what type checkers like mypy would consider as the correct falsy value for a lambda. Maybe None would be slightly better than False? (or maybe both would be rejected, idk). Anyways, False seems to do its job.

  31. maflcko commented at 12:03 pm on October 20, 2023: member

    previously done with platform.system(), which makes much more sense IMHO

    Not sure. This would force us to maintain a list of all operating systems in this test. FreeBSD, NetBSD, OpenBSD, macOS, … Seems easier to just write code that only checks for the program the test needs?

  32. in test/functional/feature_reindex_readonly.py:76 in cbd5bf07d2 outdated
    79+                pass
    80 
    81-        if used_chattr:
    82-            subprocess.check_call(['chattr', '-i', filename])
    83+        if undo_immutable:
    84+            self.log.debug("Attempt to restart and reindex the node with the unwritable block file")
    


    theStack commented at 12:04 pm on October 20, 2023:

    nit: I think it’s fine to always show this line, so one can quickly see if the actual test is executed at all:

    0            self.log.info("Attempt to restart and reindex the node with the unwritable block file")
    

    pinheadmz commented at 2:58 pm on October 23, 2023:
    yes thanks
  33. maflcko commented at 12:04 pm on October 20, 2023: member

    I’m running OpenBSD 7.4 with the package e2fsprogs installed, which has a binary /usr/local/bin/chattr installed, so the Linux branch is executed.

    If this package can be installed on all BSD and macOS, I think we can also remove the BSD branch, and just require this package?

  34. in test/functional/feature_reindex_readonly.py:61 in cbd5bf07d2 outdated
    62+            # macOS, and *BSD
    63+            try:
    64+                subprocess.run(['chflags'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
    65+                try:
    66+                    subprocess.run(['chflags', 'uchg', filename], capture_output=True, check=True)
    67+                    undo_immutable = subprocess.check_call(['chflags', 'nouchg', filename])
    


    theStack commented at 12:14 pm on October 20, 2023:
    0                    undo_immutable = lambda: subprocess.check_call(['chflags', 'nouchg', filename])
    

    (currently, the test is not executed on OpenBSD, as undo_immutable is assigned an integer of value zero, if the chflags call succeeds)


    maflcko commented at 12:55 pm on October 20, 2023:
    I wonder when we’ll be be able to write the tests in a type-safe language.

    pinheadmz commented at 2:42 pm on October 23, 2023:
    damn great catch thanks, fixing
  35. pinheadmz commented at 12:18 pm on October 20, 2023: member
    I don’t think chattr is available for macOS
  36. pinheadmz commented at 12:19 pm on October 20, 2023: member
    But, we could make this a utility function that returns an “undo” lambda? And then get the weird code out of the test itself?
  37. theStack commented at 12:29 pm on October 20, 2023: contributor

    @theStack the issue was hard-coding all the OSs. Every flavor of *BSD plus macOS has chattr, Linux has chflags and Windows, well… chmod seems to work against root on Windows. The test isn’t trying to assert that certain behaviors are expected on certain OSes, we just want that file to be immutable “by any means necessary”

    previously done with platform.system(), which makes much more sense IMHO

    Not sure. This would force us to maintain a list of all operating systems in this test. FreeBSD, NetBSD, OpenBSD, macOS, … Seems easier to just write code that only checks for the program the test needs?

    Yeah, fair enough, I agree that maintaining this list is also not ideal, and just checking for the programs we need is fine. What I particularly don’t like about catching the generic Exception as control flow instrument is that there are a whole lot of other reasons why exceptions could be thrown (when something actually goes wrong, like out of disk space/memory, or whatever else), and then we wouldn’t even notice it, as in the end everything is swallowed by the final except Exception: pass and the test just passes fine. That seems a bit fragile. But no strong opinion, I’m happy to ACK once it works on OpenBSD (see below).

  38. theStack commented at 12:57 pm on October 20, 2023: contributor

    Here’s some more practical example why catching the generic Exception is not a good idea. With the following patch, the test passes just fine, as we don’t notice the error which would normally be thrown if we try to call a method that doesn’t exist:

     0diff --git a/test/functional/feature_reindex_readonly.py b/test/functional/feature_reindex_readonly.py
     1index b276be9c03..9f3b642338 100755
     2--- a/test/functional/feature_reindex_readonly.py
     3+++ b/test/functional/feature_reindex_readonly.py
     4@@ -36,7 +36,7 @@ class BlockstoreReindexTest(BitcoinTestFramework):
     5         undo_immutable = lambda: None
     6         # Linux
     7         try:
     8-            subprocess.run(['chattr'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
     9+            subprocess.run_thismethoddoesnotevenexist(['chattr'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
    10             try:
    11                 subprocess.run(['chattr', '+i', filename], capture_output=True, check=True)
    12                 undo_immutable = lambda: subprocess.check_call(['chattr', '-i', filename])
    

    This should throw an AttributeError: module 'subprocess' has no attribute 'run_thismethoddoesnotevenexist', and it indeed does, but we don’t notice it, as we fall in the except Exception branch. But enough ranting about that topic from my side for now :)

  39. maflcko commented at 9:35 am on October 23, 2023: member
    This should be rfm, except for https://github.com/bitcoin/bitcoin/pull/28660/files#r1366893829, which can either be done here, or I’d do in a follow-up?
  40. test: enable reindex readonly test on *BSD and macOS as root 5a0688a20d
  41. pinheadmz force-pushed on Oct 23, 2023
  42. maflcko commented at 3:07 pm on October 23, 2023: member
    re-cr-lgtm-ACK 5a0688a20d88a9641c02436abbd7b49e227f1a37
  43. DrahtBot requested review from theStack on Oct 23, 2023
  44. theStack approved
  45. theStack commented at 3:44 pm on October 23, 2023: contributor

    ACK 5a0688a20d88a9641c02436abbd7b49e227f1a37

    Tested in OpenBSD 7.4, once with and once without the e2fsprogs package installed, to trigger both code paths:

     0$ which chattr && which chflags
     1/usr/local/bin/chattr
     2/usr/bin/chflags
     3$ ./test/functional/feature_reindex_readonly.py
     42023-10-23T15:41:09.438000Z TestFramework (INFO): PRNG seed is: 435157676393106655
     52023-10-23T15:41:09.493000Z TestFramework (INFO): Initializing test directory /tmp/bitcoin_func_test_2uz_nii5
     62023-10-23T15:41:10.354000Z TestFramework (INFO): Made file immutable with chattr
     72023-10-23T15:41:10.363000Z TestFramework (INFO): Attempt to restart and reindex the node with the unwritable block file
     82023-10-23T15:41:10.966000Z TestFramework (INFO): Stopping nodes
     92023-10-23T15:41:10.970000Z TestFramework (INFO): Cleaning up /tmp/bitcoin_func_test_2uz_nii5 on exit
    102023-10-23T15:41:10.971000Z TestFramework (INFO): Tests successful
    
     0$ doas pkg_delete e2fsprogs
     1...
     2$ which chattr && which chflags
     3which: chattr: Command not found.
     4$ ./test/functional/feature_reindex_readonly.py
     52023-10-23T15:43:04.599000Z TestFramework (INFO): PRNG seed is: 8556685811226418814
     62023-10-23T15:43:04.653000Z TestFramework (INFO): Initializing test directory /tmp/bitcoin_func_test_h4lvamww
     72023-10-23T15:43:05.565000Z TestFramework (INFO): Made file immutable with chflags
     82023-10-23T15:43:05.622000Z TestFramework (INFO): Attempt to restart and reindex the node with the unwritable block file
     92023-10-23T15:43:06.231000Z TestFramework (INFO): Stopping nodes
    102023-10-23T15:43:06.295000Z TestFramework (INFO): Cleaning up /tmp/bitcoin_func_test_h4lvamww on exit
    112023-10-23T15:43:06.362000Z TestFramework (INFO): Tests successful
    
  46. DrahtBot added the label CI failed on Oct 23, 2023
  47. jonatack commented at 6:31 pm on October 23, 2023: contributor

    I agree with @theStack’s concerns about catching the generic exception for control flow and would be happy to re-review if this is reverted to platform() queries. That said, I needed to refactor this a fair amount already to extract it in #28116, so I suppose it can be improved there.

    ACK 5a0688a20d88a9641c02436abbd7b49e227f1a37 tested on macOS only

  48. DrahtBot removed review request from jonatack on Oct 23, 2023
  49. maflcko assigned fanquake on Oct 24, 2023
  50. fanquake merged this on Oct 24, 2023
  51. fanquake closed this on Oct 24, 2023

  52. in test/functional/feature_reindex_readonly.py:37 in 5a0688a20d
    32@@ -34,11 +33,13 @@ def reindex_readonly(self):
    33         filename = self.nodes[0].chain_path / "blocks" / "blk00000.dat"
    34         filename.chmod(stat.S_IREAD)
    35 
    36-        used_chattr = False
    37-        if platform.system() == "Linux":
    38+        undo_immutable = lambda: None
    39+        # Linux
    


    maflcko commented at 11:19 am on October 24, 2023:
    nit: I guess the comment isn’t applicable, given that chattr can be installed on FreeBSD

    jonatack commented at 8:07 pm on October 25, 2023:
    Yes, the comment is already removed in #28116 (which I’ll be updating).
  53. in test/functional/feature_reindex_readonly.py:41 in 5a0688a20d
    38+        undo_immutable = lambda: None
    39+        # Linux
    40+        try:
    41+            subprocess.run(['chattr'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
    42             try:
    43                 subprocess.run(['chattr', '+i', filename], capture_output=True, check=True)
    


    mzumsande commented at 7:42 pm on October 25, 2023:

    On Ubuntu 22.04.3 LTS, this line throws an exception for me

    02023-10-25T19:35:49.783000Z TestFramework (DEBUG): Make the first block file read-only
    12023-10-25T19:35:49.785000Z TestFramework (WARNING): Command '['chattr', '+i', PosixPath('/tmp/bitcoin_func_test_i3g4dkjv/node0/regtest/blocks/blk00000.dat')]' returned non-zero exit status 1.
    22023-10-25T19:35:49.785000Z TestFramework (WARNING): stderr: b'chattr: Operation not permitted while setting flags on /tmp/bitcoin_func_test_i3g4dkjv/node0/regtest/blocks/blk00000.dat\n'
    

    In spite of this, the file is still changed to readonly, so the test still works as intended (it just doesn’t set undo_immutable in the next line because of the exception).


    maflcko commented at 7:44 pm on October 25, 2023:
    Yes, but this seems unrelated to the changes here, and the warning can be ignored. I guess it should just be a debug or info level?

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2024-07-05 19:13 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me