Repository navigation
feat: report each provider's deletion rules, stop S3 dropping other rules #107
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
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,101 @@ | ||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
| using Azure.ResourceManager.Storage; | ||
| using Azure.ResourceManager.Storage.Models; | ||
| using Microsoft.VisualStudio.TestTools.UnitTesting; | ||
| using SW.CloudFiles.AS; | ||
|
|
||
| namespace SW.CloudFiles.AS.UnitTest; | ||
|
|
||
| /// <summary>Pure policy logic, no storage account needed — unlike the other tests in this project.</summary> | ||
| [TestClass] | ||
| public class AzureLifecycleRulesTests | ||
| { | ||
| private static ManagementPolicyRule DeleteRule(string name, float days, params string[] prefixes) | ||
| { | ||
| var filter = new ManagementPolicyFilter(["blockBlob"]); | ||
| foreach (var prefix in prefixes) filter.PrefixMatch.Add(prefix); | ||
| var actions = new ManagementPolicyAction | ||
| { | ||
| BaseBlob = new ManagementPolicyBaseBlob { Delete = new DateAfterModification { DaysAfterModificationGreaterThan = days } } | ||
| }; | ||
| return new ManagementPolicyRule(name, ManagementPolicyRuleType.Lifecycle, | ||
| new ManagementPolicyDefinition(actions) { Filters = filter }); | ||
| } | ||
|
|
||
| [TestMethod] | ||
| public void Adds_this_containers_temp_rules_and_keeps_the_rest_of_the_policy() | ||
| { | ||
| var data = new StorageAccountManagementPolicyData(); | ||
| data.Rules.Add(DeleteRule("someone-elses", 14, "logs/")); | ||
| data.Rules.Add(DeleteRule("other-temp30", 30, "other/temp30/")); | ||
|
|
||
| Assert.IsTrue(AzureLifecycleRules.AddTempRules(data, "docs")); | ||
|
|
||
| CollectionAssert.AreEquivalent( | ||
| new[] { "someone-elses", "other-temp30", "docs-temp1", "docs-temp7", "docs-temp30", "docs-temp365" }, | ||
| data.Rules.Select(r => r.Name).ToArray()); | ||
| Assert.AreEqual("docs/temp30/", data.Rules.Single(r => r.Name == "docs-temp30").Definition.Filters.PrefixMatch.Single()); | ||
| } | ||
|
|
||
| [TestMethod] | ||
| public void Nothing_to_write_when_the_rules_are_already_there() | ||
| { | ||
| var data = new StorageAccountManagementPolicyData(); | ||
| AzureLifecycleRules.AddTempRules(data, "docs"); | ||
|
|
||
| Assert.IsFalse(AzureLifecycleRules.AddTempRules(data, "docs")); | ||
| } | ||
|
|
||
| [TestMethod] | ||
| public void A_disabled_temp_rule_is_replaced_not_duplicated() | ||
| { | ||
| var data = new StorageAccountManagementPolicyData(); | ||
| var disabled = DeleteRule("docs-temp30", 30, "docs/temp30/"); | ||
| disabled.IsEnabled = false; | ||
| data.Rules.Add(disabled); | ||
|
|
||
| AzureLifecycleRules.AddTempRules(data, "docs"); | ||
|
|
||
| var temp30 = data.Rules.Where(r => r.Name == "docs-temp30").ToList(); | ||
| Assert.AreEqual(1, temp30.Count); | ||
| Assert.IsTrue(temp30[0].IsEnabled); | ||
| } | ||
|
|
||
| [TestMethod] | ||
| public void Reads_only_this_containers_prefixes_relative_to_the_container() | ||
| { | ||
| var rules = AzureLifecycleRules.ToDeletionRules([ | ||
| DeleteRule("docs-temp30", 30, "docs/temp30/"), | ||
| DeleteRule("other-temp30", 30, "other/temp30/") | ||
| ], "docs"); | ||
|
|
||
| var rule = rules.Single(); | ||
| Assert.AreEqual("temp30/", rule.Prefix); | ||
| Assert.AreEqual(30, rule.Days); | ||
| Assert.IsTrue(rule.Enabled); | ||
| } | ||
|
|
||
| [TestMethod] | ||
| public void A_rule_without_prefixes_covers_every_container() | ||
| { | ||
| Assert.AreEqual(string.Empty, AzureLifecycleRules.ToDeletionRules([DeleteRule("all", 90)], "docs").Single().Prefix); | ||
| } | ||
|
|
||
| [TestMethod] | ||
| public void Tag_filtered_rules_and_rules_without_a_delete_are_left_out() | ||
| { | ||
| var tagged = DeleteRule("tagged", 7, "docs/temp7/"); | ||
| tagged.Definition.Filters.BlobIndexMatch.Add(new ManagementPolicyTagFilter("kind", "==", "scratch")); | ||
| var tiering = new ManagementPolicyRule("tiering", ManagementPolicyRuleType.Lifecycle, | ||
| new ManagementPolicyDefinition(new ManagementPolicyAction | ||
| { | ||
| BaseBlob = new ManagementPolicyBaseBlob | ||
| { | ||
| TierToCool = new DateAfterModification { DaysAfterModificationGreaterThan = 30 } | ||
| } | ||
| })); | ||
|
|
||
| Assert.AreEqual(0, AzureLifecycleRules.ToDeletionRules([tagged, tiering], "docs").Count); | ||
| } | ||
| } |
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
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.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
🔎 Supported by static analysis
🏁 Script executed:
Repository: simplify9/SW-CloudFiles
Length of output: 19474
🏁 Script executed:
Repository: simplify9/SW-CloudFiles
Length of output: 42473
🤖 get_repo_knowledge executed:
get_repo_knowledge simplify9/SW-CloudFiles /tmp/coderabbit-repo-knowledge/simplify9-sw-cloudfiles-de20a90b/architecture /tmp/coderabbit-repo-knowledge/simplify9-sw-cloudfiles-de20a90b/conventionsLength of output: 25966
Use a conditional update for the account-wide lifecycle policy.
EnsureLifecycleRulesreads the complete policy, adds this container's rules to the local copy, and writes the complete policy. Two containers on the same account can read the same version. The laterCreateOrUpdatecan replace the earlier container's rules. A post-write check does not prevent a later writer from replacing a verified policy.Use an account-wide compare-and-swap update with the read ETag and
If-Match. On412, re-read, merge, and retry. Azure.ResourceManager.Storage 1.6.2 does not expose this overload, so use a supported ARM conditional request or an account-wide distributed lock.🤖 Prompt for AI Agents
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.
Not changing this. Azure's managementPolicies API has no ETag or If-Match (checked API versions 2025-06-01 and 2026-06-01), so a conditional write isn't possible, and an account-wide lock is too much for a library. The window is two apps sharing one storage account starting at the same moment. If it happens, nothing is deleted by mistake: the overwritten app's temp files wait until its next start, which adds its rules back.