@@ -23,6 +23,11 @@ const globalMonitoringCache = {
2323} ;
2424
2525export class Monitoring {
26+ // A transient exec failure used to read as "not installed", which is the dangerous direction:
27+ // it offers to install over a working node. Retry before believing an inconclusive answer.
28+ static INSTALL_CHECK_ATTEMPTS = 3 ;
29+ static INSTALL_CHECK_RETRY_MS = 500 ;
30+
2631 constructor ( nodeConnection ) {
2732 this . nodeConnection = nodeConnection ;
2833 this . nodeConnectionProm = nodeConnection ;
@@ -87,10 +92,12 @@ export class Monitoring {
8792 // Start global monitoring cache refreshing periodically each X seconds async in background
8893 // Therefore no "await" is used for implemented calls (otherwise the login is blocked)!
8994 async startGlobalMonitoringCacheBackgroundWorker ( ) {
90- this . refreshGlobalMonitoringCache ( ) ;
95+ // Deliberately not awaited (it would block login), so it needs its own catch: during a
96+ // disconnect these reject with "Logging Out!" and surfaced as UnhandledPromiseRejection.
97+ this . refreshGlobalMonitoringCache ( ) . catch ( ( err ) => log . debug ( "global monitoring refresh skipped: " , err ?. message || err ) ) ;
9198 this . globalMonitoringCache . intervalHandler = setInterval (
9299 function ( me ) {
93- me . refreshGlobalMonitoringCache ( ) ;
100+ me . refreshGlobalMonitoringCache ( ) . catch ( ( err ) => log . debug ( "global monitoring refresh skipped: " , err ?. message || err ) ) ;
94101 } ,
95102 this . globalMonitoringCache . refreshIntervalSeconds * 1000 ,
96103 this
@@ -104,29 +111,42 @@ export class Monitoring {
104111 }
105112 }
106113
114+ /**
115+ * Whether /etc/stereum/stereum.yaml exists on the node.
116+ *
117+ * A wrong "false" here sends the user to the install screen for a node that is already set up,
118+ * so the check must not conflate "absent" with "could not tell". The probe echoes an explicit
119+ * marker instead of parsing `ls` output, which was both locale dependent (it matched the
120+ * English "No such file or directory") and unable to distinguish a failed command from a
121+ * missing file. Anything other than a marker is retried.
122+ */
107123 async checkStereumInstallation ( nodeConnection ) {
108124 if ( ! nodeConnection ) {
109125 nodeConnection = this . nodeConnection ;
110126 }
111- if ( nodeConnection . sshService . connected ) {
112- try {
113- const settings = await nodeConnection . sshService . exec ( "ls /etc/stereum" ) ;
114-
115- // Return true if stdout includes "stereum.yaml"
116- if ( settings ?. stdout && settings . stdout . includes ( "stereum.yaml" ) ) {
117- return true ;
118- }
127+ const ssh = nodeConnection . sshService ;
128+ const probe = "test -e /etc/stereum/stereum.yaml && echo STEREUM_PRESENT || echo STEREUM_ABSENT" ;
119129
120- // Return false if stderr includes "No such file or directory"
121- if ( settings ?. stderr && settings . stderr . includes ( "No such file or directory" ) ) {
122- return false ;
123- }
130+ for ( let attempt = 1 ; attempt <= Monitoring . INSTALL_CHECK_ATTEMPTS ; attempt ++ ) {
131+ if ( attempt > 1 ) await new Promise ( ( r ) => setTimeout ( r , Monitoring . INSTALL_CHECK_RETRY_MS ) ) ;
132+ if ( ! ssh . connected ) {
133+ log . warn ( `Stereum installation check: not connected (attempt ${ attempt } )` ) ;
134+ continue ;
135+ }
136+ let result ;
137+ try {
138+ result = await ssh . exec ( probe ) ;
124139 } catch ( err ) {
125- log . debug ( "checking stereum installation failed:" , err ) ;
140+ log . warn ( `Stereum installation check failed (attempt ${ attempt } ): ` , err ?. message || err ) ;
141+ continue ;
126142 }
143+ // A marker means the command actually ran, so its answer is trustworthy
144+ if ( result ?. stdout ?. includes ( "STEREUM_PRESENT" ) ) return true ;
145+ if ( result ?. stdout ?. includes ( "STEREUM_ABSENT" ) ) return false ;
146+ log . warn ( `Stereum installation check inconclusive (attempt ${ attempt } ): rc=${ result ?. rc } stderr=${ result ?. stderr } ` ) ;
127147 }
128148
129- // Default return false if none of the above conditions are met
149+ log . error ( "Could not determine whether Stereum is installed; treating the node as not installed" ) ;
130150 return false ;
131151 }
132152
0 commit comments