Make cache-remote-host enabled when needed

Closes #34536

Signed-off-by: Pedro Ruivo <pruivo@redhat.com>
This commit is contained in:
Pedro Ruivo 2024-11-11 07:22:21 +00:00 committed by GitHub
parent b82ec62eb7
commit 0bbe568d4f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 213 additions and 60 deletions

View File

@ -46,6 +46,7 @@ import java.util.Properties;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import org.keycloak.common.profile.ProfileException;
import org.keycloak.config.DeprecatedMetadata;
@ -164,7 +165,7 @@ public class Picocli {
if (currentSpec != null) {
CommandLine commandLine = currentSpec.commandLine();
addCommandOptions(cliArgs, commandLine);
if (commandLine != null && commandLine.getCommand() instanceof AbstractCommand ac) {
// set current parsed command
Environment.setParsedCommand(ac);
@ -219,7 +220,7 @@ public class Picocli {
// TODO: ensure that the config has not yet been initialized
// - there's currently no good way to do that directly on ConfigProviderResolver
initProfile(cliArgs, currentCommandName);
if (requiresReAugmentation(currentCommand)) {
PropertyMappers.sanitizeDisabledMappers();
exitCode = runReAugmentation(cliArgs, cmd);
@ -238,7 +239,7 @@ public class Picocli {
// override from the cli if specified
parseConfigArgs(cliArgs, (k, v) -> {
if (k.equals(Main.PROFILE_SHORT_NAME) || k.equals(Main.PROFILE_LONG_NAME)) {
Environment.setProfile(v);
Environment.setProfile(v);
}
}, ignored -> {});
}
@ -259,7 +260,7 @@ public class Picocli {
return true; // no build yet
}
var current = getNonPersistedBuildTimeOptions();
// everything but the optimized value must match
String key = Configuration.KC_OPTIMIZED;
Optional.ofNullable(rawPersistedProperties.get(key)).ifPresentOrElse(value -> current.put(key, value), () -> current.remove(key));
@ -327,13 +328,13 @@ public class Picocli {
*
* @param cliArgs
* @param abstractCommand
* @param outWriter
* @param outWriter
*/
public static void validateConfig(List<String> cliArgs, AbstractCommand abstractCommand, PrintWriter outWriter) {
if (cliArgs.contains(OPTIMIZED_BUILD_OPTION_LONG) && !wasBuildEverRun()) {
throw new PropertyException(Messages.optimizedUsedForFirstStartup());
}
IncludeOptions options = getIncludeOptions(cliArgs, abstractCommand, abstractCommand.getName());
if (!options.includeBuildTime && !options.includeRuntime) {
@ -350,6 +351,7 @@ public class Picocli {
final Set<String> disabledBuildTime = new HashSet<>();
final Set<String> disabledRunTime = new HashSet<>();
final Set<String> deprecatedInUse = new HashSet<>();
final Set<String> missingOption = new HashSet<>();
final Set<PropertyMapper<?>> disabledMappers = new HashSet<>();
if (options.includeBuildTime) {
@ -369,7 +371,14 @@ public class Picocli {
ConfigValue configValue = Configuration.getConfigValue(mapper.getFrom());
String configValueStr = configValue.getValue();
if (configValueStr == null || !isUserModifiable(configValue)) {
// don't consider missing or anything below standard env properties
if (configValueStr == null) {
if (Environment.isRuntimeMode() && mapper.isEnabled() && mapper.isRequired()) {
handleRequired(missingOption, mapper);
}
continue;
}
if (!isUserModifiable(configValue)) {
continue;
}
@ -409,6 +418,9 @@ public class Picocli {
}
}
if (!missingOption.isEmpty()) {
throw new PropertyException("The following options are required: \n%s".formatted(String.join("\n", missingOption)));
}
if (!ignoredBuildTime.isEmpty()) {
throw new PropertyException(format("The following build time options have values that differ from what is persisted - the new values will NOT be used until another build is run: %s\n",
String.join(", ", ignoredBuildTime)));
@ -510,25 +522,21 @@ public class Picocli {
}
private static void handleDisabled(Set<String> disabledInUse, PropertyMapper<?> mapper) {
String optionName = mapper.getFrom();
if (optionName.startsWith(NS_KEYCLOAK_PREFIX)) {
optionName = optionName.substring(NS_KEYCLOAK_PREFIX.length());
}
handleMessage(disabledInUse, mapper, PropertyMapper::getEnabledWhen);
}
private static void handleRequired(Set<String> requiredOptions, PropertyMapper<?> mapper) {
handleMessage(requiredOptions, mapper, PropertyMapper::getRequiredWhen);
}
private static void handleMessage(Set<String> messages, PropertyMapper<?> mapper, Function<PropertyMapper<?>, Optional<String>> retrieveMessage) {
var optionName = mapper.getOption().getKey();
final StringBuilder sb = new StringBuilder("\t- ");
sb.append(optionName);
if (mapper.getEnabledWhen().isPresent()) {
final String enabledWhen = mapper.getEnabledWhen().get();
sb.append(": ");
sb.append(enabledWhen);
if (!enabledWhen.endsWith(".")) {
sb.append(".");
}
}
disabledInUse.add(sb.toString());
retrieveMessage.apply(mapper).ifPresent(msg -> sb.append(": ").append(msg).append("."));
messages.add(sb.toString());
}
private static void warn(String text, PrintWriter outwriter) {
ColorScheme defaultColorScheme = picocli.CommandLine.Help.defaultColorScheme(Help.Ansi.AUTO);
outwriter.println(defaultColorScheme.apply("WARNING: ", Arrays.asList(Style.fg_yellow, Style.bold)) + text);
@ -622,7 +630,7 @@ public class Picocli {
protected PrintWriter getErrWriter() {
return new PrintWriter(System.err, true);
}
protected PrintWriter getOutWriter() {
return new PrintWriter(System.out, true);
}
@ -749,7 +757,7 @@ public class Picocli {
} else if (mapper.getType().isEnum()) {
// prevent the auto-conversion that picocli does
// we validate the expected values later
optBuilder.type(String.class);
optBuilder.type(String.class);
}
} else {
optBuilder.type(String.class);
@ -791,6 +799,7 @@ public class Picocli {
.ifPresent(transformedDesc::append);
mapper.getEnabledWhen().map(e -> format(" %s.", e)).ifPresent(transformedDesc::append);
mapper.getRequiredWhen().map(e -> format(" %s.", e)).ifPresent(transformedDesc::append);
// only fully deprecated options, not just deprecated values
mapper.getDeprecatedMetadata()
@ -854,19 +863,19 @@ public class Picocli {
private static void checkChangesInBuildOptionsDuringAutoBuild(PrintWriter out) {
StringBuilder options = new StringBuilder();
var current = getNonPersistedBuildTimeOptions();
var persisted = Configuration.getRawPersistedProperties();
// TODO: order is not well defined here
current.forEach((key, value) -> {
String persistedValue = persisted.get(key);
if (!value.equals(persistedValue)) {
optionChanged(options, (String)key, persistedValue, (String)value);
}
});
persisted.forEach((key, value) -> {
if (current.get(key) == null) {
optionChanged(options, key, value, null);
@ -885,7 +894,7 @@ public class Picocli {
);
}
}
private static void optionChanged(StringBuilder options, String key, String oldValue, String newValue) {
// the assumption here is that no build time options need mask handling
boolean isIgnored = !key.startsWith(MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX)

View File

@ -1,8 +1,5 @@
package org.keycloak.quarkus.runtime.configuration.mappers;
import static org.keycloak.quarkus.runtime.configuration.Configuration.getOptionalKcValue;
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption;
import java.io.File;
import java.nio.file.Paths;
import java.util.ArrayList;
@ -10,15 +7,22 @@ import java.util.List;
import java.util.Optional;
import java.util.function.BooleanSupplier;
import io.smallrye.config.ConfigSourceInterceptorContext;
import org.keycloak.common.Profile;
import org.keycloak.config.CachingOptions;
import org.keycloak.config.Option;
import org.keycloak.infinispan.util.InfinispanUtils;
import org.keycloak.quarkus.runtime.Environment;
import org.keycloak.quarkus.runtime.cli.PropertyException;
import io.smallrye.config.ConfigSourceInterceptorContext;
import static org.keycloak.quarkus.runtime.configuration.Configuration.getOptionalKcValue;
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption;
final class CachingPropertyMappers {
private static final String REMOTE_HOST_SET = "remote host is set";
private static final String MULTI_SITE_OR_EMBEDDED_REMOTE_FEATURE_SET = "feature '%s', '%s' or '%s' is set".formatted(Profile.Feature.MULTI_SITE.getKey(), Profile.Feature.CLUSTERLESS.getKey(), Profile.Feature.CACHE_EMBEDDED_REMOTE_STORE.getKey());
private static final String MULTI_SITE_FEATURE_SET = "feature '%s' or '%s' is set".formatted(Profile.Feature.MULTI_SITE.getKey(), Profile.Feature.CLUSTERLESS.getKey());
private static final String CACHE_STACK_SET_TO_ISPN = "'cache' type is set to '" + CachingOptions.Mechanism.ispn.name() + "'";
@ -66,6 +70,8 @@ final class CachingPropertyMappers {
.build(),
fromOption(CachingOptions.CACHE_REMOTE_HOST)
.paramLabel("hostname")
.addValidateEnabled(CachingPropertyMappers::isRemoteCacheHostEnabled, MULTI_SITE_OR_EMBEDDED_REMOTE_FEATURE_SET)
.isRequired(InfinispanUtils::isRemoteInfinispan, MULTI_SITE_FEATURE_SET)
.build(),
fromOption(CachingOptions.CACHE_REMOTE_PORT)
.isEnabled(CachingPropertyMappers::remoteHostSet, CachingPropertyMappers.REMOTE_HOST_SET)
@ -76,10 +82,12 @@ final class CachingPropertyMappers {
.build(),
fromOption(CachingOptions.CACHE_REMOTE_USERNAME)
.isEnabled(CachingPropertyMappers::remoteHostSet, CachingPropertyMappers.REMOTE_HOST_SET)
.validator((value) -> validateCachingOptionIsPresent(CachingOptions.CACHE_REMOTE_USERNAME, CachingOptions.CACHE_REMOTE_PASSWORD))
.paramLabel("username")
.build(),
fromOption(CachingOptions.CACHE_REMOTE_PASSWORD)
.isEnabled(CachingPropertyMappers::remoteHostSet, CachingPropertyMappers.REMOTE_HOST_SET)
.validator((value) -> validateCachingOptionIsPresent(CachingOptions.CACHE_REMOTE_PASSWORD, CachingOptions.CACHE_REMOTE_USERNAME))
.paramLabel("password")
.isMasked(true)
.build(),
@ -152,4 +160,14 @@ final class CachingPropertyMappers {
.paramLabel("max-count")
.build();
}
private static boolean isRemoteCacheHostEnabled() {
return InfinispanUtils.isRemoteInfinispan() || Profile.isFeatureEnabled(Profile.Feature.CACHE_EMBEDDED_REMOTE_STORE);
}
private static void validateCachingOptionIsPresent(Option<?> optionSet, Option<?> optionRequired) {
if (getOptionalKcValue(optionRequired).isEmpty()) {
throw new PropertyException("The option '%s' is required when '%s' is set.".formatted(optionRequired.getKey(), optionSet.getKey()));
}
}
}

View File

@ -66,7 +66,9 @@ public class PropertyMapper<T> {
null,
false,
null,
null) {
null,
() -> false,
"") {
@Override
public ConfigValue getConfigValue(String name, ConfigSourceInterceptorContext context) {
return context.proceed(name);
@ -86,12 +88,14 @@ public class PropertyMapper<T> {
private final String cliFormat;
private final BiConsumer<PropertyMapper<T>, ConfigValue> validator;
private final String description;
private final BooleanSupplier required;
private final String requiredWhen;
PropertyMapper(Option<T> option, String to, BooleanSupplier enabled, String enabledWhen,
BiFunction<String, ConfigSourceInterceptorContext, String> mapper,
String mapFrom, BiFunction<String, ConfigSourceInterceptorContext, String> parentMapper,
String mapFrom, BiFunction<String, ConfigSourceInterceptorContext, String> parentMapper,
String paramLabel, boolean mask, BiConsumer<PropertyMapper<T>, ConfigValue> validator,
String description) {
String description, BooleanSupplier required, String requiredWhen) {
this.option = option;
this.to = to == null ? getFrom() : to;
this.enabled = enabled;
@ -101,6 +105,8 @@ public class PropertyMapper<T> {
this.paramLabel = paramLabel;
this.mask = mask;
this.cliFormat = toCliFormat(option.getKey());
this.required = required;
this.requiredWhen = requiredWhen;
this.envVarFormat = toEnvVarFormat(getFrom());
this.validator = validator;
this.description = description;
@ -132,7 +138,7 @@ public class PropertyMapper<T> {
// if the property we want to map depends on another one, we use the value from the other property to call the mapper
config = Configuration.getKcConfigValue(mapFrom);
parentValue = true;
}
}
if (config != null && config.getValue() != null) {
config = transformValue(name, config, context, parentValue);
@ -142,11 +148,11 @@ public class PropertyMapper<T> {
.withValue(defaultValue).withRawValue(defaultValue).build(),
context, false);
}
if (config != null) {
return config;
}
// now try any defaults from quarkus
return context.proceed(name);
}
@ -173,6 +179,16 @@ public class PropertyMapper<T> {
this.enabledWhen = enabledWhen;
}
public boolean isRequired() {
return required.getAsBoolean();
}
public Optional<String> getRequiredWhen() {
return Optional.of(requiredWhen)
.filter(StringUtil::isNotBlank)
.map(e -> "Required when " + e);
}
public Class<T> getType() {
return this.option.getType();
}
@ -242,25 +258,25 @@ public class PropertyMapper<T> {
private ConfigValue transformValue(String name, ConfigValue configValue, ConfigSourceInterceptorContext context, boolean parentValue) {
String value = configValue.getValue();
String mappedValue = value;
boolean mapped = false;
var theMapper = parentValue ? this.parentMapper : this.mapper;
if (theMapper != null && (!name.equals(getFrom()) || parentValue)) {
mappedValue = theMapper.apply(value, context);
mapped = true;
}
// defaults and values from transformers may not have been subject to expansion
if ((mapped || configValue.getConfigSourceName() == null) && mappedValue != null && Expressions.isEnabled() && mappedValue.contains("$")) {
mappedValue = new ExpressionConfigSourceInterceptor().getValue(
new ContextWrapper(context, new ConfigValueBuilder().withName(name).withValue(mappedValue).build()),
name).getValue();
}
if (value == null && mappedValue == null) {
return null;
}
if (!mapped && name.equals(configValue.getName())) {
return configValue;
}
@ -318,6 +334,8 @@ public class PropertyMapper<T> {
private String paramLabel;
private BiConsumer<PropertyMapper<T>, ConfigValue> validator = (mapper, value) -> mapper.validateValues(value, mapper::validateExpectedValues);
private String description;
private BooleanSupplier isRequired = () -> false;
private String requiredWhen = "";
public Builder(Option<T> option) {
this.option = option;
@ -373,6 +391,29 @@ public class PropertyMapper<T> {
return this;
}
/**
* Sets this option as required when the {@link BooleanSupplier} returns {@code true}.
* <p>
* The {@code enableWhen} parameter is a message to show with the error message.
* <p>
* This check is only run in runtime mode.
*/
public Builder<T> isRequired(BooleanSupplier isRequired, String requiredWhen) {
this.requiredWhen = Objects.requireNonNull(requiredWhen);
assert !requiredWhen.endsWith(".");
return isRequired(isRequired);
}
/**
* Sets this option as required when the {@link BooleanSupplier} returns {@code true}.
* <p>
* This check is only run in runtime mode.
*/
public Builder<T> isRequired(BooleanSupplier isRequired) {
this.isRequired = Objects.requireNonNull(isRequired);
return this;
}
/**
* Set the validator, overwriting the current one.
*/
@ -384,7 +425,7 @@ public class PropertyMapper<T> {
}
return this;
}
public Builder<T> addValidator(BiConsumer<PropertyMapper<T>, ConfigValue> validator) {
var current = this.validator;
this.validator = (mapper, value) -> {
@ -403,10 +444,10 @@ public class PropertyMapper<T> {
};
return this;
}
/**
* Similar to {@link #enabledWhen}, but uses the condition as a validator that is added to the current one. This allows the option
* to appear in help.
* to appear in help.
* @return
*/
public Builder<T> addValidateEnabled(BooleanSupplier isEnabled, String enabledWhen) {
@ -423,7 +464,7 @@ public class PropertyMapper<T> {
if (paramLabel == null && Boolean.class.equals(option.getType())) {
paramLabel = Boolean.TRUE + "|" + Boolean.FALSE;
}
return new PropertyMapper<T>(option, to, isEnabled, enabledWhen, mapper, mapFrom, parentMapper, paramLabel, isMasked, validator, description);
return new PropertyMapper<>(option, to, isEnabled, enabledWhen, mapper, mapFrom, parentMapper, paramLabel, isMasked, validator, description, isRequired, requiredWhen);
}
}
@ -436,7 +477,7 @@ public class PropertyMapper<T> {
validator.accept(this, value);
}
}
public boolean isList() {
return getOption().getType() == java.util.List.class;
}
@ -453,8 +494,11 @@ public class PropertyMapper<T> {
if (!result.isEmpty()) {
result.append(".\n");
}
result.append("Invalid value for multivalued option " + getOptionAndSourceMessage(configValue)
+ ": list value '" + v + "' should not have leading nor trailing whitespace");
result.append("Invalid value for multivalued option ")
.append(getOptionAndSourceMessage(configValue))
.append(": list value '")
.append(v)
.append("' should not have leading nor trailing whitespace");
continue;
}
try {
@ -466,7 +510,7 @@ public class PropertyMapper<T> {
result.append(e.getMessage());
}
}
if (!result.isEmpty()) {
throw new PropertyException(result.toString());
}
@ -488,7 +532,7 @@ public class PropertyMapper<T> {
ShortErrorMessageHandler.getExpectedValuesMessage(expectedValues)));
}
}
String getOptionAndSourceMessage(ConfigValue configValue) {
if (isCliOption(configValue)) {
return String.format("'%s'", this.getCliFormat());

View File

@ -17,6 +17,8 @@
package org.keycloak.it.cli.dist;
import java.nio.file.Paths;
import io.quarkus.test.junit.main.Launch;
import io.quarkus.test.junit.main.LaunchResult;
import org.junit.jupiter.api.Assertions;
@ -30,8 +32,6 @@ import org.keycloak.it.junit5.extension.RawDistOnly;
import org.keycloak.it.junit5.extension.WithEnvVars;
import org.keycloak.it.utils.KeycloakDistribution;
import java.nio.file.Paths;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.keycloak.quarkus.runtime.cli.command.Main.CONFIG_FILE_LONG_NAME;
@ -129,4 +129,68 @@ public class OptionsDistTest {
assertEquals(1, result.getErrorStream().stream().filter(s -> s.contains("Disabled option: '--log-console-color'. Available only when Console log handler is activated")).count());
assertEquals(1, result.getErrorStream().stream().filter(s -> s.contains("Possible solutions: --log, --log-file, --log-file-level, --log-file-format, --log-file-output, --log-level")).count());
}
@Test
@Order(10)
@Launch({"start-dev", "--cache-remote-host=localhost"})
public void testCacheRemoteHostWithoutMultiSite(LaunchResult result) {
assertErrorStreamContains(result, "cache-remote-host available only when feature 'multi-site', 'clusterless' or 'cache-embedded-remote-store' is set");
}
@Test
@Order(11)
@Launch({"start-dev", "--cache-remote-port=11222"})
public void testCacheRemotePortWithoutCacheRemoteHost(LaunchResult result) {
assertDisabledDueToMissingRemoteHost(result, "--cache-remote-port");
}
@Test
@Order(12)
@Launch({"start-dev", "--cache-remote-username=user"})
public void testCacheRemoteUsernameWithoutCacheRemoteHost(LaunchResult result) {
assertDisabledDueToMissingRemoteHost(result, "--cache-remote-username");
}
@Test
@Order(13)
@Launch({"start-dev", "--cache-remote-password=pass"})
public void testCacheRemotePasswordWithoutCacheRemoteHost(LaunchResult result) {
assertDisabledDueToMissingRemoteHost(result, "--cache-remote-password");
}
@Test
@Order(14)
@Launch({"start-dev", "--cache-remote-tls-enabled=false"})
public void testCacheRemoteTlsEnabledWithoutCacheRemoteHost(LaunchResult result) {
assertDisabledDueToMissingRemoteHost(result, "--cache-remote-tls-enabled");
}
@Test
@Order(15)
@Launch({"start-dev", "--features=multi-site"})
public void testMultiSiteWithoutCacheRemoteHost(LaunchResult result) {
assertErrorStreamContains(result, "- cache-remote-host: Required when feature 'multi-site' or 'clusterless' is set.");
}
@Test
@Order(16)
@Launch({"start-dev", "--features=multi-site", "--cache-remote-host=localhost", "--cache-remote-username=user"})
public void testCacheRemoteUsernameWithoutCacheRemotePassword(LaunchResult result) {
assertErrorStreamContains(result, "The option 'cache-remote-password' is required when 'cache-remote-username' is set.");
}
@Test
@Order(17)
@Launch({"start-dev", "--features=multi-site", "--cache-remote-host=localhost", "--cache-remote-password=secret"})
public void testCacheRemotePasswordWithoutCacheRemoteUsername(LaunchResult result) {
assertErrorStreamContains(result, "The option 'cache-remote-username' is required when 'cache-remote-password' is set.");
}
private static void assertDisabledDueToMissingRemoteHost(LaunchResult result, String option) {
assertErrorStreamContains(result, "Disabled option: '%s'. Available only when remote host is set".formatted(option));
}
private static void assertErrorStreamContains(LaunchResult result, String msg) {
assertTrue(result.getErrorStream().stream().anyMatch(s -> s.contains(msg)));
}
}

View File

@ -69,6 +69,9 @@ Cache:
specified via XML file (see 'cache-config-file' option.). If the option is
specified, 'cache-remote-username' and 'cache-remote-password' are required
as well and the related configuration in XML file should not be present.
Available only when feature 'multi-site', 'clusterless' or
'cache-embedded-remote-store' is set. Required when feature 'multi-site' or
'clusterless' is set.
Config:
@ -351,4 +354,4 @@ Bootstrap Admin:
Do NOT start the server using this command when deploying to production.
Use 'kc.sh start-dev --help-all' to list all available options, including build
options.
options.

View File

@ -72,6 +72,9 @@ Cache:
specified via XML file (see 'cache-config-file' option.). If the option is
specified, 'cache-remote-username' and 'cache-remote-password' are required
as well and the related configuration in XML file should not be present.
Available only when feature 'multi-site', 'clusterless' or
'cache-embedded-remote-store' is set. Required when feature 'multi-site' or
'clusterless' is set.
--cache-remote-password <password>
The password for the authentication to the remote server for the remote store.
It replaces the 'password' attribute of 'digest' tag of the configuration
@ -504,4 +507,4 @@ Bootstrap Admin:
Do NOT start the server using this command when deploying to production.
Use 'kc.sh start-dev --help-all' to list all available options, including build
options.
options.

View File

@ -70,6 +70,9 @@ Cache:
specified via XML file (see 'cache-config-file' option.). If the option is
specified, 'cache-remote-username' and 'cache-remote-password' are required
as well and the related configuration in XML file should not be present.
Available only when feature 'multi-site', 'clusterless' or
'cache-embedded-remote-store' is set. Required when feature 'multi-site' or
'clusterless' is set.
--cache-stack <stack>
Define the default stack to use for cluster communication and node discovery.
Possible values are: tcp, udp, jdbc-ping, jdbc-ping-udp, kubernetes, ec2
@ -361,4 +364,4 @@ By default, this command tries to update the server configuration by running a
$ kc.sh start '--optimized'
By doing that, the server should start faster based on any previous
configuration you have set when manually running the 'build' command.
configuration you have set when manually running the 'build' command.

View File

@ -73,6 +73,9 @@ Cache:
specified via XML file (see 'cache-config-file' option.). If the option is
specified, 'cache-remote-username' and 'cache-remote-password' are required
as well and the related configuration in XML file should not be present.
Available only when feature 'multi-site', 'clusterless' or
'cache-embedded-remote-store' is set. Required when feature 'multi-site' or
'clusterless' is set.
--cache-remote-password <password>
The password for the authentication to the remote server for the remote store.
It replaces the 'password' attribute of 'digest' tag of the configuration
@ -509,4 +512,4 @@ By default, this command tries to update the server configuration by running a
$ kc.sh start '--optimized'
By doing that, the server should start faster based on any previous
configuration you have set when manually running the 'build' command.
configuration you have set when manually running the 'build' command.

View File

@ -70,6 +70,9 @@ Cache:
specified via XML file (see 'cache-config-file' option.). If the option is
specified, 'cache-remote-username' and 'cache-remote-password' are required
as well and the related configuration in XML file should not be present.
Available only when feature 'multi-site', 'clusterless' or
'cache-embedded-remote-store' is set. Required when feature 'multi-site' or
'clusterless' is set.
--cache-stack <stack>
Define the default stack to use for cluster communication and node discovery.
Possible values are: tcp, udp, jdbc-ping, jdbc-ping-udp, kubernetes, ec2
@ -303,4 +306,4 @@ By default, this command tries to update the server configuration by running a
$ kc.sh start '--optimized'
By doing that, the server should start faster based on any previous
configuration you have set when manually running the 'build' command.
configuration you have set when manually running the 'build' command.

View File

@ -73,6 +73,9 @@ Cache:
specified via XML file (see 'cache-config-file' option.). If the option is
specified, 'cache-remote-username' and 'cache-remote-password' are required
as well and the related configuration in XML file should not be present.
Available only when feature 'multi-site', 'clusterless' or
'cache-embedded-remote-store' is set. Required when feature 'multi-site' or
'clusterless' is set.
--cache-remote-password <password>
The password for the authentication to the remote server for the remote store.
It replaces the 'password' attribute of 'digest' tag of the configuration
@ -437,4 +440,4 @@ By default, this command tries to update the server configuration by running a
$ kc.sh start '--optimized'
By doing that, the server should start faster based on any previous
configuration you have set when manually running the 'build' command.
configuration you have set when manually running the 'build' command.