@@ -27,15 +27,16 @@ const (
2727 serviceManagerSystemd = "systemd"
2828 serviceManagerLaunchd = "launchd"
2929
30- linuxInstallDir = "/opt/noderax-agent"
31- linuxBinaryPath = linuxInstallDir + "/noderax-agent"
32- linuxSymlinkPath = "/usr/local/bin/noderax-agent"
33- linuxConfigPath = "/etc/noderax-agent/config.json"
34- linuxStatePath = "/var/lib/noderax-agent/agent_identity.json"
35- linuxServiceUnit = "/etc/systemd/system/noderax-agent.service"
36- linuxServiceName = "noderax-agent.service"
37- linuxServiceUser = "noderax"
38- linuxServiceHome = "/var/lib/noderax-agent"
30+ linuxInstallDir = "/opt/noderax-agent"
31+ linuxBinaryPath = linuxInstallDir + "/noderax-agent"
32+ linuxSymlinkPath = "/usr/local/bin/noderax-agent"
33+ linuxPrivilegedUpdateHelperPath = "/usr/local/libexec/noderax-agent-self-update"
34+ linuxConfigPath = "/etc/noderax-agent/config.json"
35+ linuxStatePath = "/var/lib/noderax-agent/agent_identity.json"
36+ linuxServiceUnit = "/etc/systemd/system/noderax-agent.service"
37+ linuxServiceName = "noderax-agent.service"
38+ linuxServiceUser = "noderax"
39+ linuxServiceHome = "/var/lib/noderax-agent"
3940
4041 macOSInstallDir = "/usr/local/lib/noderax-agent"
4142 macOSBinaryPath = macOSInstallDir + "/noderax-agent"
@@ -47,22 +48,23 @@ const (
4748)
4849
4950type platformSpec struct {
50- Manager string
51- InstallDir string
52- BinaryPath string
53- SymlinkPath string
54- ConfigPath string
55- StatePath string
56- ServiceUnit string
57- ServiceName string
58- WorkingDir string
59- RequiresRoot bool
60- ServiceDomain string
61- LogStdoutPath string
62- LogStderrPath string
63- ServiceUser string
64- ServiceGroup string
65- ServiceHome string
51+ Manager string
52+ InstallDir string
53+ BinaryPath string
54+ SymlinkPath string
55+ PrivilegedUpdateHelperPath string
56+ ConfigPath string
57+ StatePath string
58+ ServiceUnit string
59+ ServiceName string
60+ WorkingDir string
61+ RequiresRoot bool
62+ ServiceDomain string
63+ LogStdoutPath string
64+ LogStderrPath string
65+ ServiceUser string
66+ ServiceGroup string
67+ ServiceHome string
6668}
6769
6870type installOptions struct {
@@ -205,6 +207,9 @@ func (c CLI) Install(ctx context.Context, args []string) error {
205207 return fmt .Errorf ("create symlink: %w" , err )
206208 }
207209 }
210+ if err := writePrivilegedUpdateHelper (spec ); err != nil {
211+ return fmt .Errorf ("write privileged update helper: %w" , err )
212+ }
208213
209214 switch spec .Manager {
210215 case serviceManagerSystemd :
@@ -339,6 +344,18 @@ func (c CLI) Uninstall(ctx context.Context) error {
339344 }
340345 recordRemovalResult (& removed , & missing , "symlink" , spec .SymlinkPath , symlinkRemoved )
341346
347+ helperRemoved , err := removeFileIfExists (spec .PrivilegedUpdateHelperPath )
348+ if err != nil {
349+ return err
350+ }
351+ recordRemovalResult (
352+ & removed ,
353+ & missing ,
354+ "privileged update helper" ,
355+ spec .PrivilegedUpdateHelperPath ,
356+ helperRemoved ,
357+ )
358+
342359 configRemoved , err := removeFileIfExists (configPath )
343360 if err != nil {
344361 return err
@@ -485,36 +502,38 @@ func currentPlatformSpec() (platformSpec, error) {
485502 switch runtime .GOOS {
486503 case "linux" :
487504 return platformSpec {
488- Manager : serviceManagerSystemd ,
489- InstallDir : linuxInstallDir ,
490- BinaryPath : linuxBinaryPath ,
491- SymlinkPath : linuxSymlinkPath ,
492- ConfigPath : linuxConfigPath ,
493- StatePath : linuxStatePath ,
494- ServiceUnit : linuxServiceUnit ,
495- ServiceName : linuxServiceName ,
496- WorkingDir : linuxInstallDir ,
497- RequiresRoot : true ,
498- ServiceDomain : "system" ,
499- ServiceUser : linuxServiceUser ,
500- ServiceGroup : linuxServiceUser ,
501- ServiceHome : linuxServiceHome ,
505+ Manager : serviceManagerSystemd ,
506+ InstallDir : linuxInstallDir ,
507+ BinaryPath : linuxBinaryPath ,
508+ SymlinkPath : linuxSymlinkPath ,
509+ PrivilegedUpdateHelperPath : linuxPrivilegedUpdateHelperPath ,
510+ ConfigPath : linuxConfigPath ,
511+ StatePath : linuxStatePath ,
512+ ServiceUnit : linuxServiceUnit ,
513+ ServiceName : linuxServiceName ,
514+ WorkingDir : linuxInstallDir ,
515+ RequiresRoot : true ,
516+ ServiceDomain : "system" ,
517+ ServiceUser : linuxServiceUser ,
518+ ServiceGroup : linuxServiceUser ,
519+ ServiceHome : linuxServiceHome ,
502520 }, nil
503521 case "darwin" :
504522 return platformSpec {
505- Manager : serviceManagerLaunchd ,
506- InstallDir : macOSInstallDir ,
507- BinaryPath : macOSBinaryPath ,
508- SymlinkPath : macOSSymlinkPath ,
509- ConfigPath : macOSConfigPath ,
510- StatePath : macOSStatePath ,
511- ServiceUnit : macOSServiceUnit ,
512- ServiceName : macOSServiceName ,
513- WorkingDir : macOSInstallDir ,
514- RequiresRoot : true ,
515- ServiceDomain : "system" ,
516- LogStdoutPath : "/var/log/noderax-agent.log" ,
517- LogStderrPath : "/var/log/noderax-agent.error.log" ,
523+ Manager : serviceManagerLaunchd ,
524+ InstallDir : macOSInstallDir ,
525+ BinaryPath : macOSBinaryPath ,
526+ SymlinkPath : macOSSymlinkPath ,
527+ PrivilegedUpdateHelperPath : "" ,
528+ ConfigPath : macOSConfigPath ,
529+ StatePath : macOSStatePath ,
530+ ServiceUnit : macOSServiceUnit ,
531+ ServiceName : macOSServiceName ,
532+ WorkingDir : macOSInstallDir ,
533+ RequiresRoot : true ,
534+ ServiceDomain : "system" ,
535+ LogStdoutPath : "/var/log/noderax-agent.log" ,
536+ LogStderrPath : "/var/log/noderax-agent.error.log" ,
518537 }, nil
519538 default :
520539 return platformSpec {}, fmt .Errorf ("unsupported platform %s" , runtime .GOOS )
@@ -1018,6 +1037,53 @@ func ensureSymlink(target, link string) error {
10181037 return nil
10191038}
10201039
1040+ func writePrivilegedUpdateHelper (spec platformSpec ) error {
1041+ path := strings .TrimSpace (spec .PrivilegedUpdateHelperPath )
1042+ if path == "" {
1043+ return nil
1044+ }
1045+
1046+ if err := os .MkdirAll (filepath .Dir (path ), 0o755 ); err != nil {
1047+ return fmt .Errorf ("create helper directory for %s: %w" , path , err )
1048+ }
1049+
1050+ content := renderPrivilegedUpdateHelper (spec )
1051+ if err := os .WriteFile (path , []byte (content ), 0o755 ); err != nil {
1052+ return fmt .Errorf ("write helper %s: %w" , path , err )
1053+ }
1054+
1055+ return nil
1056+ }
1057+
1058+ func renderPrivilegedUpdateHelper (spec platformSpec ) string {
1059+ return fmt .Sprintf (`#!/bin/sh
1060+ set -eu
1061+
1062+ usage() {
1063+ echo "usage: %s --target-version <version> --target-id <target-id> [--rollback]" >&2
1064+ exit 64
1065+ }
1066+
1067+ if [ "$#" -ne 4 ] && [ "$#" -ne 5 ]; then
1068+ usage
1069+ fi
1070+
1071+ if [ "$1" != "--target-version" ] || [ -z "${2:-}" ] || [ "$3" != "--target-id" ] || [ -z "${4:-}" ]; then
1072+ usage
1073+ fi
1074+
1075+ if [ "$#" -eq 5 ] && [ "$5" != "--rollback" ]; then
1076+ usage
1077+ fi
1078+
1079+ if [ "$#" -eq 5 ]; then
1080+ exec %q update --target-version "$2" --target-id "$4" --rollback
1081+ fi
1082+
1083+ exec %q update --target-version "$2" --target-id "$4"
1084+ ` , spec .PrivilegedUpdateHelperPath , spec .BinaryPath , spec .BinaryPath )
1085+ }
1086+
10211087func writeServiceUnit (path , content string ) error {
10221088 if err := os .MkdirAll (filepath .Dir (path ), 0o755 ); err != nil {
10231089 return fmt .Errorf ("create service directory for %s: %w" , path , err )
0 commit comments