// ReSharper restore FunctionNeverReturns private void UpdateAutoUpdaterState( KioskAutoUpdaterState state, KioskVersionGetResponse versionResponse) { state.LastAppliedKioskVersion = versionResponse.AssignedKioskVersion; var stateString = JsonConvert.SerializeObject(state); File.WriteAllText(StateFilePath, stateString, Encoding.UTF8); Log.Info(LogContextEnum.KioskAutoUpdater, $"Last applied version was changed to '{versionResponse.AssignedKioskVersion}'."); }
private async Task RunUpdateCheckWorkerAsync() { while (true) { try { // get current state KioskAutoUpdaterState state = null; if (File.Exists(StateFilePath)) { try { state = JsonConvert.DeserializeObject <KioskAutoUpdaterState>(File.ReadAllText(StateFilePath, Encoding.UTF8)); } catch (Exception ex) { Log.Error(LogContextEnum.KioskAutoUpdater, "State read failed.", ex); } } // default state (first run or error) if (state == null) { state = new KioskAutoUpdaterState(); } // request kiosk version KioskVersionGetResponse versionResponse; try { versionResponse = await ServerApiProxy.Current.GetKioskVersionAsync(new EmptyRequest()); _isPreviousWebRequestFailed = false; } catch (Exception ex) { if (!_isPreviousWebRequestFailed) { Log.Error(LogContextEnum.KioskAutoUpdater, $"'{nameof(ServerApiProxy.GetKioskVersionAsync)}' failed.", ex); _isPreviousWebRequestFailed = true; } goto EndOfIteration; } // check if any version is assigned if (string.IsNullOrEmpty(versionResponse.AssignedKioskVersion)) { goto EndOfIteration; } if (string.IsNullOrEmpty(versionResponse.AssignedKioskVersionUpdateUrl)) { Log.Error(LogContextEnum.KioskAutoUpdater, $"'{nameof(ServerApiProxy.GetKioskVersionAsync)}' returned empty '{nameof(KioskVersionGetResponse.AssignedKioskVersionUpdateUrl)}' for version '{versionResponse.AssignedKioskVersion}'."); // save assigned version in case of error (to avoid re-trying of broken update) UpdateAutoUpdaterState(state, versionResponse); goto EndOfIteration; } // todo: fix - download and update on first run (if AssignedKioskVersion is not null) // check if a first run if (state.LastAppliedKioskVersion == null) { // just save assigned version UpdateAutoUpdaterState(state, versionResponse); goto EndOfIteration; } // check if an update is required if (state.LastAppliedKioskVersion == versionResponse.AssignedKioskVersion) { goto EndOfIteration; } Log.Trace(LogContextEnum.KioskAutoUpdater, $"New version '{versionResponse.AssignedKioskVersion}' is assigned."); // get update file/folder names string updateFilePath; string updateFolderPath; try { var updateFileName = Path.GetFileName(versionResponse.AssignedKioskVersionUpdateUrl); if (Path.GetExtension(updateFileName) != ".zip") { throw new ServerApiException($"Update file '{updateFileName}' is not a zip archive."); } updateFilePath = Path.Combine(UpdatesFolderPath, updateFileName); updateFolderPath = Path.Combine(UpdatesFolderPath, Path.GetFileNameWithoutExtension(updateFileName)); } catch (Exception ex) { Log.Error(LogContextEnum.KioskAutoUpdater, "Update file/folder processing failed.", ex); // save assigned version in case of error (to avoid re-trying of broken update) UpdateAutoUpdaterState(state, versionResponse); goto EndOfIteration; } // delete update file/folder (if exist) if (!RemoveUpdateFiles(updateFolderPath, updateFilePath)) { // save assigned version in case of error (to avoid re-trying of broken update) UpdateAutoUpdaterState(state, versionResponse); goto EndOfIteration; } // download update try { Log.Trace(LogContextEnum.KioskAutoUpdater, $"Download of '{versionResponse.AssignedKioskVersionUpdateUrl}' started."); var stopwatch = new Stopwatch(); stopwatch.Start(); // use simple legacy .net to download the file using (var webClient = new WebClient()) { await webClient.DownloadFileTaskAsync(versionResponse.AssignedKioskVersionUpdateUrl, updateFilePath); _isPreviousWebRequestFailed = false; } stopwatch.Stop(); Log.Trace(LogContextEnum.KioskAutoUpdater, $"Download of '{versionResponse.AssignedKioskVersionUpdateUrl}' completed ({stopwatch.Elapsed})."); } catch (Exception ex) { if (!_isPreviousWebRequestFailed) { Log.Error(LogContextEnum.KioskAutoUpdater, $"Download of '{versionResponse.AssignedKioskVersionUpdateUrl}' failed.", ex); _isPreviousWebRequestFailed = true; } goto EndOfIteration; } try { // unzip update try { ZipFile.ExtractToDirectory(updateFilePath, updateFolderPath); } catch (Exception ex) { Log.Error(LogContextEnum.KioskAutoUpdater, "Update unzip failed.", ex); // save assigned version in case of error (to avoid re-trying of broken update) UpdateAutoUpdaterState(state, versionResponse); goto EndOfIteration; } // update is ready - wait for 'ready for update' state while (true) { var lockFileInfo = new FileInfo(NotReadyForUpdateSignPath); if (!lockFileInfo.Exists) { break; } if (lockFileInfo.CreationTime < (DateTime.Now - _configuration.MaxNotReadyForUpdateLockDuration)) { Log.Warning(LogContextEnum.KioskAutoUpdater, $"Max lock file duration '{_configuration.MaxNotReadyForUpdateLockDuration}' is exceeded (lock file was created on '{lockFileInfo.CreationTime}')."); break; } await Task.Delay(_configuration.ReadyForUpdateCheckInterval); } // save assigned version before the update applying in case if Restart-Computer is presented in the update script UpdateAutoUpdaterState(state, versionResponse); // apply update try { var setLocationScript = $"Set-Location \"{updateFolderPath}\""; var installScript = await FileHelper.ReadFileAsync(Path.Combine(updateFolderPath, "ApplyUpdate.ps1")); var response = await PowerShellHelper.RunScriptsAsync( setLocationScript, installScript); if (response.HadErrors) { throw new PowerShellErrorException(response); } Log.Info(LogContextEnum.KioskAutoUpdater, $"Update '{versionResponse.AssignedKioskVersion}' successfully applied."); } catch (Exception ex) { Log.Error(LogContextEnum.KioskAutoUpdater, $"Update '{versionResponse.AssignedKioskVersion}' application failed.", ex); } } finally { // clean up update files RemoveUpdateFiles(updateFolderPath, updateFilePath); Log.Trace(LogContextEnum.KioskAutoUpdater, "Update files were cleaned up."); } } catch (Exception ex) { Log.Error(LogContextEnum.KioskAutoUpdater, $"'{nameof(RunUpdateCheckWorkerAsync)}' iteration unhandled exception.", ex); } EndOfIteration: await Task.Delay(_configuration.UpdateCheckInterval); } // ReSharper disable FunctionNeverReturns }