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
50 changes: 50 additions & 0 deletions agent/dependency/dependency.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,29 @@ const (
MacosCollectorVersion = "11.2.3"
)

// getUpdaterVersion reads the desired updater version from version.json
// (the updater_version field). Falls back to UpdaterVersion if the
// file is missing, unreadable, or the field is empty.
//
// The source of truth for this value is version.json shipped by the server,
// which the updater promotes on each successful agent update.
func getUpdaterVersion() string {
var v struct {
UpdaterVersion string `json:"updater_version"`
}
versionPath := filepath.Join(fs.GetExecutablePath(), "version.json")
if !fs.Exists(versionPath) {
return UpdaterVersion
}
if err := fs.ReadJSON(versionPath, &v); err != nil {
return UpdaterVersion
}
if v.UpdaterVersion == "" {
return UpdaterVersion
}
return v.UpdaterVersion
}

// UpdaterFile returns the updater binary name with OS and architecture suffix.
// Format: utmstack_updater_service_<os>_<arch>[.exe]
// Examples:
Expand All @@ -43,6 +66,7 @@ type Dependency struct {
DownloadURL func(server string) string // URL template to download from
DownloadName string // Filename to save as (if different from BinaryPath basename)
Critical bool // If true, failure blocks agent startup
PreDownload func() (cleanup func(), err error) // Called before download, returns cleanup for rollback
PostDownload func() error // Run after download (e.g., unzip). Can be nil.
Configure func() error // Run on first install (can be nil)
Update func() error // Run on version change (can be nil, uses Configure)
Expand Down Expand Up @@ -188,13 +212,34 @@ func Reconcile(server string, skipCertValidation bool) error {
} else if inst.Version != dep.Version {
// VERSION CHANGED: Download (if needed) and update
utils.Logger.Info("Updating dependency: %s (%s -> %s)", dep.Name, inst.Version, dep.Version)

// Call PreDownload hook if defined
var cleanup func()
if dep.PreDownload != nil {
var err error
cleanup, err = dep.PreDownload()
if err != nil {
errMsg := fmt.Errorf("failed to run PreDownload for %s: %v", dep.Name, err)
utils.Logger.ErrorF("%v", errMsg)
if dep.Critical {
criticalErrors = append(criticalErrors, errMsg)
}
continue
}
}

if dep.DownloadURL != nil {
if err := downloadDependency(dep, server, skipCertValidation); err != nil {
errMsg := fmt.Errorf("failed to download dependency update %s: %v", dep.Name, err)
utils.Logger.ErrorF("%v", errMsg)
if dep.Critical {
criticalErrors = append(criticalErrors, errMsg)
}
// Rollback: call cleanup if PreDownload succeeded
if cleanup != nil {
utils.Logger.Info("Rolling back PreDownload changes for %s", dep.Name)
cleanup()
}
continue
}
}
Expand All @@ -210,6 +255,11 @@ func Reconcile(server string, skipCertValidation bool) error {
if dep.Critical {
criticalErrors = append(criticalErrors, errMsg)
}
// Rollback: call cleanup if PreDownload succeeded
if cleanup != nil {
utils.Logger.Info("Rolling back PreDownload changes for %s", dep.Name)
cleanup()
}
continue
}
}
Expand Down
30 changes: 24 additions & 6 deletions agent/dependency/deps_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/utmstack/UTMStack/agent/config"
"github.com/utmstack/UTMStack/shared/exec"
"github.com/utmstack/UTMStack/shared/fs"
"github.com/utmstack/UTMStack/shared/svc"
)

