summaryrefslogtreecommitdiffstats
path: root/flags
diff options
context:
space:
mode:
authorMartin Polden <mpolden@mpolden.no>2019-07-12 15:49:04 +0200
committerMartin Polden <mpolden@mpolden.no>2019-07-12 15:49:04 +0200
commitc10ba446e46fe390b052958e3148cbf9bbb74a9b (patch)
tree7d422465c5c46c8e5ca8fedce4b8e1bb4f8cf68e /flags
parent12b55be208be4199178c7c1387c308fe8d80f947 (diff)
Revert "Decouple flags REST API from config server"
This reverts commit b81b21546cdff92d360cbdf7dda27e6ed7bc7170.
Diffstat (limited to 'flags')
-rw-r--r--flags/pom.xml13
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/http/DefinedFlag.java47
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/http/DefinedFlags.java43
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/http/ErrorResponse.java66
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/http/FlagDataListResponse.java58
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/http/FlagDataResponse.java30
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/http/FlagsHandler.java138
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/http/OKResponse.java19
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/http/SlimeJsonResponse.java38
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/http/V1Response.java46
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/persistence/FlagsDb.java68
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/persistence/package-info.java8
-rw-r--r--flags/src/test/java/com/yahoo/vespa/flags/http/FlagsHandlerTest.java203
-rw-r--r--flags/src/test/java/com/yahoo/vespa/flags/persistence/FlagsDbTest.java56
14 files changed, 1 insertions, 832 deletions
diff --git a/flags/pom.xml b/flags/pom.xml
index 7ef082cc1bc..c1e9eca20ab 100644
--- a/flags/pom.xml
+++ b/flags/pom.xml
@@ -59,18 +59,7 @@
<classifier>no_aop</classifier>
<scope>provided</scope>
</dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>container-dev</artifactId>
- <version>${project.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>zkfacade</artifactId>
- <version>${project.version}</version>
- <scope>provided</scope>
- </dependency>
+
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
diff --git a/flags/src/main/java/com/yahoo/vespa/flags/http/DefinedFlag.java b/flags/src/main/java/com/yahoo/vespa/flags/http/DefinedFlag.java
deleted file mode 100644
index 8234e9df725..00000000000
--- a/flags/src/main/java/com/yahoo/vespa/flags/http/DefinedFlag.java
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2019 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.flags.http;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.ArrayNode;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import com.yahoo.container.jdisc.HttpResponse;
-import com.yahoo.jdisc.Response;
-import com.yahoo.vespa.flags.FlagDefinition;
-import com.yahoo.vespa.flags.json.DimensionHelper;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * @author hakonhall
- */
-public class DefinedFlag extends HttpResponse {
- private static ObjectMapper mapper = new ObjectMapper();
-
- private final FlagDefinition flagDefinition;
-
- public DefinedFlag(FlagDefinition flagDefinition) {
- super(Response.Status.OK);
- this.flagDefinition = flagDefinition;
- }
-
- @Override
- public void render(OutputStream outputStream) throws IOException {
- ObjectNode rootNode = mapper.createObjectNode();
- renderFlagDefinition(flagDefinition, rootNode);
- mapper.writeValue(outputStream, rootNode);
- }
-
- static void renderFlagDefinition(FlagDefinition flagDefinition, ObjectNode definitionNode) {
- definitionNode.put("description", flagDefinition.getDescription());
- definitionNode.put("modification-effect", flagDefinition.getModificationEffect());
- ArrayNode dimensionsNode = definitionNode.putArray("dimensions");
- flagDefinition.getDimensions().forEach(dimension -> dimensionsNode.add(DimensionHelper.toWire(dimension)));
- }
-
- @Override
- public String getContentType() {
- return "application/json";
- }
-
-}
diff --git a/flags/src/main/java/com/yahoo/vespa/flags/http/DefinedFlags.java b/flags/src/main/java/com/yahoo/vespa/flags/http/DefinedFlags.java
deleted file mode 100644
index e1db7dda6e0..00000000000
--- a/flags/src/main/java/com/yahoo/vespa/flags/http/DefinedFlags.java
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.flags.http;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import com.yahoo.container.jdisc.HttpResponse;
-import com.yahoo.jdisc.Response;
-import com.yahoo.vespa.flags.FlagDefinition;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.Comparator;
-import java.util.List;
-
-/**
- * @author hakonhall
- */
-public class DefinedFlags extends HttpResponse {
- private static ObjectMapper mapper = new ObjectMapper();
- private static final Comparator<FlagDefinition> sortByFlagId = Comparator.comparing(flagDefinition -> flagDefinition.getUnboundFlag().id());
-
- private final List<FlagDefinition> flags;
-
- public DefinedFlags(List<FlagDefinition> flags) {
- super(Response.Status.OK);
- this.flags = flags;
- }
-
- @Override
- public void render(OutputStream outputStream) throws IOException {
- ObjectNode rootNode = mapper.createObjectNode();
- flags.stream().sorted(sortByFlagId).forEach(flagDefinition -> {
- ObjectNode definitionNode = rootNode.putObject(flagDefinition.getUnboundFlag().id().toString());
- DefinedFlag.renderFlagDefinition(flagDefinition, definitionNode);
- });
- mapper.writeValue(outputStream, rootNode);
- }
-
- @Override
- public String getContentType() {
- return "application/json";
- }
-}
diff --git a/flags/src/main/java/com/yahoo/vespa/flags/http/ErrorResponse.java b/flags/src/main/java/com/yahoo/vespa/flags/http/ErrorResponse.java
deleted file mode 100644
index 969903093a4..00000000000
--- a/flags/src/main/java/com/yahoo/vespa/flags/http/ErrorResponse.java
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.flags.http;
-
-import com.yahoo.slime.Cursor;
-import com.yahoo.slime.Slime;
-
-import static com.yahoo.jdisc.Response.Status.BAD_REQUEST;
-import static com.yahoo.jdisc.Response.Status.FORBIDDEN;
-import static com.yahoo.jdisc.Response.Status.INTERNAL_SERVER_ERROR;
-import static com.yahoo.jdisc.Response.Status.METHOD_NOT_ALLOWED;
-import static com.yahoo.jdisc.Response.Status.NOT_FOUND;
-import static com.yahoo.jdisc.Response.Status.UNAUTHORIZED;
-
-/**
- * A HTTP JSON response containing an error code and a message
- *
- * @author bratseth
- */
-public class ErrorResponse extends SlimeJsonResponse {
-
- public enum errorCodes {
- NOT_FOUND,
- BAD_REQUEST,
- FORBIDDEN,
- METHOD_NOT_ALLOWED,
- INTERNAL_SERVER_ERROR,
- UNAUTHORIZED
- }
-
- public ErrorResponse(int statusCode, String errorType, String message) {
- super(statusCode, asSlimeMessage(errorType, message));
- }
-
- private static Slime asSlimeMessage(String errorType, String message) {
- Slime slime = new Slime();
- Cursor root = slime.setObject();
- root.setString("error-code", errorType);
- root.setString("message", message);
- return slime;
- }
-
- public static ErrorResponse notFoundError(String message) {
- return new ErrorResponse(NOT_FOUND, errorCodes.NOT_FOUND.name(), message);
- }
-
- public static ErrorResponse internalServerError(String message) {
- return new ErrorResponse(INTERNAL_SERVER_ERROR, errorCodes.INTERNAL_SERVER_ERROR.name(), message);
- }
-
- public static ErrorResponse badRequest(String message) {
- return new ErrorResponse(BAD_REQUEST, errorCodes.BAD_REQUEST.name(), message);
- }
-
- public static ErrorResponse forbidden(String message) {
- return new ErrorResponse(FORBIDDEN, errorCodes.FORBIDDEN.name(), message);
- }
-
- public static ErrorResponse unauthorized(String message) {
- return new ErrorResponse(UNAUTHORIZED, errorCodes.UNAUTHORIZED.name(), message);
- }
-
- public static ErrorResponse methodNotAllowed(String message) {
- return new ErrorResponse(METHOD_NOT_ALLOWED, errorCodes.METHOD_NOT_ALLOWED.name(), message);
- }
-
-}
diff --git a/flags/src/main/java/com/yahoo/vespa/flags/http/FlagDataListResponse.java b/flags/src/main/java/com/yahoo/vespa/flags/http/FlagDataListResponse.java
deleted file mode 100644
index 5af97007997..00000000000
--- a/flags/src/main/java/com/yahoo/vespa/flags/http/FlagDataListResponse.java
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.flags.http;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.ArrayNode;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import com.yahoo.container.jdisc.HttpResponse;
-import com.yahoo.jdisc.Response;
-import com.yahoo.vespa.flags.FlagId;
-import com.yahoo.vespa.flags.json.FlagData;
-import com.yahoo.vespa.flags.json.wire.WireFlagDataList;
-
-import java.io.OutputStream;
-import java.util.Map;
-import java.util.TreeMap;
-
-import static com.yahoo.yolean.Exceptions.uncheck;
-
-/**
- * @author hakonhall
- */
-public class FlagDataListResponse extends HttpResponse {
- private static ObjectMapper mapper = new ObjectMapper();
-
- private final String flagsV1Uri;
- private final TreeMap<FlagId, FlagData> flags;
- private final boolean recursive;
-
- public FlagDataListResponse(String flagsV1Uri, Map<FlagId, FlagData> flags, boolean recursive) {
- super(Response.Status.OK);
- this.flagsV1Uri = flagsV1Uri;
- this.flags = new TreeMap<>(flags);
- this.recursive = recursive;
- }
-
- @Override
- public void render(OutputStream outputStream) {
- if (recursive) {
- WireFlagDataList list = new WireFlagDataList();
- flags.values().forEach(flagData -> list.flags.add(flagData.toWire()));
- list.serializeToOutputStream(outputStream);
- } else {
- ObjectNode rootNode = mapper.createObjectNode();
- ArrayNode flagsArray = rootNode.putArray("flags");
- flags.forEach((flagId, flagData) -> {
- ObjectNode object = flagsArray.addObject();
- object.put("id", flagId.toString());
- object.put("url", flagsV1Uri + "/data/" + flagId.toString());
- });
- uncheck(() -> mapper.writeValue(outputStream, rootNode));
- }
- }
-
- @Override
- public String getContentType() {
- return "application/json";
- }
-}
diff --git a/flags/src/main/java/com/yahoo/vespa/flags/http/FlagDataResponse.java b/flags/src/main/java/com/yahoo/vespa/flags/http/FlagDataResponse.java
deleted file mode 100644
index f6e81e030c7..00000000000
--- a/flags/src/main/java/com/yahoo/vespa/flags/http/FlagDataResponse.java
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.flags.http;
-
-import com.yahoo.container.jdisc.HttpResponse;
-import com.yahoo.jdisc.Response;
-import com.yahoo.vespa.flags.json.FlagData;
-
-import java.io.OutputStream;
-
-/**
- * @author hakonhall
- */
-public class FlagDataResponse extends HttpResponse {
- private final FlagData data;
-
- FlagDataResponse(FlagData data) {
- super(Response.Status.OK);
- this.data = data;
- }
-
- @Override
- public void render(OutputStream outputStream) {
- data.serializeToOutputStream(outputStream);
- }
-
- @Override
- public String getContentType() {
- return "application/json";
- }
-}
diff --git a/flags/src/main/java/com/yahoo/vespa/flags/http/FlagsHandler.java b/flags/src/main/java/com/yahoo/vespa/flags/http/FlagsHandler.java
deleted file mode 100644
index 76f74cbe931..00000000000
--- a/flags/src/main/java/com/yahoo/vespa/flags/http/FlagsHandler.java
+++ /dev/null
@@ -1,138 +0,0 @@
-// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.flags.http;
-
-import com.google.inject.Inject;
-import com.yahoo.container.jdisc.HttpRequest;
-import com.yahoo.container.jdisc.HttpResponse;
-import com.yahoo.container.jdisc.LoggingRequestHandler;
-import com.yahoo.log.LogLevel;
-import com.yahoo.restapi.Path;
-import com.yahoo.vespa.flags.FlagDefinition;
-import com.yahoo.vespa.flags.FlagId;
-import com.yahoo.vespa.flags.Flags;
-import com.yahoo.vespa.flags.json.FlagData;
-import com.yahoo.vespa.flags.persistence.FlagsDb;
-import com.yahoo.yolean.Exceptions;
-
-import java.io.UncheckedIOException;
-import java.net.URI;
-import java.util.Objects;
-
-/**
- * Handles /flags/v1 requests
- *
- * @author hakonhall
- */
-public class FlagsHandler extends LoggingRequestHandler {
-
- private final FlagsDb flagsDb;
-
- @Inject
- public FlagsHandler(LoggingRequestHandler.Context context, FlagsDb flagsDb) {
- super(context);
- this.flagsDb = flagsDb;
- }
-
- @Override
- public HttpResponse handle(HttpRequest request) {
- try {
- switch (request.getMethod()) {
- case GET: return handleGET(request);
- case DELETE: return handleDELETE(request);
- case PUT: return handlePUT(request);
- default: return ErrorResponse.methodNotAllowed("Method '" + request.getMethod() + "' is not supported");
- }
- }
- catch (IllegalArgumentException e) {
- return ErrorResponse.badRequest(Exceptions.toMessageString(e));
- }
- catch (RuntimeException e) {
- log.log(LogLevel.WARNING, "Unexpected error handling '" + request.getUri() + "'", e);
- return ErrorResponse.internalServerError(Exceptions.toMessageString(e));
- }
- }
-
- private HttpResponse handleGET(HttpRequest request) {
- Path path = new Path(request.getUri());
- if (path.matches("/flags/v1")) return new V1Response(flagsV1Uri(request), "data", "defined");
- if (path.matches("/flags/v1/data")) return getFlagDataList(request);
- if (path.matches("/flags/v1/data/{flagId}")) return getFlagData(findFlagId(request, path));
- if (path.matches("/flags/v1/defined")) return new DefinedFlags(Flags.getAllFlags());
- if (path.matches("/flags/v1/defined/{flagId}")) return getDefinedFlag(findFlagId(request, path));
- return ErrorResponse.notFoundError("Nothing at path '" + path + "'");
- }
-
- private HttpResponse handlePUT(HttpRequest request) {
- Path path = new Path(request.getUri());
- if (path.matches("/flags/v1/data/{flagId}")) return putFlagData(request, findFlagId(request, path));
- return ErrorResponse.notFoundError("Nothing at path '" + path + "'");
- }
-
- private HttpResponse handleDELETE(HttpRequest request) {
- Path path = new Path(request.getUri());
- if (path.matches("/flags/v1/data/{flagId}")) return deleteFlagData(findFlagId(request, path));
- return ErrorResponse.notFoundError("Nothing at path '" + path + "'");
- }
-
- private String flagsV1Uri(HttpRequest request) {
- URI uri = request.getUri();
- String port = uri.getPort() < 0 ? "" : ":" + uri.getPort();
- return uri.getScheme() + "://" + uri.getHost() + port + "/flags/v1";
- }
-
- private HttpResponse getDefinedFlag(FlagId flagId) {
- var definedFlag = Flags.getFlag(flagId).map(DefinedFlag::new);
- if (definedFlag.isPresent()) {
- return definedFlag.get();
- }
- return ErrorResponse.notFoundError("Flag " + flagId + " not defined");
- }
-
- private HttpResponse getFlagDataList(HttpRequest request) {
- return new FlagDataListResponse(flagsV1Uri(request), flagsDb.getAllFlags(),
- Objects.equals(request.getProperty("recursive"), "true"));
- }
-
- private HttpResponse getFlagData(FlagId flagId) {
- var data = flagsDb.getValue(flagId).map(FlagDataResponse::new);
- if (data.isPresent()) {
- return data.get();
- }
- return ErrorResponse.notFoundError("Flag " + flagId + " not set");
- }
-
- private HttpResponse putFlagData(HttpRequest request, FlagId flagId) {
- FlagData data;
- try {
- data = FlagData.deserialize(request.getData());
- } catch (UncheckedIOException e) {
- return ErrorResponse.badRequest("Failed to deserialize request data: " + Exceptions.toMessageString(e));
- }
-
- if (!isForce(request)) {
- FlagDefinition definition = Flags.getFlag(flagId).get(); // FlagId has been validated in findFlagId()
- data.validate(definition.getUnboundFlag().serializer());
- }
-
- flagsDb.setValue(flagId, data);
- return new OKResponse();
- }
-
- private HttpResponse deleteFlagData(FlagId flagId) {
- flagsDb.removeValue(flagId);
- return new OKResponse();
- }
-
- private FlagId findFlagId(HttpRequest request, Path path) {
- FlagId flagId = new FlagId(path.get("flagId"));
- if (!isForce(request) && Flags.getFlag(flagId).isEmpty()) {
- throw new IllegalArgumentException("There is no flag '" + flagId + "' (use ?force=true to override)");
- }
- return flagId;
- }
-
- private boolean isForce(HttpRequest request) {
- return Objects.equals(request.getProperty("force"), "true");
- }
-
-}
diff --git a/flags/src/main/java/com/yahoo/vespa/flags/http/OKResponse.java b/flags/src/main/java/com/yahoo/vespa/flags/http/OKResponse.java
deleted file mode 100644
index d094e2d5734..00000000000
--- a/flags/src/main/java/com/yahoo/vespa/flags/http/OKResponse.java
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.flags.http;
-
-import com.yahoo.container.jdisc.EmptyResponse;
-import com.yahoo.jdisc.Response;
-
-/**
- * @author hakonhall
- */
-public class OKResponse extends EmptyResponse {
- public OKResponse() {
- super(Response.Status.OK);
- }
-
- @Override
- public String getContentType() {
- return "application/json";
- }
-}
diff --git a/flags/src/main/java/com/yahoo/vespa/flags/http/SlimeJsonResponse.java b/flags/src/main/java/com/yahoo/vespa/flags/http/SlimeJsonResponse.java
deleted file mode 100644
index dd71795ae43..00000000000
--- a/flags/src/main/java/com/yahoo/vespa/flags/http/SlimeJsonResponse.java
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.flags.http;
-
-import com.yahoo.container.jdisc.HttpResponse;
-import com.yahoo.slime.JsonFormat;
-import com.yahoo.slime.Slime;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * A generic Json response using Slime for JSON encoding
- *
- * @author bratseth
- */
-public class SlimeJsonResponse extends HttpResponse {
-
- private final Slime slime;
-
- public SlimeJsonResponse(Slime slime) {
- super(200);
- this.slime = slime;
- }
-
- public SlimeJsonResponse(int statusCode, Slime slime) {
- super(statusCode);
- this.slime = slime;
- }
-
- @Override
- public void render(OutputStream stream) throws IOException {
- new JsonFormat(true).encode(stream, slime);
- }
-
- @Override
- public String getContentType() { return "application/json"; }
-
-}
diff --git a/flags/src/main/java/com/yahoo/vespa/flags/http/V1Response.java b/flags/src/main/java/com/yahoo/vespa/flags/http/V1Response.java
deleted file mode 100644
index e8ff0bd99a4..00000000000
--- a/flags/src/main/java/com/yahoo/vespa/flags/http/V1Response.java
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.flags.http;
-
-import com.yahoo.container.jdisc.HttpResponse;
-import com.yahoo.jdisc.Response;
-import com.yahoo.slime.Cursor;
-import com.yahoo.slime.JsonFormat;
-import com.yahoo.slime.Slime;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.List;
-
-/**
- * @author hakonhall
- */
-public class V1Response extends HttpResponse {
-
- private final Slime slime;
-
- public V1Response(String flagsV1Uri, String... names) {
- super(Response.Status.OK);
- this.slime = generateBody(flagsV1Uri, List.of(names));
- }
-
- @Override
- public void render(OutputStream stream) throws IOException {
- new JsonFormat(true).encode(stream, slime);
- }
-
- @Override
- public String getContentType() {
- return "application/json";
- }
-
- private static Slime generateBody(String flagsV1Uri, List<String> names) {
- Slime slime = new Slime();
- Cursor root = slime.setObject();
- names.forEach(name -> {
- Cursor data = root.setObject(name);
- data.setString("url", flagsV1Uri + "/" + name);
- });
- return slime;
- }
-
-}
diff --git a/flags/src/main/java/com/yahoo/vespa/flags/persistence/FlagsDb.java b/flags/src/main/java/com/yahoo/vespa/flags/persistence/FlagsDb.java
deleted file mode 100644
index 2ed762f2895..00000000000
--- a/flags/src/main/java/com/yahoo/vespa/flags/persistence/FlagsDb.java
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.flags.persistence;
-
-import com.google.inject.Inject;
-import com.yahoo.path.Path;
-import com.yahoo.vespa.curator.Curator;
-import com.yahoo.vespa.flags.FlagId;
-import com.yahoo.vespa.flags.json.FlagData;
-import org.apache.curator.framework.recipes.cache.ChildData;
-
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
-/**
- * @author hakonhall
- */
-public class FlagsDb {
-
- private static final Path ROOT_PATH = Path.fromString("/flags/v1");
-
- private final Curator curator;
- private final Curator.DirectoryCache cache;
-
- @Inject
- public FlagsDb(Curator curator) {
- this.curator = curator;
- curator.create(ROOT_PATH);
- ExecutorService executorService = Executors.newFixedThreadPool(1);
- this.cache = curator.createDirectoryCache(ROOT_PATH.getAbsolute(), true, false, executorService);
- cache.start();
- }
-
- /** Get the String value of the flag. */
- public Optional<FlagData> getValue(FlagId flagId) {
- return Optional.ofNullable(cache.getCurrentData(getZkPathFor(flagId)))
- .map(ChildData::getData)
- .map(FlagData::deserializeUtf8Json);
- }
-
- /** Set the String value of the flag. */
- public void setValue(FlagId flagId, FlagData data) {
- curator.set(getZkPathFor(flagId), data.serializeToUtf8Json());
- }
-
- /** Get all flags that have been set. */
- public Map<FlagId, FlagData> getAllFlags() {
- List<ChildData> dataList = cache.getCurrentData();
- return dataList.stream()
- .map(ChildData::getData)
- .map(FlagData::deserializeUtf8Json)
- .collect(Collectors.toMap(FlagData::id, Function.identity()));
- }
-
- /** Remove the flag value if it exists. */
- public void removeValue(FlagId flagId) {
- curator.delete(getZkPathFor(flagId));
- }
-
- private static Path getZkPathFor(FlagId flagId) {
- return ROOT_PATH.append(flagId.toString());
- }
-
-}
diff --git a/flags/src/main/java/com/yahoo/vespa/flags/persistence/package-info.java b/flags/src/main/java/com/yahoo/vespa/flags/persistence/package-info.java
deleted file mode 100644
index d4753ed1756..00000000000
--- a/flags/src/main/java/com/yahoo/vespa/flags/persistence/package-info.java
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-/**
- * @author mpolden
- */
-@ExportPackage
-package com.yahoo.vespa.flags.persistence;
-
-import com.yahoo.osgi.annotation.ExportPackage;
diff --git a/flags/src/test/java/com/yahoo/vespa/flags/http/FlagsHandlerTest.java b/flags/src/test/java/com/yahoo/vespa/flags/http/FlagsHandlerTest.java
deleted file mode 100644
index 8ae1008ba22..00000000000
--- a/flags/src/test/java/com/yahoo/vespa/flags/http/FlagsHandlerTest.java
+++ /dev/null
@@ -1,203 +0,0 @@
-// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.flags.http;
-
-import com.yahoo.container.jdisc.HttpRequest;
-import com.yahoo.container.jdisc.HttpResponse;
-import com.yahoo.jdisc.http.HttpRequest.Method;
-import com.yahoo.text.Utf8;
-import com.yahoo.vespa.curator.mock.MockCurator;
-import com.yahoo.vespa.flags.FetchVector;
-import com.yahoo.vespa.flags.FlagId;
-import com.yahoo.vespa.flags.Flags;
-import com.yahoo.vespa.flags.UnboundBooleanFlag;
-import com.yahoo.vespa.flags.persistence.FlagsDb;
-import com.yahoo.yolean.Exceptions;
-import org.junit.Test;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertEquals;
-
-/**
- * @author hakonhall
- */
-public class FlagsHandlerTest {
- private static final UnboundBooleanFlag FLAG1 = Flags.defineFeatureFlag(
- "id1", false, "desc1", "mod1");
- private static final UnboundBooleanFlag FLAG2 = Flags.defineFeatureFlag(
- "id2", true, "desc2", "mod2",
- FetchVector.Dimension.HOSTNAME, FetchVector.Dimension.APPLICATION_ID);
-
- private static final String FLAGS_V1_URL = "https://foo.com:4443/flags/v1";
-
- private final FlagsDb flagsDb = new FlagsDb(new MockCurator());
- private final FlagsHandler handler = new FlagsHandler(FlagsHandler.testOnlyContext(), flagsDb);
-
- @Test
- public void testV1() {
- String expectedResponse = "{" +
- Stream.of("data", "defined")
- .map(name -> "\"" + name + "\":{\"url\":\"https://foo.com:4443/flags/v1/" + name + "\"}")
- .collect(Collectors.joining(",")) +
- "}";
- verifySuccessfulRequest(Method.GET, "", "", expectedResponse);
- verifySuccessfulRequest(Method.GET, "/", "", expectedResponse);
- }
-
- @Test
- public void testDefined() {
- try (Flags.Replacer replacer = Flags.clearFlagsForTesting()) {
- fixUnusedWarning(replacer);
- Flags.defineFeatureFlag("id", false, "desc", "mod", FetchVector.Dimension.HOSTNAME);
- verifySuccessfulRequest(Method.GET, "/defined", "",
- "{\"id\":{\"description\":\"desc\",\"modification-effect\":\"mod\",\"dimensions\":[\"hostname\"]}}");
-
- verifySuccessfulRequest(Method.GET, "/defined/id", "",
- "{\"description\":\"desc\",\"modification-effect\":\"mod\",\"dimensions\":[\"hostname\"]}");
- }
- }
-
- private void fixUnusedWarning(Flags.Replacer replacer) { }
-
- @Test
- public void testData() {
- // PUT flag with ID id1
- verifySuccessfulRequest(Method.PUT, "/data/" + FLAG1.id(),
- "{\n" +
- " \"id\": \"id1\",\n" +
- " \"rules\": [\n" +
- " {\n" +
- " \"value\": true\n" +
- " }\n" +
- " ]\n" +
- "}",
- "");
-
- // GET on ID id1 should return the same as the put.
- verifySuccessfulRequest(Method.GET, "/data/" + FLAG1.id(),
- "", "{\"id\":\"id1\",\"rules\":[{\"value\":true}]}");
-
- // List all flags should list only id1
- verifySuccessfulRequest(Method.GET, "/data",
- "", "{\"flags\":[{\"id\":\"id1\",\"url\":\"https://foo.com:4443/flags/v1/data/id1\"}]}");
-
- // Should be identical to above: suffix / on path should be ignored
- verifySuccessfulRequest(Method.GET, "/data/",
- "", "{\"flags\":[{\"id\":\"id1\",\"url\":\"https://foo.com:4443/flags/v1/data/id1\"}]}");
-
- // Verify absent port => absent in response
- assertThat(handleWithPort(Method.GET, -1, "/data", "", 200),
- is("{\"flags\":[{\"id\":\"id1\",\"url\":\"https://foo.com/flags/v1/data/id1\"}]}"));
-
- // PUT id2
- verifySuccessfulRequest(Method.PUT, "/data/" + FLAG2.id(),
- "{\n" +
- " \"id\": \"id2\",\n" +
- " \"rules\": [\n" +
- " {\n" +
- " \"conditions\": [\n" +
- " {\n" +
- " \"type\": \"whitelist\",\n" +
- " \"dimension\": \"hostname\",\n" +
- " \"values\": [ \"host1\", \"host2\" ]\n" +
- " },\n" +
- " {\n" +
- " \"type\": \"blacklist\",\n" +
- " \"dimension\": \"application\",\n" +
- " \"values\": [ \"app1\", \"app2\" ]\n" +
- " }\n" +
- " ],\n" +
- " \"value\": true\n" +
- " }\n" +
- " ],\n" +
- " \"attributes\": {\n" +
- " \"zone\": \"zone1\"\n" +
- " }\n" +
- "}\n",
- "");
-
- // GET on id2 should now return what was put
- verifySuccessfulRequest(Method.GET, "/data/" + FLAG2.id(), "",
- "{\"id\":\"id2\",\"rules\":[{\"conditions\":[{\"type\":\"whitelist\",\"dimension\":\"hostname\",\"values\":[\"host1\",\"host2\"]},{\"type\":\"blacklist\",\"dimension\":\"application\",\"values\":[\"app1\",\"app2\"]}],\"value\":true}],\"attributes\":{\"zone\":\"zone1\"}}");
-
- // The list of flag data should return id1 and id2
- verifySuccessfulRequest(Method.GET, "/data",
- "",
- "{\"flags\":[{\"id\":\"id1\",\"url\":\"https://foo.com:4443/flags/v1/data/id1\"},{\"id\":\"id2\",\"url\":\"https://foo.com:4443/flags/v1/data/id2\"}]}");
-
- // Putting (overriding) id1 should work silently
- verifySuccessfulRequest(Method.PUT, "/data/" + FLAG1.id(),
- "{\n" +
- " \"id\": \"id1\",\n" +
- " \"rules\": [\n" +
- " {\n" +
- " \"value\": false\n" +
- " }\n" +
- " ]\n" +
- "}\n",
- "");
-
- // Verify PUT
- verifySuccessfulRequest(Method.GET, "/data/" + FLAG1.id(), "", "{\"id\":\"id1\",\"rules\":[{\"value\":false}]}");
-
- // Get all recursivelly displays all flag data
- verifySuccessfulRequest(Method.GET, "/data?recursive=true", "",
- "{\"flags\":[{\"id\":\"id1\",\"rules\":[{\"value\":false}]},{\"id\":\"id2\",\"rules\":[{\"conditions\":[{\"type\":\"whitelist\",\"dimension\":\"hostname\",\"values\":[\"host1\",\"host2\"]},{\"type\":\"blacklist\",\"dimension\":\"application\",\"values\":[\"app1\",\"app2\"]}],\"value\":true}],\"attributes\":{\"zone\":\"zone1\"}}]}");
-
- // Deleting both flags
- verifySuccessfulRequest(Method.DELETE, "/data/" + FLAG1.id(), "", "");
- verifySuccessfulRequest(Method.DELETE, "/data/" + FLAG2.id(), "", "");
-
- // And the list of data flags should now be empty
- verifySuccessfulRequest(Method.GET, "/data", "", "{\"flags\":[]}");
- }
-
- @Test
- public void testForcing() {
- assertThat(handle(Method.PUT, "/data/" + new FlagId("undef"), "", 400),
- containsString("There is no flag 'undef'"));
-
- assertThat(handle(Method.PUT, "/data/" + new FlagId("undef") + "?force=true", "", 400),
- containsString("No content to map due to end-of-input"));
-
- assertThat(handle(Method.PUT, "/data/" + FLAG1.id(), "{}", 400),
- containsString("Flag ID missing"));
-
- assertThat(handle(Method.PUT, "/data/" + FLAG1.id(), "{\"id\": \"id1\",\"rules\": [{\"value\":\"string\"}]}", 400),
- containsString("Wrong type of JsonNode: STRING"));
-
- assertThat(handle(Method.PUT, "/data/" + FLAG1.id() + "?force=true", "{\"id\": \"id1\",\"rules\": [{\"value\":\"string\"}]}", 200),
- is(""));
- }
-
- private void verifySuccessfulRequest(Method method, String pathSuffix, String requestBody, String expectedResponseBody) {
- assertThat(handle(method, pathSuffix, requestBody, 200), is(expectedResponseBody));
- }
-
- private String handle(Method method, String pathSuffix, String requestBody, int expectedStatus) {
- return handleWithPort(method, 4443, pathSuffix, requestBody, expectedStatus);
- }
-
- private String handleWithPort(Method method, int port, String pathSuffix, String requestBody, int expectedStatus) {
- String uri = "https://foo.com" + (port < 0 ? "" : ":" + port) + "/flags/v1" + pathSuffix;
- HttpRequest request = HttpRequest.createTestRequest(uri, method, makeInputStream(requestBody));
- HttpResponse response = handler.handle(request);
- assertEquals(expectedStatus, response.getStatus());
- assertEquals("application/json", response.getContentType());
- var outputStream = new ByteArrayOutputStream();
- Exceptions.uncheck(() -> response.render(outputStream));
- return outputStream.toString(StandardCharsets.UTF_8);
- }
-
- private InputStream makeInputStream(String content) {
- return new ByteArrayInputStream(Utf8.toBytes(content));
- }
-}
diff --git a/flags/src/test/java/com/yahoo/vespa/flags/persistence/FlagsDbTest.java b/flags/src/test/java/com/yahoo/vespa/flags/persistence/FlagsDbTest.java
deleted file mode 100644
index 5102305af90..00000000000
--- a/flags/src/test/java/com/yahoo/vespa/flags/persistence/FlagsDbTest.java
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.flags.persistence;
-
-import com.yahoo.vespa.curator.mock.MockCurator;
-import com.yahoo.vespa.flags.FetchVector;
-import com.yahoo.vespa.flags.FlagId;
-import com.yahoo.vespa.flags.JsonNodeRawFlag;
-import com.yahoo.vespa.flags.json.Condition;
-import com.yahoo.vespa.flags.json.FlagData;
-import com.yahoo.vespa.flags.json.Rule;
-import org.junit.Test;
-
-import java.util.Map;
-import java.util.Optional;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.notNullValue;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-/**
- * @author hakonhall
- */
-public class FlagsDbTest {
- @Test
- public void test() {
- MockCurator curator = new MockCurator();
- FlagsDb db = new FlagsDb(curator);
-
- Condition condition1 = new Condition(Condition.Type.WHITELIST, FetchVector.Dimension.HOSTNAME, "host1");
- Rule rule1 = new Rule(Optional.of(JsonNodeRawFlag.fromJson("13")), condition1);
- FlagId flagId = new FlagId("id");
- FlagData data = new FlagData(flagId, new FetchVector().with(FetchVector.Dimension.ZONE_ID, "zone-a"), rule1);
- db.setValue(flagId, data);
-
- Optional<FlagData> dataCopy = db.getValue(flagId);
- assertTrue(dataCopy.isPresent());
-
- assertEquals("{\"id\":\"id\",\"rules\":[{\"conditions\":[{\"type\":\"whitelist\",\"dimension\":\"hostname\"," +
- "\"values\":[\"host1\"]}],\"value\":13}],\"attributes\":{\"zone\":\"zone-a\"}}",
- dataCopy.get().serializeToJson());
-
- FlagId flagId2 = new FlagId("id2");
- FlagData data2 = new FlagData(flagId2, new FetchVector().with(FetchVector.Dimension.ZONE_ID, "zone-a"), rule1);
- db.setValue(flagId2, data2);
- Map<FlagId, FlagData> flags = db.getAllFlags();
- assertThat(flags.size(), equalTo(2));
- assertThat(flags.get(flagId), notNullValue());
- assertThat(flags.get(flagId2), notNullValue());
-
- db.removeValue(flagId2);
- assertFalse(db.getValue(flagId2).isPresent());
- }
-}