aboutsummaryrefslogtreecommitdiffstats
path: root/flags/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'flags/src/main')
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/Flag.java64
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/Flags.java101
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/JacksonArraySerializer.java11
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/PermanentFlags.java37
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/UnboundListFlag.java8
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/custom/SharedHost.java14
6 files changed, 186 insertions, 49 deletions
diff --git a/flags/src/main/java/com/yahoo/vespa/flags/Flag.java b/flags/src/main/java/com/yahoo/vespa/flags/Flag.java
index 7ca5066969f..4d49d52fa73 100644
--- a/flags/src/main/java/com/yahoo/vespa/flags/Flag.java
+++ b/flags/src/main/java/com/yahoo/vespa/flags/Flag.java
@@ -1,6 +1,20 @@
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.flags;
+import com.yahoo.component.Version;
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.CloudAccount;
+import com.yahoo.config.provision.CloudName;
+import com.yahoo.config.provision.ClusterSpec;
+import com.yahoo.config.provision.Environment;
+import com.yahoo.config.provision.NodeResources.Architecture;
+import com.yahoo.config.provision.NodeType;
+import com.yahoo.config.provision.SystemName;
+import com.yahoo.config.provision.TenantName;
+import com.yahoo.config.provision.Zone;
+import com.yahoo.config.provision.zone.ZoneApi;
+import com.yahoo.config.provision.zone.ZoneId;
+
import java.util.Optional;
/**
@@ -10,7 +24,7 @@ import java.util.Optional;
* @param <F> The concrete subclass type of the flag
* @author hakonhall
*/
-public interface Flag<T, F> {
+public interface Flag<T, F extends Flag<T, F>> {
/** The flag ID. */
FlagId id();
@@ -28,6 +42,54 @@ public interface Flag<T, F> {
return dimensionValue.map(value -> with(dimension, value)).orElse(self());
}
+ /** Sets the tenant, application, and instance dimensions. */
+ default F with(ApplicationId applicationId) {
+ return with(Dimension.TENANT_ID, applicationId.tenant().value())
+ .with(Dimension.APPLICATION, applicationId.toSerializedFormWithoutInstance())
+ .with(Dimension.INSTANCE_ID, applicationId.serializedForm());
+ }
+
+ /** architecture MUST NOT be 'any'. */
+ default F with(Architecture architecture) { return with(Dimension.ARCHITECTURE, architecture.name()); }
+ /**
+ * Sets the cloud-account dimension ONLY IF it is an enclave account.<br/>
+ * Sets the clave dimension to "enclave" if it is an enclave account, otherwise "noclave".
+ */
+ default F with(CloudAccount cloudAccount, Zone zone) {
+ return cloudAccount.isEnclave(zone)
+ ? with(Dimension.CLOUD_ACCOUNT, Optional.of(cloudAccount).map(CloudAccount::value))
+ .with(Dimension.CLAVE, "enclave")
+ : with(Dimension.CLAVE, "noclave");
+ }
+ default F with(CloudName cloud) { return with(Dimension.CLOUD, cloud.value()); }
+ default F with(ClusterSpec.Id clusterId) { return with(Dimension.CLUSTER_ID, clusterId.value()); }
+ default F with(ClusterSpec.Type clusterType) { return with(Dimension.CLUSTER_TYPE, clusterType.name()); }
+ default F with(Environment environment) { return with(Dimension.ENVIRONMENT, environment.value()); }
+ default F with(NodeType nodeType) { return with(Dimension.NODE_TYPE, nodeType.name()); }
+ default F with(SystemName systemName) { return with(Dimension.SYSTEM, systemName.value()); }
+ default F with(TenantName tenantName) { return with(Dimension.TENANT_ID, tenantName.value()); }
+ default F with(Version vespaVersion) { return with(Dimension.VESPA_VERSION, vespaVersion.toFullString()); }
+ default F with(ZoneId zoneId) { return with(Dimension.ZONE_ID, zoneId.value()); }
+ default F with(Zone zone) { return with(Dimension.ZONE_ID, zone.systemLocalValue()); }
+ default F with(ZoneApi zoneApi) { return with(zoneApi.getVirtualId()); }
+
+ /** Sets the tenant, application, and instance dimensions. */
+ default F withApplicationId(Optional<ApplicationId> applicationId) { return applicationId.map(this::with).orElse(self()); }
+ /** architecture MUST NOT be 'any'. */
+ default F withArchitecture(Optional<Architecture> architecture) { return architecture.map(this::with).orElse(self()); }
+ default F withCloudAccount(Optional<CloudAccount> cloudAccount, Zone zone) { return cloudAccount.map(account -> with(account, zone)).orElse(self()); }
+ default F withCloudName(Optional<CloudName> cloud) { return cloud.map(this::with).orElse(self()); }
+ default F withClusterId(Optional<ClusterSpec.Id> clusterId) { return clusterId.map(this::with).orElse(self()); }
+ default F withClusterType(Optional<ClusterSpec.Type> clusterType) { return clusterType.map(this::with).orElse(self()); }
+ default F withEnvironment(Optional<Environment> environment) { return environment.map(this::with).orElse(self()); }
+ default F withNodeType(Optional<NodeType> nodeType) { return nodeType.map(this::with).orElse(self()); }
+ default F withSystemName(Optional<SystemName> systemName) { return systemName.map(this::with).orElse(self()); }
+ default F withTenantName(Optional<TenantName> tenantName) { return tenantName.map(this::with).orElse(self()); }
+ default F withVersion(Optional<Version> vespaVersion) { return vespaVersion.map(this::with).orElse(self()); }
+ default F withZoneId(Optional<ZoneId> zoneId) { return zoneId.map(this::with).orElse(self()); }
+ default F withZone(Optional<Zone> zone) { return zone.map(this::with).orElse(self()); }
+ default F withZoneApi(Optional<ZoneApi> zoneApi) { return zoneApi.map(this::with).orElse(self()); }
+
/** Returns the value, boxed if the flag wraps a primitive type. */
T boxedValue();
}
diff --git a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
index a3fe010c65b..8b40d68539d 100644
--- a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
+++ b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
@@ -12,6 +12,7 @@ import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Optional;
import java.util.TreeMap;
+import java.util.function.Consumer;
import java.util.function.Predicate;
import static com.yahoo.vespa.flags.Dimension.APPLICATION;
@@ -84,18 +85,6 @@ public class Flags {
"Takes effect at redeployment",
INSTANCE_ID);
- public static final UnboundBooleanFlag NEW_RESOURCES_FORMULA = defineFeatureFlag(
- "new-resources-formula", true,
- List.of("hakonhall"), "2024-04-25", "2024-05-25",
- "Use an easier to understand formula for calculating the memory and disk resources",
- "Takes effect on next deployment of an applications.");
-
- public static final UnboundBooleanFlag FIX_CONFIG_SERVER_HEAP = defineFeatureFlag(
- "fix-config-server-heap", false,
- List.of("hakonhall"), "2024-04-23", "2024-05-23",
- "Base the calculation of the config server JVM heap size on the amount of memory available to the container.",
- "Takes effect on start of config server Podman container");
-
public static final UnboundStringFlag RESPONSE_SEQUENCER_TYPE = defineStringFlag(
"response-sequencer-type", "ADAPTIVE",
List.of("baldersheim"), "2020-12-02", "2024-12-31",
@@ -212,7 +201,7 @@ public class Flags {
public static final UnboundIntFlag MAX_ACTIVATION_INHIBITED_OUT_OF_SYNC_GROUPS = defineIntFlag(
"max-activation-inhibited-out-of-sync-groups", 0,
- List.of("vekterli"), "2021-02-19", "2024-06-01",
+ List.of("vekterli"), "2021-02-19", "2025-02-01",
"Allows replicas in up to N content groups to not be activated " +
"for query visibility if they are out of sync with a majority of other replicas",
"Takes effect at redeployment",
@@ -220,7 +209,7 @@ public class Flags {
public static final UnboundDoubleFlag MIN_NODE_RATIO_PER_GROUP = defineDoubleFlag(
"min-node-ratio-per-group", 0.0,
- List.of("geirst", "vekterli"), "2021-07-16", "2024-06-01",
+ List.of("geirst", "vekterli"), "2021-07-16", "2025-02-01",
"Minimum ratio of nodes that have to be available (i.e. not Down) in any hierarchic content cluster group for the group to be Up",
"Takes effect at redeployment",
INSTANCE_ID);
@@ -243,13 +232,6 @@ public class Flags {
"Takes effect on next tick.",
NODE_TYPE);
- public static final UnboundStringFlag DIST_HOST = defineStringFlag(
- "dist-host", "",
- List.of("freva"), "2024-04-15", "2024-05-31",
- "Sets dist_host YUM variable, empty means old behavior. Only effective in Public.",
- "Provisioning of instance or next host-admin tick",
- HOSTNAME, NODE_TYPE, CLOUD_ACCOUNT);
-
public static final UnboundBooleanFlag ENABLED_HORIZON_DASHBOARD = defineFeatureFlag(
"enabled-horizon-dashboard", false,
List.of("olaa"), "2021-09-13", "2024-09-01",
@@ -318,32 +300,32 @@ public class Flags {
public static final UnboundStringFlag CORE_ENCRYPTION_PUBLIC_KEY_ID = defineStringFlag(
"core-encryption-public-key-id", "",
- List.of("vekterli"), "2022-11-03", "2024-06-01",
+ List.of("vekterli"), "2022-11-03", "2025-02-01",
"Specifies which public key to use for core dump encryption.",
"Takes effect on the next tick.",
NODE_TYPE, HOSTNAME);
public static final UnboundListFlag<String> ZONAL_WEIGHTED_ENDPOINT_RECORDS = defineListFlag(
- "zonal-weighted-endpoint-records", List.of(), String.class, List.of("jonmv"), "2023-12-15", "2024-06-01",
+ "zonal-weighted-endpoint-records", List.of(), String.class, List.of("jonmv"), "2023-12-15", "2024-12-01",
"A list of weighted (application) endpoint fqdns for which we should use zonal endpoints as targets, not LBs.",
"Takes effect at redeployment from controller");
public static final UnboundListFlag<String> WEIGHTED_ENDPOINT_RECORD_TTL = defineListFlag(
- "weighted-endpoint-record-ttl", List.of(), String.class, List.of("jonmv"), "2023-05-16", "2024-06-01",
+ "weighted-endpoint-record-ttl", List.of(), String.class, List.of("jonmv"), "2023-05-16", "2024-12-01",
"A list of endpoints and custom TTLs, on the form \"endpoint-fqdn:TTL-seconds\". " +
"Where specified, CNAME records are used instead of the default ALIAS records, which have a default 60s TTL.",
"Takes effect at redeployment from controller");
public static final UnboundBooleanFlag SORT_BLUEPRINTS_BY_COST = defineFeatureFlag(
"sort-blueprints-by-cost", false,
- List.of("baldersheim"), "2023-12-19", "2024-05-31",
+ List.of("baldersheim"), "2023-12-19", "2024-10-31",
"If true blueprints are sorted based on cost estimate, rather that absolute estimated hits",
"Takes effect at redeployment",
INSTANCE_ID);
public static final UnboundBooleanFlag ALWAYS_MARK_PHRASE_EXPENSIVE = defineFeatureFlag(
"always-mark-phrase-expensive", false,
- List.of("baldersheim"), "2023-11-20", "2024-05-31",
+ List.of("baldersheim"), "2023-11-20", "2024-10-31",
"If true all phrases will be marked expensive, independent of parents",
"Takes effect at redeployment",
INSTANCE_ID);
@@ -383,7 +365,7 @@ public class Flags {
public static final UnboundIntFlag CONTENT_LAYER_METADATA_FEATURE_LEVEL = defineIntFlag(
"content-layer-metadata-feature-level", 0,
- List.of("vekterli"), "2022-09-12", "2024-06-01",
+ List.of("vekterli"), "2022-09-12", "2024-12-01",
"Value semantics: 0) legacy behavior, 1) operation cancellation, 2) operation " +
"cancellation and ephemeral content node sequence numbers for bucket replicas",
"Takes effect at redeployment",
@@ -405,7 +387,7 @@ public class Flags {
public static final UnboundStringFlag ENDPOINT_CONFIG = defineStringFlag(
"endpoint-config", "legacy",
- List.of("mpolden", "tokle"), "2023-10-06", "2024-06-01",
+ List.of("mpolden", "tokle"), "2023-10-06", "2024-09-01",
"Set the endpoint config to use for an application. Must be 'legacy', 'combined' or 'generated'. See EndpointConfig for further details",
"Takes effect on next deployment through controller",
TENANT_ID, APPLICATION, INSTANCE_ID);
@@ -418,13 +400,13 @@ public class Flags {
public static UnboundBooleanFlag CALYPSO_ENABLED = defineFeatureFlag(
"calypso-enabled", true,
- List.of("mortent"), "2024-02-19", "2024-05-01",
+ List.of("mortent"), "2024-02-19", "2024-08-01",
"Whether to enable calypso for host",
"Takes effect immediately", HOSTNAME);
public static UnboundBooleanFlag ATHENZ_PROVIDER = defineFeatureFlag(
"athenz-provider", false,
- List.of("mortent"), "2024-02-19", "2024-05-01",
+ List.of("mortent"), "2024-02-19", "2024-08-01",
"Whether to use athenz as node identity provider",
"Takes effect on next identity refresh", HOSTNAME);
@@ -435,7 +417,7 @@ public class Flags {
"Takes effect immediately");
public static final UnboundIntFlag PERSISTENCE_THREAD_MAX_FEED_OP_BATCH_SIZE = defineIntFlag(
- "persistence-thread-max-feed-op-batch-size", 1,
+ "persistence-thread-max-feed-op-batch-size", 64,
List.of("vekterli"), "2024-04-12", "2025-01-01",
"Maximum number of enqueued feed operations (put/update/remove) bound "+
"towards the same bucket that can be async dispatched as part of the " +
@@ -449,6 +431,54 @@ public class Flags {
"Whether logserver container should run otel agent",
"Takes effect at redeployment", INSTANCE_ID);
+ public static UnboundBooleanFlag HUBSPOT_SYNC_TENANTS = defineFeatureFlag(
+ "hubspot-sync-tenants", false,
+ List.of("bjorncs"), "2024-05-07", "2025-01-01",
+ "Whether to sync tenants to HubSpot",
+ "Takes effect immediately");
+
+ public static final UnboundBooleanFlag REMOVE_ORPHANED_DNS_RECORDS = defineFeatureFlag(
+ "remove-orphaned-dns-records", false,
+ List.of("mpolden"), "2024-05-07", "2024-10-01",
+ "Whether EndpointDnsMaintainer should remove orphaned records instead of logging them",
+ "Takes effect on next maintenance run");
+
+ public static final UnboundBooleanFlag SYMMETRIC_PUT_AND_ACTIVATE_REPLICA_SELECTION = defineFeatureFlag(
+ "symmetric-put-and-activate-replica-selection", false,
+ List.of("vekterli"), "2024-05-23", "2024-08-01",
+ "Iff true there will be an 1-1 symmetry between the replicas chosen as feed targets " +
+ "for Put operations and the replica selection logic for bucket activation. If false, " +
+ "legacy feed behavior is used.",
+ "Takes effect immediately",
+ INSTANCE_ID);
+
+ public static final UnboundBooleanFlag ENFORCE_STRICTLY_INCREASING_CLUSTER_STATE_VERSIONS = defineFeatureFlag(
+ "enforce-strictly-increasing-cluster-state-versions", false,
+ List.of("vekterli"), "2024-06-03", "2024-08-01",
+ "Iff true, received cluster state versions that are lower than the current active " +
+ "state version on the node will be explicitly rejected.",
+ "Takes effect immediately",
+ INSTANCE_ID);
+
+ public static final UnboundBooleanFlag LAUNCH_APPLICATION_ATHENZ_SERVICE = defineFeatureFlag(
+ "launch-application-athenz-service", false,
+ List.of("jonmv"), "2024-06-11", "2024-09-01",
+ "Whether to launch an Athenz service unique to the application. Only valid in public systems!",
+ "Takes effect on next deployment",
+ INSTANCE_ID);
+
+ public static final UnboundBooleanFlag HUBSPOT_SYNC_CONTACTS = defineFeatureFlag(
+ "hubspot-sync-contacts", false,
+ List.of("bjorncs"), "2024-05-27", "2025-01-01",
+ "Whether to sync contacts to HubSpot",
+ "Takes effect immediately");
+
+ public static final UnboundBooleanFlag DELETE_EXPIRED_CONFIG_SESSIONS_NEW_PROCEDURE = defineFeatureFlag(
+ "delete-expired-config-sessions-new-procedure", false,
+ List.of("hmusum"), "2024-06-10", "2024-08-10",
+ "Whether to delete remote and local config sessions at the same time",
+ "Takes effect immediately");
+
/** WARNING: public for testing: All flags should be defined in {@link Flags}. */
public static UnboundBooleanFlag defineFeatureFlag(String flagId, boolean defaultValue, List<String> owners,
String createdAt, String expiresAt, String description,
@@ -512,6 +542,15 @@ public class Flags {
flagId, defaultValue, owners, createdAt, expiresAt, description, modificationEffect, dimensions);
}
+ /** WARNING: public for testing: All flags should be defined in {@link Flags}. */
+ public static <T> UnboundListFlag<T> defineListFlag(String flagId, List<T> defaultValue, Class<T> elementClass,
+ List<String> owners, String createdAt, String expiresAt,
+ String description, String modificationEffect,
+ Predicate<List<T>> validator, Dimension... dimensions) {
+ return define((fid, dval, fvec) -> new UnboundListFlag<>(fid, dval, elementClass, fvec, validator),
+ flagId, defaultValue, owners, createdAt, expiresAt, description, modificationEffect, dimensions);
+ }
+
@FunctionalInterface
private interface TypedUnboundFlagFactory<T, U extends UnboundFlag<?, ?, ?>> {
U create(FlagId id, T defaultValue, FetchVector defaultFetchVector);
diff --git a/flags/src/main/java/com/yahoo/vespa/flags/JacksonArraySerializer.java b/flags/src/main/java/com/yahoo/vespa/flags/JacksonArraySerializer.java
index 8f7b7d71734..38e3b57d28b 100644
--- a/flags/src/main/java/com/yahoo/vespa/flags/JacksonArraySerializer.java
+++ b/flags/src/main/java/com/yahoo/vespa/flags/JacksonArraySerializer.java
@@ -4,20 +4,27 @@ package com.yahoo.vespa.flags;
import com.fasterxml.jackson.databind.JavaType;
import java.util.List;
+import java.util.function.Predicate;
/**
* @author freva
*/
public class JacksonArraySerializer<T> implements FlagSerializer<List<T>> {
private final JavaType type;
+ private final Predicate<List<T>> validator;
- public JacksonArraySerializer(Class<T> clazz) {
+ public JacksonArraySerializer(Class<T> clazz, Predicate<List<T>> validator) {
type = JsonNodeRawFlag.constructCollectionType(List.class, clazz);
+ this.validator = validator;
}
@Override
public List<T> deserialize(RawFlag rawFlag) {
- return JsonNodeRawFlag.fromJsonNode(rawFlag.asJsonNode()).toJacksonClass(type);
+ var list = JsonNodeRawFlag.fromJsonNode(rawFlag.asJsonNode()).<List<T>>toJacksonClass(type);
+ if (!validator.test(list)) {
+ throw new IllegalArgumentException("Invalid value: " + list);
+ }
+ return list;
}
@Override
diff --git a/flags/src/main/java/com/yahoo/vespa/flags/PermanentFlags.java b/flags/src/main/java/com/yahoo/vespa/flags/PermanentFlags.java
index 3b73d9d6013..7d1c6bcecdb 100644
--- a/flags/src/main/java/com/yahoo/vespa/flags/PermanentFlags.java
+++ b/flags/src/main/java/com/yahoo/vespa/flags/PermanentFlags.java
@@ -18,12 +18,12 @@ import static com.yahoo.vespa.flags.Dimension.ARCHITECTURE;
import static com.yahoo.vespa.flags.Dimension.CERTIFICATE_PROVIDER;
import static com.yahoo.vespa.flags.Dimension.CLAVE;
import static com.yahoo.vespa.flags.Dimension.CLOUD_ACCOUNT;
-import static com.yahoo.vespa.flags.Dimension.FLAVOR;
-import static com.yahoo.vespa.flags.Dimension.INSTANCE_ID;
import static com.yahoo.vespa.flags.Dimension.CLUSTER_ID;
import static com.yahoo.vespa.flags.Dimension.CLUSTER_TYPE;
import static com.yahoo.vespa.flags.Dimension.CONSOLE_USER_EMAIL;
+import static com.yahoo.vespa.flags.Dimension.FLAVOR;
import static com.yahoo.vespa.flags.Dimension.HOSTNAME;
+import static com.yahoo.vespa.flags.Dimension.INSTANCE_ID;
import static com.yahoo.vespa.flags.Dimension.NODE_TYPE;
import static com.yahoo.vespa.flags.Dimension.TENANT_ID;
import static com.yahoo.vespa.flags.Dimension.VESPA_VERSION;
@@ -178,7 +178,7 @@ public class PermanentFlags {
HOSTNAME, NODE_TYPE, TENANT_ID, INSTANCE_ID, CLUSTER_TYPE, CLUSTER_ID, VESPA_VERSION);
public static final UnboundStringFlag ZOOKEEPER_SERVER_VERSION = defineStringFlag(
- "zookeeper-server-version", "3.9.1",
+ "zookeeper-server-version", "3.9.2",
"ZooKeeper server version, a jar file zookeeper-server-<ZOOKEEPER_SERVER_VERSION>-jar-with-dependencies.jar must exist",
"Takes effect on restart of Docker container",
NODE_TYPE, INSTANCE_ID, HOSTNAME);
@@ -195,12 +195,6 @@ public class PermanentFlags {
"Takes effect on next api request"
);
- public static final UnboundBooleanFlag JVM_OMIT_STACK_TRACE_IN_FAST_THROW = defineFeatureFlag(
- "jvm-omit-stack-trace-in-fast-throw", true,
- "Controls JVM option OmitStackTraceInFastThrow (default feature flag value is true, which is the default JVM option value as well)",
- "takes effect on JVM restart",
- CLUSTER_TYPE, INSTANCE_ID);
-
public static final UnboundIntFlag MAX_TRIAL_TENANTS = defineIntFlag(
"max-trial-tenants", -1,
"The maximum nr. of tenants with trial plan, -1 is unlimited",
@@ -372,6 +366,13 @@ public class PermanentFlags {
"Takes effect immediately"
);
+ public static final UnboundLongFlag CONFIG_SERVER_SESSION_LIFETIME = defineLongFlag(
+ "config-server-session-lifetime", 3600,
+ "Lifetime / expiry time in seconds for config sessions. " +
+ "This can be lowered if there are incidents/bugs where one needs to delete sessions quickly",
+ "Takes effect immediately"
+ );
+
public static final UnboundBooleanFlag NOTIFICATION_DISPATCH_FLAG = defineFeatureFlag(
"dispatch-notifications", true,
"Whether we should send notification for a given tenant",
@@ -404,6 +405,12 @@ public class PermanentFlags {
"Takes effect immediately",
INSTANCE_ID);
+ public static final UnboundBooleanFlag AUTOSCALING_DETAILED_LOGGING = defineFeatureFlag(
+ "autoscaling-detailed-logging", false,
+ "Whether to log autoscaling decision data",
+ "Takes effect immediately",
+ INSTANCE_ID);
+
public static final UnboundIntFlag MAX_HOSTS_PER_HOUR = defineIntFlag(
"max-hosts-per-hour", 40,
"The number of hosts that can be provisioned per hour in a zone, before throttling is " +
@@ -455,6 +462,13 @@ public class PermanentFlags {
HOSTNAME
);
+ public static final UnboundListFlag<String> LOG_REQUEST_CONTENT = defineListFlag(
+ "log-request-content", List.of(), String.class,
+ "Include request content in access log for paths starting with any of these prefixes",
+ "Takes effect on next redeployment",
+ list -> list.stream().allMatch(s -> s.matches("^[a-zA-Z/.0-9-]+:(0(\\.\\d+)?|1(\\.0+)?):\\d+(B|kB|MB|GB)?$")),
+ INSTANCE_ID);
+
private PermanentFlags() {}
private static UnboundBooleanFlag defineFeatureFlag(
@@ -497,5 +511,10 @@ public class PermanentFlags {
return Flags.defineListFlag(flagId, defaultValue, elementClass, OWNERS, toString(CREATED_AT), toString(EXPIRES_AT), description, modificationEffect, dimensions);
}
+ private static <T> UnboundListFlag<T> defineListFlag(
+ String flagId, List<T> defaultValue, Class<T> elementClass, String description, String modificationEffect, Predicate<List<T>> validator, Dimension... dimensions) {
+ return Flags.defineListFlag(flagId, defaultValue, elementClass, OWNERS, toString(CREATED_AT), toString(EXPIRES_AT), description, modificationEffect, validator, dimensions);
+ }
+
private static String toString(Instant instant) { return DateTimeFormatter.ISO_DATE.withZone(ZoneOffset.UTC).format(instant); }
}
diff --git a/flags/src/main/java/com/yahoo/vespa/flags/UnboundListFlag.java b/flags/src/main/java/com/yahoo/vespa/flags/UnboundListFlag.java
index f88a0648021..8411a286d54 100644
--- a/flags/src/main/java/com/yahoo/vespa/flags/UnboundListFlag.java
+++ b/flags/src/main/java/com/yahoo/vespa/flags/UnboundListFlag.java
@@ -2,6 +2,7 @@
package com.yahoo.vespa.flags;
import java.util.List;
+import java.util.function.Predicate;
/**
* @author freva
@@ -12,8 +13,13 @@ public class UnboundListFlag<T> extends UnboundFlagImpl<List<T>, ListFlag<T>, Un
}
public UnboundListFlag(FlagId id, List<T> defaultValue, Class<T> clazz, FetchVector defaultFetchVector) {
+ this(id, defaultValue, clazz, defaultFetchVector, __ -> true);
+ }
+
+ public UnboundListFlag(FlagId id, List<T> defaultValue, Class<T> clazz, FetchVector defaultFetchVector,
+ Predicate<List<T>> validator) {
super(id, defaultValue, defaultFetchVector,
- new JacksonArraySerializer<T>(clazz),
+ new JacksonArraySerializer<T>(clazz, validator),
(flagId, defVal, fetchVector) -> new UnboundListFlag<>(flagId, defVal, clazz, fetchVector),
ListFlag::new);
}
diff --git a/flags/src/main/java/com/yahoo/vespa/flags/custom/SharedHost.java b/flags/src/main/java/com/yahoo/vespa/flags/custom/SharedHost.java
index 66356d979a4..3f229862d7a 100644
--- a/flags/src/main/java/com/yahoo/vespa/flags/custom/SharedHost.java
+++ b/flags/src/main/java/com/yahoo/vespa/flags/custom/SharedHost.java
@@ -7,6 +7,8 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
+import com.yahoo.config.provision.ClusterSpec;
+import com.yahoo.config.provision.SharedHosts;
import com.yahoo.vespa.flags.PermanentFlags;
import java.util.List;
@@ -19,7 +21,7 @@ import java.util.Objects;
*/
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(value = JsonInclude.Include.NON_NULL)
-public class SharedHost {
+public class SharedHost implements SharedHosts {
private final List<HostResources> resources;
@@ -43,14 +45,16 @@ public class SharedHost {
/** Whether there are any shared hosts specifically for the given cluster type, or without a cluster type restriction. */
@JsonIgnore
- public boolean supportsClusterType(String clusterType) {
- return resources.stream().anyMatch(resource -> resource.clusterType().map(clusterType::equalsIgnoreCase).orElse(true));
+ @Override
+ public boolean supportsClusterType(ClusterSpec.Type clusterType) {
+ return resources.stream().anyMatch(resource -> resource.clusterType().map(type -> clusterType.name().equalsIgnoreCase(type)).orElse(true));
}
/** Whether there are any shared hosts specifically for the given cluster type. */
@JsonIgnore
- public boolean hasClusterType(String clusterType) {
- return resources.stream().anyMatch(resource -> resource.clusterType().map(clusterType::equalsIgnoreCase).orElse(false));
+ @Override
+ public boolean hasClusterType(ClusterSpec.Type clusterType) {
+ return resources.stream().anyMatch(resource -> resource.clusterType().map(type -> clusterType.name().equalsIgnoreCase(type)).orElse(false));
}
@JsonIgnore