/* * Copyright 2011-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.glowroot.agent.embedded.repo; import java.io.File; import java.io.IOException; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Set; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import org.checkerframework.checker.nullness.qual.Nullable; import org.glowroot.agent.config.AllConfig; import org.glowroot.agent.config.ConfigService; import org.glowroot.agent.config.PluginCache; import org.glowroot.agent.config.PluginConfig; import org.glowroot.agent.config.PluginDescriptor; import org.glowroot.agent.embedded.config.AdminConfigService; import org.glowroot.common.config.AdvancedConfig; import org.glowroot.common.config.AlertConfig; import org.glowroot.common.config.GaugeConfig; import org.glowroot.common.config.ImmutableAlertConfig; import org.glowroot.common.config.InstrumentationConfig; import org.glowroot.common.config.JvmConfig; import org.glowroot.common.config.TransactionConfig; import org.glowroot.common.config.UiDefaultsConfig; import org.glowroot.common.util.OnlyUsedByTests; import org.glowroot.common.util.Versions; import org.glowroot.common2.config.AllCentralAdminConfig; import org.glowroot.common2.config.AllEmbeddedAdminConfig; import org.glowroot.common2.config.CentralAdminGeneralConfig; import org.glowroot.common2.config.CentralStorageConfig; import org.glowroot.common2.config.CentralWebConfig; import org.glowroot.common2.config.EmbeddedAdminGeneralConfig; import org.glowroot.common2.config.EmbeddedStorageConfig; import org.glowroot.common2.config.EmbeddedWebConfig; import org.glowroot.common2.config.HealthchecksIoConfig; import org.glowroot.common2.config.HttpProxyConfig; import org.glowroot.common2.config.ImmutableAllEmbeddedAdminConfig; import org.glowroot.common2.config.ImmutableRoleConfig; import org.glowroot.common2.config.ImmutableUserConfig; import org.glowroot.common2.config.LdapConfig; import org.glowroot.common2.config.PagerDutyConfig; import org.glowroot.common2.config.PagerDutyConfig.PagerDutyIntegrationKey; import org.glowroot.common2.config.RoleConfig; import org.glowroot.common2.config.SlackConfig; import org.glowroot.common2.config.SlackConfig.SlackWebhook; import org.glowroot.common2.config.SmtpConfig; import org.glowroot.common2.config.StorageConfig; import org.glowroot.common2.config.UserConfig; import org.glowroot.common2.config.WebConfig; import org.glowroot.common2.repo.ConfigRepository; import org.glowroot.common2.repo.ConfigValidation; import org.glowroot.common2.repo.util.LazySecretKey; import org.glowroot.wire.api.model.AgentConfigOuterClass.AgentConfig; import static com.google.common.base.Preconditions.checkState; public class ConfigRepositoryImpl implements ConfigRepository { private final ConfigService configService; private final AdminConfigService adminConfigService; private final PluginCache pluginCache; private final boolean configReadOnly; private final ImmutableList<RollupConfig> rollupConfigs; private final LazySecretKey lazySecretKey; private final Object writeLock = new Object(); public ConfigRepositoryImpl(List<File> confDirs, boolean configReadOnly, @Nullable Integer webPortOverride, ConfigService configService, PluginCache pluginCache) { this.configService = configService; this.adminConfigService = AdminConfigService.create(confDirs, configReadOnly, webPortOverride); this.pluginCache = pluginCache; this.configReadOnly = configReadOnly; rollupConfigs = ImmutableList.copyOf(RollupConfig.buildRollupConfigs()); lazySecretKey = new LazySecretKeyImpl(confDirs); } @Override public AgentConfig.GeneralConfig getGeneralConfig(String agentRollupId) { throw new UnsupportedOperationException(); } @Override public AgentConfig.TransactionConfig getTransactionConfig(String agentId) { return configService.getTransactionConfig().toProto(); } @Override public AgentConfig.JvmConfig getJvmConfig(String agentRollupId) { return configService.getJvmConfig().toProto(); } @Override public AgentConfig.UiDefaultsConfig getUiDefaultsConfig(String agentRollupId) { return configService.getUiDefaultsConfig().toProto(); } @Override public AgentConfig.AdvancedConfig getAdvancedConfig(String agentRollupId) { return configService.getAdvancedConfig().toProto(); } @Override public List<AgentConfig.GaugeConfig> getGaugeConfigs(String agentId) { List<AgentConfig.GaugeConfig> gaugeConfigs = Lists.newArrayList(); for (GaugeConfig gaugeConfig : configService.getGaugeConfigs()) { gaugeConfigs.add(gaugeConfig.toProto()); } return gaugeConfigs; } @Override public AgentConfig. /*@Nullable*/ GaugeConfig getGaugeConfig(String agentId, String version) { for (GaugeConfig gaugeConfig : configService.getGaugeConfigs()) { AgentConfig.GaugeConfig config = gaugeConfig.toProto(); if (Versions.getVersion(config).equals(version)) { return config; } } return null; } @Override public List<AgentConfig.SyntheticMonitorConfig> getSyntheticMonitorConfigs( String agentRollupId) { throw new UnsupportedOperationException(); } @Override public AgentConfig. /*@Nullable*/ SyntheticMonitorConfig getSyntheticMonitorConfig( String agentRollupId, String syntheticMonitorId) { throw new UnsupportedOperationException(); } @Override public List<AgentConfig.AlertConfig> getAlertConfigs(String agentRollupId) throws Exception { List<AgentConfig.AlertConfig> configs = Lists.newArrayList(); for (AlertConfig config : configService.getAlertConfigs()) { configs.add(config.toProto()); } return configs; } @Override public AgentConfig. /*@Nullable*/ AlertConfig getAlertConfig(String agentRollupId, String alertVersion) { for (AlertConfig alertConfig : configService.getAlertConfigs()) { AgentConfig.AlertConfig config = alertConfig.toProto(); if (Versions.getVersion(config).equals(alertVersion)) { return config; } } return null; } @Override public List<AgentConfig.PluginConfig> getPluginConfigs(String agentId) { List<AgentConfig.PluginConfig> configs = Lists.newArrayList(); for (PluginConfig config : configService.getPluginConfigs()) { configs.add(config.toProto()); } return configs; } @Override public AgentConfig. /*@Nullable*/ PluginConfig getPluginConfig(String agentId, String pluginId) { PluginConfig pluginConfig = configService.getPluginConfig(pluginId); if (pluginConfig == null) { return null; } return pluginConfig.toProto(); } @Override public List<AgentConfig.InstrumentationConfig> getInstrumentationConfigs(String agentId) { List<AgentConfig.InstrumentationConfig> configs = Lists.newArrayList(); for (InstrumentationConfig config : configService.getInstrumentationConfigs()) { configs.add(config.toProto()); } return configs; } @Override public AgentConfig. /*@Nullable*/ InstrumentationConfig getInstrumentationConfig(String agentId, String version) { for (InstrumentationConfig config : configService.getInstrumentationConfigs()) { AgentConfig.InstrumentationConfig protoConfig = config.toProto(); if (Versions.getVersion(protoConfig).equals(version)) { return protoConfig; } } return null; } @Override public AgentConfig getAllConfig(String agentId) { return configService.getAgentConfig(); } @Override public EmbeddedAdminGeneralConfig getEmbeddedAdminGeneralConfig() { return adminConfigService.getEmbeddedAdminGeneralConfig(); } @Override public CentralAdminGeneralConfig getCentralAdminGeneralConfig() { throw new UnsupportedOperationException(); } @Override public List<UserConfig> getUserConfigs() { return adminConfigService.getUserConfigs(); } @Override public @Nullable UserConfig getUserConfig(String username) { for (UserConfig config : getUserConfigs()) { if (config.username().equals(username)) { return config; } } return null; } @Override public @Nullable UserConfig getUserConfigCaseInsensitive(String username) { for (UserConfig config : getUserConfigs()) { if (config.username().equalsIgnoreCase(username)) { return config; } } return null; } @Override public boolean namedUsersExist() { for (UserConfig config : getUserConfigs()) { if (!config.username().equalsIgnoreCase("anonymous")) { return true; } } return false; } @Override public List<RoleConfig> getRoleConfigs() { return adminConfigService.getRoleConfigs(); } @Override public @Nullable RoleConfig getRoleConfig(String name) { for (RoleConfig config : getRoleConfigs()) { if (config.name().equals(name)) { return config; } } return null; } @Override public WebConfig getWebConfig() { return getEmbeddedWebConfig(); } @Override public EmbeddedWebConfig getEmbeddedWebConfig() { return adminConfigService.getEmbeddedWebConfig(); } @Override public CentralWebConfig getCentralWebConfig() { throw new UnsupportedOperationException(); } @Override public StorageConfig getStorageConfig() { return getEmbeddedStorageConfig(); } @Override public EmbeddedStorageConfig getEmbeddedStorageConfig() { return adminConfigService.getEmbeddedStorageConfig(); } @Override public CentralStorageConfig getCentralStorageConfig() { throw new UnsupportedOperationException(); } @Override public SmtpConfig getSmtpConfig() { return adminConfigService.getSmtpConfig(); } @Override public HttpProxyConfig getHttpProxyConfig() { return adminConfigService.getHttpProxyConfig(); } @Override public LdapConfig getLdapConfig() { return adminConfigService.getLdapConfig(); } @Override public PagerDutyConfig getPagerDutyConfig() { return adminConfigService.getPagerDutyConfig(); } @Override public SlackConfig getSlackConfig() { return adminConfigService.getSlackConfig(); } @Override public HealthchecksIoConfig getHealthchecksIoConfig() { return adminConfigService.getHealthchecksIoConfig(); } @Override public AllEmbeddedAdminConfig getAllEmbeddedAdminConfig() { return adminConfigService.getAllAdminConfig(); } @Override public AllCentralAdminConfig getAllCentralAdminConfig() { throw new UnsupportedOperationException(); } @Override public boolean isConfigReadOnly(String agentId) { return configReadOnly; } @Override public void updateGeneralConfig(String agentId, AgentConfig.GeneralConfig protoConfig, String priorVersion) throws Exception { throw new UnsupportedOperationException(); } @Override public void updateTransactionConfig(String agentId, AgentConfig.TransactionConfig protoConfig, String priorVersion) throws Exception { TransactionConfig config = TransactionConfig.create(protoConfig); synchronized (writeLock) { String currVersion = Versions.getVersion(configService.getTransactionConfig().toProto()); checkVersionsEqual(currVersion, priorVersion); configService.updateTransactionConfig(config); } } @Override public void insertGaugeConfig(String agentId, AgentConfig.GaugeConfig protoConfig) throws Exception { GaugeConfig config = GaugeConfig.create(protoConfig); synchronized (writeLock) { List<GaugeConfig> configs = Lists.newArrayList(configService.getGaugeConfigs()); // check for duplicate mbeanObjectName for (GaugeConfig loopConfig : configs) { if (loopConfig.mbeanObjectName().equals(protoConfig.getMbeanObjectName())) { throw new DuplicateMBeanObjectNameException(); } } // no need to check for exact match since redundant with dup mbean object name check configs.add(config); configService.updateGaugeConfigs(configs); } } @Override public void updateGaugeConfig(String agentId, AgentConfig.GaugeConfig protoConfig, String priorVersion) throws Exception { GaugeConfig config = GaugeConfig.create(protoConfig); synchronized (writeLock) { List<GaugeConfig> configs = Lists.newArrayList(configService.getGaugeConfigs()); boolean found = false; for (ListIterator<GaugeConfig> i = configs.listIterator(); i.hasNext();) { GaugeConfig loopConfig = i.next(); String loopVersion = Versions.getVersion(loopConfig.toProto()); if (loopVersion.equals(priorVersion)) { i.set(config); found = true; } else if (loopConfig.mbeanObjectName().equals(protoConfig.getMbeanObjectName())) { throw new DuplicateMBeanObjectNameException(); } // no need to check for exact match since redundant with dup mbean object name check } if (!found) { throw new OptimisticLockException(); } configService.updateGaugeConfigs(configs); } } @Override public void deleteGaugeConfig(String agentId, String version) throws Exception { synchronized (writeLock) { List<GaugeConfig> configs = Lists.newArrayList(configService.getGaugeConfigs()); boolean found = false; for (ListIterator<GaugeConfig> i = configs.listIterator(); i.hasNext();) { GaugeConfig loopConfig = i.next(); String loopVersion = Versions.getVersion(loopConfig.toProto()); if (loopVersion.equals(version)) { i.remove(); found = true; break; } } if (!found) { throw new OptimisticLockException(); } configService.updateGaugeConfigs(configs); } } @Override public void updateJvmConfig(String agentId, AgentConfig.JvmConfig protoConfig, String priorVersion) throws Exception { JvmConfig config = JvmConfig.create(protoConfig); synchronized (writeLock) { String currVersion = Versions.getVersion(configService.getJvmConfig().toProto()); checkVersionsEqual(currVersion, priorVersion); configService.updateJvmConfig(config); } } @Override public void insertSyntheticMonitorConfig(String agentRollupId, AgentConfig.SyntheticMonitorConfig config) { throw new UnsupportedOperationException(); } @Override public void updateSyntheticMonitorConfig(String agentRollupId, AgentConfig.SyntheticMonitorConfig config, String priorVersion) { throw new UnsupportedOperationException(); } @Override public void deleteSyntheticMonitorConfig(String agentRollupId, String syntheticMonitorId) { throw new UnsupportedOperationException(); } @Override public void insertAlertConfig(String agentRollupId, AgentConfig.AlertConfig protoConfig) throws Exception { String version = Versions.getVersion(protoConfig); ImmutableAlertConfig config = AlertConfig.create(protoConfig); synchronized (writeLock) { List<AlertConfig> configs = Lists.newArrayList(configService.getAlertConfigs()); // check for exact duplicate for (AlertConfig loopConfig : configs) { if (Versions.getVersion(loopConfig.toProto()).equals(version)) { throw new IllegalStateException("This exact alert already exists"); } } configs.add(config); configService.updateAlertConfigs(configs); } } @Override public void updateAlertConfig(String agentRollupId, AgentConfig.AlertConfig config, String priorVersion) throws Exception { synchronized (writeLock) { List<AlertConfig> configs = Lists.newArrayList(configService.getAlertConfigs()); boolean found = false; for (ListIterator<AlertConfig> i = configs.listIterator(); i.hasNext();) { AlertConfig loopConfig = i.next(); String loopVersion = Versions.getVersion(loopConfig.toProto()); if (loopVersion.equals(priorVersion)) { i.set(AlertConfig.create(config)); found = true; break; } } if (!found) { throw new OptimisticLockException(); } configService.updateAlertConfigs(configs); } } @Override public void deleteAlertConfig(String agentRollupId, String version) throws Exception { synchronized (writeLock) { List<AlertConfig> configs = Lists.newArrayList(configService.getAlertConfigs()); boolean found = false; for (ListIterator<AlertConfig> i = configs.listIterator(); i.hasNext();) { AlertConfig loopConfig = i.next(); String loopVersion = Versions.getVersion(loopConfig.toProto()); if (version.equals(loopVersion)) { i.remove(); found = true; break; } } if (!found) { throw new OptimisticLockException(); } configService.updateAlertConfigs(configs); } } @Override public void updateUiDefaultsConfig(String agentId, AgentConfig.UiDefaultsConfig protoConfig, String priorVersion) throws Exception { UiDefaultsConfig config = UiDefaultsConfig.create(protoConfig); synchronized (writeLock) { String currVersion = Versions.getVersion(configService.getUiDefaultsConfig().toProto()); checkVersionsEqual(currVersion, priorVersion); configService.updateUiDefaultsConfig(config); } } @Override public void updatePluginConfig(String agentId, AgentConfig.PluginConfig protoConfig, String priorVersion) throws Exception { PluginDescriptor pluginDescriptor = getPluginDescriptor(protoConfig.getId()); PluginConfig config = PluginConfig.create(pluginDescriptor, protoConfig.getPropertyList()); synchronized (writeLock) { List<PluginConfig> configs = Lists.newArrayList(configService.getPluginConfigs()); boolean found = false; for (ListIterator<PluginConfig> i = configs.listIterator(); i.hasNext();) { PluginConfig loopPluginConfig = i.next(); if (protoConfig.getId().equals(loopPluginConfig.id())) { String loopVersion = Versions.getVersion(loopPluginConfig.toProto()); checkVersionsEqual(loopVersion, priorVersion); i.set(config); found = true; break; } } checkState(found, "Plugin config not found: %s", protoConfig.getId()); configService.updatePluginConfigs(configs); } } @Override public void insertInstrumentationConfig(String agentId, AgentConfig.InstrumentationConfig protoConfig) throws Exception { InstrumentationConfig config = InstrumentationConfig.create(protoConfig); synchronized (writeLock) { List<InstrumentationConfig> configs = Lists.newArrayList(configService.getInstrumentationConfigs()); if (configs.contains(config)) { throw new IllegalStateException("This exact instrumentation already exists"); } configs.add(config); configService.updateInstrumentationConfigs(configs); } } @Override public void updateInstrumentationConfig(String agentId, AgentConfig.InstrumentationConfig protoConfig, String priorVersion) throws Exception { InstrumentationConfig config = InstrumentationConfig.create(protoConfig); synchronized (writeLock) { List<InstrumentationConfig> configs = Lists.newArrayList(configService.getInstrumentationConfigs()); boolean found = false; for (ListIterator<InstrumentationConfig> i = configs.listIterator(); i.hasNext();) { InstrumentationConfig loopConfig = i.next(); String loopVersion = Versions.getVersion(loopConfig.toProto()); if (loopVersion.equals(priorVersion)) { i.set(config); found = true; } else if (loopConfig.equals(config)) { throw new IllegalStateException("This exact instrumentation already exists"); } } if (!found) { throw new OptimisticLockException(); } configService.updateInstrumentationConfigs(configs); } } @Override public void deleteInstrumentationConfigs(String agentId, List<String> versions) throws Exception { synchronized (writeLock) { List<InstrumentationConfig> configs = Lists.newArrayList(configService.getInstrumentationConfigs()); List<String> remainingVersions = Lists.newArrayList(versions); for (ListIterator<InstrumentationConfig> i = configs.listIterator(); i.hasNext();) { InstrumentationConfig loopConfig = i.next(); String loopVersion = Versions.getVersion(loopConfig.toProto()); if (remainingVersions.contains(loopVersion)) { i.remove(); remainingVersions.remove(loopVersion); } } if (!remainingVersions.isEmpty()) { throw new OptimisticLockException(); } configService.updateInstrumentationConfigs(configs); } } // ignores any instrumentation configs that are duplicates of existing instrumentation configs @Override public void insertInstrumentationConfigs(String agentId, List<AgentConfig.InstrumentationConfig> protoConfigs) throws Exception { List<InstrumentationConfig> configs = Lists.newArrayList(); for (AgentConfig.InstrumentationConfig instrumentationConfig : protoConfigs) { InstrumentationConfig config = InstrumentationConfig.create(instrumentationConfig); configs.add(config); } synchronized (writeLock) { List<InstrumentationConfig> existingConfigs = Lists.newArrayList(configService.getInstrumentationConfigs()); for (InstrumentationConfig config : configs) { if (!existingConfigs.contains(config)) { existingConfigs.add(config); } } configService.updateInstrumentationConfigs(existingConfigs); } } @Override public void updateAdvancedConfig(String agentId, AgentConfig.AdvancedConfig protoConfig, String priorVersion) throws Exception { AdvancedConfig config = AdvancedConfig.create(protoConfig); synchronized (writeLock) { String currVersion = Versions.getVersion(configService.getAdvancedConfig().toProto()); checkVersionsEqual(currVersion, priorVersion); configService.updateAdvancedConfig(config); } } @Override public void updateAllConfig(String agentId, AgentConfig config, @Nullable String priorVersion) throws Exception { ConfigValidation.validatePartOne(config); Set<String> validPluginIds = Sets.newHashSet(); for (PluginDescriptor pluginDescriptor : pluginCache.pluginDescriptors()) { validPluginIds.add(pluginDescriptor.id()); } ConfigValidation.validatePartTwo(config, validPluginIds); synchronized (writeLock) { AgentConfig existingAgentConfig = configService.getAgentConfig(); if (priorVersion != null && !priorVersion.equals(Versions.getVersion(existingAgentConfig))) { throw new OptimisticLockException(); } configService .updateAllConfig(AllConfig.create(config, pluginCache.pluginDescriptors())); } } @Override public void updateEmbeddedAdminGeneralConfig(EmbeddedAdminGeneralConfig config, String priorVersion) throws Exception { synchronized (writeLock) { String currVersion = adminConfigService.getEmbeddedAdminGeneralConfig().version(); checkVersionsEqual(currVersion, priorVersion); adminConfigService.updateEmbeddedAdminGeneralConfig(config); } } @Override public void updateCentralAdminGeneralConfig(CentralAdminGeneralConfig config, String priorVersion) { throw new UnsupportedOperationException(); } @Override public void insertUserConfig(UserConfig config) throws Exception { synchronized (writeLock) { List<UserConfig> configs = Lists.newArrayList(adminConfigService.getUserConfigs()); // check for case-insensitive duplicate String username = config.username(); for (UserConfig loopConfig : configs) { if (loopConfig.username().equalsIgnoreCase(username)) { throw new DuplicateUsernameException(); } } configs.add(ImmutableUserConfig.copyOf(config)); adminConfigService.updateUserConfigs(configs); } } @Override public void updateUserConfig(UserConfig config, String priorVersion) throws Exception { synchronized (writeLock) { List<UserConfig> configs = Lists.newArrayList(adminConfigService.getUserConfigs()); String username = config.username(); boolean found = false; for (ListIterator<UserConfig> i = configs.listIterator(); i.hasNext();) { UserConfig loopConfig = i.next(); if (loopConfig.username().equals(username)) { if (loopConfig.version().equals(priorVersion)) { i.set(ImmutableUserConfig.copyOf(config)); found = true; break; } else { throw new OptimisticLockException(); } } } if (!found) { throw new UserNotFoundException(); } adminConfigService.updateUserConfigs(configs); } } @Override public void deleteUserConfig(String username) throws Exception { synchronized (writeLock) { List<UserConfig> configs = Lists.newArrayList(adminConfigService.getUserConfigs()); boolean found = false; for (ListIterator<UserConfig> i = configs.listIterator(); i.hasNext();) { UserConfig loopConfig = i.next(); if (loopConfig.username().equals(username)) { i.remove(); found = true; break; } } if (!found) { throw new UserNotFoundException(); } if (getLdapConfig().host().isEmpty() && configs.isEmpty()) { throw new CannotDeleteLastUserException(); } adminConfigService.updateUserConfigs(configs); } } @Override public void insertRoleConfig(RoleConfig config) throws Exception { synchronized (writeLock) { List<RoleConfig> configs = Lists.newArrayList(adminConfigService.getRoleConfigs()); // check for case-insensitive duplicate String name = config.name(); for (RoleConfig loopConfig : configs) { if (loopConfig.name().equalsIgnoreCase(name)) { throw new DuplicateRoleNameException(); } } configs.add(ImmutableRoleConfig.copyOf(config)); adminConfigService.updateRoleConfigs(configs); } } @Override public void updateRoleConfig(RoleConfig config, String priorVersion) throws Exception { synchronized (writeLock) { List<RoleConfig> configs = Lists.newArrayList(adminConfigService.getRoleConfigs()); String name = config.name(); boolean found = false; for (ListIterator<RoleConfig> i = configs.listIterator(); i.hasNext();) { RoleConfig loopConfig = i.next(); if (loopConfig.name().equals(name)) { if (loopConfig.version().equals(priorVersion)) { i.set(ImmutableRoleConfig.copyOf(config)); found = true; break; } else { throw new OptimisticLockException(); } } } if (!found) { throw new RoleNotFoundException(); } adminConfigService.updateRoleConfigs(configs); } } @Override public void deleteRoleConfig(String name) throws Exception { synchronized (writeLock) { List<RoleConfig> configs = Lists.newArrayList(adminConfigService.getRoleConfigs()); boolean found = false; for (ListIterator<RoleConfig> i = configs.listIterator(); i.hasNext();) { RoleConfig loopConfig = i.next(); if (loopConfig.name().equals(name)) { i.remove(); found = true; break; } } if (!found) { throw new RoleNotFoundException(); } if (configs.isEmpty()) { throw new CannotDeleteLastRoleException(); } adminConfigService.updateRoleConfigs(configs); } } @Override public void updateEmbeddedWebConfig(EmbeddedWebConfig config, String priorVersion) throws Exception { synchronized (writeLock) { String currVersion = adminConfigService.getEmbeddedWebConfig().version(); checkVersionsEqual(currVersion, priorVersion); adminConfigService.updateEmbeddedWebConfig(config); } } @Override public void updateCentralWebConfig(CentralWebConfig config, String priorVersion) { throw new UnsupportedOperationException(); } @Override public void updateEmbeddedStorageConfig(EmbeddedStorageConfig config, String priorVersion) throws Exception { synchronized (writeLock) { String currVersion = adminConfigService.getEmbeddedStorageConfig().version(); checkVersionsEqual(currVersion, priorVersion); adminConfigService.updateEmbeddedStorageConfig(config); } } @Override public void updateCentralStorageConfig(CentralStorageConfig config, String priorVersion) { throw new UnsupportedOperationException(); } @Override public void updateSmtpConfig(SmtpConfig config, String priorVersion) throws Exception { synchronized (writeLock) { String currVersion = adminConfigService.getSmtpConfig().version(); checkVersionsEqual(currVersion, priorVersion); adminConfigService.updateSmtpConfig(config); } } @Override public void updateHttpProxyConfig(HttpProxyConfig config, String priorVersion) throws Exception { synchronized (writeLock) { String currVersion = adminConfigService.getHttpProxyConfig().version(); checkVersionsEqual(currVersion, priorVersion); adminConfigService.updateHttpProxyConfig(config); } } @Override public void updateLdapConfig(LdapConfig config, String priorVersion) throws Exception { synchronized (writeLock) { String currVersion = adminConfigService.getLdapConfig().version(); checkVersionsEqual(currVersion, priorVersion); adminConfigService.updateLdapConfig(config); } } @Override public void updatePagerDutyConfig(PagerDutyConfig config, String priorVersion) throws Exception { synchronized (writeLock) { String currVersion = adminConfigService.getPagerDutyConfig().version(); checkVersionsEqual(currVersion, priorVersion); validatePagerDutyConfig(config); adminConfigService.updatePagerDutyConfig(config); } } @Override public void updateSlackConfig(SlackConfig config, String priorVersion) throws Exception { synchronized (writeLock) { String currVersion = adminConfigService.getSlackConfig().version(); checkVersionsEqual(currVersion, priorVersion); validateSlackConfig(config); adminConfigService.updateSlackConfig(config); } } @Override public void updateHealthchecksIoConfig(HealthchecksIoConfig config, String priorVersion) throws Exception { synchronized (writeLock) { String currVersion = adminConfigService.getHealthchecksIoConfig().version(); checkVersionsEqual(currVersion, priorVersion); adminConfigService.updateHealthchecksIoConfig(config); } } @Override public void updateAllEmbeddedAdminConfig(AllEmbeddedAdminConfig config, @Nullable String priorVersion) throws Exception { synchronized (writeLock) { AllEmbeddedAdminConfig existingConfig = adminConfigService.getAllAdminConfig(); String currVersion = existingConfig.version(); if (priorVersion != null) { checkVersionsEqual(currVersion, priorVersion); } Set<String> usernames = Sets.newHashSet(); for (UserConfig userConfig : config.users()) { if (!usernames.add(userConfig.username())) { throw new DuplicateUsernameException(); } } Set<String> roleNames = Sets.newHashSet(); for (RoleConfig roleConfig : config.roles()) { if (!roleNames.add(roleConfig.name())) { throw new DuplicateRoleNameException(); } } if (config.ldap().host().isEmpty() && config.users().isEmpty()) { throw new CannotDeleteLastUserException(); } if (config.roles().isEmpty()) { throw new CannotDeleteLastRoleException(); } validatePagerDutyConfig(config.pagerDuty()); validateSlackConfig(config.slack()); Map<String, String> existingUserPasswordHashes = Maps.newHashMap(); for (UserConfig userConfig : existingConfig.users()) { String passwordHash = userConfig.passwordHash(); if (!passwordHash.isEmpty()) { existingUserPasswordHashes.put(userConfig.username(), passwordHash); } } List<UserConfig> userConfigs = Lists.newArrayList(); for (UserConfig userConfig : config.users()) { String passwordHash = userConfig.passwordHash(); if (passwordHash.isEmpty() && !userConfig.ldap() && !userConfig.username().equalsIgnoreCase("anonymous")) { String existingUserPasswordHash = existingUserPasswordHashes.get(userConfig.username()); if (existingUserPasswordHash == null) { throw new IllegalStateException( "No existing password for user: " + userConfig.username()); } userConfig = ImmutableUserConfig.copyOf(userConfig) .withPasswordHash(existingUserPasswordHash); } userConfigs.add(userConfig); } config = ImmutableAllEmbeddedAdminConfig.copyOf(config) .withUsers(userConfigs); adminConfigService.updateAllAdminConfig(config); } } @Override public void updateAllCentralAdminConfig(AllCentralAdminConfig config, @Nullable String priorVersion) { throw new UnsupportedOperationException(); } @Override public long getGaugeCollectionIntervalMillis() { return configService.getGaugeCollectionIntervalMillis(); } @Override public ImmutableList<RollupConfig> getRollupConfigs() { return rollupConfigs; } @Override public LazySecretKey getLazySecretKey() throws Exception { return lazySecretKey; } private PluginDescriptor getPluginDescriptor(String pluginId) { for (PluginDescriptor pluginDescriptor : pluginCache.pluginDescriptors()) { if (pluginDescriptor.id().equals(pluginId)) { return pluginDescriptor; } } throw new IllegalStateException("Could not find plugin descriptor: " + pluginId); } @OnlyUsedByTests public void resetAdminConfigForTests() throws IOException { adminConfigService.resetAdminConfigForTests(); } private static void checkVersionsEqual(String version, String priorVersion) throws OptimisticLockException { if (!version.equals(priorVersion)) { throw new OptimisticLockException(); } } private static void validatePagerDutyConfig(PagerDutyConfig config) throws Exception { Set<String> integrationKeys = Sets.newHashSet(); Set<String> integrationDisplays = Sets.newHashSet(); for (PagerDutyIntegrationKey integrationKey : config.integrationKeys()) { if (!integrationKeys.add(integrationKey.key())) { throw new DuplicatePagerDutyIntegrationKeyException(); } if (!integrationDisplays.add(integrationKey.display())) { throw new DuplicatePagerDutyIntegrationKeyDisplayException(); } } } private static void validateSlackConfig(SlackConfig config) throws Exception { Set<String> webhookUrls = Sets.newHashSet(); Set<String> webhookDisplays = Sets.newHashSet(); for (SlackWebhook webhook : config.webhooks()) { if (!webhookUrls.add(webhook.url())) { throw new DuplicateSlackWebhookUrlException(); } if (!webhookDisplays.add(webhook.display())) { throw new DuplicateSlackWebhookDisplayException(); } } } }