Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/ci/cleanup.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ To resolve the principals behind orphaned role assignments, `shared-leftovers` r

For `cleanup-sweeper` `rg-ordered`, candidate resource groups are chosen using `tooling/cleanup-sweeper/resourcegroups.policy.yaml`. Discovery treats the `createdAt` tag (RFC3339 timestamp on the resource group) as required for any `action: delete` rule: groups without a parseable tag are not candidates.

The policy excludes long-lived slot-managed identity pools whose resource-group
names start with `aro-hcp-msi-container-`. These groups carry `persist=true`,
but they back repeated E2E leases and must not be treated as resources that
expire after 15 days.

Azure does not set that tag by default. Subscriptions where `rg-ordered` should run must apply an Azure Policy (or equivalent) that stamps `tags['createdAt']` when a resource group is created, using `[utcNow()]` in the policy rule. The rule body lives in `tooling/cleanup-sweeper/scripts/rg-createdat-policy-rule.json`.

Because the `createdAt` tag is required for every `action: delete` rule, this policy is a hard per-subscription prerequisite: `rg-ordered` must not be enabled in a subscription (including the INT, STG, and PROD e2e subscriptions) until the `createdAt` policy is assigned there. The sweeper only requires a parseable `createdAt` tag, so groups created before the policy was assigned are not candidates until the tag is present (either stamped by the policy on new groups or backfilled onto existing ones, for example via policy remediation). Until the policy is in place, only `shared-leftovers` is safe to run in that subscription, since it does not delete resource groups.
Expand Down
63 changes: 63 additions & 0 deletions tooling/cleanup-sweeper/pkg/policy/policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import (
"testing"
"time"

"k8s.io/apimachinery/pkg/util/sets"

"sigs.k8s.io/yaml"
)

Expand Down Expand Up @@ -68,6 +70,67 @@ rgOrdered:
}
}

func TestRepositoryPolicyProtectsIdentityContainerPools(t *testing.T) {
t.Parallel()

pol, err := Load("../../resourcegroups.policy.yaml")
if err != nil {
t.Fatalf("failed to load repository policy: %v", err)
}
if err := pol.Validate(); err != nil {
t.Fatalf("failed to validate repository policy: %v", err)
}

now := time.Date(2026, time.September, 12, 12, 0, 0, 0, time.UTC)
excluded := sets.New(pol.RGOrdered.ExcludedResourceGroups...)

testCases := []struct {
name string
resourceGroup string
expectSelected bool
expectRule string
}{
{
name: "identity container older than 15 days is skipped case insensitively",
resourceGroup: "ARO-HCP-MSI-Container-dev-shard0-00-00",
expectSelected: false,
expectRule: "skip-identity-container-pools",
},
{
name: "unrelated persistent group older than 15 days is selected",
resourceGroup: "example-persistent-resource-group",
expectSelected: true,
expectRule: "persist-true-delete-after-15d",
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()

resourceGroup := newResourceGroup(
tc.resourceGroup,
timePtr(now.Add(-16*24*time.Hour)),
map[string]string{"persist": "true"},
false,
)
selected, reason := pol.RGOrdered.Discovery.SelectsResourceGroup(
resourceGroup,
excluded,
sets.New[string](),
now,
)

if selected != tc.expectSelected {
t.Fatalf("expected selected=%t, got selected=%t", tc.expectSelected, selected)
}
if reason.Rule == nil || reason.Rule.Name != tc.expectRule {
t.Fatalf("expected rule %q, got %#v", tc.expectRule, reason.Rule)
}
})
}
}

func TestPolicyValidate_RejectsInvalidRules(t *testing.T) {
t.Parallel()

Expand Down
5 changes: 5 additions & 0 deletions tooling/cleanup-sweeper/resourcegroups.policy.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# cleanup-sweeper rg-ordered discovery policy (first matching rule wins).
# - excludedResourceGroups: never candidates (see list below).
# - Skip managed RGs (managedBy alive).
# - Skip slot-managed identity-container pools.
# - Skip *-shared-resources RGs.
# - hcp-underlay-pers-*: delete after 15d if persist=true; after 2d if persist is absent or not "true".
# - hcp-underlay-prow-*: delete after 6h (transitional, remove after rename rollout).
Expand Down Expand Up @@ -30,6 +31,10 @@ rgOrdered:
any: true
conditions:
managedByAlive: true
- name: skip-identity-container-pools
action: skip
match:
nameRegex: "(?i)^aro-hcp-msi-container-"
- name: skip-shared-resources
action: skip
match:
Expand Down