v0.8.82: UpdateLastAttempt audit tag + Step.05 Last Updated column + Step.08 attempts-without-run reconciliation#88
Merged
Merged
Conversation
…ers detail by Status priority - Summary counts: each row reused the icon-map cell (which already includes its own label) AND appended a duplicate trailing label, producing 'Ready for Update Ready for update' / 'Up to Date Up to date' / 'Action Required Not ready for update' / 'Health Failure Clusters with Critical health failures'. Fixed by emitting the icon-map cell unmodified; the HealthFailure row keeps the 'Clusters with Critical health failures' qualifier in parentheses since it counts something different from the readiness cascade. - All-clusters detail: previously sorted only by UpdateRing + ClusterName so operator-actionable rows were scattered. Now sorts by Status priority first (InProgress -> HealthFailure -> UpdateFailed -> ActionRequired -> SbeBlocked -> NeedsInvestigation -> ReadyForUpdate -> UpToDate), then UpdateRing + ClusterName as before. Up-to-Date clusters drop to the bottom; in-flight + remediation rows surface at the top. - Pester: 1211 passed, 0 failed (no behavioural test relied on the duplicated labels or the prior sort order).
…column + Step.08 reconciliation Three Item-5 deliverables shipped together, plus the residual v0.8.81 review polish. Step.05 (Get-AzLocalClusterUpdateReadiness + Export-AzLocalClusterUpdateReadinessReport): - New LastUpdated property on every output row, computed from max packageVersions[].lastUpdated across all packageTypes (Solution / Services / Platform / SBE). - New 'Last Updated' column in the detail table between 'Status' and 'Recommended update'. - Detail-table sort changed to UpdateRing -> Status priority -> ClusterName so ring cohorts stay grouped with in-flight/remediation rows at the top of each cohort. UpdateLastAttempt cluster tag (new, owned by AzLocal.UpdateManagement): - Format: <UTC>;<Outcome>;<UpdateName>;<Reason> truncated to the 256-char Azure tag-value limit (reason field truncated first with a trailing ~ sentinel). - Stamped by Start-AzLocalClusterUpdate at every attempt outcome (HealthCheckBlocked, UpdateStarted, Failed). Writes swallow ARM errors and log Warning - the audit tag MUST NOT block the apply flow. - Auto-cleared by Invoke-AzLocalSideloadedAutoResetForCluster (independent block, separate from sideloaded reset logic) when the latest update run is Succeeded AND either the UpdateName matches the recorded attempt OR the attempt was a HealthCheckBlocked/Failed outcome more than 1h old. - New Private helpers: Format-AzLocalUpdateLastAttemptTagValue, ConvertFrom-AzLocalUpdateLastAttemptTagValue, Write-AzLocalUpdateLastAttemptTag. New script-scoped constant $script:UpdateLastAttemptTagName = 'UpdateLastAttempt'. Step.08 (Export-AzLocalUpdateRunMonitorReport): - New 'Recent update attempts with no observable updateRun' section. Joins the per-cluster UpdateLastAttempt tag against the observed updateRun list and surfaces any attempt within the last -RecentAttemptWindowHours (default 72) whose matching updateRun is missing or has a StartTimeUtc before (attempt-5min). - Captures Portland-style URP package-internal pre-install health-check failures (cluster activity log shows 'Allows to apply updates: Succeeded' but no updateRun resource is ever persisted - confirmed via ARG on 2026-06-15: Portland has 10 updateRuns across 5 versions, none for Solution12.2605.1003.210 which is in its UpdateVersionInProgress tag). - by-update-ring scope path now ALWAYS calls Get-AzLocalClusterInventory (with -ScopeByUpdateRingTag) so per-cluster tags are available for the reconciliation pass. The all-clusters path already had inventory. - New pipeline output: attempts_without_run. - New PassThru fields: AttemptWithoutRunCount + AttemptGaps (uses .ToArray() not @(...) - empty Generic.List[object] wrap throws ArgumentException in PS 5.1). - New parameter: [ValidateRange(0,8760)][int] = 72. - Each gap also emits a JUnit testcase with Type='AttemptWithoutRun', ClassName='UpdateMonitor'. Tests: - Pester suite expanded: 1248 tests, 0 failures. New Describe blocks cover Format/Convert tag helpers (round-trip, truncation, semicolon stripping, whitespace collapse, malformed-input -> ), the script-scoped tag-name constant, Get-AzLocalClusterUpdateReadiness LastUpdated presence on all shapes (success / NotFound / Error), Step.05 column + sort, Start-AzLocalClusterUpdate three call sites, Invoke-AzLocalSideloadedAutoResetForCluster clear logic, and Step.08 reconciliation (param, builder, 5-min slack, by-update-ring inventory, pipeline output, markdown section, JUnit Type, PassThru shape, empty-fleet PassThru). - Pre-existing Scope=by-update-ring test updated: now asserts Get-AzLocalClusterInventory IS called with -ScopeByUpdateRingTag (was asserting it must NOT be called - intentional behavior change for the reconciliation pass). CHANGELOG + docs/release-history.md + README.md updated. Pipeline YAML GENERATED_AGAINST_MODULE_VERSION pins bumped to '0.8.82' across all 22 ADO + GHA workflow templates. Module export count unchanged (60). Resolves: ship Item-5 backlog from /memories/repo/AzLocal.UpdateManagement-post-v0.7.76-backlog.md.
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Summary
Ships Item 5 from the post-v0.7.76 backlog: a per-cluster
UpdateLastAttemptARM tag stamped byStart-AzLocalClusterUpdateand an in-flight Step.08 reconciliation pass that compares those audit tags to the observed updateRun list. Also lands the Step.05Last Updatedreadiness column +UpdateRingsort that was discussed alongside it.What's new
Step.05 - readiness report
Get-AzLocalClusterUpdateReadinessnow emitsLastUpdatedon every row shape (success / NotFound / Error), computed from the maxpackageVersions[].lastUpdatedacross all four package types.Export-AzLocalClusterUpdateReadinessReportrenders a new Last Updated column between Status and Recommended update, and sorts the detail table by UpdateRing -> Status priority -> ClusterName so each ring cohort stays grouped with its in-flight / remediation rows at the top.UpdateLastAttempt audit tag (new)
<UTC>;<Outcome>;<UpdateName>;<Reason>truncated to the 256-char Azure tag-value limit (reason truncated first with a trailing~sentinel).Start-AzLocalClusterUpdateat every outcome (HealthCheckBlocked,UpdateStarted,Failed). Writes swallow ARM errors and log a warning - the audit tag must NEVER block the apply flow.Invoke-AzLocalSideloadedAutoResetForCluster(independent block, separate from the sideloaded-reset logic) when the latest update run isSucceededAND eitherUpdateNamematches the recorded attempt OR the attempt was aHealthCheckBlocked/Failedoutcome more than 1h old.Format-AzLocalUpdateLastAttemptTagValue,ConvertFrom-AzLocalUpdateLastAttemptTagValue,Write-AzLocalUpdateLastAttemptTag. New script-scoped constant= 'UpdateLastAttempt'.Step.08 - in-flight monitor reconciliation
Recent update attempts with no observable updateRunsection. Joins the per-clusterUpdateLastAttempttag against the observed updateRun list and surfaces any attempt within the last-RecentAttemptWindowHours(default 72) whose matching updateRun is missing or has aStartTimeUtcbeforeattempt - 5min.Allows to apply updates: Succeededbut no updateRun resource is ever persisted - confirmed via ARG against Portland in the design pass: 10 updateRuns across 5 versions, none for the version recorded in itsUpdateVersionInProgresstag).Scope=by-update-ringpath now ALWAYS callsGet-AzLocalClusterInventory -ScopeByUpdateRingTagso per-cluster tags are available for the reconciliation pass. The all-clusters path already had inventory. This is a behavior change vs. v0.8.81 (documented in CHANGELOG).[ValidateRange(0,8760)][int] = 72.attempts_without_run.-PassThrufields:AttemptWithoutRunCount+AttemptGaps.Type='AttemptWithoutRun',ClassName='UpdateMonitor'.Tests
1248 tests, 0 failures (Pester v5.7.1 / Windows PowerShell 5.1).
New Describe blocks cover: Format / Convert tag helpers (round-trip, truncation, semicolon stripping, whitespace collapse, malformed ->
), the script-scoped tag-name constant, `LastUpdated` on all three row shapes, Step.05 column + sort, the three `Write-AzLocalUpdateLastAttemptTag` call sites in `Start-AzLocalClusterUpdate`, the auto-clear logic in `Invoke-AzLocalSideloadedAutoResetForCluster`, and Step.08 reconciliation (param,builder, 5-min slack, by-update-ring inventory, pipeline output, markdown section, JUnit Type, PassThru shape, empty-fleet PassThru).Pre-existing
Scope=by-update-ringtest updated to assertGet-AzLocalClusterInventoryIS called (was asserting it must NOT be called - intentional behavior change for the reconciliation pass).Implementation notes
AttemptGapsin the-PassThruoutput uses.ToArray()rather than@(). Reason:@()around an EMPTYSystem.Collections.Generic.List[object]throwsArgumentException: Argument types do not matchin Windows PowerShell 5.1 with no inner exception..ToArray()returnsT[0]cleanly. Saved to/memories/powershell-patterns.md.GENERATED_AGAINST_MODULE_VERSIONbumped to'0.8.82'.Files touched
AzLocal.UpdateManagement.psd1(ModuleVersion + 3 new Private dot-source entries)AzLocal.UpdateManagement.psm1(`` constant)Private/Format-AzLocalUpdateLastAttemptTagValue.ps1(new)Private/ConvertFrom-AzLocalUpdateLastAttemptTagValue.ps1(new)Private/Write-AzLocalUpdateLastAttemptTag.ps1(new)Private/Invoke-AzLocalSideloadedAutoResetForCluster.ps1(auto-clear block)Public/Get-AzLocalClusterUpdateReadiness.ps1(LastUpdated)Public/Export-AzLocalClusterUpdateReadinessReport.ps1(column + sort)Public/Export-AzLocalUpdateRunMonitorReport.ps1(reconciliation)Public/Export-AzLocalFleetHealthStatusReport.ps1(residual v0.8.81 polish)Public/Start-AzLocalClusterUpdate.ps1(3 tag-write call sites)Tests/AzLocal.UpdateManagement.Tests.ps1(+Tests/test-run-timings.csv)CHANGELOG.md/README.md/docs/release-history.mdAutomation-Pipeline-Examples/