const macosCollectorBinary = "utmstack-collector-mac"
Expand All @@ -20,15 +21,16 @@ func GetDependencies() []Dependency {

return []Dependency{
{
Name: "updater",
Version: UpdaterVersion,
BinaryPath: filepath.Join(basePath, UpdaterFile("")),
Name: "updater",
Version: getUpdaterVersion(),
BinaryPath: filepath.Join(basePath, UpdaterFile("")),
DownloadURL: func(server string) string {
return fmt.Sprintf(config.DependUrl, server, config.DependenciesPort, UpdaterFile(""))
},
Critical: false,
Configure: configureUpdater,
Uninstall: uninstallUpdater,
Critical: false,
PreDownload: preDownloadUpdater,
Configure: configureUpdater,
Uninstall: uninstallUpdater,
},
{
Name: "macos-collector",
Expand Down Expand Up @@ -66,3 +68,19 @@ func uninstallUpdater() error {
}
return exec.Run(updaterPath, fs.GetExecutablePath(), "uninstall")
}

func preDownloadUpdater() (func(), error) {
// Stop the updater service before download
if err := svc.Stop(config.SERVICE_UPDATER_NAME); err != nil {
// Service might not be running or installed yet - that's OK
// Return cleanup function anyway (safe to start)
return func() {
_ = svc.Start(config.SERVICE_UPDATER_NAME)
}, nil
}

// Return cleanup function that restarts the service
return func() {
_ = svc.Start(config.SERVICE_UPDATER_NAME)
}, nil
}
30 changes: 24 additions & 6 deletions agent/dependency/deps_linux_amd64.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/utmstack/UTMStack/agent/config"
"github.com/utmstack/UTMStack/shared/exec"
"github.com/utmstack/UTMStack/shared/fs"
"github.com/utmstack/UTMStack/shared/svc"
)

// GetDependencies returns the list of dependencies for Linux amd64.
Expand All @@ -19,15 +20,16 @@ func GetDependencies() []Dependency {

return []Dependency{
{
Name: "updater",
Version: UpdaterVersion,
BinaryPath: filepath.Join(basePath, UpdaterFile("")),
Name: "updater",
Version: getUpdaterVersion(),
BinaryPath: filepath.Join(basePath, UpdaterFile("")),
DownloadURL: func(server string) string {
return fmt.Sprintf(config.DependUrl, server, config.DependenciesPort, UpdaterFile(""))
},
Critical: false,
Configure: configureUpdater,
Uninstall: uninstallUpdater,
Critical: false,
PreDownload: preDownloadUpdater,
Configure: configureUpdater,
Uninstall: uninstallUpdater,
},

// New beats dependency - only for uninstalling existing filebeat/winlogbeat
Expand Down Expand Up @@ -75,3 +77,19 @@ func uninstallUpdater() error {
func uninstallBeats() error {
return collector.UninstallAll()
}

func preDownloadUpdater() (func(), error) {
// Stop the updater service before download
if err := svc.Stop(config.SERVICE_UPDATER_NAME); err != nil {
// Service might not be running or installed yet - that's OK
// Return cleanup function anyway (safe to start)
return func() {
_ = svc.Start(config.SERVICE_UPDATER_NAME)
}, nil
}

// Return cleanup function that restarts the service
return func() {
_ = svc.Start(config.SERVICE_UPDATER_NAME)
}, nil
}
30 changes: 24 additions & 6 deletions agent/dependency/deps_linux_arm64.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/utmstack/UTMStack/agent/config"
"github.com/utmstack/UTMStack/shared/exec"
"github.com/utmstack/UTMStack/shared/fs"
"github.com/utmstack/UTMStack/shared/svc"
)

// GetDependencies returns the list of dependencies for Linux arm64.
Expand All @@ -19,15 +20,16 @@ func GetDependencies() []Dependency {

return []Dependency{
{
Name: "updater",
Version: UpdaterVersion,
BinaryPath: filepath.Join(basePath, UpdaterFile("")),
Name: "updater",
Version: getUpdaterVersion(),
BinaryPath: filepath.Join(basePath, UpdaterFile("")),
DownloadURL: func(server string) string {
return fmt.Sprintf(config.DependUrl, server, config.DependenciesPort, UpdaterFile(""))
},
Critical: false,
Configure: configureUpdater,
Uninstall: uninstallUpdater,
Critical: false,
PreDownload: preDownloadUpdater,
Configure: configureUpdater,
Uninstall: uninstallUpdater,
},

// Auditd dependency - auto-configures Linux audit daemon
Expand Down Expand Up @@ -61,3 +63,19 @@ func uninstallUpdater() error {
}
return exec.Run(updaterPath, fs.GetExecutablePath(), "uninstall")
}

func preDownloadUpdater() (func(), error) {
// Stop the updater service before download
if err := svc.Stop(config.SERVICE_UPDATER_NAME); err != nil {
// Service might not be running or installed yet - that's OK
// Return cleanup function anyway (safe to start)
return func() {
_ = svc.Start(config.SERVICE_UPDATER_NAME)
}, nil
}

// Return cleanup function that restarts the service
return func() {
_ = svc.Start(config.SERVICE_UPDATER_NAME)
}, nil
}
30 changes: 24 additions & 6 deletions agent/dependency/deps_windows_amd64.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/utmstack/UTMStack/agent/config"
"github.com/utmstack/UTMStack/shared/exec"
"github.com/utmstack/UTMStack/shared/fs"
"github.com/utmstack/UTMStack/shared/svc"
)

// GetDependencies returns the list of dependencies for Windows amd64.
Expand All @@ -19,15 +20,16 @@ func GetDependencies() []Dependency {

return []Dependency{
{
Name: "updater",
Version: UpdaterVersion,
BinaryPath: filepath.Join(basePath, UpdaterFile("")),
Name: "updater",
Version: getUpdaterVersion(),
BinaryPath: filepath.Join(basePath, UpdaterFile("")),
DownloadURL: func(server string) string {
return fmt.Sprintf(config.DependUrl, server, config.DependenciesPort, UpdaterFile(""))
},
Critical: false, // Agent can run without updater
Configure: configureUpdater,
Uninstall: uninstallUpdater,
Critical: false, // Agent can run without updater
PreDownload: preDownloadUpdater,
Configure: configureUpdater,
Uninstall: uninstallUpdater,
},

// New beats dependency - only for uninstalling existing filebeat/winlogbeat
Expand Down Expand Up @@ -57,3 +59,19 @@ func uninstallUpdater() error {
func uninstallBeats() error {
return collector.UninstallAll()
}

func preDownloadUpdater() (func(), error) {
// Stop the updater service before download
if err := svc.Stop(config.SERVICE_UPDATER_NAME); err != nil {
// Service might not be running or installed yet - that's OK
// Return cleanup function anyway (safe to start)
return func() {
_ = svc.Start(config.SERVICE_UPDATER_NAME)
}, nil
}

// Return cleanup function that restarts the service
return func() {
_ = svc.Start(config.SERVICE_UPDATER_NAME)
}, nil
}
30 changes: 24 additions & 6 deletions agent/dependency/deps_windows_arm64.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/utmstack/UTMStack/agent/config"
"github.com/utmstack/UTMStack/shared/exec"
"github.com/utmstack/UTMStack/shared/fs"
"github.com/utmstack/UTMStack/shared/svc"
)

// GetDependencies returns the list of dependencies for Windows arm64.
Expand All @@ -19,15 +20,16 @@ func GetDependencies() []Dependency {

return []Dependency{
{
Name: "updater",
Version: UpdaterVersion,
BinaryPath: filepath.Join(basePath, UpdaterFile("")),
Name: "updater",
Version: getUpdaterVersion(),
BinaryPath: filepath.Join(basePath, UpdaterFile("")),
DownloadURL: func(server string) string {
return fmt.Sprintf(config.DependUrl, server, config.DependenciesPort, UpdaterFile(""))
},
Critical: false,
Configure: configureUpdater,
Uninstall: uninstallUpdater,
Critical: false,
PreDownload: preDownloadUpdater,
Configure: configureUpdater,
Uninstall: uninstallUpdater,
},
}
}
Expand All @@ -44,3 +46,19 @@ func uninstallUpdater() error {
}
return exec.Run(updaterPath, fs.GetExecutablePath(), "uninstall")
}

func preDownloadUpdater() (func(), error) {
// Stop the updater service before download
if err := svc.Stop(config.SERVICE_UPDATER_NAME); err != nil {
// Service might not be running or installed yet - that's OK
// Return cleanup function anyway (safe to start)
return func() {
_ = svc.Start(config.SERVICE_UPDATER_NAME)
}, nil
}

// Return cleanup function that restarts the service
return func() {
_ = svc.Start(config.SERVICE_UPDATER_NAME)
}, nil
}
Loading
Loading