summaryrefslogtreecommitdiffstats
path: root/configserver
diff options
context:
space:
mode:
authorHarald Musum <musum@yahooinc.com>2023-03-07 12:53:56 +0100
committerHarald Musum <musum@yahooinc.com>2023-03-07 12:53:56 +0100
commitc49fb0a11c12fad7d87a1d949e1c128767922c61 (patch)
tree92a932277bd40808ab09ddf66b50bb31af273741 /configserver
parent6a3858bbff93e2075999aebafdc26105f8f21b4b (diff)
Add request property requiredGeneration to http get config requests
Response should be 412 Precondition Failed if config found does not have same generations as property.
Diffstat (limited to 'configserver')
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpConfigRequest.java33
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpErrorResponse.java8
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpHandler.java2
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/http/NotFoundException.java1
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/http/PreconditionFailedException.java16
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/HttpGetConfigHandler.java14
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/HttpGetConfigHandlerTest.java15
7 files changed, 73 insertions, 16 deletions
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpConfigRequest.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpConfigRequest.java
index 3baa57b2b01..e043afdbf43 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpConfigRequest.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpConfigRequest.java
@@ -30,15 +30,18 @@ import java.util.Set;
*/
public class HttpConfigRequest implements GetConfigRequest, TenantRequest {
- private static final String HTTP_PROPERTY_NOCACHE = "noCache";
+ private static final String NOCACHE = "noCache";
+ private static final String REQUIRED_GENERATION = "requiredGeneration";
private final ConfigKey<?> key;
private final ApplicationId appId;
private final boolean noCache;
+ private final Optional<Long> requiredGeneration;
- private HttpConfigRequest(ConfigKey<?> key, ApplicationId appId, boolean noCache) {
+ private HttpConfigRequest(ConfigKey<?> key, ApplicationId appId, boolean noCache, Optional<Long> requiredGeneration) {
this.key = key;
this.appId = appId;
this.noCache = noCache;
+ this.requiredGeneration = requiredGeneration;
}
private static ConfigKey<?> fromRequestV1(HttpRequest req) {
@@ -59,7 +62,10 @@ public class HttpConfigRequest implements GetConfigRequest, TenantRequest {
}
public static HttpConfigRequest createFromRequestV1(HttpRequest req) {
- return new HttpConfigRequest(fromRequestV1(req), ApplicationId.defaultId(), req.getBooleanProperty(HTTP_PROPERTY_NOCACHE));
+ return new HttpConfigRequest(fromRequestV1(req),
+ ApplicationId.defaultId(),
+ req.getBooleanProperty(NOCACHE),
+ requiredGeneration(req));
}
public static HttpConfigRequest createFromRequestV2(HttpRequest req) {
@@ -89,7 +95,8 @@ public class HttpConfigRequest implements GetConfigRequest, TenantRequest {
cNamespace = nns.second;
return new HttpConfigRequest(new ConfigKey<>(cName, cId, cNamespace),
new ApplicationId.Builder().applicationName(application).tenant(tenant).build(),
- req.getBooleanProperty(HTTP_PROPERTY_NOCACHE));
+ req.getBooleanProperty(NOCACHE),
+ requiredGeneration(req));
}
// The URL pattern with full app id given
@@ -117,7 +124,10 @@ public class HttpConfigRequest implements GetConfigRequest, TenantRequest {
.applicationName(application)
.instanceName(instance)
.build();
- return new HttpConfigRequest(new ConfigKey<>(cName, cId, cNamespace), appId, req.getBooleanProperty(HTTP_PROPERTY_NOCACHE));
+ return new HttpConfigRequest(new ConfigKey<>(cName, cId, cNamespace),
+ appId,
+ req.getBooleanProperty(NOCACHE),
+ requiredGeneration(req));
}
/**
@@ -144,7 +154,11 @@ public class HttpConfigRequest implements GetConfigRequest, TenantRequest {
public static void throwModelNotReady() {
throw new NotFoundException("Config not available, verify that an application package has been deployed and activated.");
}
-
+
+ public static void throwPreconditionFailed(long requiredGeneration) {
+ throw new PreconditionFailedException("Config for required generation " + requiredGeneration + " could not be found.");
+ }
+
/**
* If the given config is produced by the model at all
*
@@ -199,4 +213,11 @@ public class HttpConfigRequest implements GetConfigRequest, TenantRequest {
@Override
public PayloadChecksums configPayloadChecksums() { return PayloadChecksums.empty(); }
+ public Optional<Long> requiredGeneration() { return requiredGeneration; }
+
+ static Optional<Long> requiredGeneration(HttpRequest req) {
+ Optional<String> requiredGeneration = Optional.ofNullable(req.getProperty(REQUIRED_GENERATION));
+ return requiredGeneration.map(Long::parseLong);
+ }
+
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpErrorResponse.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpErrorResponse.java
index 40ce16145e7..3b5269cdf11 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpErrorResponse.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpErrorResponse.java
@@ -16,6 +16,7 @@ import static com.yahoo.jdisc.Response.Status.CONFLICT;
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.PRECONDITION_FAILED;
import static com.yahoo.jdisc.Response.Status.REQUEST_TIMEOUT;
/**
@@ -51,7 +52,8 @@ public class HttpErrorResponse extends HttpResponse {
CERTIFICATE_NOT_READY,
LOAD_BALANCER_NOT_READY,
CONFIG_NOT_CONVERGED,
- REINDEXING_STATUS_UNAVAILABLE
+ REINDEXING_STATUS_UNAVAILABLE,
+ PRECONDITION_FAILED
}
public static HttpErrorResponse notFoundError(String msg) {
@@ -114,6 +116,10 @@ public class HttpErrorResponse extends HttpResponse {
return new HttpErrorResponse(CONFLICT, ErrorCode.REINDEXING_STATUS_UNAVAILABLE.name(), msg);
}
+ public static HttpResponse preconditionFailed(String msg) {
+ return new HttpErrorResponse(PRECONDITION_FAILED, ErrorCode.PRECONDITION_FAILED.name(), msg);
+ }
+
@Override
public void render(OutputStream stream) throws IOException {
new JsonFormat(true).encode(stream, slime);
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpHandler.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpHandler.java
index dc3a05e65f9..a0e814f32d8 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpHandler.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpHandler.java
@@ -71,6 +71,8 @@ public class HttpHandler extends ThreadedHttpRequestHandler {
return HttpErrorResponse.loadBalancerNotReady(getMessage(e, request));
} catch (ReindexingStatusException e) {
return HttpErrorResponse.reindexingStatusUnavailable(getMessage(e, request));
+ } catch (PreconditionFailedException e) {
+ return HttpErrorResponse.preconditionFailed(getMessage(e, request));
} catch (Exception e) {
log.log(Level.WARNING, "Unexpected exception handling a config server request", e);
return HttpErrorResponse.internalServerError(getMessage(e, request));
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/NotFoundException.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/NotFoundException.java
index eb008de6ee5..688890c75b2 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/NotFoundException.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/NotFoundException.java
@@ -5,7 +5,6 @@ package com.yahoo.vespa.config.server.http;
* Exception that will create a http response with NOT_FOUND response code (404)
*
* @author hmusum
- * @since 5.1.17
*/
public class NotFoundException extends RuntimeException {
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/PreconditionFailedException.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/PreconditionFailedException.java
new file mode 100644
index 00000000000..ef3924425c2
--- /dev/null
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/PreconditionFailedException.java
@@ -0,0 +1,16 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.config.server.http;
+
+/**
+ * Exception that will create a http response with NOT_FOUND response code (404)
+ *
+ * @author hmusum
+ */
+public class PreconditionFailedException extends RuntimeException {
+
+ public PreconditionFailedException(String message) {
+ super(message);
+ }
+
+}
+
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/HttpGetConfigHandler.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/HttpGetConfigHandler.java
index 3ab3df99a10..0389b2a6c98 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/HttpGetConfigHandler.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/HttpGetConfigHandler.java
@@ -4,31 +4,27 @@ package com.yahoo.vespa.config.server.http.v2;
import com.yahoo.component.annotation.Inject;
import com.yahoo.container.jdisc.HttpRequest;
import com.yahoo.container.jdisc.HttpResponse;
-import java.util.logging.Level;
import com.yahoo.vespa.config.protocol.ConfigResponse;
import com.yahoo.vespa.config.server.RequestHandler;
-import com.yahoo.vespa.config.server.http.v2.request.HttpConfigRequests;
-import com.yahoo.vespa.config.server.tenant.TenantRepository;
import com.yahoo.vespa.config.server.http.HttpConfigRequest;
import com.yahoo.vespa.config.server.http.HttpConfigResponse;
import com.yahoo.vespa.config.server.http.HttpHandler;
-
+import com.yahoo.vespa.config.server.http.v2.request.HttpConfigRequests;
+import com.yahoo.vespa.config.server.tenant.TenantRepository;
import java.util.Optional;
+import java.util.logging.Level;
/**
* HTTP handler for a getConfig operation
*
* @author Ulf Lilleengen
- * @since 5.1
*/
public class HttpGetConfigHandler extends HttpHandler {
private final TenantRepository tenantRepository;
@Inject
- public HttpGetConfigHandler(HttpHandler.Context ctx,
- TenantRepository tenantRepository)
- {
+ public HttpGetConfigHandler(HttpHandler.Context ctx, TenantRepository tenantRepository) {
super(ctx);
this.tenantRepository = tenantRepository;
}
@@ -45,6 +41,8 @@ public class HttpGetConfigHandler extends HttpHandler {
log.log(Level.FINE, () -> "nocache=" + request.noCache());
ConfigResponse config = requestHandler.resolveConfig(request.getApplicationId(), request, Optional.empty());
if (config == null) HttpConfigRequest.throwModelNotReady();
+ if (request.requiredGeneration().isPresent() && request.requiredGeneration().get() != config.getGeneration())
+ HttpConfigRequest.throwPreconditionFailed(request.requiredGeneration().get());
return config;
}
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/HttpGetConfigHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/HttpGetConfigHandlerTest.java
index a0b5b879e45..401bd1ae55b 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/HttpGetConfigHandlerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/HttpGetConfigHandlerTest.java
@@ -25,6 +25,7 @@ import org.junit.rules.TemporaryFolder;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
+import java.util.Map;
import static com.yahoo.jdisc.Response.Status.BAD_REQUEST;
import static com.yahoo.jdisc.Response.Status.NOT_FOUND;
@@ -131,6 +132,20 @@ public class HttpGetConfigHandlerTest {
assertTrue(renderedString, renderedString.startsWith(expected));
}
+ @Test
+ public void require_that_required_generation_property_works() throws IOException {
+ HttpRequest request = HttpRequest.createTestRequest(configUri, GET, null, Map.of("requiredGeneration", "2"));
+ HttpResponse response = handler.handle(request);
+ String renderedString = SessionHandlerTest.getRenderedString(response);
+ assertTrue(renderedString, renderedString.startsWith(expected));
+
+ request = HttpRequest.createTestRequest(configUri, GET, null, Map.of("requiredGeneration", "3"));
+ response = handler.handle(request);
+ assertEquals(412, response.getStatus());
+ renderedString = SessionHandlerTest.getRenderedString(response);
+ assertEquals("{\"error-code\":\"PRECONDITION_FAILED\",\"message\":\"Config for required generation 3 could not be found.\"}", renderedString);
+ }
+
private PrepareParams prepareParams() {
return new PrepareParams.Builder().applicationId(applicationId).build();
}