Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.glowroot.central.repo.ConfigRepositoryImpl Maven / Gradle / Ivy
/*
* Copyright 2015-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.central.repo;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import com.google.common.base.Joiner;
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 com.google.common.io.BaseEncoding;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.glowroot.central.repo.AgentConfigDao.AgentConfigUpdater;
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.ImmutableAllCentralAdminConfig;
import org.glowroot.common2.config.ImmutableCentralAdminGeneralConfig;
import org.glowroot.common2.config.ImmutableCentralStorageConfig;
import org.glowroot.common2.config.ImmutableCentralWebConfig;
import org.glowroot.common2.config.ImmutableHttpProxyConfig;
import org.glowroot.common2.config.ImmutableLdapConfig;
import org.glowroot.common2.config.ImmutablePagerDutyConfig;
import org.glowroot.common2.config.ImmutableRoleConfig;
import org.glowroot.common2.config.ImmutableSlackConfig;
import org.glowroot.common2.config.ImmutableSmtpConfig;
import org.glowroot.common2.config.ImmutableUserConfig;
import org.glowroot.common2.config.LdapConfig;
import org.glowroot.common2.config.MoreConfigDefaults;
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 org.glowroot.wire.api.model.AgentConfigOuterClass.AgentConfig.AdvancedConfig;
import org.glowroot.wire.api.model.AgentConfigOuterClass.AgentConfig.AlertConfig;
import org.glowroot.wire.api.model.AgentConfigOuterClass.AgentConfig.AlertConfig.AlertCondition;
import org.glowroot.wire.api.model.AgentConfigOuterClass.AgentConfig.GaugeConfig;
import org.glowroot.wire.api.model.AgentConfigOuterClass.AgentConfig.GeneralConfig;
import org.glowroot.wire.api.model.AgentConfigOuterClass.AgentConfig.InstrumentationConfig;
import org.glowroot.wire.api.model.AgentConfigOuterClass.AgentConfig.JvmConfig;
import org.glowroot.wire.api.model.AgentConfigOuterClass.AgentConfig.PluginConfig;
import org.glowroot.wire.api.model.AgentConfigOuterClass.AgentConfig.PluginProperty;
import org.glowroot.wire.api.model.AgentConfigOuterClass.AgentConfig.PluginProperty.Value.ValCase;
import org.glowroot.wire.api.model.AgentConfigOuterClass.AgentConfig.SyntheticMonitorConfig;
import org.glowroot.wire.api.model.AgentConfigOuterClass.AgentConfig.TransactionConfig;
import org.glowroot.wire.api.model.AgentConfigOuterClass.AgentConfig.UiDefaultsConfig;
public class ConfigRepositoryImpl implements ConfigRepository {
// TODO this needs to be in sync with agents, so have agents pick up value from central
private static final long GAUGE_COLLECTION_INTERVAL_MILLIS =
Long.getLong("glowroot.internal.gaugeCollectionIntervalMillis", 5000);
private final CentralConfigDao centralConfigDao;
private final AgentConfigDao agentConfigDao;
private final UserDao userDao;
private final RoleDao roleDao;
private final ImmutableList rollupConfigs;
private final LazySecretKey lazySecretKey;
private final Set agentConfigListeners = Sets.newCopyOnWriteArraySet();
ConfigRepositoryImpl(CentralConfigDao centralConfigDao, AgentConfigDao agentConfigDao,
UserDao userDao, RoleDao roleDao, String symmetricEncryptionKey) {
this.centralConfigDao = centralConfigDao;
this.agentConfigDao = agentConfigDao;
this.userDao = userDao;
this.roleDao = roleDao;
rollupConfigs = ImmutableList.copyOf(RollupConfig.buildRollupConfigs());
lazySecretKey = new LazySecretKeyImpl(symmetricEncryptionKey);
centralConfigDao.addKeyType(GENERAL_KEY, ImmutableCentralAdminGeneralConfig.class);
centralConfigDao.addKeyType(WEB_KEY, ImmutableCentralWebConfig.class);
centralConfigDao.addKeyType(STORAGE_KEY, ImmutableCentralStorageConfig.class);
centralConfigDao.addKeyType(SMTP_KEY, ImmutableSmtpConfig.class);
centralConfigDao.addKeyType(HTTP_PROXY_KEY, ImmutableHttpProxyConfig.class);
centralConfigDao.addKeyType(LDAP_KEY, ImmutableLdapConfig.class);
centralConfigDao.addKeyType(PAGER_DUTY_KEY, ImmutablePagerDutyConfig.class);
centralConfigDao.addKeyType(SLACK_KEY, ImmutableSlackConfig.class);
}
@Override
public GeneralConfig getGeneralConfig(String agentRollupId) throws Exception {
AgentConfig agentConfig = agentConfigDao.read(agentRollupId);
if (agentConfig == null) {
throw new AgentConfigNotFoundException(agentRollupId);
}
return agentConfig.getGeneralConfig();
}
@Override
public TransactionConfig getTransactionConfig(String agentId) throws Exception {
AgentConfig agentConfig = agentConfigDao.read(agentId);
if (agentConfig == null) {
throw new AgentConfigNotFoundException(agentId);
}
return agentConfig.getTransactionConfig();
}
@Override
public JvmConfig getJvmConfig(String agentId) throws Exception {
AgentConfig agentConfig = agentConfigDao.read(agentId);
if (agentConfig == null) {
throw new AgentConfigNotFoundException(agentId);
}
return agentConfig.getJvmConfig();
}
// central supports ui config on rollups
@Override
public UiDefaultsConfig getUiDefaultsConfig(String agentRollupId) throws Exception {
AgentConfig agentConfig = agentConfigDao.read(agentRollupId);
if (agentConfig == null) {
throw new AgentConfigNotFoundException(agentRollupId);
}
return agentConfig.getUiDefaultsConfig();
}
// central supports advanced config on rollups (maxQueryAggregates and maxServiceCallAggregates)
@Override
public AdvancedConfig getAdvancedConfig(String agentRollupId) throws Exception {
AgentConfig agentConfig = agentConfigDao.read(agentRollupId);
if (agentConfig == null) {
throw new AgentConfigNotFoundException(agentRollupId);
}
return agentConfig.getAdvancedConfig();
}
@Override
public List getGaugeConfigs(String agentId) throws Exception {
AgentConfig agentConfig = agentConfigDao.read(agentId);
if (agentConfig == null) {
throw new AgentConfigNotFoundException(agentId);
}
return agentConfig.getGaugeConfigList();
}
@Override
public GaugeConfig getGaugeConfig(String agentId, String configVersion) throws Exception {
for (GaugeConfig config : getGaugeConfigs(agentId)) {
if (Versions.getVersion(config).equals(configVersion)) {
return config;
}
}
throw new IllegalStateException("Gauge config not found: " + configVersion);
}
// central supports synthetic monitor configs on rollups
@Override
public List getSyntheticMonitorConfigs(String agentRollupId)
throws Exception {
AgentConfig agentConfig = agentConfigDao.read(agentRollupId);
if (agentConfig == null) {
throw new AgentConfigNotFoundException(agentRollupId);
}
return agentConfig.getSyntheticMonitorConfigList();
}
// central supports synthetic monitor configs on rollups
@Override
public @Nullable SyntheticMonitorConfig getSyntheticMonitorConfig(String agentRollupId,
String syntheticMonitorId) throws Exception {
for (SyntheticMonitorConfig config : getSyntheticMonitorConfigs(agentRollupId)) {
if (config.getId().equals(syntheticMonitorId)) {
return config;
}
}
return null;
}
// central supports alert configs on rollups
@Override
public List getAlertConfigs(String agentRollupId) throws Exception {
AgentConfig agentConfig = agentConfigDao.read(agentRollupId);
if (agentConfig == null) {
throw new AgentConfigNotFoundException(agentRollupId);
}
return agentConfig.getAlertConfigList();
}
// central supports alert configs on rollups
public List getAlertConfigsForSyntheticMonitorId(String agentRollupId,
String syntheticMonitorId) throws Exception {
List configs = new ArrayList<>();
for (AlertConfig config : getAlertConfigs(agentRollupId)) {
AlertCondition alertCondition = config.getCondition();
if (alertCondition.getValCase() == AlertCondition.ValCase.SYNTHETIC_MONITOR_CONDITION
&& alertCondition.getSyntheticMonitorCondition().getSyntheticMonitorId()
.equals(syntheticMonitorId)) {
configs.add(config);
}
}
return configs;
}
// central supports alert configs on rollups
@Override
public @Nullable AlertConfig getAlertConfig(String agentRollupId, String configVersion)
throws Exception {
for (AlertConfig config : getAlertConfigs(agentRollupId)) {
if (Versions.getVersion(config).equals(configVersion)) {
return config;
}
}
throw new IllegalStateException("Alert config not found: " + configVersion);
}
@Override
public List getPluginConfigs(String agentId) throws Exception {
AgentConfig agentConfig = agentConfigDao.read(agentId);
if (agentConfig == null) {
throw new AgentConfigNotFoundException(agentId);
}
return agentConfig.getPluginConfigList();
}
@Override
public PluginConfig getPluginConfig(String agentId, String pluginId) throws Exception {
for (PluginConfig config : getPluginConfigs(agentId)) {
if (config.getId().equals(pluginId)) {
return config;
}
}
throw new IllegalStateException("Plugin config not found: " + pluginId);
}
@Override
public List getInstrumentationConfigs(String agentId) throws Exception {
AgentConfig agentConfig = agentConfigDao.read(agentId);
if (agentConfig == null) {
throw new AgentConfigNotFoundException(agentId);
}
return agentConfig.getInstrumentationConfigList();
}
@Override
public InstrumentationConfig getInstrumentationConfig(String agentId, String configVersion)
throws Exception {
for (InstrumentationConfig config : getInstrumentationConfigs(agentId)) {
if (Versions.getVersion(config).equals(configVersion)) {
return config;
}
}
throw new IllegalStateException("Instrumentation config not found: " + configVersion);
}
@Override
public AgentConfig getAllConfig(String agentId) throws Exception {
AgentConfig agentConfig = agentConfigDao.read(agentId);
if (agentConfig == null) {
throw new AgentConfigNotFoundException(agentId);
} else {
return agentConfig;
}
}
@Override
public EmbeddedAdminGeneralConfig getEmbeddedAdminGeneralConfig() {
throw new UnsupportedOperationException();
}
@Override
public ImmutableCentralAdminGeneralConfig getCentralAdminGeneralConfig() throws Exception {
ImmutableCentralAdminGeneralConfig config =
(ImmutableCentralAdminGeneralConfig) centralConfigDao.read(GENERAL_KEY);
if (config == null) {
return ImmutableCentralAdminGeneralConfig.builder().build();
}
return config;
}
@Override
public List getUserConfigs() throws Exception {
return userDao.read();
}
@Override
public UserConfig getUserConfig(String username) throws Exception {
UserConfig config = userDao.read(username);
if (config == null) {
throw new UserNotFoundException();
}
return config;
}
@Override
public @Nullable UserConfig getUserConfigCaseInsensitive(String username) throws Exception {
return userDao.readCaseInsensitive(username);
}
@Override
public boolean namedUsersExist() throws Exception {
return userDao.namedUsersExist();
}
@Override
public List getRoleConfigs() throws Exception {
return roleDao.read();
}
@Override
public @Nullable RoleConfig getRoleConfig(String name) throws Exception {
return roleDao.read(name);
}
@Override
public WebConfig getWebConfig() throws Exception {
return getCentralWebConfig();
}
@Override
public EmbeddedWebConfig getEmbeddedWebConfig() {
throw new UnsupportedOperationException();
}
@Override
public CentralWebConfig getCentralWebConfig() throws Exception {
CentralWebConfig config = (CentralWebConfig) centralConfigDao.read(WEB_KEY);
if (config == null) {
return ImmutableCentralWebConfig.builder().build();
}
return config;
}
@Override
public StorageConfig getStorageConfig() throws Exception {
return getCentralStorageConfig();
}
@Override
public EmbeddedStorageConfig getEmbeddedStorageConfig() {
throw new UnsupportedOperationException();
}
@Override
public CentralStorageConfig getCentralStorageConfig() throws Exception {
CentralStorageConfig config = (CentralStorageConfig) centralConfigDao.read(STORAGE_KEY);
if (config == null) {
return ImmutableCentralStorageConfig.builder().build();
}
if (config.hasListIssues()) {
return withCorrectedLists(config);
}
return config;
}
@Override
public SmtpConfig getSmtpConfig() throws Exception {
SmtpConfig config = (SmtpConfig) centralConfigDao.read(SMTP_KEY);
if (config == null) {
return ImmutableSmtpConfig.builder().build();
}
return config;
}
@Override
public HttpProxyConfig getHttpProxyConfig() throws Exception {
HttpProxyConfig config = (HttpProxyConfig) centralConfigDao.read(HTTP_PROXY_KEY);
if (config == null) {
return ImmutableHttpProxyConfig.builder().build();
}
return config;
}
@Override
public LdapConfig getLdapConfig() throws Exception {
LdapConfig config = (LdapConfig) centralConfigDao.read(LDAP_KEY);
if (config == null) {
return ImmutableLdapConfig.builder().build();
}
return config;
}
@Override
public PagerDutyConfig getPagerDutyConfig() throws Exception {
PagerDutyConfig config = (PagerDutyConfig) centralConfigDao.read(PAGER_DUTY_KEY);
if (config == null) {
return ImmutablePagerDutyConfig.builder().build();
}
return config;
}
@Override
public SlackConfig getSlackConfig() throws Exception {
SlackConfig config = (SlackConfig) centralConfigDao.read(SLACK_KEY);
if (config == null) {
return ImmutableSlackConfig.builder().build();
}
return config;
}
@Override
public HealthchecksIoConfig getHealthchecksIoConfig() {
throw new UnsupportedOperationException();
}
@Override
public AllEmbeddedAdminConfig getAllEmbeddedAdminConfig() {
throw new UnsupportedOperationException();
}
@Override
public AllCentralAdminConfig getAllCentralAdminConfig() throws Exception {
ImmutableAllCentralAdminConfig.Builder builder = ImmutableAllCentralAdminConfig.builder()
.general(getCentralAdminGeneralConfig());
for (UserConfig userConfig : getUserConfigs()) {
builder.addUsers(ImmutableUserConfig.copyOf(userConfig));
}
for (RoleConfig roleConfig : getRoleConfigs()) {
builder.addRoles(ImmutableRoleConfig.copyOf(roleConfig));
}
return builder.web(ImmutableCentralWebConfig.copyOf(getCentralWebConfig()))
.storage(ImmutableCentralStorageConfig.copyOf(getCentralStorageConfig()))
.smtp(ImmutableSmtpConfig.copyOf(getSmtpConfig()))
.httpProxy(ImmutableHttpProxyConfig.copyOf(getHttpProxyConfig()))
.ldap(ImmutableLdapConfig.copyOf(getLdapConfig()))
.pagerDuty(ImmutablePagerDutyConfig.copyOf(getPagerDutyConfig()))
.slack(ImmutableSlackConfig.copyOf(getSlackConfig()))
.build();
}
@Override
public boolean isConfigReadOnly(String agentId) throws Exception {
AgentConfig agentConfig = agentConfigDao.read(agentId);
if (agentConfig == null) {
throw new AgentConfigNotFoundException(agentId);
}
return agentConfig.getConfigReadOnly();
}
@Override
public void updateGeneralConfig(String agentId, GeneralConfig config, String priorVersion)
throws Exception {
agentConfigDao.updateCentralOnly(agentId, new AgentConfigUpdater() {
@Override
public AgentConfig updateAgentConfig(AgentConfig agentConfig) throws Exception {
String existingVersion = Versions.getVersion(agentConfig.getGeneralConfig());
if (!priorVersion.equals(existingVersion)) {
throw new OptimisticLockException();
}
return agentConfig.toBuilder()
.setGeneralConfig(config)
.build();
}
});
// no need to call notifyAgentConfigListeners since updating "central only" data
}
@Override
public void updateTransactionConfig(String agentId, TransactionConfig config,
String priorVersion) throws Exception {
agentConfigDao.update(agentId, new AgentConfigUpdater() {
@Override
public AgentConfig updateAgentConfig(AgentConfig agentConfig) throws Exception {
String existingVersion = Versions.getVersion(agentConfig.getTransactionConfig());
if (!priorVersion.equals(existingVersion)) {
throw new OptimisticLockException();
}
return agentConfig.toBuilder()
.setTransactionConfig(config)
.build();
}
});
notifyAgentConfigListeners(agentId);
}
@Override
public void insertGaugeConfig(String agentId, GaugeConfig config) throws Exception {
agentConfigDao.update(agentId, new AgentConfigUpdater() {
@Override
public AgentConfig updateAgentConfig(AgentConfig agentConfig) throws Exception {
// check for duplicate mbeanObjectName
for (GaugeConfig loopConfig : agentConfig.getGaugeConfigList()) {
if (loopConfig.getMbeanObjectName().equals(config.getMbeanObjectName())) {
throw new DuplicateMBeanObjectNameException();
}
}
// no need to check for exact match since redundant with dup mbean object name check
return agentConfig.toBuilder()
.addGaugeConfig(config)
.build();
}
});
notifyAgentConfigListeners(agentId);
}
@Override
public void updateGaugeConfig(String agentId, GaugeConfig config, String priorVersion)
throws Exception {
agentConfigDao.update(agentId, new AgentConfigUpdater() {
@Override
public AgentConfig updateAgentConfig(AgentConfig agentConfig) throws Exception {
List existingConfigs =
Lists.newArrayList(agentConfig.getGaugeConfigList());
ListIterator i = existingConfigs.listIterator();
boolean found = false;
while (i.hasNext()) {
GaugeConfig loopConfig = i.next();
String loopVersion = Versions.getVersion(loopConfig);
if (loopVersion.equals(priorVersion)) {
i.set(config);
found = true;
} else if (loopConfig.getMbeanObjectName()
.equals(config.getMbeanObjectName())) {
throw new DuplicateMBeanObjectNameException();
}
// no need to check for exact match since redundant with dup mbean object name
// check
}
if (!found) {
throw new OptimisticLockException();
}
return agentConfig.toBuilder()
.clearGaugeConfig()
.addAllGaugeConfig(existingConfigs)
.build();
}
});
notifyAgentConfigListeners(agentId);
}
@Override
public void deleteGaugeConfig(String agentId, String version) throws Exception {
agentConfigDao.update(agentId, new AgentConfigUpdater() {
@Override
public AgentConfig updateAgentConfig(AgentConfig agentConfig) throws Exception {
List existingConfigs =
Lists.newArrayList(agentConfig.getGaugeConfigList());
ListIterator i = existingConfigs.listIterator();
boolean found = false;
while (i.hasNext()) {
if (Versions.getVersion(i.next()).equals(version)) {
i.remove();
found = true;
break;
}
}
if (!found) {
throw new OptimisticLockException();
}
return agentConfig.toBuilder()
.clearGaugeConfig()
.addAllGaugeConfig(existingConfigs)
.build();
}
});
notifyAgentConfigListeners(agentId);
}
@Override
public void updateJvmConfig(String agentId, JvmConfig config, String priorVersion)
throws Exception {
agentConfigDao.update(agentId, new AgentConfigUpdater() {
@Override
public AgentConfig updateAgentConfig(AgentConfig agentConfig) throws Exception {
String existingVersion = Versions.getVersion(agentConfig.getJvmConfig());
if (!priorVersion.equals(existingVersion)) {
throw new OptimisticLockException();
}
return agentConfig.toBuilder()
.setJvmConfig(config)
.build();
}
});
notifyAgentConfigListeners(agentId);
}
// central supports synthetic monitor configs on rollups
@Override
public void insertSyntheticMonitorConfig(String agentRollupId, SyntheticMonitorConfig config)
throws Exception {
agentConfigDao.update(agentRollupId, new AgentConfigUpdater() {
@Override
public AgentConfig updateAgentConfig(AgentConfig agentConfig) throws Exception {
// check for duplicate display
String display = MoreConfigDefaults.getDisplayOrDefault(config);
for (SyntheticMonitorConfig loopConfig : agentConfig
.getSyntheticMonitorConfigList()) {
if (MoreConfigDefaults.getDisplayOrDefault(loopConfig).equals(display)) {
throw new DuplicateSyntheticMonitorDisplayException();
}
}
// no need to check for exact match since redundant with duplicate name check
return agentConfig.toBuilder()
.addSyntheticMonitorConfig(config)
.build();
}
});
notifyAgentConfigListeners(agentRollupId);
}
// central supports synthetic monitor configs on rollups
@Override
public void updateSyntheticMonitorConfig(String agentRollupId, SyntheticMonitorConfig config,
String priorVersion) throws Exception {
agentConfigDao.update(agentRollupId, new AgentConfigUpdater() {
@Override
public AgentConfig updateAgentConfig(AgentConfig agentConfig) throws Exception {
List existingConfigs =
Lists.newArrayList(agentConfig.getSyntheticMonitorConfigList());
ListIterator i = existingConfigs.listIterator();
boolean found = false;
String display = config.getDisplay();
while (i.hasNext()) {
SyntheticMonitorConfig loopConfig = i.next();
if (loopConfig.getId().equals(config.getId())) {
if (!Versions.getVersion(loopConfig).equals(priorVersion)) {
throw new OptimisticLockException();
}
i.set(config);
found = true;
} else if (MoreConfigDefaults.getDisplayOrDefault(loopConfig).equals(display)) {
throw new DuplicateSyntheticMonitorDisplayException();
}
}
if (!found) {
throw new SyntheticNotFoundException();
}
return agentConfig.toBuilder()
.clearSyntheticMonitorConfig()
.addAllSyntheticMonitorConfig(existingConfigs)
.build();
}
});
notifyAgentConfigListeners(agentRollupId);
}
// central supports synthetic monitor configs on rollups
@Override
public void deleteSyntheticMonitorConfig(String agentRollupId, String syntheticMonitorId)
throws Exception {
agentConfigDao.update(agentRollupId, new AgentConfigUpdater() {
@Override
public AgentConfig updateAgentConfig(AgentConfig agentConfig) throws Exception {
if (!getAlertConfigsForSyntheticMonitorId(agentRollupId, syntheticMonitorId)
.isEmpty()) {
throw new IllegalStateException(
"Cannot delete synthetic monitor is being used by active alert");
}
List existingConfigs =
Lists.newArrayList(agentConfig.getSyntheticMonitorConfigList());
ListIterator i = existingConfigs.listIterator();
boolean found = false;
while (i.hasNext()) {
if (i.next().getId().equals(syntheticMonitorId)) {
i.remove();
found = true;
break;
}
}
if (!found) {
throw new OptimisticLockException();
}
return agentConfig.toBuilder()
.clearSyntheticMonitorConfig()
.addAllSyntheticMonitorConfig(existingConfigs)
.build();
}
});
notifyAgentConfigListeners(agentRollupId);
}
// central supports alert configs on rollups
@Override
public void insertAlertConfig(String agentRollupId, AlertConfig config) throws Exception {
agentConfigDao.update(agentRollupId, new AgentConfigUpdater() {
@Override
public AgentConfig updateAgentConfig(AgentConfig agentConfig) throws Exception {
for (AlertConfig loopConfig : agentConfig.getAlertConfigList()) {
if (loopConfig.getCondition().equals(config.getCondition())) {
throw new IllegalStateException(
"This exact alert condition already exists");
}
}
return agentConfig.toBuilder()
.addAlertConfig(config)
.build();
}
});
notifyAgentConfigListeners(agentRollupId);
}
// central supports alert configs on rollups
@Override
public void updateAlertConfig(String agentRollupId, AlertConfig config, String priorVersion)
throws Exception {
agentConfigDao.update(agentRollupId, new AgentConfigUpdater() {
@Override
public AgentConfig updateAgentConfig(AgentConfig agentConfig) throws Exception {
List existingConfigs =
Lists.newArrayList(agentConfig.getAlertConfigList());
ListIterator i = existingConfigs.listIterator();
boolean found = false;
while (i.hasNext()) {
AlertConfig loopConfig = i.next();
if (Versions.getVersion(loopConfig).equals(priorVersion)) {
i.set(config);
found = true;
} else if (loopConfig.getCondition().equals(config.getCondition())) {
throw new IllegalStateException(
"This exact alert condition already exists");
}
}
if (!found) {
throw new AlertNotFoundException();
}
return agentConfig.toBuilder()
.clearAlertConfig()
.addAllAlertConfig(existingConfigs)
.build();
}
});
notifyAgentConfigListeners(agentRollupId);
}
// central supports alert configs on rollups
@Override
public void deleteAlertConfig(String agentRollupId, String version) throws Exception {
agentConfigDao.update(agentRollupId, new AgentConfigUpdater() {
@Override
public AgentConfig updateAgentConfig(AgentConfig agentConfig) throws Exception {
List existingConfigs =
Lists.newArrayList(agentConfig.getAlertConfigList());
ListIterator i = existingConfigs.listIterator();
boolean found = false;
while (i.hasNext()) {
if (Versions.getVersion(i.next()).equals(version)) {
i.remove();
found = true;
break;
}
}
if (!found) {
throw new OptimisticLockException();
}
return agentConfig.toBuilder()
.clearAlertConfig()
.addAllAlertConfig(existingConfigs)
.build();
}
});
notifyAgentConfigListeners(agentRollupId);
}
// central supports ui config on rollups
@Override
public void updateUiDefaultsConfig(String agentRollupId, UiDefaultsConfig config,
String priorVersion) throws Exception {
agentConfigDao.update(agentRollupId, new AgentConfigUpdater() {
@Override
public AgentConfig updateAgentConfig(AgentConfig agentConfig) throws Exception {
String existingVersion = Versions.getVersion(agentConfig.getUiDefaultsConfig());
if (!priorVersion.equals(existingVersion)) {
throw new OptimisticLockException();
}
return agentConfig.toBuilder()
.setUiDefaultsConfig(config)
.build();
}
});
notifyAgentConfigListeners(agentRollupId);
}
@Override
public void updatePluginConfig(String agentId, PluginConfig config, String priorVersion)
throws Exception {
agentConfigDao.update(agentId, new AgentConfigUpdater() {
@Override
public AgentConfig updateAgentConfig(AgentConfig agentConfig) throws Exception {
List pluginConfigs =
buildPluginConfigs(config, priorVersion, agentConfig);
return agentConfig.toBuilder()
.clearPluginConfig()
.addAllPluginConfig(pluginConfigs)
.build();
}
});
notifyAgentConfigListeners(agentId);
}
@Override
public void insertInstrumentationConfig(String agentId, InstrumentationConfig config)
throws Exception {
agentConfigDao.update(agentId, new AgentConfigUpdater() {
@Override
public AgentConfig updateAgentConfig(AgentConfig agentConfig) throws Exception {
if (agentConfig.getInstrumentationConfigList().contains(config)) {
throw new IllegalStateException("This exact instrumentation already exists");
}
return agentConfig.toBuilder()
.addInstrumentationConfig(config)
.build();
}
});
notifyAgentConfigListeners(agentId);
}
@Override
public void updateInstrumentationConfig(String agentId, InstrumentationConfig config,
String priorVersion) throws Exception {
agentConfigDao.update(agentId, new AgentConfigUpdater() {
@Override
public AgentConfig updateAgentConfig(AgentConfig agentConfig) throws Exception {
String newVersion = Versions.getVersion(config);
List existingConfigs =
Lists.newArrayList(agentConfig.getInstrumentationConfigList());
ListIterator i = existingConfigs.listIterator();
boolean found = false;
while (i.hasNext()) {
String loopVersion = Versions.getVersion(i.next());
if (loopVersion.equals(priorVersion)) {
i.set(config);
found = true;
} else if (loopVersion.equals(newVersion)) {
throw new IllegalStateException(
"This exact instrumentation already exists");
}
}
if (!found) {
throw new OptimisticLockException();
}
return agentConfig.toBuilder()
.clearInstrumentationConfig()
.addAllInstrumentationConfig(existingConfigs)
.build();
}
});
notifyAgentConfigListeners(agentId);
}
@Override
public void deleteInstrumentationConfigs(String agentId, List versions)
throws Exception {
agentConfigDao.update(agentId, new AgentConfigUpdater() {
@Override
public AgentConfig updateAgentConfig(AgentConfig agentConfig) throws Exception {
List existingConfigs =
Lists.newArrayList(agentConfig.getInstrumentationConfigList());
ListIterator i = existingConfigs.listIterator();
List remainingVersions = Lists.newArrayList(versions);
while (i.hasNext()) {
String currVersion = Versions.getVersion(i.next());
if (remainingVersions.contains(currVersion)) {
i.remove();
remainingVersions.remove(currVersion);
}
}
if (!remainingVersions.isEmpty()) {
throw new OptimisticLockException();
}
return agentConfig.toBuilder()
.clearInstrumentationConfig()
.addAllInstrumentationConfig(existingConfigs)
.build();
}
});
notifyAgentConfigListeners(agentId);
}
// ignores any instrumentation configs that are duplicates of existing instrumentation configs
@Override
public void insertInstrumentationConfigs(String agentId, List configs)
throws Exception {
agentConfigDao.update(agentId, new AgentConfigUpdater() {
@Override
public AgentConfig updateAgentConfig(AgentConfig agentConfig) throws Exception {
AgentConfig.Builder builder = agentConfig.toBuilder();
List existingConfigs =
Lists.newArrayList(agentConfig.getInstrumentationConfigList());
for (InstrumentationConfig config : configs) {
if (!existingConfigs.contains(config)) {
existingConfigs.add(config);
}
}
return builder.clearInstrumentationConfig()
.addAllInstrumentationConfig(existingConfigs)
.build();
}
});
notifyAgentConfigListeners(agentId);
}
@Override
public void updateAdvancedConfig(String agentRollupId, AdvancedConfig config,
String priorVersion) throws Exception {
agentConfigDao.update(agentRollupId, new AgentConfigUpdater() {
@Override
public AgentConfig updateAgentConfig(AgentConfig agentConfig)
throws OptimisticLockException {
String existingVersion = Versions.getVersion(agentConfig.getAdvancedConfig());
if (!priorVersion.equals(existingVersion)) {
throw new OptimisticLockException();
}
return agentConfig.toBuilder()
.setAdvancedConfig(config)
.build();
}
});
notifyAgentConfigListeners(agentRollupId);
}
@Override
public void updateAllConfig(String agentId, AgentConfig config, @Nullable String priorVersion)
throws Exception {
ConfigValidation.validatePartOne(config);
agentConfigDao.update(agentId, new AgentConfigUpdater() {
@Override
public AgentConfig updateAgentConfig(AgentConfig agentConfig) throws Exception {
if (priorVersion != null) {
String existingVersion = Versions.getVersion(agentConfig);
if (!priorVersion.equals(existingVersion)) {
throw new OptimisticLockException();
}
}
Set validPluginIds = Sets.newHashSet();
for (PluginConfig pluginConfig : agentConfig.getPluginConfigList()) {
validPluginIds.add(pluginConfig.getId());
}
ConfigValidation.validatePartTwo(config, validPluginIds);
return config.toBuilder()
.clearPluginConfig()
.addAllPluginConfig(
buildPluginConfigs(config.getPluginConfigList(), agentConfig))
.build();
}
});
notifyAgentConfigListeners(agentId);
}
@Override
public void updateEmbeddedAdminGeneralConfig(EmbeddedAdminGeneralConfig config,
String priorVersion) {
throw new UnsupportedOperationException();
}
@Override
public void updateCentralAdminGeneralConfig(CentralAdminGeneralConfig config,
String priorVersion) throws Exception {
centralConfigDao.write(GENERAL_KEY, config, priorVersion);
}
@Override
public void insertUserConfig(UserConfig config) throws Exception {
// check for case-insensitive duplicate
String username = config.username();
for (UserConfig loopConfig : userDao.read()) {
if (loopConfig.username().equalsIgnoreCase(username)) {
throw new DuplicateUsernameException();
}
}
userDao.insertIfNotExists(config);
}
@Override
public void updateUserConfig(UserConfig config, String priorVersion) throws Exception {
UserConfig existingConfig = userDao.read(config.username());
if (existingConfig == null) {
throw new UserNotFoundException();
}
if (!existingConfig.version().equals(priorVersion)) {
throw new OptimisticLockException();
}
userDao.insert(config);
}
@Override
public void deleteUserConfig(String username) throws Exception {
boolean found = false;
List configs = userDao.read();
for (UserConfig config : configs) {
if (config.username().equalsIgnoreCase(username)) {
found = true;
break;
}
}
if (!found) {
throw new UserNotFoundException();
}
if (getSmtpConfig().host().isEmpty() && configs.size() == 1) {
throw new CannotDeleteLastUserException();
}
userDao.delete(username);
}
@Override
public void insertRoleConfig(RoleConfig config) throws Exception {
// check for case-insensitive duplicate
String name = config.name();
for (RoleConfig loopConfig : roleDao.read()) {
if (loopConfig.name().equalsIgnoreCase(name)) {
throw new DuplicateRoleNameException();
}
}
roleDao.insertIfNotExists(config);
}
@Override
public void updateRoleConfig(RoleConfig config, String priorVersion) throws Exception {
RoleConfig existingConfig = roleDao.read(config.name());
if (existingConfig == null) {
throw new RoleNotFoundException();
}
if (!existingConfig.version().equals(priorVersion)) {
throw new OptimisticLockException();
}
roleDao.insert(config);
}
@Override
public void deleteRoleConfig(String name) throws Exception {
boolean found = false;
List configs = roleDao.read();
for (RoleConfig config : configs) {
if (config.name().equalsIgnoreCase(name)) {
found = true;
break;
}
}
if (!found) {
throw new RoleNotFoundException();
}
if (configs.size() == 1) {
throw new CannotDeleteLastRoleException();
}
roleDao.delete(name);
}
@Override
public void updateEmbeddedWebConfig(EmbeddedWebConfig config, String priorVersion)
throws Exception {
throw new UnsupportedOperationException();
}
@Override
public void updateCentralWebConfig(CentralWebConfig config, String priorVersion)
throws Exception {
centralConfigDao.write(WEB_KEY, config, priorVersion);
}
@Override
public void updateEmbeddedStorageConfig(EmbeddedStorageConfig config, String priorVersion) {
throw new UnsupportedOperationException();
}
@Override
public void updateCentralStorageConfig(CentralStorageConfig config, String priorVersion)
throws Exception {
centralConfigDao.write(STORAGE_KEY, config, priorVersion);
}
@Override
public void updateSmtpConfig(SmtpConfig config, String priorVersion) throws Exception {
centralConfigDao.write(SMTP_KEY, config, priorVersion);
}
@Override
public void updateHttpProxyConfig(HttpProxyConfig config, String priorVersion)
throws Exception {
centralConfigDao.write(HTTP_PROXY_KEY, config, priorVersion);
}
@Override
public void updateLdapConfig(LdapConfig config, String priorVersion) throws Exception {
centralConfigDao.write(LDAP_KEY, config, priorVersion);
}
@Override
public void updatePagerDutyConfig(PagerDutyConfig config, String priorVersion)
throws Exception {
validatePagerDutyConfig(config);
centralConfigDao.write(PAGER_DUTY_KEY, config, priorVersion);
}
@Override
public void updateSlackConfig(SlackConfig config, String priorVersion) throws Exception {
validateSlackConfig(config);
centralConfigDao.write(SLACK_KEY, config, priorVersion);
}
@Override
public void updateHealthchecksIoConfig(HealthchecksIoConfig healthchecksIoConfig,
String priorVersion) throws Exception {
throw new UnsupportedOperationException();
}
@Override
public void updateAllEmbeddedAdminConfig(AllEmbeddedAdminConfig config,
@Nullable String priorVersion) {
throw new UnsupportedOperationException();
}
@Override
public void updateAllCentralAdminConfig(AllCentralAdminConfig config,
@Nullable String priorVersion) throws Exception {
validatePagerDutyConfig(config.pagerDuty());
validateSlackConfig(config.slack());
if (priorVersion == null) {
centralConfigDao.writeWithoutOptimisticLocking(GENERAL_KEY, config.general());
centralConfigDao.writeWithoutOptimisticLocking(WEB_KEY, config.web());
centralConfigDao.writeWithoutOptimisticLocking(STORAGE_KEY, config.storage());
centralConfigDao.writeWithoutOptimisticLocking(SMTP_KEY, config.smtp());
centralConfigDao.writeWithoutOptimisticLocking(HTTP_PROXY_KEY, config.httpProxy());
centralConfigDao.writeWithoutOptimisticLocking(LDAP_KEY, config.ldap());
centralConfigDao.writeWithoutOptimisticLocking(PAGER_DUTY_KEY, config.pagerDuty());
centralConfigDao.writeWithoutOptimisticLocking(SLACK_KEY, config.slack());
writeUsersWithoutOptimisticLocking(config.users());
writeRolesWithoutOptimisticLocking(config.roles());
} else {
AllCentralAdminConfig currConfig = getAllCentralAdminConfig();
if (!priorVersion.equals(currConfig.version())) {
throw new OptimisticLockException();
}
centralConfigDao.write(GENERAL_KEY, config.general(), currConfig.general().version());
centralConfigDao.write(WEB_KEY, config.web(), currConfig.web().version());
centralConfigDao.write(STORAGE_KEY, config.storage(), currConfig.storage().version());
centralConfigDao.write(SMTP_KEY, config.smtp(), currConfig.smtp().version());
centralConfigDao.write(HTTP_PROXY_KEY, config.httpProxy(),
currConfig.httpProxy().version());
centralConfigDao.write(LDAP_KEY, config.ldap(), currConfig.ldap().version());
centralConfigDao.write(PAGER_DUTY_KEY, config.pagerDuty(),
currConfig.pagerDuty().version());
centralConfigDao.write(SLACK_KEY, config.slack(), currConfig.slack().version());
// there is currently no optimistic locking when updating users
writeUsersWithoutOptimisticLocking(config.users());
writeRolesWithoutOptimisticLocking(config.roles());
}
}
@Override
public long getGaugeCollectionIntervalMillis() {
return GAUGE_COLLECTION_INTERVAL_MILLIS;
}
@Override
public ImmutableList getRollupConfigs() {
return rollupConfigs;
}
@Override
public LazySecretKey getLazySecretKey() throws Exception {
return lazySecretKey;
}
public void addAgentConfigListener(AgentConfigListener listener) {
agentConfigListeners.add(listener);
}
private void writeUsersWithoutOptimisticLocking(List userConfigs)
throws Exception {
Map remainingUserConfigs = new HashMap<>();
for (UserConfig userConfig : getUserConfigs()) {
remainingUserConfigs.put(userConfig.username(), userConfig);
}
for (UserConfig userConfig : userConfigs) {
UserConfig existingUserConfig =
remainingUserConfigs.remove(userConfig.username());
if (userConfig.passwordHash().isEmpty() && !userConfig.ldap()) {
if (existingUserConfig == null) {
throw new IllegalStateException(
"New user " + userConfig.username() + " is missing password");
}
userConfig = ImmutableUserConfig.copyOf(userConfig)
.withPasswordHash(existingUserConfig.passwordHash());
}
userDao.insert(userConfig);
}
for (String remainingUsername : remainingUserConfigs.keySet()) {
userDao.delete(remainingUsername);
}
}
private void writeRolesWithoutOptimisticLocking(List roleConfigs)
throws Exception {
Map remainingRoleConfigs = new HashMap<>();
for (RoleConfig roleConfig : getRoleConfigs()) {
remainingRoleConfigs.put(roleConfig.name(), roleConfig);
}
for (RoleConfig roleConfig : roleConfigs) {
remainingRoleConfigs.remove(roleConfig.name());
roleDao.insert(roleConfig);
}
for (String remainingRolename : remainingRoleConfigs.keySet()) {
roleDao.delete(remainingRolename);
}
}
// the updated config is not passed to the listeners to avoid the race condition of multiple
// config updates being sent out of order, instead listeners must call get*Config() which will
// never return the updates out of order (at worst it may return the most recent update twice
// which is ok)
private void notifyAgentConfigListeners(String agentRollupId) throws Exception {
for (AgentConfigListener agentConfigListener : agentConfigListeners) {
agentConfigListener.onChange(agentRollupId);
}
}
private static List buildPluginConfigs(PluginConfig updatedConfig,
String priorVersion, AgentConfig agentConfig) throws OptimisticLockException {
List pluginConfigs =
Lists.newArrayList(agentConfig.getPluginConfigList());
ListIterator i = pluginConfigs.listIterator();
boolean found = false;
while (i.hasNext()) {
PluginConfig pluginConfig = i.next();
if (pluginConfig.getId().equals(updatedConfig.getId())) {
String existingVersion = Versions.getVersion(pluginConfig);
if (!priorVersion.equals(existingVersion)) {
throw new OptimisticLockException();
}
i.set(buildPluginConfig(pluginConfig, updatedConfig.getPropertyList(), true));
found = true;
break;
}
}
if (found) {
return pluginConfigs;
} else {
throw new IllegalStateException("Plugin config not found: " + updatedConfig.getId());
}
}
private static List buildPluginConfigs(List newConfigs,
AgentConfig agentConfig) {
List pluginConfigs = new ArrayList<>();
Map remainingNewConfigs = new HashMap<>();
for (PluginConfig newConfig : newConfigs) {
remainingNewConfigs.put(newConfig.getId(), newConfig);
}
for (PluginConfig pluginConfig : agentConfig.getPluginConfigList()) {
PluginConfig newConfig = remainingNewConfigs.remove(pluginConfig.getId());
List newProperties;
if (newConfig == null) {
newProperties = new ArrayList<>();
} else {
newProperties = newConfig.getPropertyList();
}
pluginConfigs.add(buildPluginConfig(pluginConfig, newProperties, false));
}
if (remainingNewConfigs.isEmpty()) {
return pluginConfigs;
} else {
throw new IllegalStateException("Plugin config(s) not found: "
+ Joiner.on(", ").join(remainingNewConfigs.keySet()));
}
}
private static PluginConfig buildPluginConfig(PluginConfig existingConfig,
List newProperties, boolean errorOnMissingProperty) {
Map newProps = buildMutablePropertiesMap(newProperties);
PluginConfig.Builder builder = PluginConfig.newBuilder()
.setId(existingConfig.getId())
.setName(existingConfig.getName());
for (PluginProperty existingProperty : existingConfig.getPropertyList()) {
PluginProperty prop = newProps.remove(existingProperty.getName());
if (prop == null) {
if (errorOnMissingProperty) {
throw new IllegalStateException(
"Missing plugin property name: " + existingProperty.getName());
} else {
builder.addProperty(existingProperty.toBuilder()
.setValue(existingProperty.getDefault()));
continue;
}
}
if (!isSameType(prop.getValue(), existingProperty.getValue())) {
throw new IllegalStateException("Plugin property " + prop.getName()
+ " has incorrect type: " + prop.getValue().getValCase());
}
builder.addProperty(existingProperty.toBuilder()
.setValue(prop.getValue()));
}
if (!newProps.isEmpty()) {
throw new IllegalStateException(
"Unexpected property name(s): " + Joiner.on(", ").join(newProps.keySet()));
}
return builder.build();
}
private static Map buildMutablePropertiesMap(
List properties) {
return Maps.newHashMap(Maps.uniqueIndex(properties, PluginProperty::getName));
}
private static boolean isSameType(PluginProperty.Value left, PluginProperty.Value right) {
if (left.getValCase() == ValCase.DVAL && right.getValCase() == ValCase.DVAL_NULL) {
return true;
}
if (left.getValCase() == ValCase.DVAL_NULL && right.getValCase() == ValCase.DVAL) {
return true;
}
return left.getValCase() == right.getValCase();
}
private static CentralStorageConfig withCorrectedLists(CentralStorageConfig config) {
CentralStorageConfig defaultConfig = ImmutableCentralStorageConfig.builder().build();
return ImmutableCentralStorageConfig.builder()
.copyFrom(config)
.rollupExpirationHours(
fix(config.rollupExpirationHours(), defaultConfig.rollupExpirationHours()))
.queryAndServiceCallRollupExpirationHours(
fix(config.queryAndServiceCallRollupExpirationHours(),
defaultConfig.queryAndServiceCallRollupExpirationHours()))
.profileRollupExpirationHours(fix(config.profileRollupExpirationHours(),
defaultConfig.profileRollupExpirationHours()))
.build();
}
private static ImmutableList fix(ImmutableList thisList,
List defaultList) {
if (thisList.size() >= defaultList.size()) {
return thisList.subList(0, defaultList.size());
}
List correctedList = Lists.newArrayList(thisList);
for (int i = thisList.size(); i < defaultList.size(); i++) {
correctedList.add(defaultList.get(i));
}
return ImmutableList.copyOf(correctedList);
}
private static void validatePagerDutyConfig(PagerDutyConfig config) throws Exception {
Set integrationKeys = new HashSet<>();
Set integrationDisplays = new HashSet<>();
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 webhookUrls = Sets.newHashSet();
Set webhookDisplays = Sets.newHashSet();
for (SlackWebhook webhook : config.webhooks()) {
if (!webhookUrls.add(webhook.url())) {
throw new DuplicateSlackWebhookUrlException();
}
if (!webhookDisplays.add(webhook.display())) {
throw new DuplicateSlackWebhookDisplayException();
}
}
}
public interface AgentConfigListener {
// the new config is not passed to onChange so that the receiver has to get the latest,
// this avoids race condition worries that two updates may get sent to the receiver in the
// wrong order
void onChange(String agentRollupId) throws Exception;
}
public static class LazySecretKeyImpl implements LazySecretKey {
private final @Nullable SecretKey secretKey;
public LazySecretKeyImpl(String symmetricEncryptionKey) {
if (symmetricEncryptionKey.isEmpty()) {
secretKey = null;
} else {
byte[] bytes = BaseEncoding.base16()
.decode(symmetricEncryptionKey.toUpperCase(Locale.ENGLISH));
secretKey = new SecretKeySpec(bytes, "AES");
}
}
@Override
public @Nullable SecretKey getExisting() throws Exception {
return secretKey;
}
@Override
public SecretKey getOrCreate() throws Exception {
if (secretKey == null) {
throw new SymmetricEncryptionKeyMissingException();
}
return secretKey;
}
}
}