mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-08-31 02:14:29 +00:00
A Docker install of the Pro runtime had no sane update path: the binary
self-updater refuses to run in a container, the customer compose file pins
the previous image digest (so `docker compose pull` never updates), and the
in-app guidance showed community `docker pull rcourtman/pulse:<tag>` commands
that silently downgrade the container to the community build - the same
failure mode 93bccaff1 fixed for binary installs.
The update check on the Pro binary already fetches the license server
download broker manifest (GET /v1/downloads/pulse-pro), which carries the
digest-pinned image ref plus ready-to-run compose commands. Relay that block
on UpdateInfo.dockerUpdate for Docker deployments, behind the existing
stable/rc channel guard, failing closed unless both compose commands
reference the digest-pinned ref (never a mutable tag).
Settings -> Updates now renders those commands as copy blocks when an update
is available, and the idle Docker box, the update banner, and the docker
update plan suppress every community-image command whenever the compiled
runtime identity is pro.
Contracts: api-contracts (update transport dockerUpdate block),
deployment-installability (Pro Docker update path), frontend-primitives
(install guide Pro-runtime guidance), with transport and settings-shell
proofs extended accordingly.
146 lines
4 KiB
Go
146 lines
4 KiB
Go
package updates
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/pkg/edition"
|
|
)
|
|
|
|
// The adapters in this file are plan providers only: they describe how an
|
|
// update would be performed for a deployment type (GET /api/updates/plan).
|
|
// The actual apply for every deployment runs through the in-Go pipeline in
|
|
// manager.go ApplyUpdate.
|
|
|
|
// InstallShAdapter describes install.sh-based updates for systemd/LXC deployments
|
|
type InstallShAdapter struct{}
|
|
|
|
// NewInstallShAdapter creates a new install.sh adapter
|
|
func NewInstallShAdapter() *InstallShAdapter {
|
|
return &InstallShAdapter{}
|
|
}
|
|
|
|
// SupportsApply returns true for systemd and proxmoxve deployments
|
|
func (a *InstallShAdapter) SupportsApply() bool {
|
|
return true
|
|
}
|
|
|
|
// GetDeploymentType returns the deployment type
|
|
func (a *InstallShAdapter) GetDeploymentType() string {
|
|
return "systemd" // Can be "systemd" or "proxmoxve"
|
|
}
|
|
|
|
// PrepareUpdate returns update plan information
|
|
func (a *InstallShAdapter) PrepareUpdate(ctx context.Context, request UpdateRequest) (*UpdatePlan, error) {
|
|
plan := &UpdatePlan{
|
|
CanAutoUpdate: true,
|
|
RequiresRoot: true,
|
|
RollbackSupport: true,
|
|
EstimatedTime: "2-5 minutes",
|
|
Instructions: []string{
|
|
fmt.Sprintf("Download and install Pulse %s", request.Version),
|
|
"Create backup of current installation",
|
|
"Extract and apply update",
|
|
"Restart Pulse service",
|
|
},
|
|
Prerequisites: []string{
|
|
"Root access (sudo)",
|
|
"Internet connection",
|
|
"About 1.2GB free disk space for update staging",
|
|
},
|
|
}
|
|
|
|
return plan, nil
|
|
}
|
|
|
|
// DockerUpdater provides instructions for Docker deployments
|
|
type DockerUpdater struct{}
|
|
|
|
const defaultDockerImageRepo = "rcourtman/pulse"
|
|
|
|
// NewDockerUpdater creates an updater for Docker deployments.
|
|
func NewDockerUpdater() *DockerUpdater {
|
|
return &DockerUpdater{}
|
|
}
|
|
|
|
func (u *DockerUpdater) SupportsApply() bool {
|
|
return false
|
|
}
|
|
|
|
func (u *DockerUpdater) GetDeploymentType() string {
|
|
return "docker"
|
|
}
|
|
|
|
func (u *DockerUpdater) PrepareUpdate(ctx context.Context, request UpdateRequest) (*UpdatePlan, error) {
|
|
// The compiled Pro binary must never be pointed at the community image:
|
|
// pulling rcourtman/pulse silently replaces the container with the
|
|
// community build. Its digest-pinned commands come from the license
|
|
// server broker on the update-check response instead.
|
|
if edition.IsPro() {
|
|
return &UpdatePlan{
|
|
CanAutoUpdate: false,
|
|
Instructions: []string{
|
|
fmt.Sprintf("Update to Pulse Pro %s with the digest-pinned Docker commands shown in Settings → Updates (fetched from your license server).", request.Version),
|
|
},
|
|
RequiresRoot: false,
|
|
RollbackSupport: true,
|
|
EstimatedTime: "1-2 minutes",
|
|
}, nil
|
|
}
|
|
|
|
imageRepo := strings.TrimSpace(os.Getenv("PULSE_DOCKER_IMAGE_REPO"))
|
|
if imageRepo == "" {
|
|
imageRepo = defaultDockerImageRepo
|
|
}
|
|
imageRef := fmt.Sprintf("%s:%s", imageRepo, strings.TrimPrefix(request.Version, "v"))
|
|
return &UpdatePlan{
|
|
CanAutoUpdate: false,
|
|
Instructions: []string{
|
|
fmt.Sprintf("docker pull %s", imageRef),
|
|
"docker stop pulse",
|
|
fmt.Sprintf("docker run -d --name pulse %s", imageRef),
|
|
},
|
|
RequiresRoot: false,
|
|
RollbackSupport: true,
|
|
EstimatedTime: "1-2 minutes",
|
|
}, nil
|
|
}
|
|
|
|
// AURUpdater provides instructions for Arch Linux AUR deployments
|
|
type AURUpdater struct{}
|
|
|
|
// NewAURUpdater creates an updater for Arch Linux AUR deployments.
|
|
func NewAURUpdater() *AURUpdater {
|
|
return &AURUpdater{}
|
|
}
|
|
|
|
func (u *AURUpdater) SupportsApply() bool {
|
|
return false
|
|
}
|
|
|
|
func (u *AURUpdater) GetDeploymentType() string {
|
|
return "aur"
|
|
}
|
|
|
|
func (u *AURUpdater) PrepareUpdate(ctx context.Context, request UpdateRequest) (*UpdatePlan, error) {
|
|
return &UpdatePlan{
|
|
CanAutoUpdate: false,
|
|
Instructions: []string{
|
|
"yay -Syu pulse-monitoring",
|
|
"# or",
|
|
"paru -Syu pulse-monitoring",
|
|
},
|
|
RequiresRoot: false,
|
|
RollbackSupport: false,
|
|
EstimatedTime: "1-2 minutes",
|
|
}, nil
|
|
}
|
|
|
|
// Ensure adapters implement Updater interface
|
|
var (
|
|
_ Updater = (*InstallShAdapter)(nil)
|
|
_ Updater = (*DockerUpdater)(nil)
|
|
_ Updater = (*AURUpdater)(nil)
|
|
)
|