private boolean canUpdateConfigWithLastValidPartials() { List<PartialConfig> lastKnownPartials = cachedGoPartials.lastKnownPartials(); List<PartialConfig> lastValidPartials = cachedGoPartials.lastValidPartials(); return (!lastKnownPartials.isEmpty() && !areKnownPartialsSameAsValidPartials(lastKnownPartials, lastValidPartials)); }
@Deprecated public synchronized GoConfigHolder write(String configFileContent, boolean shouldMigrate) throws Exception { File configFile = fileLocation(); try { if (shouldMigrate) { configFileContent = upgrader.upgradeIfNecessary(configFileContent); } GoConfigHolder configHolder = internalLoad(configFileContent, new ConfigModifyingUser(), new ArrayList<>()); String toWrite = configAsXml(configHolder.configForEdit, false); LOGGER.trace("Writing config file: {}", configFile.getAbsolutePath()); writeToConfigXmlFile(toWrite); return configHolder; } catch (Exception e) { LOGGER.error("Unable to write config file: {}\n{}", configFile.getAbsolutePath(), e.getMessage(), e); throw e; } }
private GoConfigHolder trySavingConfig(UpdateConfigCommand updatingCommand, GoConfigHolder configHolder, List<PartialConfig> partials) throws Exception { String configAsXml; GoConfigHolder validatedConfigHolder; LOGGER.debug("[Config Save] ==-- Getting modified config"); if (shouldMergeConfig(updatingCommand, configHolder)) { if (!systemEnvironment.get(SystemEnvironment.ENABLE_CONFIG_MERGE_FEATURE)) { throw new ConfigMergeException(ConfigFileHasChangedException.CONFIG_CHANGED_PLEASE_REFRESH); } configAsXml = getMergedConfig((NoOverwriteUpdateConfigCommand) updatingCommand, configHolder.configForEdit.getMd5(), partials); try { validatedConfigHolder = internalLoad(configAsXml, getConfigUpdatingUser(updatingCommand), partials); } catch (Exception e) { LOGGER.info("[CONFIG_MERGE] Post merge validation failed, latest-md5: {}", configHolder.configForEdit.getMd5()); throw new ConfigMergePostValidationException(e.getMessage(), e); } } else { configAsXml = getUnmergedConfig(updatingCommand, configHolder, partials); validatedConfigHolder = internalLoad(configAsXml, getConfigUpdatingUser(updatingCommand), partials); } LOGGER.info("[Configuration Changed] Saving updated configuration."); writeToConfigXmlFile(configAsXml); return validatedConfigHolder; }
public synchronized GoConfigSaveResult writeFullConfigWithLock(FullConfigUpdateCommand updatingCommand, GoConfigHolder configHolder) { try { GoConfigHolder validatedConfigHolder; try { validatedConfigHolder = trySavingConfigWithLastKnownPartials(updatingCommand, configHolder); } catch (Exception e) { if (!canUpdateConfigWithLastValidPartials()) throw e; LOGGER.warn("Merged config update operation failed on LATEST {} partials. Falling back to using LAST VALID {} partials. Exception message was: {}", cachedGoPartials.lastKnownPartials().size(), cachedGoPartials.lastValidPartials().size(), e.getMessage(), e); validatedConfigHolder = trySavingConfigWithLastValidPartials(updatingCommand, configHolder); } ConfigSaveState configSaveState = shouldMergeConfig(updatingCommand, configHolder) ? ConfigSaveState.MERGED : ConfigSaveState.UPDATED; return new GoConfigSaveResult(validatedConfigHolder, configSaveState); } catch (ConfigFileHasChangedException e) { LOGGER.warn("Configuration file could not be merged successfully after a concurrent edit: {}", e.getMessage(), e); throw e; } catch (GoConfigInvalidException e) { LOGGER.warn("Configuration file is invalid: {}", e.getMessage(), e); throw bomb(e.getMessage(), e); } catch (Exception e) { LOGGER.error("Configuration file is not valid: {}", e.getMessage(), e); throw bomb(e.getMessage(), e); } finally { LOGGER.debug("[Config Save] Done writing with lock"); } }
List<PartialConfig> lastValidPartials = cachedGoPartials.lastValidPartials(); try { validatedConfigHolder = trySavingConfig(updatingCommand, configHolder, lastKnownPartials); updateMergedConfigForEdit(validatedConfigHolder, lastKnownPartials); } catch (Exception e) { if (lastKnownPartials.isEmpty() || areKnownPartialsSameAsValidPartials(lastKnownPartials, lastValidPartials)) { throw e; } else { LOGGER.warn("Merged config update operation failed on LATEST {} partials. Falling back to using LAST VALID {} partials. Exception message was: {}", lastKnownPartials.size(), lastValidPartials.size(), e.getMessage(), e); try { validatedConfigHolder = trySavingConfig(updatingCommand, configHolder, lastValidPartials); updateMergedConfigForEdit(validatedConfigHolder, lastValidPartials); LOGGER.info("Update operation on merged configuration succeeded with old {} LAST VALID partials.", lastValidPartials.size()); } catch (GoConfigInvalidException fallbackFailed) { ConfigSaveState configSaveState = shouldMergeConfig(updatingCommand, configHolder) ? ConfigSaveState.MERGED : ConfigSaveState.UPDATED; return new GoConfigSaveResult(validatedConfigHolder, configSaveState); } catch (ConfigFileHasChangedException e) {
if (lastKnownPartials.isEmpty() || areKnownPartialsSameAsValidPartials(lastKnownPartials, lastValidPartials)) { return trySavingEntity(updatingCommand, currentUser, modifiedConfig, lastValidPartials); return trySavingEntity(updatingCommand, currentUser, modifiedConfig, lastValidPartials); } catch (GoConfigInvalidException e) { StringBuilder errorMessageBuilder = new StringBuilder(); updatingCommand.clearErrors(); modifiedConfig.setPartials(lastKnownPartials); String configAsXml = configAsXml(modifiedConfig, false); GoConfigHolder holder = internalLoad(configAsXml, new ConfigModifyingUser(currentUser.getUsername().toString()), lastKnownPartials); LOGGER.info("Update operation on merged configuration succeeded with {} KNOWN partials. Now there are {} LAST KNOWN partials", lastKnownPartials.size(), cachedGoPartials.lastKnownPartials().size());
private String getMergedConfig(NoOverwriteUpdateConfigCommand noOverwriteCommand, String latestMd5, List<PartialConfig> partials) throws Exception { LOGGER.debug("[Config Save] Getting merged config"); String oldMd5 = noOverwriteCommand.unmodifiedMd5(); CruiseConfig modifiedConfig = getOldConfigAndMutateWithChanges(noOverwriteCommand, oldMd5); modifiedConfig.setPartials(partials); String modifiedConfigAsXml = convertMutatedConfigToXml(modifiedConfig, latestMd5); GoConfigRevision configRevision = new GoConfigRevision(modifiedConfigAsXml, "temporary-md5-for-branch", getConfigUpdatingUser(noOverwriteCommand).getUserName(), CurrentGoCDVersion.getInstance().formatted(), timeProvider); String mergedConfigXml = configRepository.getConfigMergedWithLatestRevision(configRevision, oldMd5); LOGGER.debug("[Config Save] -=- Done converting merged config to XML"); return mergedConfigXml; }
public GoConfigHolder load() throws Exception { File configFile = fileLocation(); ReloadStrategy.ReloadTestResult result = reloadStrategy.requiresReload(configFile); if (!result.requiresReload) { reloadStrategy.hasLatest(result); return null; } synchronized (this) { result = reloadStrategy.requiresReload(configFile); if (!result.requiresReload) { reloadStrategy.hasLatest(result); return null; } reloadStrategy.performingReload(result); LOGGER.info("Config file changed at {}", result.modifiedTime); LOGGER.info("Reloading config file: {}", configFile); if (systemEnvironment.optimizeFullConfigSave()) { LOGGER.debug("Starting config reload using the optimized flow."); return forceLoad(); } else { encryptPasswords(configFile); LOGGER.debug("Detected change in config file."); return forceLoad(configFile); } } }
private EntityConfigSaveResult trySavingEntity(EntityConfigUpdateCommand updatingCommand, Username currentUser, CruiseConfig modifiedConfig, List<PartialConfig> partials) { modifiedConfig.setPartials(partials); CruiseConfig preprocessedConfig = cloner.deepClone(modifiedConfig); MagicalGoConfigXmlLoader.preprocess(preprocessedConfig); updatingCommand.encrypt(preprocessedConfig); if (updatingCommand.isValid(preprocessedConfig)) { try { LOGGER.info("[Configuration Changed] Saving updated configuration."); String configAsXml = configAsXml(modifiedConfig, true); String md5 = CachedDigestUtils.md5Hex(configAsXml); MagicalGoConfigXmlLoader.setMd5(modifiedConfig, md5); MagicalGoConfigXmlLoader.setMd5(preprocessedConfig, md5); writeToConfigXmlFile(configAsXml); checkinConfigToGitRepo(partials, preprocessedConfig, configAsXml, md5, currentUser.getUsername().toString()); LOGGER.debug("[Config Save] Done writing with lock"); CruiseConfig mergedCruiseConfigForEdit = modifiedConfig; if (!partials.isEmpty()) { LOGGER.debug("[Config Save] Updating GoConfigHolder with mergedCruiseConfigForEdit: Starting."); mergedCruiseConfigForEdit = cloner.deepClone(modifiedConfig); mergedCruiseConfigForEdit.merge(partials, true); LOGGER.debug("[Config Save] Updating GoConfigHolder with mergedCruiseConfigForEdit: Done."); } return new EntityConfigSaveResult(updatingCommand.getPreprocessedEntityConfig(), new GoConfigHolder(preprocessedConfig, modifiedConfig, mergedCruiseConfigForEdit)); } catch (Exception e) { throw new RuntimeException("failed to save : " + e.getMessage()); } } else { throw new GoConfigInvalidException(preprocessedConfig, "Validation failed."); } }
synchronized GoConfigHolder forceLoad(File configFile) throws Exception { LOGGER.debug("Reloading config file: {}", configFile.getAbsolutePath()); GoConfigHolder holder; try { try { List<PartialConfig> lastKnownPartials = cloner.deepClone(cachedGoPartials.lastKnownPartials()); holder = internalLoad(FileUtils.readFileToString(configFile, UTF_8), new ConfigModifyingUser(FILESYSTEM), lastKnownPartials); } catch (GoConfigInvalidException e) { if (!canUpdateConfigWithLastValidPartials()) { throw e; } else { List<PartialConfig> lastValidPartials = cloner.deepClone(cachedGoPartials.lastValidPartials()); holder = internalLoad(FileUtils.readFileToString(configFile, UTF_8), new ConfigModifyingUser(FILESYSTEM), lastValidPartials); } } return holder; } catch (Exception e) { LOGGER.error("Unable to load config file: {} {}", configFile.getAbsolutePath(), e.getMessage(), e); if (configFile.exists()) { LOGGER.warn("--- {} ---", configFile.getAbsolutePath()); LOGGER.warn(FileUtils.readFileToString(configFile, StandardCharsets.UTF_8)); LOGGER.warn("------"); } LOGGER.debug("", e); throw e; } }
private GoConfigHolder trySavingFullConfig(FullConfigUpdateCommand updatingCommand, GoConfigHolder configHolder, List<PartialConfig> partials) throws Exception { String userName = getConfigUpdatingUser(updatingCommand).getUserName(); GoConfigHolder goConfigHolder; LOGGER.debug("[Config Save] ==-- Getting modified config"); if (shouldMergeConfig(updatingCommand, configHolder)) { if (!systemEnvironment.get(SystemEnvironment.ENABLE_CONFIG_MERGE_FEATURE)) { throw new ConfigMergeException(ConfigFileHasChangedException.CONFIG_CHANGED_PLEASE_REFRESH); } goConfigHolder = this.fullConfigSaveMergeFlow.execute(updatingCommand, partials, userName); } else { goConfigHolder = this.fullConfigSaveNormalFlow.execute(updatingCommand, partials, userName); } reloadStrategy.latestState(goConfigHolder.config); return goConfigHolder; }
public String getFileLocation() { return fileLocation().getAbsolutePath(); }
private String convertMutatedConfigToXml(CruiseConfig modifiedConfig, String latestMd5) { try { return configAsXml(modifiedConfig, false); } catch (Exception e) { LOGGER.info("[CONFIG_MERGE] Pre merge validation failed, latest-md5: {}", latestMd5); throw new ConfigMergePreValidationException(e.getMessage(), e); } }
public String getFileLocation() { return dataSource.getFileLocation(); }
synchronized GoConfigHolder forceLoad() throws Exception { File configFile = goConfigFileReader.fileLocation(); CruiseConfig cruiseConfig = this.magicalGoConfigXmlLoader.deserializeConfig(goConfigFileReader.configXml()); LOGGER.debug("Reloading config file: {}", configFile.getAbsolutePath()); GoConfigHolder goConfigHolder; try { try { goConfigHolder = fullConfigSaveNormalFlow.execute(new FullConfigUpdateCommand(cruiseConfig, null), cachedGoPartials.lastKnownPartials(), FILESYSTEM); } catch (GoConfigInvalidException e) { if (!canUpdateConfigWithLastValidPartials()) throw e; goConfigHolder = fullConfigSaveNormalFlow.execute(new FullConfigUpdateCommand(cruiseConfig, null), cachedGoPartials.lastValidPartials(), FILESYSTEM); } reloadStrategy.latestState(goConfigHolder.config); return goConfigHolder; } catch (Exception e) { LOGGER.error("Unable to load config file: {} {}", configFile.getAbsolutePath(), e.getMessage(), e); if (configFile.exists()) { LOGGER.warn("--- {} ---", configFile.getAbsolutePath()); LOGGER.warn(FileUtils.readFileToString(configFile, StandardCharsets.UTF_8)); LOGGER.warn("------"); } LOGGER.debug("", e); throw e; } }
private GoConfigHolder internalLoad(final String content, final ConfigModifyingUser configModifyingUser, final List<PartialConfig> partials) throws Exception { GoConfigHolder configHolder = magicalGoConfigXmlLoader.loadConfigHolder(content, cruiseConfig -> cruiseConfig.setPartials(partials)); CruiseConfig config = configHolder.config; checkinConfigToGitRepo(partials, config, content, configHolder.configForEdit.getMd5(), configModifyingUser.getUserName()); return configHolder; }
public void upgradeIfNecessary() { GoConfigMigrationResult migrationResult = this.upgrader.upgradeIfNecessary(fileLocation(), CurrentGoCDVersion.getInstance().formatted()); if (migrationResult.isUpgradeFailure()) { String message = migrationResult.message(); serverHealthService.update(ServerHealthState.warning("Invalid Configuration", message, HealthStateType.general(HealthStateScope.forInvalidConfig()))); LOGGER.warn(message); } }
private String getUnmergedConfig(UpdateConfigCommand updatingCommand, GoConfigHolder configHolder, List<PartialConfig> partials) throws Exception { CruiseConfig deepCloneForEdit = cloner.deepClone(configHolder.configForEdit); deepCloneForEdit.setPartials(partials); CruiseConfig config = updatingCommand.update(deepCloneForEdit); String configAsXml = configAsXml(config, false); if (deepCloneForEdit.getPartials().size() < partials.size()) throw new RuntimeException("should never be called"); return configAsXml; }