mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-10 02:25:56 +00:00
Prepare v6.0.3 patch release
This commit is contained in:
@@ -2,9 +2,9 @@ apiVersion: v2
|
||||
name: pulse
|
||||
description: Helm chart for deploying the Pulse hub and optional Docker monitoring agent.
|
||||
type: application
|
||||
version: 6.0.2
|
||||
appVersion: "6.0.2"
|
||||
icon: https://raw.githubusercontent.com/rcourtman/Pulse/v6.0.2/docs/images/pulse-logo.svg
|
||||
version: 6.0.3
|
||||
appVersion: "6.0.3"
|
||||
icon: https://raw.githubusercontent.com/rcourtman/Pulse/v6.0.3/docs/images/pulse-logo.svg
|
||||
keywords:
|
||||
- monitoring
|
||||
- proxmox
|
||||
@@ -32,7 +32,7 @@ annotations:
|
||||
description: Smoke tests with kind cluster deployment
|
||||
artifacthub.io/links: |
|
||||
- name: Documentation
|
||||
url: https://github.com/rcourtman/Pulse/blob/v6.0.2/docs/KUBERNETES.md
|
||||
url: https://github.com/rcourtman/Pulse/blob/v6.0.3/docs/KUBERNETES.md
|
||||
- name: Support
|
||||
url: https://github.com/rcourtman/Pulse/discussions
|
||||
artifacthub.io/maintainers: |
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# pulse
|
||||
|
||||
  
|
||||
  
