From 74ceae1004a255a77f6fc36dc168b29b02fc2588 Mon Sep 17 00:00:00 2001
From: Richard Petersen <richard.petersen@open-xchange.com>
Date: Fri, 18 Nov 2022 12:21:15 +0100
Subject: [PATCH] Improve robustness for version updates

Do not accept updates when the ui-containers are not available
---
 src/version.js | 85 ++++++++++++++++++++++++++------------------------
 1 file changed, 44 insertions(+), 41 deletions(-)

diff --git a/src/version.js b/src/version.js
index de71643..615a095 100644
--- a/src/version.js
+++ b/src/version.js
@@ -36,14 +36,10 @@ export const fetchLatestVersion = async () => {
     } catch (err) {
       logger.warn(`[Version] UI container at ${baseUrl} does not have meta.json. Fall back to version hash based on manifest.`)
     }
-    try {
-      const response = await fetch(new URL('manifest.json', baseUrl))
-      if (!response.ok) throw new Error()
-      const manifest = await response.json()
-      return hash(manifest)
-    } catch (err) {
-      logger.error(`[Version] Cannot fetch manifest from ${baseUrl}. Version info will not be correct.`)
-    }
+    const response = await fetch(new URL('manifest.json', baseUrl))
+    if (!response.ok) throw new Error(`Cannot fetch manifest.json from ${baseUrl}`)
+    const manifest = await response.json()
+    return hash(manifest)
   }))
   return `${hash(infos)}${configMap.salt ? `-${configMap.salt}` : ''}`
 }
@@ -63,12 +59,13 @@ export async function getLatestVersion () {
   const version = await fetchLatestVersion()
   logger.info(`[Version] Fetched initial version: '${version}'`)
 
+  latestVersion = version
   if (redis.isEnabled()) {
     redis.pubClient.publish(getRedisKey({ name: 'updateLatestVersion' }), version)
     await redis.client.set(getRedisKey({ name: 'latestVersion' }), version)
   }
 
-  return (latestVersion = version)
+  return version
 }
 
 export function registerLatestVersionListener (client) {
@@ -78,6 +75,7 @@ export function registerLatestVersionListener (client) {
   client.subscribe(key, (errs, count) => logger.info(`[Redis] Subscribed to ${key}.`))
   client.on('message', async (channel, version) => {
     if (channel !== key) return
+    if (latestVersion === version) return logger.info(`[Version] Received 'updateLatestVersion' event but already contains that version '${version}'`)
     logger.info(`[Version] Received 'updateLatestVersion' event. Clearing cache. New version: '${version}'`)
     await configMap.load()
     versionUpdateGauge.setToCurrentTime({ version })
@@ -87,41 +85,46 @@ export function registerLatestVersionListener (client) {
 }
 
 export async function updateVersionProcessor () {
-  logger.info('[Version] Check for new version')
-  await configMap.load()
-  const [storedVersion, fetchedVersion] = await Promise.all([
-    getLatestVersion(),
-    fetchLatestVersion()
-  ])
+  try {
+    logger.info('[Version] Check for new version')
+    await configMap.load()
 
-  // don't wait for the data, can be done in background
-  getViteManifests({ version: fetchedVersion }).then(manifests => {
-    manifestFileEntriesGauge.set({ version: fetchedVersion }, Object.keys(manifests).length)
-  })
+    const [storedVersion, fetchedVersion] = await Promise.all([
+      getLatestVersion(),
+      fetchLatestVersion()
+    ])
 
-  if (storedVersion === fetchedVersion) {
-    logger.info(`[Version] No new version has been found. No update needed. Current version: ${storedVersion}`)
-    return fetchedVersion
-  }
-  logger.info(`[Version] Found new source version. Current version: '${storedVersion}', new version: '${fetchedVersion}'`)
-  if (redis.isEnabled()) {
-    const prevProcessedVersion = await redis.client.get(getRedisKey({ name: 'prevProcessedVersion' }))
-    // that means, that between the previous update processing and this one, there was no version change
-    if (prevProcessedVersion === fetchedVersion) {
-      logger.info('[Version] publish update to other nodes.')
-      redis.pubClient.publish(getRedisKey({ name: 'updateLatestVersion' }), fetchedVersion)
-      await redis.client.set(getRedisKey({ name: 'latestVersion' }), fetchedVersion)
-      latestVersion = fetchedVersion
+    // don't wait for the data, can be done in background
+    getViteManifests({ version: fetchedVersion }).then(manifests => {
+      manifestFileEntriesGauge.set({ version: fetchedVersion }, Object.keys(manifests).length)
+    })
+
+    if (storedVersion === fetchedVersion) {
+      logger.info(`[Version] No new version has been found. No update needed. Current version: ${storedVersion}`)
+      return fetchedVersion
+    }
+    logger.info(`[Version] Found new source version. Current version: '${storedVersion}', new version: '${fetchedVersion}'`)
+    if (redis.isEnabled()) {
+      const prevProcessedVersion = await redis.client.get(getRedisKey({ name: 'prevProcessedVersion' }))
+      // that means, that between the previous update processing and this one, there was no version change
+      if (prevProcessedVersion === fetchedVersion) {
+        logger.info('[Version] publish update to other nodes.')
+        redis.pubClient.publish(getRedisKey({ name: 'updateLatestVersion' }), fetchedVersion)
+        await redis.client.set(getRedisKey({ name: 'latestVersion' }), fetchedVersion)
+        latestVersion = fetchedVersion
+      } else {
+        logger.info(`[Version] do not execute update yet. Store version ${fetchedVersion} as previous version.`)
+        await redis.client.set(getRedisKey({ name: 'prevProcessedVersion' }), fetchedVersion)
+      }
     } else {
-      logger.info(`[Version] do not execute update yet. Store version ${fetchedVersion} as previous version.`)
-      await redis.client.set(getRedisKey({ name: 'prevProcessedVersion' }), fetchedVersion)
+      versionUpdateGauge.setToCurrentTime({ version: latestVersion })
+      // if redis is disabled, this will only be trigger by a setInterval and not from a redis event
+      logger.info('[Version] Clear local cache due to version update.')
+      cache.clear()
+      latestVersion = fetchedVersion
     }
-  } else {
-    versionUpdateGauge.setToCurrentTime({ version: latestVersion })
-    // if redis is disabled, this will only be trigger by a setInterval and not from a redis event
-    logger.info('[Version] Clear local cache due to version update.')
-    cache.clear()
-    latestVersion = fetchedVersion
+    return latestVersion
+  } catch (err) {
+    logger.error(`[Version] comparing version is not possible. Error: ${err.message}`)
   }
-  return latestVersion
 }
-- 
GitLab