From 55b72a9d324cbab8492db6b5556f69214f05701a Mon Sep 17 00:00:00 2001 From: Harald Musum Date: Wed, 31 Jan 2018 09:37:19 +0100 Subject: Add handler that does prepare and activate in one call --- .../vespa/config/server/ApplicationRepository.java | 7 ++ .../vespa/config/server/http/SessionHandler.java | 13 ++++ .../server/http/v2/SessionActiveHandler.java | 15 ----- .../http/v2/SessionPrepareAndActivateHandler.java | 68 +++++++++++++++++++ .../http/v2/SessionPrepareAndActivateResponse.java | 42 ++++++++++++ .../main/resources/configserver-app/services.xml | 4 ++ .../src/test/apps/app-jdisc-only/hosts.xml | 7 ++ .../apps/app-jdisc-only/searchdefinitions/music.sd | 57 ++++++++++++++++ .../src/test/apps/app-jdisc-only/services.xml | 22 ++++++ .../config/server/ApplicationRepositoryTest.java | 78 ++++++++++++++++++++++ 10 files changed, 298 insertions(+), 15 deletions(-) create mode 100644 configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/SessionPrepareAndActivateHandler.java create mode 100644 configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/SessionPrepareAndActivateResponse.java create mode 100644 configserver/src/test/apps/app-jdisc-only/hosts.xml create mode 100644 configserver/src/test/apps/app-jdisc-only/searchdefinitions/music.sd create mode 100644 configserver/src/test/apps/app-jdisc-only/services.xml create mode 100644 configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java (limited to 'configserver') diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java b/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java index 633207f1fcb..967cb06a13a 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java @@ -353,6 +353,13 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye return new PrepareResult(sessionId, actions, deployLog); } + public PrepareResult prepareAndActivate(Tenant tenant, long sessionId, PrepareParams prepareParams, + boolean ignoreLockFailure, boolean ignoreSessionStaleFailure, Instant now) { + PrepareResult result = prepare(tenant, sessionId, prepareParams, now); + activate(tenant, sessionId, prepareParams.getTimeoutBudget(), ignoreLockFailure, ignoreSessionStaleFailure); + return result; + } + private List listApplicationIds(Tenant tenant) { TenantApplications applicationRepo = tenant.getApplicationRepo(); return applicationRepo.listApplications(); diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/SessionHandler.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/SessionHandler.java index 4d25ba9eeba..f6547288702 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/SessionHandler.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/SessionHandler.java @@ -83,4 +83,17 @@ public class SessionHandler extends HttpHandler { return deployLog; } + protected static boolean shouldIgnoreLockFailure(HttpRequest request) { + return request.getBooleanProperty("force"); + } + + /** + * True if this request should ignore activation failure because the session was made from an active session that is not active now + * @param request a {@link com.yahoo.container.jdisc.HttpRequest} + * @return true if ignore failure + */ + protected static boolean shouldIgnoreSessionStaleFailure(HttpRequest request) { + return request.getBooleanProperty("force"); + } + } diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/SessionActiveHandler.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/SessionActiveHandler.java index b2330ebd97f..fc2b4c62dde 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/SessionActiveHandler.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/SessionActiveHandler.java @@ -2,7 +2,6 @@ package com.yahoo.vespa.config.server.http.v2; import java.time.Duration; -import java.util.concurrent.Executor; import com.google.inject.Inject; import com.yahoo.config.application.api.ApplicationMetaData; @@ -11,7 +10,6 @@ import com.yahoo.config.provision.TenantName; import com.yahoo.config.provision.Zone; import com.yahoo.container.jdisc.HttpRequest; import com.yahoo.container.jdisc.HttpResponse; -import com.yahoo.container.logging.AccessLog; import com.yahoo.vespa.config.server.ApplicationRepository; import com.yahoo.vespa.config.server.tenant.Tenant; import com.yahoo.vespa.config.server.tenant.Tenants; @@ -56,17 +54,4 @@ public class SessionActiveHandler extends SessionHandler { return new SessionActiveResponse(metaData.getSlime(), request, applicationId, sessionId, zone); } - private boolean shouldIgnoreLockFailure(HttpRequest request) { - return request.getBooleanProperty("force"); - } - - /** - * True if this request should ignore activation failure because the session was made from an active session that is not active now - * @param request a {@link com.yahoo.container.jdisc.HttpRequest} - * @return true if ignore failure - */ - private boolean shouldIgnoreSessionStaleFailure(HttpRequest request) { - return request.getBooleanProperty("force"); - } - } diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/SessionPrepareAndActivateHandler.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/SessionPrepareAndActivateHandler.java new file mode 100644 index 00000000000..dc88a6ddddc --- /dev/null +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/SessionPrepareAndActivateHandler.java @@ -0,0 +1,68 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config.server.http.v2; + +import com.google.inject.Inject; +import com.yahoo.cloud.config.ConfigserverConfig; +import com.yahoo.config.provision.TenantName; +import com.yahoo.config.provision.Zone; +import com.yahoo.container.jdisc.HttpRequest; +import com.yahoo.container.jdisc.HttpResponse; +import com.yahoo.vespa.config.server.ApplicationRepository; +import com.yahoo.vespa.config.server.http.SessionHandler; +import com.yahoo.vespa.config.server.http.Utils; +import com.yahoo.vespa.config.server.session.PrepareParams; +import com.yahoo.vespa.config.server.tenant.Tenant; +import com.yahoo.vespa.config.server.tenant.Tenants; + +import java.time.Duration; +import java.time.Instant; + +/** + * A handler that prepares and activates a session/application given by a session id in the request. + * + * @author hmusum + */ +public class SessionPrepareAndActivateHandler extends SessionHandler { + + private final Tenants tenants; + private final Duration zookeeperBarrierTimeout; + private final Zone zone; + + @Inject + public SessionPrepareAndActivateHandler(Context ctx, + ApplicationRepository applicationRepository, + Tenants tenants, + ConfigserverConfig configserverConfig, + Zone zone) { + super(ctx, applicationRepository); + this.tenants = tenants; + this.zookeeperBarrierTimeout = Duration.ofSeconds(configserverConfig.zookeeper().barrierTimeout()); + this.zone = zone; + } + + @Override + protected HttpResponse handlePUT(HttpRequest request) { + Tenant tenant = getExistingTenant(request); + TenantName tenantName = tenant.getName(); + long sessionId = getSessionIdV2(request); + PrepareParams prepareParams = PrepareParams.fromHttpRequest(request, tenantName, zookeeperBarrierTimeout); + + PrepareResult result = applicationRepository.prepareAndActivate(tenant, sessionId, prepareParams, + shouldIgnoreLockFailure(request), + shouldIgnoreSessionStaleFailure(request), + Instant.now()); + return new SessionPrepareAndActivateResponse(result, tenantName, request, prepareParams.getApplicationId(), zone); + } + + @Override + public Duration getTimeout() { + return zookeeperBarrierTimeout.plus(Duration.ofSeconds(10)); + } + + private Tenant getExistingTenant(HttpRequest request) { + TenantName tenantName = Utils.getTenantNameFromSessionRequest(request); + Utils.checkThatTenantExists(tenants, tenantName); + return tenants.getTenant(tenantName); + } + +} diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/SessionPrepareAndActivateResponse.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/SessionPrepareAndActivateResponse.java new file mode 100644 index 00000000000..4665a33c647 --- /dev/null +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/SessionPrepareAndActivateResponse.java @@ -0,0 +1,42 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config.server.http.v2; + +import com.yahoo.config.provision.ApplicationId; +import com.yahoo.config.provision.TenantName; +import com.yahoo.config.provision.Zone; +import com.yahoo.container.jdisc.HttpRequest; +import com.yahoo.slime.Slime; +import com.yahoo.vespa.config.server.configchange.ConfigChangeActions; +import com.yahoo.vespa.config.server.configchange.ConfigChangeActionsSlimeConverter; +import com.yahoo.vespa.config.server.http.SessionResponse; + +/** + * Creates a response for SessionPrepareHandler. + * + * @author hmusum + */ +class SessionPrepareAndActivateResponse extends SessionResponse { + + SessionPrepareAndActivateResponse(PrepareResult result, TenantName tenantName, HttpRequest request, + ApplicationId applicationId, Zone zone) { + this(result.deployLog(), tenantName, request, result.sessionId(), result.configChangeActions(), + zone, applicationId); + } + + private SessionPrepareAndActivateResponse(Slime deployLog, TenantName tenantName, HttpRequest request, + long sessionId, ConfigChangeActions actions, Zone zone, + ApplicationId applicationId) { + super(deployLog, deployLog.get()); + String message = "Session " + sessionId + " for tenant '" + tenantName.value() + "' prepared and activated."; + this.root.setString("tenant", tenantName.value()); + root.setString("url", "http://" + request.getHost() + ":" + request.getPort() + + "/application/v2/tenant/" + tenantName + + "/application/" + applicationId.application().value() + + "/environment/" + zone.environment().value() + + "/region/" + zone.region().value() + + "/instance/" + applicationId.instance().value()); + root.setString("message", message); + new ConfigChangeActionsSlimeConverter(actions).toSlime(root); + } + +} diff --git a/configserver/src/main/resources/configserver-app/services.xml b/configserver/src/main/resources/configserver-app/services.xml index 52f63ad398c..bf38eedc97f 100644 --- a/configserver/src/main/resources/configserver-app/services.xml +++ b/configserver/src/main/resources/configserver-app/services.xml @@ -106,6 +106,10 @@ http://*/application/v2/tenant/*/session/*/active https://*/application/v2/tenant/*/session/*/active + + http://*/application/v2/tenant/*/session/*/prepareandactivate + https://*/application/v2/tenant/*/session/*/prepareandactivate + http://*/application/v2/tenant/*/session/*/content/* https://*/application/v2/tenant/*/session/*/content/* diff --git a/configserver/src/test/apps/app-jdisc-only/hosts.xml b/configserver/src/test/apps/app-jdisc-only/hosts.xml new file mode 100644 index 00000000000..f4256c9fc81 --- /dev/null +++ b/configserver/src/test/apps/app-jdisc-only/hosts.xml @@ -0,0 +1,7 @@ + + + + + node1 + + diff --git a/configserver/src/test/apps/app-jdisc-only/searchdefinitions/music.sd b/configserver/src/test/apps/app-jdisc-only/searchdefinitions/music.sd new file mode 100644 index 00000000000..2e40523a6d9 --- /dev/null +++ b/configserver/src/test/apps/app-jdisc-only/searchdefinitions/music.sd @@ -0,0 +1,57 @@ +# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +# A basic search definition - called music, should be saved to music.sd +search music { + + # It contains one document type only - called music as well + document music { + + field title type string { + indexing: summary | index # How this field should be indexed + # index-to: title, default # Create two indexes + weight: 75 # Ranking importancy of this field, used by the built in nativeRank feature + header + } + + field artist type string { + indexing: summary | attribute | index + # index-to: artist, default + + weight: 25 + header + } + + field year type int { + indexing: summary | attribute + header + } + + # Increase query + field popularity type int { + indexing: summary | attribute + body + } + + field url type uri { + indexing: summary | index + header + } + + } + + rank-profile default inherits default { + first-phase { + expression: nativeRank(title,artist) + attribute(popularity) + } + + } + + rank-profile textmatch inherits default { + first-phase { + expression: nativeRank(title,artist) + } + + } + + + +} diff --git a/configserver/src/test/apps/app-jdisc-only/services.xml b/configserver/src/test/apps/app-jdisc-only/services.xml new file mode 100644 index 00000000000..755ca1fd585 --- /dev/null +++ b/configserver/src/test/apps/app-jdisc-only/services.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + someval + + + + + + + + + diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java new file mode 100644 index 00000000000..52c711e3157 --- /dev/null +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java @@ -0,0 +1,78 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config.server; + +import com.yahoo.config.model.application.provider.FilesApplicationPackage; +import com.yahoo.config.provision.Provisioner; +import com.yahoo.config.provision.TenantName; +import com.yahoo.vespa.config.server.http.SessionHandlerTest; +import com.yahoo.vespa.config.server.http.v2.PrepareResult; +import com.yahoo.vespa.config.server.session.PrepareParams; +import com.yahoo.vespa.config.server.tenant.Tenant; +import com.yahoo.vespa.config.server.tenant.Tenants; +import com.yahoo.vespa.curator.Curator; +import com.yahoo.vespa.curator.mock.MockCurator; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; +import java.time.Clock; +import java.time.Duration; +import java.time.Instant; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * @author hmusum + */ +public class ApplicationRepositoryTest { + + private final static File testApp = new File("src/test/apps/app"); + private final static File testAppJdiscOnly = new File("src/test/apps/app-jdisc-only"); + private final static TenantName tenantName = TenantName.from("test"); + private final static Clock clock = Clock.systemUTC(); + + private Tenant tenant; + private ApplicationRepository applicationRepository; + private TimeoutBudget timeoutBudget; + + @Before + public void setup() { + Curator curator = new MockCurator(); + Tenants tenants = new Tenants(new TestComponentRegistry.Builder() + .curator(curator) + .build()); + tenants.addTenant(tenantName); + tenant = tenants.getTenant(tenantName); + Provisioner provisioner = new SessionHandlerTest.MockProvisioner(); + applicationRepository = new ApplicationRepository(tenants, provisioner, clock); + timeoutBudget = new TimeoutBudget(clock, Duration.ofSeconds(60)); + } + + @Test + public void prepareAndActivate() throws IOException { + PrepareResult result = prepareAndActivateApp(testApp); + assertTrue(result.configChangeActions().getRefeedActions().isEmpty()); + assertTrue(result.configChangeActions().getRestartActions().isEmpty()); + } + + @Test + public void prepareAndActivateWithRestart() throws IOException { + prepareAndActivateApp(testAppJdiscOnly); + PrepareResult result = prepareAndActivateApp(testApp); + assertTrue(result.configChangeActions().getRefeedActions().isEmpty()); + assertFalse(result.configChangeActions().getRestartActions().isEmpty()); + } + + private PrepareResult prepareAndActivateApp(File application) throws IOException { + FilesApplicationPackage appDir = FilesApplicationPackage.fromFile(application); + long sessionId = applicationRepository.createSession(tenant, timeoutBudget, appDir.getAppDir(), "testapp"); + return applicationRepository.prepareAndActivate(tenant, sessionId, prepareParams(), false, false, Instant.now()); + } + + private PrepareParams prepareParams() { + return new PrepareParams.Builder().build(); + } + +} -- cgit v1.2.3