Allow remote config to send non-boolean values

This version of remote config allows non-boolean values to be returned
to clients but unfortunately limits the configuration to only one
value or another. There is no way to configure more than two values
for the same key with this setup.
This commit is contained in:
Ehren Kret
2020-04-29 10:51:10 -07:00
parent f39a5f6e68
commit 50ccfee201
10 changed files with 202 additions and 114 deletions

View File

@@ -2,6 +2,7 @@ package org.whispersystems.textsecuregcm.controllers;
import com.codahale.metrics.annotation.Timed;
import com.google.common.annotations.VisibleForTesting;
import io.dropwizard.auth.Auth;
import org.whispersystems.textsecuregcm.entities.UserRemoteConfig;
import org.whispersystems.textsecuregcm.entities.UserRemoteConfigList;
import org.whispersystems.textsecuregcm.storage.Account;
@@ -29,8 +30,6 @@ import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import io.dropwizard.auth.Auth;
@Path("/v1/config")
public class RemoteConfigController {
@@ -50,12 +49,10 @@ public class RemoteConfigController {
try {
MessageDigest digest = MessageDigest.getInstance("SHA1");
return new UserRemoteConfigList(remoteConfigsManager.getAll().stream().map(config -> new UserRemoteConfig(config.getName(),
isInBucket(digest, account.getUuid(),
config.getName().getBytes(),
config.getPercentage(),
config.getUuids())))
.collect(Collectors.toList()));
return new UserRemoteConfigList(remoteConfigsManager.getAll().stream().map(config -> {
boolean inBucket = isInBucket(digest, account.getUuid(), config.getName().getBytes(), config.getPercentage(), config.getUuids());
return new UserRemoteConfig(config.getName(), inBucket, inBucket ? config.getValue() : config.getDefaultValue());
}).collect(Collectors.toList()));
} catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
}
@@ -104,5 +101,4 @@ public class RemoteConfigController {
private boolean isAuthorized(String configToken) {
return configToken != null && configAuthTokens.stream().anyMatch(authorized -> MessageDigest.isEqual(authorized.getBytes(), configToken.getBytes()));
}
}

View File

@@ -5,16 +5,20 @@ import com.fasterxml.jackson.annotation.JsonProperty;
public class UserRemoteConfig {
@JsonProperty
private String name;
private String name;
@JsonProperty
private boolean enabled;
@JsonProperty
private String value;
public UserRemoteConfig() {}
public UserRemoteConfig(String name, boolean enabled) {
public UserRemoteConfig(String name, boolean enabled, String value) {
this.name = name;
this.enabled = enabled;
this.value = value;
}
public String getName() {
@@ -24,4 +28,8 @@ public class UserRemoteConfig {
public boolean isEnabled() {
return enabled;
}
public String getValue() {
return value;
}
}

View File

@@ -7,8 +7,6 @@ import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.UUID;
@@ -28,12 +26,20 @@ public class RemoteConfig {
@NotNull
private Set<UUID> uuids = new HashSet<>();
@JsonProperty
private String defaultValue;
@JsonProperty
private String value;
public RemoteConfig() {}
public RemoteConfig(String name, int percentage, Set<UUID> uuids) {
this.name = name;
this.percentage = percentage;
this.uuids = uuids;
public RemoteConfig(String name, int percentage, Set<UUID> uuids, String defaultValue, String value) {
this.name = name;
this.percentage = percentage;
this.uuids = uuids;
this.defaultValue = defaultValue;
this.value = value;
}
public int getPercentage() {
@@ -47,4 +53,12 @@ public class RemoteConfig {
public Set<UUID> getUuids() {
return uuids;
}
public String getDefaultValue() {
return defaultValue;
}
public String getValue() {
return value;
}
}

View File

@@ -3,27 +3,22 @@ package org.whispersystems.textsecuregcm.storage;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.SharedMetricRegistries;
import com.codahale.metrics.Timer;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.jdbi.v3.core.transaction.TransactionIsolationLevel;
import org.whispersystems.textsecuregcm.storage.mappers.AccountRowMapper;
import org.whispersystems.textsecuregcm.storage.mappers.RemoteConfigRowMapper;
import org.whispersystems.textsecuregcm.util.Constants;
import org.whispersystems.textsecuregcm.util.SystemMapper;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import static com.codahale.metrics.MetricRegistry.name;
public class RemoteConfigs {
public static final String ID = "id";
public static final String NAME = "name";
public static final String PERCENTAGE = "percentage";
public static final String UUIDS = "uuids";
public static final String ID = "id";
public static final String NAME = "name";
public static final String PERCENTAGE = "percentage";
public static final String UUIDS = "uuids";
public static final String DEFAULT_VALUE = "default_value";
public static final String VALUE = "value";
private final MetricRegistry metricRegistry = SharedMetricRegistries.getOrCreate(Constants.METRICS_NAME);
private final Timer setTimer = metricRegistry.timer(name(Accounts.class, "set" ));
@@ -41,11 +36,13 @@ public class RemoteConfigs {
public void set(RemoteConfig remoteConfig) {
database.use(jdbi -> jdbi.useHandle(handle -> {
try (Timer.Context ignored = setTimer.time()) {
handle.createUpdate("INSERT INTO remote_config (" + NAME + ", " + PERCENTAGE + ", " + UUIDS + ") VALUES (:name, :percentage, :uuids) ON CONFLICT(" + NAME + ") DO UPDATE SET " + PERCENTAGE + " = EXCLUDED." + PERCENTAGE + ", " + UUIDS + " = EXCLUDED." + UUIDS)
.bind("name", remoteConfig.getName())
.bind("percentage", remoteConfig.getPercentage())
.bind("uuids", remoteConfig.getUuids().toArray(new UUID[0]))
.execute();
handle.createUpdate("INSERT INTO remote_config (" + NAME + ", " + PERCENTAGE + ", " + UUIDS + ", " + DEFAULT_VALUE + ", " + VALUE + ") VALUES (:name, :percentage, :uuids, :default_value, :value) ON CONFLICT(" + NAME + ") DO UPDATE SET " + PERCENTAGE + " = EXCLUDED." + PERCENTAGE + ", " + UUIDS + " = EXCLUDED." + UUIDS + ", " + DEFAULT_VALUE + " = EXCLUDED." + DEFAULT_VALUE + ", " + VALUE + " = EXCLUDED." + VALUE)
.bind("name", remoteConfig.getName())
.bind("percentage", remoteConfig.getPercentage())
.bind("uuids", remoteConfig.getUuids().toArray(new UUID[0]))
.bind("default_value", remoteConfig.getDefaultValue())
.bind("value", remoteConfig.getValue())
.execute();
}
}));
}

View File

@@ -1,6 +1,7 @@
package org.whispersystems.textsecuregcm.storage;
import com.google.common.annotations.VisibleForTesting;
import io.dropwizard.lifecycle.Managed;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.util.Util;
@@ -10,8 +11,6 @@ import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import io.dropwizard.lifecycle.Managed;
public class RemoteConfigsManager implements Managed {
private final Logger logger = LoggerFactory.getLogger(RemoteConfigsManager.class);
@@ -19,7 +18,7 @@ public class RemoteConfigsManager implements Managed {
private final RemoteConfigs remoteConfigs;
private final long sleepInterval;
private AtomicReference<List<RemoteConfig>> cachedConfigs = new AtomicReference<>(new LinkedList<>());
private final AtomicReference<List<RemoteConfig>> cachedConfigs = new AtomicReference<>(new LinkedList<>());
public RemoteConfigsManager(RemoteConfigs remoteConfigs) {
this(remoteConfigs, TimeUnit.SECONDS.toMillis(10));

View File

@@ -16,6 +16,10 @@ public class RemoteConfigRowMapper implements RowMapper<RemoteConfig> {
@Override
public RemoteConfig map(ResultSet rs, StatementContext ctx) throws SQLException {
return new RemoteConfig(rs.getString(RemoteConfigs.NAME), rs.getInt(RemoteConfigs.PERCENTAGE), new HashSet<>(Arrays.asList((UUID[])rs.getArray(RemoteConfigs.UUIDS).getArray())));
return new RemoteConfig(rs.getString(RemoteConfigs.NAME),
rs.getInt(RemoteConfigs.PERCENTAGE),
new HashSet<>(Arrays.asList((UUID[])rs.getArray(RemoteConfigs.UUIDS).getArray())),
rs.getString(RemoteConfigs.DEFAULT_VALUE),
rs.getString(RemoteConfigs.VALUE));
}
}