summaryrefslogtreecommitdiffstats
path: root/flags
diff options
context:
space:
mode:
authorBjørn Christian Seime <bjorncs@verizonmedia.com>2020-12-02 14:00:43 +0100
committerBjørn Christian Seime <bjorncs@verizonmedia.com>2020-12-02 14:00:45 +0100
commit1a976d946e39067269d6aeabe78502d72616a490 (patch)
treea4e12b1a02998615384bd85be1a13473b048a6c7 /flags
parentac66edc8eab81f21f57b39b74e87fabe60e56ac7 (diff)
Specify owner and expected time-to-leave for feature flags
Actual owners will be specified in upcoming PR
Diffstat (limited to 'flags')
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/FlagDefinition.java40
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/Flags.java116
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/PermanentFlags.java57
-rw-r--r--flags/src/test/java/com/yahoo/vespa/flags/FlagsTest.java16
4 files changed, 193 insertions, 36 deletions
diff --git a/flags/src/main/java/com/yahoo/vespa/flags/FlagDefinition.java b/flags/src/main/java/com/yahoo/vespa/flags/FlagDefinition.java
index 0dce61cf0bb..af3c7e253bf 100644
--- a/flags/src/main/java/com/yahoo/vespa/flags/FlagDefinition.java
+++ b/flags/src/main/java/com/yahoo/vespa/flags/FlagDefinition.java
@@ -2,6 +2,7 @@
package com.yahoo.vespa.flags;
import javax.annotation.concurrent.Immutable;
+import java.time.Instant;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -12,13 +13,26 @@ import java.util.List;
@Immutable
public class FlagDefinition {
private final UnboundFlag<?, ?, ?> unboundFlag;
+ private final List<String> owners;
+ private final Instant createdAt;
+ private final Instant expiresAt;
private final String description;
private final String modificationEffect;
private final List<FetchVector.Dimension> dimensions;
- public FlagDefinition(UnboundFlag<?, ?, ?> unboundFlag, String description, String modificationEffect,
- FetchVector.Dimension... dimensions) {
+ public FlagDefinition(
+ UnboundFlag<?, ?, ?> unboundFlag,
+ List<String> owners,
+ Instant createdAt,
+ Instant expiresAt,
+ String description,
+ String modificationEffect,
+ FetchVector.Dimension... dimensions) {
+ validate(owners, createdAt, expiresAt);
this.unboundFlag = unboundFlag;
+ this.owners = owners;
+ this.createdAt = createdAt;
+ this.expiresAt = expiresAt;
this.description = description;
this.modificationEffect = modificationEffect;
this.dimensions = Collections.unmodifiableList(Arrays.asList(dimensions));
@@ -39,4 +53,26 @@ public class FlagDefinition {
public String getModificationEffect() {
return modificationEffect;
}
+
+ public List<String> getOwners() { return owners; }
+
+ public Instant getCreatedAt() { return createdAt; }
+
+ public Instant getExpiresAt() { return expiresAt; }
+
+ private static void validate(List<String> owners, Instant createdAt, Instant expiresAt) {
+ if (expiresAt.isBefore(createdAt)) {
+ throw new IllegalArgumentException(
+ String.format(
+ "Flag cannot expire before its creation date (createdAt='%s', expiresAt='%s')",
+ createdAt, expiresAt));
+ }
+ if (owners.equals(PermanentFlags.OWNERS)) {
+ if (!createdAt.equals(PermanentFlags.CREATED_AT) || !expiresAt.equals(PermanentFlags.EXPIRES_AT)) {
+ throw new IllegalArgumentException("Invalid creation or expiration date for permanent flag");
+ }
+ } else if (owners.isEmpty()) {
+ throw new IllegalArgumentException("Owner(s) must be specified");
+ }
+ }
}
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 f087714896b..a2ef8a073dc 100644
--- a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
+++ b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
@@ -6,6 +6,10 @@ import com.yahoo.vespa.defaults.Defaults;
import com.yahoo.vespa.flags.custom.ClusterCapacity;
import com.yahoo.vespa.flags.custom.SharedHost;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Optional;
import java.util.TreeMap;
@@ -44,12 +48,14 @@ public class Flags {
public static final UnboundBooleanFlag FLEET_CANARY = defineFeatureFlag(
"fleet-canary", false,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"Whether the host is a fleet canary.",
"Takes effect on next host admin tick.",
HOSTNAME);
public static final UnboundListFlag<String> DISABLED_HOST_ADMIN_TASKS = defineListFlag(
"disabled-host-admin-tasks", List.of(), String.class,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"List of host-admin task names (as they appear in the log, e.g. root>main>UpgradeTask), or some node-agent " +
"functionality (see NodeAgentTask), that should be skipped",
"Takes effect on next host admin tick",
@@ -57,6 +63,7 @@ public class Flags {
public static final UnboundStringFlag DOCKER_VERSION = defineStringFlag(
"docker-version", "1.13.1-102.git7f2769b",
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"The version of the docker to use of the format VERSION-REL: The YUM package to be installed will be " +
"2:docker-VERSION-REL.el7.centos.x86_64 in AWS (and without '.centos' otherwise). " +
"If docker-version is not of this format, it must be parseable by YumPackageName::fromString.",
@@ -65,6 +72,7 @@ public class Flags {
public static final UnboundDoubleFlag CONTAINER_CPU_CAP = defineDoubleFlag(
"container-cpu-cap", 0,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"Hard limit on how many CPUs a container may use. This value is multiplied by CPU allocated to node, so " +
"to cap CPU at 200%, set this to 2, etc.",
"Takes effect on next node agent tick. Change is orchestrated, but does NOT require container restart",
@@ -72,12 +80,14 @@ public class Flags {
public static final UnboundIntFlag REBOOT_INTERVAL_IN_DAYS = defineIntFlag(
"reboot-interval-in-days", 30,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"No reboots are scheduled 0x-1x reboot intervals after the previous reboot, while reboot is " +
"scheduled evenly distributed in the 1x-2x range (and naturally guaranteed at the 2x boundary).",
"Takes effect on next run of NodeRebooter");
public static final UnboundBooleanFlag RETIRE_WITH_PERMANENTLY_DOWN = defineFeatureFlag(
"retire-with-permanently-down", false,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"If enabled, retirement will end with setting the host status to PERMANENTLY_DOWN, " +
"instead of ALLOWED_TO_BE_DOWN (old behavior).",
"Takes effect on the next run of RetiredExpirer.",
@@ -85,6 +95,7 @@ public class Flags {
public static final UnboundListFlag<ClusterCapacity> PREPROVISION_CAPACITY = defineListFlag(
"preprovision-capacity", List.of(), ClusterCapacity.class,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"Specifies the resources that ought to be immediately available for additional cluster " +
"allocations. If the resources are not available, additional hosts will be provisioned. " +
"Only applies to dynamically provisioned zones.",
@@ -92,163 +103,191 @@ public class Flags {
public static final UnboundBooleanFlag COMPACT_PREPROVISION_CAPACITY = defineFeatureFlag(
"compact-preprovision-capacity", true,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"Whether preprovision capacity can be satisfied with available capacity on hosts with " +
"existing allocations. Historically preprovision-capacity referred to empty hosts.",
"Takes effect on next iteration of DynamicProvisioningMaintainer.");
public static final UnboundJacksonFlag<SharedHost> SHARED_HOST = defineJacksonFlag(
"shared-host", SharedHost.createDisabled(), SharedHost.class,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"Specifies whether shared hosts can be provisioned, and if so, the advertised " +
"node resources of the host, the maximum number of containers, etc.",
"Takes effect on next iteration of DynamicProvisioningMaintainer.");
public static final UnboundListFlag<String> INACTIVE_MAINTENANCE_JOBS = defineListFlag(
"inactive-maintenance-jobs", List.of(), String.class,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"The list of maintenance jobs that are inactive.",
"Takes effect immediately, but any currently running jobs will run until completion.");
public static final UnboundDoubleFlag DEFAULT_TERM_WISE_LIMIT = defineDoubleFlag(
"default-term-wise-limit", 1.0,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"Default limit for when to apply termwise query evaluation",
"Takes effect at redeployment",
ZONE_ID, APPLICATION_ID);
public static final UnboundStringFlag JVM_GC_OPTIONS = defineStringFlag(
"jvm-gc-options", "",
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"Sets deafult jvm gc options",
"Takes effect at redeployment",
ZONE_ID, APPLICATION_ID);
public static final UnboundStringFlag FEED_SEQUENCER_TYPE = defineStringFlag(
"feed-sequencer-type", "LATENCY",
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"Selects type of sequenced executor used for feeding, valid values are LATENCY, ADAPTIVE, THROUGHPUT",
"Takes effect at redeployment",
ZONE_ID, APPLICATION_ID);
public static final UnboundStringFlag RESPONSE_SEQUENCER_TYPE = defineStringFlag(
"response-sequencer-type", "ADAPTIVE",
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"Selects type of sequenced executor used for mbus responses, valid values are LATENCY, ADAPTIVE, THROUGHPUT",
"Takes effect at redeployment",
ZONE_ID, APPLICATION_ID);
public static final UnboundIntFlag RESPONSE_NUM_THREADS = defineIntFlag(
"response-num-threads", 2,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"Number of threads used for mbus responses, default is 2, negative number = numcores/4",
"Takes effect at redeployment",
ZONE_ID, APPLICATION_ID);
public static final UnboundBooleanFlag SKIP_COMMUNICATIONMANAGER_THREAD = defineFeatureFlag(
"skip-communicatiomanager-thread", false,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"Should we skip the communicationmanager thread",
"Takes effect at redeployment",
ZONE_ID, APPLICATION_ID);
public static final UnboundBooleanFlag SKIP_MBUS_REQUEST_THREAD = defineFeatureFlag(
"skip-mbus-request-thread", false,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"Should we skip the mbus request thread",
"Takes effect at redeployment",
ZONE_ID, APPLICATION_ID);
public static final UnboundBooleanFlag SKIP_MBUS_REPLY_THREAD = defineFeatureFlag(
"skip-mbus-reply-thread", false,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"Should we skip the mbus reply thread",
"Takes effect at redeployment",
ZONE_ID, APPLICATION_ID);
public static final UnboundBooleanFlag USE_THREE_PHASE_UPDATES = defineFeatureFlag(
"use-three-phase-updates", false,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"Whether to enable the use of three-phase updates when bucket replicas are out of sync.",
"Takes effect at redeployment",
ZONE_ID, APPLICATION_ID);
public static final UnboundBooleanFlag USE_DIRECT_STORAGE_API_RPC = defineFeatureFlag(
"use-direct-storage-api-rpc", false,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"Whether to use direct RPC for Storage API communication between content cluster nodes.",
"Takes effect at restart of distributor and content node process",
ZONE_ID, APPLICATION_ID);
public static final UnboundBooleanFlag USE_FAST_VALUE_TENSOR_IMPLEMENTATION = defineFeatureFlag(
"use-fast-value-tensor-implementation", false,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"Whether to use FastValueBuilderFactory as the tensor implementation on all content nodes.",
"Takes effect at restart of content node process",
ZONE_ID, APPLICATION_ID);
public static final UnboundBooleanFlag HOST_HARDENING = defineFeatureFlag(
"host-hardening", false,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"Whether to enable host hardening Linux baseline.",
"Takes effect on next tick or on host-admin restart (may vary where used).",
HOSTNAME);
public static final UnboundBooleanFlag TCP_ABORT_ON_OVERFLOW = defineFeatureFlag(
"tcp-abort-on-overflow", false,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"Whether to set /proc/sys/net/ipv4/tcp_abort_on_overflow to 0 (false) or 1 (true)",
"Takes effect on next host-admin tick.",
HOSTNAME);
public static final UnboundStringFlag ZOOKEEPER_SERVER_VERSION = defineStringFlag(
"zookeeper-server-version", "3.5.6",
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"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, APPLICATION_ID, HOSTNAME);
public static final UnboundStringFlag TLS_FOR_ZOOKEEPER_CLIENT_SERVER_COMMUNICATION = defineStringFlag(
"tls-for-zookeeper-client-server-communication", "OFF",
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"How to setup TLS for ZooKeeper client/server communication. Valid values are OFF, PORT_UNIFICATION, TLS_WITH_PORT_UNIFICATION, TLS_ONLY",
"Takes effect on restart of config server",
NODE_TYPE, HOSTNAME);
public static final UnboundBooleanFlag USE_TLS_FOR_ZOOKEEPER_CLIENT = defineFeatureFlag(
"use-tls-for-zookeeper-client", false,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"Whether to use TLS for ZooKeeper clients",
"Takes effect on restart of process",
NODE_TYPE, HOSTNAME);
public static final UnboundBooleanFlag VALIDATE_ENDPOINT_CERTIFICATES = defineFeatureFlag(
"validate-endpoint-certificates", false,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"Whether endpoint certificates should be validated before use",
"Takes effect on the next deployment of the application");
public static final UnboundStringFlag DELETE_UNUSED_ENDPOINT_CERTIFICATES = defineStringFlag(
"delete-unused-endpoint-certificates", "disable",
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"Whether the endpoint certificate maintainer should delete unused certificates in cameo/zk",
"Takes effect on next scheduled run of maintainer - set to \"disable\", \"dryrun\" or \"enable\"");
public static final UnboundBooleanFlag USE_ALTERNATIVE_ENDPOINT_CERTIFICATE_PROVIDER = defineFeatureFlag(
"use-alternative-endpoint-certificate-provider", false,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"Whether to use an alternative CA when provisioning new certificates",
"Takes effect only on initial application deployment - not on later certificate refreshes!");
public static final UnboundStringFlag DOCKER_IMAGE_REPO = defineStringFlag(
"docker-image-repo", "",
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"Override default docker image repo. Docker image version will be Vespa version.",
"Takes effect on next deployment from controller",
ZONE_ID, APPLICATION_ID);
public static final UnboundBooleanFlag ENDPOINT_CERT_IN_SHARED_ROUTING = defineFeatureFlag(
"endpoint-cert-in-shared-routing", false,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"Whether to provision and use endpoint certs for apps in shared routing zones",
"Takes effect on next deployment of the application", APPLICATION_ID);
public static final UnboundBooleanFlag USE_CLOUD_INIT_FORMAT = defineFeatureFlag(
"use-cloud-init", false,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"Use the cloud-init format when provisioning hosts",
"Takes effect immediately",
ZONE_ID);
public static final UnboundBooleanFlag PROVISION_APPLICATION_ROLES = defineFeatureFlag(
"provision-application-roles", false,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"Whether application roles should be provisioned",
"Takes effect on next deployment (controller)",
ZONE_ID);
public static final UnboundBooleanFlag APPLICATION_IAM_ROLE = defineFeatureFlag(
"application-iam-roles", false,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"Allow separate iam roles when provisioning/assigning hosts",
"Takes effect immediately on new hosts, on next redeploy for applications",
APPLICATION_ID);
public static final UnboundBooleanFlag ENABLE_PUBLIC_SIGNUP_FLOW = defineFeatureFlag(
"enable-public-signup-flow", false,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"Show the public signup flow for a user in the console",
"takes effect on browser reload of api/user/v1/user",
CONSOLE_USER_EMAIL
@@ -256,6 +295,7 @@ public class Flags {
public static final UnboundBooleanFlag CONTROLLER_PROVISION_LB = defineFeatureFlag(
"controller-provision-lb", false,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"Provision load balancer for controller cluster",
"Takes effect when controller application is redeployed",
ZONE_ID
@@ -263,6 +303,7 @@ public class Flags {
public static final UnboundIntFlag TENANT_NODE_QUOTA = defineIntFlag(
"tenant-node-quota", 5,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"The number of nodes a tenant is allowed to request per cluster",
"Only takes effect on next deployment, if set to a value other than the default for flag!",
APPLICATION_ID
@@ -270,6 +311,7 @@ public class Flags {
public static final UnboundIntFlag TENANT_BUDGET_QUOTA = defineIntFlag(
"tenant-budget-quota", -1,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"The budget in cents/hr a tenant is allowed spend per instance, as calculated by NodeResources",
"Only takes effect on next deployment, if set to a value other than the default for flag!",
TENANT_ID
@@ -277,128 +319,142 @@ public class Flags {
public static final UnboundBooleanFlag ONLY_PUBLIC_ACCESS = defineFeatureFlag(
"enable-public-only", false,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"Only access public hosts from container",
"Takes effect on next tick"
);
public static final UnboundListFlag<String> OUTBOUND_BLOCKED_IPV4 = defineListFlag(
"container-outbound-blocked-ipv4", List.of(), String.class,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"List of IPs or CIDRs that are blocked for outbound connections",
"Takes effect on next tick"
);
public static final UnboundListFlag<String> OUTBOUND_BLOCKED_IPV6 = defineListFlag(
"container-outbound-blocked-ipv6", List.of(), String.class,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"List of IPs or CIDRs that are blocked for outbound connections",
"Takes effect on next tick"
);
public static final UnboundBooleanFlag HIDE_SHARED_ROUTING_ENDPOINT = defineFeatureFlag(
- "hide-shared-routing-endpoint",
- false,
+ "hide-shared-routing-endpoint", false,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"Whether the controller should hide shared routing layer endpoint",
"Takes effect immediately",
APPLICATION_ID
);
public static final UnboundBooleanFlag SKIP_MAINTENANCE_DEPLOYMENT = defineFeatureFlag(
- "node-repository-skip-maintenance-deployment",
- false,
+ "node-repository-skip-maintenance-deployment", false,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"Whether PeriodicApplicationMaintainer should skip deployment for an application",
"Takes effect at next run of maintainer",
APPLICATION_ID);
public static final UnboundBooleanFlag USE_ACCESS_CONTROL_CLIENT_AUTHENTICATION = defineFeatureFlag(
- "use-access-control-client-authentication",
- false,
+ "use-access-control-client-authentication", false,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"Whether application container should set up client authentication on default port based on access control element",
"Takes effect on next internal redeployment",
APPLICATION_ID);
public static final UnboundBooleanFlag USE_ASYNC_MESSAGE_HANDLING_ON_SCHEDULE = defineFeatureFlag(
"async-message-handling-on-schedule", false,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"Optionally deliver async messages in own thread",
"Takes effect at redeployment",
ZONE_ID, APPLICATION_ID);
public static final UnboundIntFlag CONTENT_NODE_BUCKET_DB_STRIPE_BITS = defineIntFlag(
"content-node-bucket-db-stripe-bits", 0,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"Number of bits used for striping the bucket DB in service layer",
"Takes effect at redeployment",
ZONE_ID, APPLICATION_ID);
public static final UnboundIntFlag MERGE_CHUNK_SIZE = defineIntFlag(
"merge-chunk-size", 0x400000,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"Size of merge buffer in service layer",
"Takes effect at redeployment",
ZONE_ID, APPLICATION_ID);
public static final UnboundDoubleFlag FEED_CONCURRENCY = defineDoubleFlag(
"feed-concurrency", 0.5,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"How much concurrency should be allowed for feed",
"Takes effect at redeployment",
ZONE_ID, APPLICATION_ID);
public static final UnboundBooleanFlag ENABLE_AUTOMATIC_REINDEXING = defineFeatureFlag(
- "enable-automatic-reindexing",
- false,
+ "enable-automatic-reindexing", false,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"Whether to automatically trigger reindexing from config change",
"Takes effect on next internal redeployment",
APPLICATION_ID);
public static final UnboundBooleanFlag USE_POWER_OF_TWO_CHOICES_LOAD_BALANCING = defineFeatureFlag(
- "use-power-of-two-choices-load-balancing",
- false,
+ "use-power-of-two-choices-load-balancing", false,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"Whether to use Power of two load balancing algorithm for application",
"Takes effect on next internal redeployment",
APPLICATION_ID);
public static final UnboundBooleanFlag DYNAMIC_RECONFIGURATION_OF_ZOOKEEPER_CLUSTER = defineFeatureFlag(
- "dynamic-reconfiguration-of-zookeeper-cluster",
- false,
+ "dynamic-reconfiguration-of-zookeeper-cluster", false,
+ List.of("nobody"), "2020-12-02", "2021-02-01",
"Whether to allow dynamic reconfiguration of zookeeper cluster",
"Takes effect on next deployment",
APPLICATION_ID);
/** WARNING: public for testing: All flags should be defined in {@link Flags}. */
- public static UnboundBooleanFlag defineFeatureFlag(String flagId, boolean defaultValue, String description,
+ public static UnboundBooleanFlag defineFeatureFlag(String flagId, boolean defaultValue, List<String> owners,
+ String createdAt, String expiresAt, String description,
String modificationEffect, FetchVector.Dimension... dimensions) {
- return define(UnboundBooleanFlag::new, flagId, defaultValue, description, modificationEffect, dimensions);
+ return define(UnboundBooleanFlag::new, flagId, defaultValue, owners, createdAt, expiresAt, description, modificationEffect, dimensions);
}
/** WARNING: public for testing: All flags should be defined in {@link Flags}. */
- public static UnboundStringFlag defineStringFlag(String flagId, String defaultValue, String description,
+ public static UnboundStringFlag defineStringFlag(String flagId, String defaultValue, List<String> owners,
+ String createdAt, String expiresAt, String description,
String modificationEffect, FetchVector.Dimension... dimensions) {
- return define(UnboundStringFlag::new, flagId, defaultValue, description, modificationEffect, dimensions);
+ return define(UnboundStringFlag::new, flagId, defaultValue, owners, createdAt, expiresAt, description, modificationEffect, dimensions);
}
/** WARNING: public for testing: All flags should be defined in {@link Flags}. */
- public static UnboundIntFlag defineIntFlag(String flagId, int defaultValue, String description,
+ public static UnboundIntFlag defineIntFlag(String flagId, int defaultValue, List<String> owners,
+ String createdAt, String expiresAt, String description,
String modificationEffect, FetchVector.Dimension... dimensions) {
- return define(UnboundIntFlag::new, flagId, defaultValue, description, modificationEffect, dimensions);
+ return define(UnboundIntFlag::new, flagId, defaultValue, owners, createdAt, expiresAt, description, modificationEffect, dimensions);
}
/** WARNING: public for testing: All flags should be defined in {@link Flags}. */
- public static UnboundLongFlag defineLongFlag(String flagId, long defaultValue, String description,
+ public static UnboundLongFlag defineLongFlag(String flagId, long defaultValue, List<String> owners,
+ String createdAt, String expiresAt, String description,
String modificationEffect, FetchVector.Dimension... dimensions) {
- return define(UnboundLongFlag::new, flagId, defaultValue, description, modificationEffect, dimensions);
+ return define(UnboundLongFlag::new, flagId, defaultValue, owners, createdAt, expiresAt, description, modificationEffect, dimensions);
}
/** WARNING: public for testing: All flags should be defined in {@link Flags}. */
- public static UnboundDoubleFlag defineDoubleFlag(String flagId, double defaultValue, String description,
+ public static UnboundDoubleFlag defineDoubleFlag(String flagId, double defaultValue, List<String> owners,
+ String createdAt, String expiresAt, String description,
String modificationEffect, FetchVector.Dimension... dimensions) {
- return define(UnboundDoubleFlag::new, flagId, defaultValue, description, modificationEffect, dimensions);
+ return define(UnboundDoubleFlag::new, flagId, defaultValue, owners, createdAt, expiresAt, description, modificationEffect, dimensions);
}
/** WARNING: public for testing: All flags should be defined in {@link Flags}. */
- public static <T> UnboundJacksonFlag<T> defineJacksonFlag(String flagId, T defaultValue, Class<T> jacksonClass, String description,
+ public static <T> UnboundJacksonFlag<T> defineJacksonFlag(String flagId, T defaultValue, Class<T> jacksonClass, List<String> owners,
+ String createdAt, String expiresAt, String description,
String modificationEffect, FetchVector.Dimension... dimensions) {
return define((id2, defaultValue2, vector2) -> new UnboundJacksonFlag<>(id2, defaultValue2, vector2, jacksonClass),
- flagId, defaultValue, description, modificationEffect, dimensions);
+ 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, FetchVector.Dimension... dimensions) {
return define((fid, dval, fvec) -> new UnboundListFlag<>(fid, dval, elementClass, fvec),
- flagId, defaultValue, description, modificationEffect, dimensions);
+ flagId, defaultValue, owners, createdAt, expiresAt, description, modificationEffect, dimensions);
}
@FunctionalInterface
@@ -429,6 +485,9 @@ public class Flags {
private static <T, U extends UnboundFlag<?, ?, ?>> U define(TypedUnboundFlagFactory<T, U> factory,
String flagId,
T defaultValue,
+ List<String> owners,
+ String createdAt,
+ String expiresAt,
String description,
String modificationEffect,
FetchVector.Dimension[] dimensions) {
@@ -439,11 +498,16 @@ public class Flags {
// (determined by the current major version). Consider not setting VESPA_VERSION if minor = micro = 0.
.with(VESPA_VERSION, Vtag.currentVersion.toFullString());
U unboundFlag = factory.create(id, defaultValue, vector);
- FlagDefinition definition = new FlagDefinition(unboundFlag, description, modificationEffect, dimensions);
+ FlagDefinition definition = new FlagDefinition(
+ unboundFlag, owners, parseDate(createdAt), parseDate(expiresAt), description, modificationEffect, dimensions);
flags.put(id, definition);
return unboundFlag;
}
+ private static Instant parseDate(String rawDate) {
+ return DateTimeFormatter.ISO_DATE.parse(rawDate, LocalDate::from).atStartOfDay().toInstant(ZoneOffset.UTC);
+ }
+
public static List<FlagDefinition> getAllFlags() {
return List.copyOf(flags.values());
}
diff --git a/flags/src/main/java/com/yahoo/vespa/flags/PermanentFlags.java b/flags/src/main/java/com/yahoo/vespa/flags/PermanentFlags.java
new file mode 100644
index 00000000000..c1f0f269f73
--- /dev/null
+++ b/flags/src/main/java/com/yahoo/vespa/flags/PermanentFlags.java
@@ -0,0 +1,57 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.flags;
+
+import java.time.Instant;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+
+/**
+ * Definition for permanent feature flags
+ *
+ * @author bjorncs
+ */
+public class PermanentFlags {
+
+ static final List<String> OWNERS = List.of();
+ static final Instant CREATED_AT = Instant.EPOCH;
+ static final Instant EXPIRES_AT = Instant.MAX;
+
+ private PermanentFlags() {}
+
+ private static UnboundBooleanFlag defineFeatureFlag(
+ String flagId, boolean defaultValue, String description, String modificationEffect, FetchVector.Dimension... dimensions) {
+ return Flags.defineFeatureFlag(flagId, defaultValue, OWNERS, toString(CREATED_AT), toString(EXPIRES_AT), description, modificationEffect, dimensions);
+ }
+
+ private static UnboundStringFlag defineStringFlag(
+ String flagId, String defaultValue, String description, String modificationEffect, FetchVector.Dimension... dimensions) {
+ return Flags.defineStringFlag(flagId, defaultValue, OWNERS, toString(CREATED_AT), toString(EXPIRES_AT), description, modificationEffect, dimensions);
+ }
+
+ private static UnboundIntFlag defineIntFlag(
+ String flagId, int defaultValue, String description, String modificationEffect, FetchVector.Dimension... dimensions) {
+ return Flags.defineIntFlag(flagId, defaultValue, OWNERS, toString(CREATED_AT), toString(EXPIRES_AT), description, modificationEffect, dimensions);
+ }
+
+ private static UnboundLongFlag defineLongFlag(
+ String flagId, long defaultValue, String description, String modificationEffect, FetchVector.Dimension... dimensions) {
+ return Flags.defineLongFlag(flagId, defaultValue, OWNERS, toString(CREATED_AT), toString(EXPIRES_AT), description, modificationEffect, dimensions);
+ }
+
+ private static UnboundDoubleFlag defineDoubleFlag(
+ String flagId, double defaultValue, String description, String modificationEffect, FetchVector.Dimension... dimensions) {
+ return Flags.defineDoubleFlag(flagId, defaultValue, OWNERS, toString(CREATED_AT), toString(EXPIRES_AT), description, modificationEffect, dimensions);
+ }
+
+ private static <T> UnboundJacksonFlag<T> defineJacksonFlag(
+ String flagId, T defaultValue, Class<T> jacksonClass, String description, String modificationEffect, FetchVector.Dimension... dimensions) {
+ return Flags.defineJacksonFlag(flagId, defaultValue, jacksonClass, 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, FetchVector.Dimension... dimensions) {
+ return Flags.defineListFlag(flagId, defaultValue, elementClass, OWNERS, toString(CREATED_AT), toString(EXPIRES_AT), description, modificationEffect, dimensions);
+ }
+
+ private static String toString(Instant instant) { return DateTimeFormatter.ISO_DATE.format(instant); }
+}
diff --git a/flags/src/test/java/com/yahoo/vespa/flags/FlagsTest.java b/flags/src/test/java/com/yahoo/vespa/flags/FlagsTest.java
index 6b043ea5a89..8882a15e526 100644
--- a/flags/src/test/java/com/yahoo/vespa/flags/FlagsTest.java
+++ b/flags/src/test/java/com/yahoo/vespa/flags/FlagsTest.java
@@ -34,7 +34,7 @@ public class FlagsTest {
public void testBoolean() {
final boolean defaultValue = false;
FlagSource source = mock(FlagSource.class);
- BooleanFlag booleanFlag = Flags.defineFeatureFlag("id", defaultValue, "description",
+ BooleanFlag booleanFlag = Flags.defineFeatureFlag("id", defaultValue, List.of("owner"), "1970-01-01", "2100-01-01", "description",
"modification effect", FetchVector.Dimension.ZONE_ID, FetchVector.Dimension.HOSTNAME)
.with(FetchVector.Dimension.ZONE_ID, "a-zone")
.bindTo(source);
@@ -69,29 +69,29 @@ public class FlagsTest {
@Test
public void testString() {
- testGeneric(Flags.defineStringFlag("string-id", "default value", "description",
+ testGeneric(Flags.defineStringFlag("string-id", "default value", List.of("owner"), "1970-01-01", "2100-01-01", "description",
"modification effect", FetchVector.Dimension.ZONE_ID, FetchVector.Dimension.HOSTNAME),
"other value");
}
@Test
public void testInt() {
- testGeneric(Flags.defineIntFlag("int-id", 2, "desc", "mod"), 3);
+ testGeneric(Flags.defineIntFlag("int-id", 2, List.of("owner"), "1970-01-01", "2100-01-01", "desc", "mod"), 3);
}
@Test
public void testLong() {
- testGeneric(Flags.defineLongFlag("long-id", 1L, "desc", "mod"), 2L);
+ testGeneric(Flags.defineLongFlag("long-id", 1L, List.of("owner"), "1970-01-01", "2100-01-01", "desc", "mod"), 2L);
}
@Test
public void testDouble() {
- testGeneric(Flags.defineDoubleFlag("double-id", 3.142, "desc", "mod"), 2.718);
+ testGeneric(Flags.defineDoubleFlag("double-id", 3.142, List.of("owner"), "1970-01-01", "2100-01-01", "desc", "mod"), 2.718);
}
@Test
public void testList() {
- testGeneric(Flags.defineListFlag("list-id", List.of("a"), String.class, "desc", "mod"), List.of("a", "b", "c"));
+ testGeneric(Flags.defineListFlag("list-id", List.of("a"), String.class, List.of("owner"), "1970-01-01", "2100-01-01", "desc", "mod"), List.of("a", "b", "c"));
}
@Test
@@ -102,10 +102,10 @@ public class FlagsTest {
instance.string = "foo";
testGeneric(Flags.defineJacksonFlag("jackson-id", defaultInstance, ExampleJacksonClass.class,
- "description", "modification effect", FetchVector.Dimension.HOSTNAME),
+ List.of("owner"), "1970-01-01", "2100-01-01", "description", "modification effect", FetchVector.Dimension.HOSTNAME),
instance);
- testGeneric(Flags.defineListFlag("jackson-list-id", List.of(defaultInstance), ExampleJacksonClass.class, "desc", "mod"),
+ testGeneric(Flags.defineListFlag("jackson-list-id", List.of(defaultInstance), ExampleJacksonClass.class, List.of("owner"), "1970-01-01", "2100-01-01", "desc", "mod"),
List.of(instance));
}