-
Notifications
You must be signed in to change notification settings - Fork 200
ENT-14297: Improve error message when cf-agent cannot write to an immutable file #6236
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
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| ############################################################################## | ||
| # | ||
| # Test that agent reports the immutable bit in the error message when it | ||
| # cannot write to an immutable file (content attribute, no override). | ||
| # | ||
| # The whole scenario (create the file, set it immutable, then fail to write it) | ||
| # runs in a self-contained sub policy: its setup happens in its own agent | ||
| # process, so it does not depend on this test's evaluation order, and its | ||
| # non-zero exit does not fail this test. We only assert on the captured error | ||
| # output. Teardown clears the immutable bit natively via body fsattrs. | ||
| # | ||
| ############################################################################## | ||
| body common control | ||
| { | ||
| inputs => { "../../default.sub.cf" }; | ||
| bundlesequence => { default("$(this.promise_filename)") }; | ||
| version => "1.0"; | ||
| } | ||
|
|
||
| bundle agent global | ||
| { | ||
| vars: | ||
| "testfile" string => "/tmp/14_immutable.txt"; | ||
| } | ||
|
|
||
| body fsattrs clear_immutable | ||
| { | ||
| immutable => "false"; | ||
| } | ||
|
|
||
| bundle agent test | ||
| { | ||
| meta: | ||
| "description" -> { "ENT-14297" } | ||
| string => "Test that the error message names the immutable bit when agent cannot write to an immutable file"; | ||
|
|
||
| "test_skip_unsupported" string => "hpux|aix|solaris|windows"; | ||
| } | ||
|
|
||
| bundle agent check | ||
| { | ||
| vars: | ||
| "run" | ||
| data => execresult_as_data( | ||
| "$(sys.cf_agent) -KI -f $(with)", "noshell", "both" | ||
| ), | ||
| with => regex_replace("$(this.promise_filename)", "\.cf$", ".sub.cf", ""); | ||
|
|
||
| classes: | ||
| "ok" | ||
| expression => regcmp( | ||
| ".*Cannot open file.*the immutable bit is set.*", "$(run[output])" | ||
| ); | ||
|
|
||
| reports: | ||
| ok:: | ||
| "$(this.promise_filename) Pass"; | ||
|
|
||
| !ok:: | ||
| "$(this.promise_filename) FAIL"; | ||
|
|
||
| DEBUG:: | ||
| "$(this.bundle): agent exited '$(run[exit_code])', output was:"; | ||
| "$(run[output])"; | ||
| } | ||
|
|
||
| bundle agent destroy | ||
| { | ||
| files: | ||
| "$(global.testfile)" | ||
| delete => tidy, | ||
| fsattrs => clear_immutable; | ||
| } |
30 changes: 30 additions & 0 deletions
30
tests/acceptance/10_files/unsafe/14_immutable_content.sub.cf
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| body common control | ||
| { | ||
| bundlesequence => { "setup", "try_modify" }; | ||
| } | ||
|
|
||
| body fsattrs set_immutable | ||
| { | ||
| immutable => "true"; | ||
| } | ||
|
|
||
| bundle agent setup | ||
| { | ||
| files: | ||
| # Create the target and set the immutable bit natively. This runs in its | ||
| # own agent process (invoked from the test's check bundle), so setup is | ||
| # ordered before the write below and does not depend on the parent test. | ||
| "/tmp/14_immutable.txt" | ||
| content => "original content", | ||
| fsattrs => set_immutable; | ||
| } | ||
|
|
||
| bundle agent try_modify | ||
| { | ||
| files: | ||
| # No fsattrs => ... immutable, so the immutable bit is not overridden. | ||
| # The content differs from what setup wrote, so the agent tries to open the | ||
| # immutable file for writing and fails. | ||
| "/tmp/14_immutable.txt" | ||
| content => "the agent tries to modify an immutable file"; | ||
| } |
77 changes: 77 additions & 0 deletions
77
tests/acceptance/10_files/unsafe/15_immutable_edit_line.cf
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| ############################################################################## | ||
| # | ||
| # Test that agent reports the immutable bit in the error message when it | ||
| # cannot write to an immutable file via edit_line (no override). | ||
| # | ||
| # edit_line writes a temporary file and renames it over the target, so the | ||
| # failure surfaces from OverrideImmutableRename(). The whole scenario (create | ||
| # the file, set it immutable, then fail to edit it) runs in a self-contained | ||
| # sub policy: its setup happens in its own agent process, so it does not depend | ||
| # on this test's evaluation order, and its non-zero exit does not fail this | ||
| # test. We only assert on the captured error output. Teardown clears the | ||
| # immutable bit natively via body fsattrs. | ||
| # | ||
| ############################################################################## | ||
| body common control | ||
| { | ||
| inputs => { "../../default.sub.cf" }; | ||
| bundlesequence => { default("$(this.promise_filename)") }; | ||
| version => "1.0"; | ||
| } | ||
|
|
||
| bundle agent global | ||
| { | ||
| vars: | ||
| "testfile" string => "/tmp/15_immutable.txt"; | ||
| } | ||
|
|
||
| body fsattrs clear_immutable | ||
| { | ||
| immutable => "false"; | ||
| } | ||
|
|
||
| bundle agent test | ||
| { | ||
| meta: | ||
| "description" -> { "ENT-14297" } | ||
| string => "Test that edit_line on an immutable file names the immutable bit in the error"; | ||
|
|
||
| "test_skip_unsupported" string => "hpux|aix|solaris|windows"; | ||
| } | ||
|
|
||
| bundle agent check | ||
| { | ||
| vars: | ||
| "run" | ||
| data => execresult_as_data( | ||
| "$(sys.cf_agent) -KI -f $(with)", "noshell", "both" | ||
| ), | ||
| with => regex_replace("$(this.promise_filename)", "\.cf$", ".sub.cf", ""); | ||
|
|
||
| classes: | ||
| "ok" | ||
| expression => regcmp( | ||
| ".*Failed to replace.*the immutable bit is set.*", "$(run[output])" | ||
| ); | ||
|
|
||
| reports: | ||
| ok:: | ||
| "$(this.promise_filename) Pass"; | ||
|
|
||
| !ok:: | ||
| "$(this.promise_filename) FAIL"; | ||
|
|
||
| DEBUG:: | ||
| "$(this.bundle): agent exited '$(run[exit_code])', output was:"; | ||
| "$(run[output])"; | ||
| } | ||
|
|
||
| bundle agent destroy | ||
| { | ||
| files: | ||
| "$(global.testfile)" | ||
| delete => tidy, | ||
| fsattrs => clear_immutable; | ||
|
|
||
| "$(global.testfile).cf-before-edit" delete => tidy; | ||
| } |
35 changes: 35 additions & 0 deletions
35
tests/acceptance/10_files/unsafe/15_immutable_edit_line.sub.cf
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| body common control | ||
| { | ||
| bundlesequence => { "setup", "try_modify" }; | ||
| } | ||
|
|
||
| body fsattrs set_immutable | ||
| { | ||
| immutable => "true"; | ||
| } | ||
|
|
||
| bundle edit_line insert_a_line | ||
| { | ||
| insert_lines: | ||
| "a line the agent tries to insert into an immutable file"; | ||
| } | ||
|
|
||
| bundle agent setup | ||
| { | ||
| files: | ||
| # Create the target and set the immutable bit natively. This runs in its | ||
| # own agent process (invoked from the test's check bundle), so setup is | ||
| # ordered before the edit below and does not depend on the parent test. | ||
| "/tmp/15_immutable.txt" | ||
| content => "original line", | ||
| fsattrs => set_immutable; | ||
| } | ||
|
|
||
| bundle agent try_modify | ||
| { | ||
| files: | ||
| # No fsattrs => ... immutable, so the immutable bit is not overridden. | ||
| # Inserting a new line changes the file, so the agent writes a temporary | ||
| # file and renames it over the immutable target, which fails. | ||
| "/tmp/15_immutable.txt" edit_line => insert_a_line; | ||
| } |
78 changes: 78 additions & 0 deletions
78
tests/acceptance/10_files/unsafe/16_immutable_copy_from.cf
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| ############################################################################## | ||
| # | ||
| # Test that agent reports the immutable bit in the error message when it | ||
| # cannot write to an immutable file via copy_from (no override). | ||
| # | ||
| # copy_from writes a temporary file and renames it over the target, so the | ||
| # failure surfaces from OverrideImmutableRename(). The whole scenario (create | ||
| # source + immutable destination, then fail to copy) runs in a self-contained | ||
| # sub policy: its setup happens in its own agent process, so it does not depend | ||
| # on this test's evaluation order, and its non-zero exit does not fail this | ||
| # test. We only assert on the captured error output. Teardown clears the | ||
| # immutable bit natively via body fsattrs. | ||
| # | ||
| ############################################################################## | ||
| body common control | ||
| { | ||
| inputs => { "../../default.sub.cf" }; | ||
| bundlesequence => { default("$(this.promise_filename)") }; | ||
| version => "1.0"; | ||
| } | ||
|
|
||
| bundle agent global | ||
| { | ||
| vars: | ||
| "source" string => "/tmp/16_immutable.src"; | ||
| "testfile" string => "/tmp/16_immutable.txt"; | ||
| } | ||
|
|
||
| body fsattrs clear_immutable | ||
| { | ||
| immutable => "false"; | ||
| } | ||
|
|
||
| bundle agent test | ||
| { | ||
| meta: | ||
| "description" -> { "ENT-14297" } | ||
| string => "Test that copy_from onto an immutable file names the immutable bit in the error"; | ||
|
|
||
| "test_skip_unsupported" string => "hpux|aix|solaris|windows"; | ||
| } | ||
|
|
||
| bundle agent check | ||
| { | ||
| vars: | ||
| "run" | ||
| data => execresult_as_data( | ||
| "$(sys.cf_agent) -KI -f $(with)", "noshell", "both" | ||
| ), | ||
| with => regex_replace("$(this.promise_filename)", "\.cf$", ".sub.cf", ""); | ||
|
|
||
| classes: | ||
| "ok" | ||
| expression => regcmp( | ||
| ".*Failed to replace.*the immutable bit is set.*", "$(run[output])" | ||
| ); | ||
|
|
||
| reports: | ||
| ok:: | ||
| "$(this.promise_filename) Pass"; | ||
|
|
||
| !ok:: | ||
| "$(this.promise_filename) FAIL"; | ||
|
|
||
| DEBUG:: | ||
| "$(this.bundle): agent exited '$(run[exit_code])', output was:"; | ||
| "$(run[output])"; | ||
| } | ||
|
|
||
| bundle agent destroy | ||
| { | ||
| files: | ||
| "$(global.testfile)" | ||
| delete => tidy, | ||
| fsattrs => clear_immutable; | ||
|
|
||
| "$(global.source)" delete => tidy; | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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 wonder if you could have used
new_is_immutablefrom above in this condition instead?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.
Good instinct to reuse it, but
new_is_immutableisn't valid in this branch:TemporarilyClearImmutableBit()returns early on!overridewithout writing*was_immutable(andnew_is_immutableis declared uninitialized), while this check runs specifically in the!overridecase — so reusing it here would read an uninitialized value. Keeping the explicitFSAttrsGetImmutableFlag()also keeps the extra stat on the rare rename-failure path rather than on every temp-file write. Happy to refactorTemporarilyClearImmutableBit()to always populate the flag if you'd prefer the reuse. (Analysis via Claude.)