From a8ae4352af2699775345a543a0c67c391fc97c31 Mon Sep 17 00:00:00 2001 From: Steven Berler Date: Tue, 6 Apr 2021 14:34:56 -0700 Subject: [PATCH 1/3] add dbkvs runtime password config This adds support for live reloading DB passwords for products that use the standard install and runtime configs for Atlas. Note that previously there was no dbkvs runtime config, so that has been added. When the dbkvs runtime config is absent, behavior is identical to before (the password from the install config is used). --- .../atlasdb/timestamp/DbTimeLockFactory.java | 18 ++++++++- .../db/pool/config/ConnectionConfig.java | 9 ++++- .../keyvalue/dbkvs/DbAtlasDbFactory.java | 2 +- .../dbkvs/DbKeyValueServiceRuntimeConfig.java | 38 +++++++++++++++++++ .../dbkvs/RelationalDbTimeLockFactory.java | 8 +++- .../impl/ConnectionManagerAwareDbKvs.java | 35 +++++++++++++++++ .../memory/InMemoryDbTimeLockFactory.java | 8 +++- 7 files changed, 112 insertions(+), 6 deletions(-) create mode 100644 atlasdb-dbkvs/src/main/java/com/palantir/atlasdb/keyvalue/dbkvs/DbKeyValueServiceRuntimeConfig.java diff --git a/atlasdb-api/src/main/java/com/palantir/atlasdb/timestamp/DbTimeLockFactory.java b/atlasdb-api/src/main/java/com/palantir/atlasdb/timestamp/DbTimeLockFactory.java index cf2630cd784..60dea8f06ae 100644 --- a/atlasdb-api/src/main/java/com/palantir/atlasdb/timestamp/DbTimeLockFactory.java +++ b/atlasdb-api/src/main/java/com/palantir/atlasdb/timestamp/DbTimeLockFactory.java @@ -22,8 +22,11 @@ import com.palantir.atlasdb.keyvalue.api.TableReference; import com.palantir.atlasdb.keyvalue.api.TimestampSeriesProvider; import com.palantir.atlasdb.spi.KeyValueServiceConfig; +import com.palantir.atlasdb.spi.KeyValueServiceRuntimeConfig; import com.palantir.atlasdb.util.MetricsManager; +import com.palantir.refreshable.Refreshable; import com.palantir.timestamp.ManagedTimestampService; +import java.util.Optional; /** * See {@link com.palantir.atlasdb.spi.AtlasDbFactory}. A {@link DbTimeLockFactory} is an extension of an @@ -33,8 +36,21 @@ public interface DbTimeLockFactory { String getType(); + /** + * @deprecated Creating a DbKeyValueService with this method will not support live reloading the DB password. Use + * {@link #createRawKeyValueService(MetricsManager, KeyValueServiceConfig, Refreshable, LeaderConfig)} instead. + */ + @Deprecated + default KeyValueService createRawKeyValueService( + MetricsManager metricsManager, KeyValueServiceConfig config, LeaderConfig leaderConfig) { + return createRawKeyValueService(metricsManager, config, Refreshable.only(Optional.empty()), leaderConfig); + } + KeyValueService createRawKeyValueService( - MetricsManager metricsManager, KeyValueServiceConfig config, LeaderConfig leaderConfig); + MetricsManager metricManager, + KeyValueServiceConfig config, + Refreshable> runtimeConfig, + LeaderConfig leaderConfig); ManagedTimestampService createManagedTimestampService( KeyValueService rawKvs, DbTimestampCreationSetting dbTimestampCreationSetting, boolean initializeAsync); diff --git a/atlasdb-dbkvs-hikari/src/main/java/com/palantir/nexus/db/pool/config/ConnectionConfig.java b/atlasdb-dbkvs-hikari/src/main/java/com/palantir/nexus/db/pool/config/ConnectionConfig.java index 7dbab26897a..1464df417db 100644 --- a/atlasdb-dbkvs-hikari/src/main/java/com/palantir/nexus/db/pool/config/ConnectionConfig.java +++ b/atlasdb-dbkvs-hikari/src/main/java/com/palantir/nexus/db/pool/config/ConnectionConfig.java @@ -44,7 +44,14 @@ public abstract class ConnectionConfig { public abstract String getDbLogin(); - public abstract MaskedValue getDbPassword(); + /** + * If a runtime config is present, this does not need to be set and will not be used. A default value is provided + * so that the config will parse when this field is missing. + */ + @Value.Default + public MaskedValue getDbPassword() { + return ImmutableMaskedValue.of(""); + } public abstract String getUrl(); diff --git a/atlasdb-dbkvs/src/main/java/com/palantir/atlasdb/keyvalue/dbkvs/DbAtlasDbFactory.java b/atlasdb-dbkvs/src/main/java/com/palantir/atlasdb/keyvalue/dbkvs/DbAtlasDbFactory.java index f9963f7695e..79d3d319d77 100644 --- a/atlasdb-dbkvs/src/main/java/com/palantir/atlasdb/keyvalue/dbkvs/DbAtlasDbFactory.java +++ b/atlasdb-dbkvs/src/main/java/com/palantir/atlasdb/keyvalue/dbkvs/DbAtlasDbFactory.java @@ -69,7 +69,7 @@ public KeyValueService createRawKeyValueService( config instanceof DbKeyValueServiceConfig, "DbAtlasDbFactory expects a configuration of type DbKeyValueServiceConfiguration, found %s", config.getClass()); - return ConnectionManagerAwareDbKvs.create((DbKeyValueServiceConfig) config, initializeAsync); + return ConnectionManagerAwareDbKvs.create((DbKeyValueServiceConfig) config, runtimeConfig, initializeAsync); } @Override diff --git a/atlasdb-dbkvs/src/main/java/com/palantir/atlasdb/keyvalue/dbkvs/DbKeyValueServiceRuntimeConfig.java b/atlasdb-dbkvs/src/main/java/com/palantir/atlasdb/keyvalue/dbkvs/DbKeyValueServiceRuntimeConfig.java new file mode 100644 index 00000000000..f46b9d3b51e --- /dev/null +++ b/atlasdb-dbkvs/src/main/java/com/palantir/atlasdb/keyvalue/dbkvs/DbKeyValueServiceRuntimeConfig.java @@ -0,0 +1,38 @@ +/* + * (c) Copyright 2021 Palantir Technologies Inc. All rights reserved. + * + * 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 com.palantir.atlasdb.keyvalue.dbkvs; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.google.auto.service.AutoService; +import com.palantir.atlasdb.spi.KeyValueServiceRuntimeConfig; +import com.palantir.nexus.db.pool.config.MaskedValue; +import org.immutables.value.Value; + +@AutoService(KeyValueServiceRuntimeConfig.class) +@JsonSerialize(as = ImmutableDbKeyValueServiceRuntimeConfig.class) +@JsonDeserialize(as = ImmutableDbKeyValueServiceRuntimeConfig.class) +@Value.Immutable +public abstract class DbKeyValueServiceRuntimeConfig implements KeyValueServiceRuntimeConfig { + + @Override + public String type() { + return DbAtlasDbFactory.TYPE; + } + + public abstract MaskedValue getDbPassword(); +} diff --git a/atlasdb-dbkvs/src/main/java/com/palantir/atlasdb/keyvalue/dbkvs/RelationalDbTimeLockFactory.java b/atlasdb-dbkvs/src/main/java/com/palantir/atlasdb/keyvalue/dbkvs/RelationalDbTimeLockFactory.java index 31a49f985c3..72917d3beb9 100644 --- a/atlasdb-dbkvs/src/main/java/com/palantir/atlasdb/keyvalue/dbkvs/RelationalDbTimeLockFactory.java +++ b/atlasdb-dbkvs/src/main/java/com/palantir/atlasdb/keyvalue/dbkvs/RelationalDbTimeLockFactory.java @@ -30,6 +30,7 @@ import com.palantir.atlasdb.keyvalue.dbkvs.timestamp.MultiSequenceTimestampSeriesProvider; import com.palantir.atlasdb.spi.AtlasDbFactory; import com.palantir.atlasdb.spi.KeyValueServiceConfig; +import com.palantir.atlasdb.spi.KeyValueServiceRuntimeConfig; import com.palantir.atlasdb.timestamp.DbTimeLockFactory; import com.palantir.atlasdb.util.MetricsManager; import com.palantir.refreshable.Refreshable; @@ -51,11 +52,14 @@ public String getType() { @Override public KeyValueService createRawKeyValueService( - MetricsManager metricsManager, KeyValueServiceConfig config, LeaderConfig leaderConfig) { + MetricsManager metricsManager, + KeyValueServiceConfig config, + Refreshable> runtimeConfig, + LeaderConfig leaderConfig) { return delegate.createRawKeyValueService( metricsManager, config, - Refreshable.only(Optional.empty()), + runtimeConfig, Optional.of(leaderConfig), Optional.empty(), // This refers to an AtlasDB namespace - we use the config to talk to the db AtlasDbFactory.THROWING_FRESH_TIMESTAMP_SOURCE, // This is how we give out timestamps! diff --git a/atlasdb-dbkvs/src/main/java/com/palantir/atlasdb/keyvalue/dbkvs/impl/ConnectionManagerAwareDbKvs.java b/atlasdb-dbkvs/src/main/java/com/palantir/atlasdb/keyvalue/dbkvs/impl/ConnectionManagerAwareDbKvs.java index 06194b33e3c..d23f4304603 100644 --- a/atlasdb-dbkvs/src/main/java/com/palantir/atlasdb/keyvalue/dbkvs/impl/ConnectionManagerAwareDbKvs.java +++ b/atlasdb-dbkvs/src/main/java/com/palantir/atlasdb/keyvalue/dbkvs/impl/ConnectionManagerAwareDbKvs.java @@ -19,7 +19,9 @@ import com.palantir.atlasdb.AtlasDbConstants; import com.palantir.atlasdb.keyvalue.api.KeyValueService; import com.palantir.atlasdb.keyvalue.dbkvs.DbKeyValueServiceConfig; +import com.palantir.atlasdb.keyvalue.dbkvs.DbKeyValueServiceRuntimeConfig; import com.palantir.atlasdb.keyvalue.impl.ForwardingKeyValueService; +import com.palantir.atlasdb.spi.KeyValueServiceRuntimeConfig; import com.palantir.nexus.db.monitoring.timer.SqlTimer; import com.palantir.nexus.db.monitoring.timer.SqlTimers; import com.palantir.nexus.db.pool.ConnectionManager; @@ -29,7 +31,9 @@ import com.palantir.nexus.db.sql.SQL; import com.palantir.nexus.db.sql.SqlConnection; import com.palantir.nexus.db.sql.SqlConnectionHelper; +import com.palantir.refreshable.Refreshable; import java.sql.Connection; +import java.util.Optional; import java.util.function.Supplier; // This class should be removed and replaced by DbKvs when InDbTimestampStore depends directly on DbKvs @@ -38,18 +42,49 @@ public final class ConnectionManagerAwareDbKvs extends ForwardingKeyValueService private final ConnectionManager connManager; private final SqlConnectionSupplier sqlConnectionSupplier; + /** + * @deprecated This method does not support live reloading the DB password. Use + * {@link #create(DbKeyValueServiceConfig, Refreshable, boolean)} instead. + */ + @Deprecated public static ConnectionManagerAwareDbKvs create(DbKeyValueServiceConfig config) { return create(config, AtlasDbConstants.DEFAULT_INITIALIZE_ASYNC); } + /** + * @deprecated This method does not support live reloading the DB password. Use + * {@link #create(DbKeyValueServiceConfig, Refreshable, boolean)} instead. + */ + @Deprecated public static ConnectionManagerAwareDbKvs create(DbKeyValueServiceConfig config, boolean initializeAsync) { + return create(config, Refreshable.only(Optional.empty()), initializeAsync); + } + + public static ConnectionManagerAwareDbKvs create( + DbKeyValueServiceConfig config, + Refreshable> runtimeConfig, + boolean initializeAsync) { HikariCPConnectionManager connManager = new HikariCPConnectionManager(config.connection()); + runtimeConfig.subscribe(newRuntimeConfig -> updateConnManagerConfig(connManager, config, newRuntimeConfig)); ReentrantManagedConnectionSupplier connSupplier = new ReentrantManagedConnectionSupplier(connManager); SqlConnectionSupplier sqlConnSupplier = getSimpleTimedSqlConnectionSupplier(connSupplier); return new ConnectionManagerAwareDbKvs( DbKvs.create(config, sqlConnSupplier, initializeAsync), connManager, sqlConnSupplier); } + private static void updateConnManagerConfig( + HikariCPConnectionManager connManager, + DbKeyValueServiceConfig config, + Optional runtimeConfig) { + if (runtimeConfig.isPresent() && runtimeConfig.get() instanceof DbKeyValueServiceRuntimeConfig) { + DbKeyValueServiceRuntimeConfig dbRuntimeConfig = (DbKeyValueServiceRuntimeConfig) runtimeConfig.get(); + connManager.setPassword(dbRuntimeConfig.getDbPassword().unmasked()); + } else { + // no runtime config (or wrong type), use the password from the install config + connManager.setPassword(config.connection().getDbPassword().unmasked()); + } + } + private static SqlConnectionSupplier getSimpleTimedSqlConnectionSupplier( ReentrantManagedConnectionSupplier connectionSupplier) { Supplier supplier = connectionSupplier; diff --git a/atlasdb-impl-shared/src/main/java/com/palantir/atlasdb/memory/InMemoryDbTimeLockFactory.java b/atlasdb-impl-shared/src/main/java/com/palantir/atlasdb/memory/InMemoryDbTimeLockFactory.java index c7de4ba0868..d70386872c8 100644 --- a/atlasdb-impl-shared/src/main/java/com/palantir/atlasdb/memory/InMemoryDbTimeLockFactory.java +++ b/atlasdb-impl-shared/src/main/java/com/palantir/atlasdb/memory/InMemoryDbTimeLockFactory.java @@ -25,12 +25,15 @@ import com.palantir.atlasdb.keyvalue.api.TimestampSeriesProvider; import com.palantir.atlasdb.keyvalue.impl.InMemoryKeyValueService; import com.palantir.atlasdb.spi.KeyValueServiceConfig; +import com.palantir.atlasdb.spi.KeyValueServiceRuntimeConfig; import com.palantir.atlasdb.timestamp.DbTimeLockFactory; import com.palantir.atlasdb.util.MetricsManager; +import com.palantir.refreshable.Refreshable; import com.palantir.timestamp.InMemoryTimestampService; import com.palantir.timestamp.ManagedTimestampService; import java.util.HashMap; import java.util.Map; +import java.util.Optional; @AutoService(DbTimeLockFactory.class) public class InMemoryDbTimeLockFactory implements DbTimeLockFactory { @@ -43,7 +46,10 @@ public String getType() { @Override public KeyValueService createRawKeyValueService( - MetricsManager metricsManager, KeyValueServiceConfig config, LeaderConfig leaderConfig) { + MetricsManager metricManager, + KeyValueServiceConfig config, + Refreshable> runtimeConfig, + LeaderConfig leaderConfig) { return new InMemoryKeyValueService(true); } From 7e57e9544d3d949164ae8351da477f8d33219b48 Mon Sep 17 00:00:00 2001 From: Steven Berler Date: Tue, 6 Apr 2021 21:34:56 +0000 Subject: [PATCH 2/3] Add generated changelog entries --- changelog/@unreleased/pr-5365.v2.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 changelog/@unreleased/pr-5365.v2.yml diff --git a/changelog/@unreleased/pr-5365.v2.yml b/changelog/@unreleased/pr-5365.v2.yml new file mode 100644 index 00000000000..89e1b2eb574 --- /dev/null +++ b/changelog/@unreleased/pr-5365.v2.yml @@ -0,0 +1,21 @@ +type: improvement +improvement: + description: |- + add dbkvs runtime password config + + **Goals (and why)**: Supports live password changes for products that use the standard atlas install and runtime config. Previously only products that had their own custom config and managed their own hikari pool could update the password at runtime. No behavior is changed when the new dbkvs runtime config is absent. + + **Implementation Description (bullets)**: + * adds a new dbkvs runtime config + * when the dbkvs runtime config is absent, behavior is the same as before + * subscribes to the runtime config so that the hikari pool is updated any time the config changes + + **Testing (What was existing testing like? What have you done to improve it?)**: + + **Concerns (what feedback would you like?)**: + + **Where should we start reviewing?**: + + **Priority (whenever / two weeks / yesterday)**: normal + links: + - https://github.com/palantir/atlasdb/pull/5365 From f548ae6a21f4bc866ab82eec4bef8660356fe776 Mon Sep 17 00:00:00 2001 From: Steven Berler Date: Tue, 6 Apr 2021 15:01:17 -0700 Subject: [PATCH 3/3] Update pr-5365.v2.yml --- changelog/@unreleased/pr-5365.v2.yml | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/changelog/@unreleased/pr-5365.v2.yml b/changelog/@unreleased/pr-5365.v2.yml index 89e1b2eb574..435b93116c0 100644 --- a/changelog/@unreleased/pr-5365.v2.yml +++ b/changelog/@unreleased/pr-5365.v2.yml @@ -1,21 +1,6 @@ type: improvement improvement: description: |- - add dbkvs runtime password config - - **Goals (and why)**: Supports live password changes for products that use the standard atlas install and runtime config. Previously only products that had their own custom config and managed their own hikari pool could update the password at runtime. No behavior is changed when the new dbkvs runtime config is absent. - - **Implementation Description (bullets)**: - * adds a new dbkvs runtime config - * when the dbkvs runtime config is absent, behavior is the same as before - * subscribes to the runtime config so that the hikari pool is updated any time the config changes - - **Testing (What was existing testing like? What have you done to improve it?)**: - - **Concerns (what feedback would you like?)**: - - **Where should we start reviewing?**: - - **Priority (whenever / two weeks / yesterday)**: normal + Adds supports for live password changes for products that use the standard atlas install and runtime config. To take advantage of this products must add a runtime config for dbkvs (this appears in the same place as the cassandra runtime config would appear but has type "relational"). No behavior is changed when the new dbkvs runtime config is absent. links: - https://github.com/palantir/atlasdb/pull/5365