|
||||
|
||||
Helm chart for deploying the Pulse hub and optional Docker monitoring agent.
|
||||
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ version: '3.8'
|
||||
|
||||
services:
|
||||
pulse:
|
||||
image: ${PULSE_IMAGE:-rcourtman/pulse:6.0.2}
|
||||
image: ${PULSE_IMAGE:-rcourtman/pulse:6.0.3}
|
||||
container_name: pulse
|
||||
restart: unless-stopped
|
||||
logging:
|
||||
|
||||
@@ -7,6 +7,8 @@ For historical v4 notes that previously lived in this repo, see:
|
||||
`docs/releases/RELEASE_NOTES_v4.md`
|
||||
|
||||
For the current stable v6 patch packet, see:
|
||||
- `docs/releases/RELEASE_NOTES_v6.0.3.md`
|
||||
- `docs/releases/V6_CHANGELOG_v6.0.3.md`
|
||||
- `docs/releases/RELEASE_NOTES_v6.0.2.md`
|
||||
- `docs/releases/V6_CHANGELOG_v6.0.2.md`
|
||||
- `docs/UPGRADE_v6.md`
|
||||
|
||||
+2
-2
@@ -4,8 +4,8 @@ This guide covers practical upgrade steps for existing Pulse installs moving to
|
||||
|
||||
For the current stable v6 packet and rollout references, see:
|
||||
|
||||
- `docs/releases/RELEASE_NOTES_v6.0.2.md`
|
||||
- `docs/releases/V6_CHANGELOG_v6.0.2.md`
|
||||
- `docs/releases/RELEASE_NOTES_v6.0.3.md`
|
||||
- `docs/releases/V6_CHANGELOG_v6.0.3.md`
|
||||
- `docs/releases/RELEASE_NOTES_v6.md`
|
||||
- `docs/releases/V6_CHANGELOG.md`
|
||||
|
||||
|
||||
@@ -793,10 +793,10 @@ TLS floor in the dynamic config.
|
||||
|
||||
## Current State
|
||||
|
||||
The active stable `v6.0.2` cut sets the repo-root `VERSION`, repo-root
|
||||
The active stable `v6.0.3` cut sets the repo-root `VERSION`, repo-root
|
||||
`docker-compose.yml` image default, `scripts/install-docker.sh` fallback, and
|
||||
Helm chart release metadata to the same `6.0.2` release version. This patch
|
||||
release uses the stable hotfix path with `rollback_version=v6.0.1`,
|
||||
Helm chart release metadata to the same `6.0.3` release version. This patch
|
||||
release uses the stable hotfix path with `rollback_version=v6.0.2`,
|
||||
`hotfix_exception=true`, a release-owner reason, and no fabricated
|
||||
same-version RC tag. The initial GA promotion metadata remains
|
||||
`promoted_from_tag=v6.0.0-rc.7`, `rollback_version=v5.1.35`,
|
||||
@@ -850,8 +850,8 @@ compose image default, standalone installer fallback constant, and packaged
|
||||
Helm metadata. A draft release workflow failure caused by stale image or chart
|
||||
pins is a release-packet blocker until the defaults, tests, and evidence
|
||||
record are refreshed from the new branch head.
|
||||
For the active stable `v6.0.2` cut, the repo-root compose default and
|
||||
`scripts/install-docker.sh` fallback must both pin `6.0.2`; the stable
|
||||
For the active stable `v6.0.3` cut, the repo-root compose default and
|
||||
`scripts/install-docker.sh` fallback must both pin `6.0.3`; the stable
|
||||
promotion guard remains in force by rejecting leftover `-rc.` defaults when
|
||||
the governed `VERSION` is a stable release.
|
||||
The RC7 packet refresh records `fc10de9b5477613316473267b72b05b6b2b7aaff`
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
# Pulse v6.0.3 Release Notes
|
||||
|
||||
`v6.0.3` is a stable patch release for the Pulse v6 line. It follows
|
||||
`v6.0.2` and focuses on the remaining v5-to-v6 agent upgrade failure reported
|
||||
after the first two v6 patch releases.
|
||||
|
||||
## Fixes
|
||||
|
||||
- Fixed v5 agent update recovery when the generated update command supplies an
|
||||
explicit Pulse URL but the old running v5 agent process is still the only
|
||||
source for the token and enabled telemetry scopes. The installer now merges
|
||||
the explicit URL with the recovered process state instead of failing with
|
||||
"No existing Pulse Agent connection state found."
|
||||
- Treated incomplete saved v6 agent state as recoverable during `--update`.
|
||||
This keeps the update path on the legacy recovery branch whenever any
|
||||
required connection detail is still missing.
|
||||
- Allowed carrier-grade NAT / overlay network addresses in `100.64.0.0/10` as
|
||||
local-network HTTP and WebSocket targets for Pulse agent connections. This
|
||||
fixes deployments that legitimately use addresses such as
|
||||
`http://100.100.100.5:7655` while keeping public non-local addresses on the
|
||||
HTTPS-required path.
|
||||
- Refreshed Docker, Helm, and installer release metadata for the stable patch
|
||||
line.
|
||||
|
||||
## Upgrade Notes
|
||||
|
||||
Use the normal v6 install or update flow for `v6.0.3`. The rollback target for
|
||||
this patch release is `v6.0.2`.
|
||||
|
||||
If a v5.1.35 agent upgrade failed on `v6.0.0`, `v6.0.1`, or `v6.0.2`, rerun the
|
||||
current update command from **Settings -> Infrastructure** after updating the
|
||||
Pulse server to `v6.0.3`.
|
||||
@@ -0,0 +1,21 @@
|
||||
# Pulse v6.0.3
|
||||
|
||||
_This changelog describes the stable `v6.0.3` patch release compared with
|
||||
`v6.0.2`._
|
||||
|
||||
## Fixed
|
||||
|
||||
- Legacy v5 agent updates now recover complete connection state when saved v6
|
||||
state is missing or incomplete.
|
||||
- Agent update commands now merge explicit Pulse URLs with token and scope
|
||||
details recovered from the running v5 agent process.
|
||||
- Agent URL validation now treats `100.64.0.0/10` carrier-grade NAT and overlay
|
||||
addresses as local network targets for HTTP and WebSocket URLs.
|
||||
- Docker, Helm, and installer release metadata now track the active stable
|
||||
patch version.
|
||||
|
||||
## Release Metadata
|
||||
|
||||
- Version: `v6.0.3`
|
||||
- Rollback target: `v6.0.2`
|
||||
- Promotion path: stable patch hotfix from `pulse/v6-release`
|
||||
@@ -88,7 +88,7 @@ describe('ConnectionsAPI', () => {
|
||||
lastError: null,
|
||||
source: 'agent',
|
||||
agentVersion: '6.0.0',
|
||||
expectedAgentVersion: '6.0.2',
|
||||
expectedAgentVersion: '6.0.3',
|
||||
agentUpdateAvailable: true,
|
||||
capabilities: { supportsPause: false, supportsScope: false, supportsTest: false },
|
||||
},
|
||||
@@ -99,7 +99,7 @@ describe('ConnectionsAPI', () => {
|
||||
|
||||
expect(result.connections[0]).toMatchObject({
|
||||
agentVersion: '6.0.0',
|
||||
expectedAgentVersion: '6.0.2',
|
||||
expectedAgentVersion: '6.0.3',
|
||||
agentUpdateAvailable: true,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -906,7 +906,7 @@ describe('InfrastructureWorkspace', () => {
|
||||
surfaces: ['host'],
|
||||
scope: { host: true } as any,
|
||||
source: 'agent',
|
||||
agentVersion: '6.0.2',
|
||||
agentVersion: '6.0.3',
|
||||
agentIdentity: {
|
||||
hostname: 'tower',
|
||||
platform: 'linux',
|
||||
@@ -970,7 +970,7 @@ describe('InfrastructureWorkspace', () => {
|
||||
|
||||
await waitFor(() => expect(screen.getByRole('dialog')).toBeInTheDocument());
|
||||
expect(screen.getByText('Pulse Agent version')).toBeInTheDocument();
|
||||
expect(screen.getByText('6.0.2')).toBeInTheDocument();
|
||||
expect(screen.getByText('6.0.3')).toBeInTheDocument();
|
||||
expect(screen.getByText('Operating system')).toBeInTheDocument();
|
||||
expect(screen.getAllByText('Unraid 7.1.0').length).toBeGreaterThan(0);
|
||||
expect(screen.getByText('Reported hostname')).toBeInTheDocument();
|
||||
@@ -996,7 +996,7 @@ describe('InfrastructureWorkspace', () => {
|
||||
scope: { host: true } as any,
|
||||
lastSeen: new Date().toISOString(),
|
||||
agentVersion: '6.0.0',
|
||||
expectedAgentVersion: '6.0.2',
|
||||
expectedAgentVersion: '6.0.3',
|
||||
agentUpdateAvailable: true,
|
||||
capabilities: { supportsPause: true, supportsScope: true, supportsTest: true },
|
||||
});
|
||||
@@ -1042,7 +1042,7 @@ describe('InfrastructureWorkspace', () => {
|
||||
expect(screen.getByText('Pulse Agent augmentation')).toBeInTheDocument();
|
||||
expect(screen.getByText('zeus-agent')).toBeInTheDocument();
|
||||
expect(screen.getByText('Update available')).toBeInTheDocument();
|
||||
expect(screen.getByText('6.0.0 -> 6.0.2')).toBeInTheDocument();
|
||||
expect(screen.getByText('6.0.0 -> 6.0.3')).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: /Copy uninstall command/i })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
|
||||
@@ -330,11 +330,11 @@ func TestBuildConnections_AgentVersionUpdateAvailability(t *testing.T) {
|
||||
now := time.Now()
|
||||
in := aggregatorInputs{
|
||||
hosts: []models.Host{
|
||||
{ID: "current", Hostname: "current", LastSeen: now, AgentVersion: "6.0.2"},
|
||||
{ID: "current", Hostname: "current", LastSeen: now, AgentVersion: "6.0.3"},
|
||||
{ID: "outdated", Hostname: "outdated", LastSeen: now, AgentVersion: "6.0.0"},
|
||||
{ID: "unknown", Hostname: "unknown", LastSeen: now},
|
||||
},
|
||||
expectedAgentVersion: "6.0.2",
|
||||
expectedAgentVersion: "6.0.3",
|
||||
now: now,
|
||||
}
|
||||
|
||||
@@ -344,14 +344,14 @@ func TestBuildConnections_AgentVersionUpdateAvailability(t *testing.T) {
|
||||
byID[connection.ID] = connection
|
||||
}
|
||||
|
||||
if byID["agent:current"].AgentVersion != "6.0.2" {
|
||||
t.Fatalf("current agent version = %q, want %q", byID["agent:current"].AgentVersion, "6.0.2")
|
||||
if byID["agent:current"].AgentVersion != "6.0.3" {
|
||||
t.Fatalf("current agent version = %q, want %q", byID["agent:current"].AgentVersion, "6.0.3")
|
||||
}
|
||||
if byID["agent:current"].ExpectedAgentVersion != "6.0.2" {
|
||||
if byID["agent:current"].ExpectedAgentVersion != "6.0.3" {
|
||||
t.Fatalf(
|
||||
"current expected agent version = %q, want %q",
|
||||
byID["agent:current"].ExpectedAgentVersion,
|
||||
"6.0.2",
|
||||
"6.0.3",
|
||||
)
|
||||
}
|
||||
if byID["agent:current"].AgentUpdateAvailable {
|
||||
@@ -387,7 +387,7 @@ func TestBuildConnections_AgentFleetGovernance(t *testing.T) {
|
||||
ID: "current",
|
||||
Hostname: "current",
|
||||
LastSeen: now,
|
||||
AgentVersion: "6.0.2",
|
||||
AgentVersion: "6.0.3",
|
||||
CommandsEnabled: true,
|
||||
},
|
||||
{
|
||||
@@ -404,7 +404,7 @@ func TestBuildConnections_AgentFleetGovernance(t *testing.T) {
|
||||
agentDesiredConfigs: map[string]connectionAgentDesiredConfig{
|
||||
"current": currentDesiredConfig,
|
||||
},
|
||||
expectedAgentVersion: "6.0.2",
|
||||
expectedAgentVersion: "6.0.3",
|
||||
now: now,
|
||||
}
|
||||
|
||||
|
||||
@@ -14549,7 +14549,7 @@ func TestContract_AgentConnectionPayloadIncludesVersionFields(t *testing.T) {
|
||||
CommandsEnabled: true,
|
||||
},
|
||||
AgentVersion: "6.0.0",
|
||||
ExpectedAgentVersion: "6.0.2",
|
||||
ExpectedAgentVersion: "6.0.3",
|
||||
AgentUpdateAvailable: true,
|
||||
Fleet: ConnectionFleetGovernance{
|
||||
EnrollmentState: fleetStateEnrolled,
|
||||
@@ -14583,7 +14583,7 @@ func TestContract_AgentConnectionPayloadIncludesVersionFields(t *testing.T) {
|
||||
t.Fatalf("agent platform must remain runtime platform, got profile id %q", conn.AgentIdentity.Platform)
|
||||
}
|
||||
|
||||
want := `{"id":"agent:host-1","type":"agent","name":"host-1","address":"host-1","hostAliases":["host-1","192.168.0.2"],"state":"active","enabled":true,"surfaces":["host"],"scope":{"host":true},"lastSeen":"2026-04-22T12:00:00Z","source":"agent","agentIdentity":{"hostname":"host-1","platform":"linux","hostProfile":"unraid","osName":"Unraid","osVersion":"7.1.0","kernelVersion":"6.12.0","architecture":"x86_64","reportIp":"192.168.0.2","commandsEnabled":true},"agentVersion":"6.0.0","expectedAgentVersion":"6.0.2","agentUpdateAvailable":true,"fleet":{"enrollmentState":"enrolled","livenessState":"active","versionDrift":"behind","adapterHealth":"healthy","configRollout":"reported","credentialStatus":"verified","updateStatus":"update-available","remoteControl":"enabled","commandPolicy":{"status":"enabled","desired":"enabled","applied":"enabled","enforcement":"in-sync","reason":"agent command execution matches the desired enabled policy"}},"capabilities":{"supportsPause":false,"supportsScope":false,"supportsTest":false}}`
|
||||
want := `{"id":"agent:host-1","type":"agent","name":"host-1","address":"host-1","hostAliases":["host-1","192.168.0.2"],"state":"active","enabled":true,"surfaces":["host"],"scope":{"host":true},"lastSeen":"2026-04-22T12:00:00Z","source":"agent","agentIdentity":{"hostname":"host-1","platform":"linux","hostProfile":"unraid","osName":"Unraid","osVersion":"7.1.0","kernelVersion":"6.12.0","architecture":"x86_64","reportIp":"192.168.0.2","commandsEnabled":true},"agentVersion":"6.0.0","expectedAgentVersion":"6.0.3","agentUpdateAvailable":true,"fleet":{"enrollmentState":"enrolled","livenessState":"active","versionDrift":"behind","adapterHealth":"healthy","configRollout":"reported","credentialStatus":"verified","updateStatus":"update-available","remoteControl":"enabled","commandPolicy":{"status":"enabled","desired":"enabled","applied":"enabled","enforcement":"in-sync","reason":"agent command execution matches the desired enabled policy"}},"capabilities":{"supportsPause":false,"supportsScope":false,"supportsTest":false}}`
|
||||
assertJSONSnapshot(t, body, want)
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
|
||||
DOCKER_IMAGE_REPO="${DOCKER_IMAGE_REPO:-rcourtman/pulse}"
|
||||
CANONICAL_DEFAULT_PULSE_VERSION="6.0.2"
|
||||
CANONICAL_DEFAULT_PULSE_VERSION="6.0.3"
|
||||
|
||||
resolve_default_pulse_version() {
|
||||
if [ -n "${PULSE_IMAGE_VERSION:-}" ]; then
|
||||
|
||||
@@ -240,6 +240,60 @@ func TestCreateReleaseUploadsPowerShellInstaller(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCurrentStablePatchReleasePacketTracksInstallMetadata(t *testing.T) {
|
||||
version := currentReleaseVersion(t)
|
||||
if isPrereleaseVersion(version) {
|
||||
t.Skip("current release is a prerelease")
|
||||
}
|
||||
previous, ok := previousStablePatchVersion(version)
|
||||
if !ok {
|
||||
t.Skip("current release is not a stable patch release")
|
||||
}
|
||||
|
||||
releaseNotesPath := repoFile("docs", "releases", "RELEASE_NOTES_v"+version+".md")
|
||||
changelogPath := repoFile("docs", "releases", "V6_CHANGELOG_v"+version+".md")
|
||||
|
||||
assertFileContainsAllNormalized(t, releaseNotesPath,
|
||||
"`v"+version+"` is a stable patch release",
|
||||
"`v"+previous+"`",
|
||||
"rollback target for this patch release is `v"+previous+"`",
|
||||
)
|
||||
assertFileContainsAll(t, changelogPath,
|
||||
"Version: `v"+version+"`",
|
||||
"Rollback target: `v"+previous+"`",
|
||||
"Promotion path: stable patch hotfix from `pulse/v6-release`",
|
||||
)
|
||||
assertFileContainsAll(t, repoFile("docs", "RELEASE_NOTES.md"),
|
||||
"docs/releases/RELEASE_NOTES_v"+version+".md",
|
||||
"docs/releases/V6_CHANGELOG_v"+version+".md",
|
||||
)
|
||||
assertFileContainsAll(t, repoFile("docs", "UPGRADE_v6.md"),
|
||||
"docs/releases/RELEASE_NOTES_v"+version+".md",
|
||||
"docs/releases/V6_CHANGELOG_v"+version+".md",
|
||||
)
|
||||
assertFileContainsAll(t, repoFile("deploy", "helm", "pulse", "Chart.yaml"),
|
||||
"version: "+version,
|
||||
`appVersion: "`+version+`"`,
|
||||
"raw.githubusercontent.com/rcourtman/Pulse/v"+version+"/docs/images/pulse-logo.svg",
|
||||
"blob/v"+version+"/docs/KUBERNETES.md",
|
||||
)
|
||||
assertFileContainsAll(t, repoFile("deploy", "helm", "pulse", "README.md"),
|
||||
"Version-"+version+"-informational",
|
||||
"AppVersion-"+version+"-informational",
|
||||
)
|
||||
assertFileContainsAll(t, repoFile("docker-compose.yml"),
|
||||
"image: ${PULSE_IMAGE:-rcourtman/pulse:"+version+"}",
|
||||
)
|
||||
assertFileContainsAll(t, repoFile("scripts", "install-docker.sh"),
|
||||
`CANONICAL_DEFAULT_PULSE_VERSION="`+version+`"`,
|
||||
)
|
||||
assertFileContainsAllNormalized(t, repoFile("docs", "release-control", "v6", "internal", "subsystems", "deployment-installability.md"),
|
||||
"The active stable `v"+version+"` cut sets the repo-root `VERSION`, repo-root `docker-compose.yml` image default, `scripts/install-docker.sh` fallback, and Helm chart release metadata to the same `"+version+"` release version.",
|
||||
"This patch release uses the stable hotfix path with `rollback_version=v"+previous+"`, `hotfix_exception=true`, a release-owner reason, and no fabricated same-version RC tag.",
|
||||
"For the active stable `v"+version+"` cut, the repo-root compose default and `scripts/install-docker.sh` fallback must both pin `"+version+"`",
|
||||
)
|
||||
}
|
||||
|
||||
func TestBackfillReleaseWorkflowRepairsPublishedAssetsWithoutRebuilds(t *testing.T) {
|
||||
scriptBytes, err := os.ReadFile(repoFile("scripts", "backfill-release-assets.sh"))
|
||||
if err != nil {
|
||||
@@ -1297,6 +1351,24 @@ func assertFileContainsAll(t *testing.T, path string, required ...string) {
|
||||
}
|
||||
}
|
||||
|
||||
func assertFileContainsAllNormalized(t *testing.T, path string, required ...string) {
|
||||
t.Helper()
|
||||
content, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatalf("read %s: %v", path, err)
|
||||
}
|
||||
s := normalizedInstallTestWhitespace(string(content))
|
||||
for _, needle := range required {
|
||||
if !strings.Contains(s, normalizedInstallTestWhitespace(needle)) {
|
||||
t.Fatalf("%s missing required normalized substring: %s", path, needle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func normalizedInstallTestWhitespace(text string) string {
|
||||
return strings.Join(strings.Fields(text), " ")
|
||||
}
|
||||
|
||||
func workflowJobBlock(t *testing.T, workflow, job string) string {
|
||||
t.Helper()
|
||||
|
||||
|
||||
@@ -155,6 +155,23 @@ func TestInstallDockerScriptFallbackPinsCurrentVersion(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestInstallDockerProofTracksStablePatchReleaseContract(t *testing.T) {
|
||||
version := currentReleaseVersion(t)
|
||||
if isPrereleaseVersion(version) {
|
||||
t.Skip("current release is a prerelease")
|
||||
}
|
||||
previous, ok := previousStablePatchVersion(version)
|
||||
if !ok {
|
||||
t.Skip("current release is not a stable patch release")
|
||||
}
|
||||
|
||||
assertFileContainsAllNormalized(t, repoFile("docs", "release-control", "v6", "internal", "subsystems", "deployment-installability.md"),
|
||||
"The active stable `v"+version+"` cut sets the repo-root `VERSION`, repo-root `docker-compose.yml` image default, `scripts/install-docker.sh` fallback, and Helm chart release metadata to the same `"+version+"` release version.",
|
||||
"This patch release uses the stable hotfix path with `rollback_version=v"+previous+"`, `hotfix_exception=true`, a release-owner reason, and no fabricated same-version RC tag.",
|
||||
"For the active stable `v"+version+"` cut, the repo-root compose default and `scripts/install-docker.sh` fallback must both pin `"+version+"`",
|
||||
)
|
||||
}
|
||||
|
||||
func runInstallDockerScript(t *testing.T, workDir string, envVars ...string) {
|
||||
t.Helper()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user