mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-27 05:57:27 +00:00
816b20bfc5
Replace the copyable guessed stop/run sequence with mounted-data backup and original-deployment recreation guidance. Quote the pull target as one literal shell argument, retaining manual-only and private-image boundaries. Add real-shell controls and a real handler-path check; no Docker daemon or installed recovery claim. Change-source: pulse-maintainer
160 lines
5.1 KiB
Go
160 lines
5.1 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{
|
|
"docker pull " + quoteShellArgument(imageRef),
|
|
"Before recreating Pulse, record the current image and take a consistent private backup of its mounted data. Keep the previous image and backup; do not delete volumes.",
|
|
"Set the image above in the original Compose file, saved docker run command or container manager. Recreate only Pulse with the same data mounts, ports, environment and restart policy, using your actual service or container name.",
|
|
"If the image pull failed, leave the running container unchanged. Do not start a second Pulse against the same data.",
|
|
"Check the running image, Pulse version and health after recreation. Rolling back an image alone does not undo data migrations.",
|
|
},
|
|
Prerequisites: []string{
|
|
"Access to the original saved deployment and the Docker daemon",
|
|
"A consistent private backup of the mounted Pulse data and the previous image reference",
|
|
},
|
|
RequiresRoot: false,
|
|
RollbackSupport: true,
|
|
EstimatedTime: "1-2 minutes",
|
|
}, nil
|
|
}
|
|
|
|
// The plan is copyable in the update banner. Keep even a caller-supplied version
|
|
// or configured repository as one literal argument, never shell syntax. The
|
|
// server cannot infer a safe replacement command from its own container.
|
|
func quoteShellArgument(value string) string {
|
|
return "'" + strings.ReplaceAll(value, "'", "'\\''") + "'"
|
|
}
|
|
|
|
// 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)
|
|
)
|