diff options
author | Harald Musum <musum@oath.com> | 2018-02-05 14:33:31 +0100 |
---|---|---|
committer | Harald Musum <musum@oath.com> | 2018-02-05 14:33:31 +0100 |
commit | 9da3356c15e5c4f9f148716a1d773a35435df5ae (patch) | |
tree | 44fd2d5b7046bb703cfd5ea00f414be80038aacd /configserver | |
parent | c7cbe1364113698465758840e51b3acf0390903c (diff) |
Support POST with app for prepareandactivate
Start move to ApplicationApiHandler
Diffstat (limited to 'configserver')
8 files changed, 71 insertions, 116 deletions
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 967cb06a13a..96b9651a55f 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 @@ -1,6 +1,7 @@ // Copyright 2017 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.google.common.io.Files; import com.google.inject.Inject; import com.yahoo.cloud.config.ConfigserverConfig; import com.yahoo.component.Version; @@ -15,6 +16,7 @@ import com.yahoo.config.provision.HostFilter; import com.yahoo.config.provision.Provisioner; import com.yahoo.config.provision.TenantName; import com.yahoo.container.jdisc.HttpResponse; +import com.yahoo.io.IOUtils; import com.yahoo.log.LogLevel; import com.yahoo.path.Path; import com.yahoo.slime.Slime; @@ -31,6 +33,7 @@ import com.yahoo.vespa.config.server.configchange.RefeedActions; import com.yahoo.vespa.config.server.configchange.RestartActions; import com.yahoo.vespa.config.server.deploy.DeployHandlerLogger; import com.yahoo.vespa.config.server.deploy.Deployment; +import com.yahoo.vespa.config.server.http.CompressedApplicationInputStream; import com.yahoo.vespa.config.server.http.SimpleHttpFetcher; import com.yahoo.vespa.config.server.http.v2.PrepareResult; import com.yahoo.vespa.config.server.provision.HostProvisionerProvider; @@ -46,6 +49,8 @@ import com.yahoo.vespa.config.server.tenant.Tenant; import com.yahoo.vespa.config.server.tenant.Tenants; import java.io.File; +import java.io.IOException; +import java.io.InputStream; import java.net.URI; import java.time.Clock; import java.time.Duration; @@ -360,6 +365,23 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye return result; } + public PrepareResult createSessionAndPrepareAndActivate(Tenant tenant, InputStream in, String contentType, TimeoutBudget timeoutBudget, + String name, PrepareParams prepareParams, + boolean ignoreLockFailure, boolean ignoreSessionStaleFailure, + Instant now) { + long sessionId = createSession(tenant, timeoutBudget, in, contentType, name); + return prepareAndActivate(tenant, sessionId, prepareParams, ignoreLockFailure, ignoreSessionStaleFailure, now); + } + + private File decompressApplication(InputStream in, String contentType, File tempDir) { + try (CompressedApplicationInputStream application = + CompressedApplicationInputStream.createFromCompressedStream(in, contentType)) { + return application.decompress(tempDir); + } catch (IOException e) { + throw new IllegalArgumentException("Unable to decompress data in body", e); + } + } + private List<ApplicationId> listApplicationIds(Tenant tenant) { TenantApplications applicationRepo = tenant.getApplicationRepo(); return applicationRepo.listApplications(); @@ -375,6 +397,13 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye return session.getSessionId(); } + public long createSession(Tenant tenant, TimeoutBudget timeoutBudget, InputStream in, String contentType, String applicationName) { + File tempDir = Files.createTempDir(); + long sessionId = createSession(tenant, timeoutBudget, decompressApplication(in, contentType, tempDir), applicationName); + cleanupApplicationDirectory(tempDir, logger); + return sessionId; + } + public long createSession(Tenant tenant, TimeoutBudget timeoutBudget, File applicationDirectory, String applicationName) { LocalSessionRepo localSessionRepo = tenant.getLocalSessionRepo(); SessionFactory sessionFactory = tenant.getSessionFactory(); @@ -391,6 +420,13 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye deploymentExecutor.awaitTermination(365, TimeUnit.DAYS); // Timeout should never happen } + private static void cleanupApplicationDirectory(File tempDir, DeployLogger logger) { + logger.log(LogLevel.DEBUG, "Deleting tmp dir '" + tempDir + "'"); + if (!IOUtils.recursiveDeleteDir(tempDir)) { + logger.log(LogLevel.WARNING, "Not able to delete tmp dir '" + tempDir + "'"); + } + } + private void redeployApplication(ApplicationId applicationId, Deployer deployer, ExecutorService deploymentExecutor) { log.log(LogLevel.DEBUG, () -> "Redeploying " + applicationId); deployer.deployFromLocalActive(applicationId) diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/CompressedApplicationInputStream.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/CompressedApplicationInputStream.java index 7cbd5b8a15a..548d2f0a43c 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/CompressedApplicationInputStream.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/CompressedApplicationInputStream.java @@ -4,7 +4,7 @@ package com.yahoo.vespa.config.server.http; import com.google.common.io.ByteStreams; import com.google.common.io.Files; import com.yahoo.log.LogLevel; -import com.yahoo.vespa.config.server.http.v2.SessionCreateHandler; +import com.yahoo.vespa.config.server.http.v2.ApplicationApiHandler; import org.apache.commons.compress.archivers.ArchiveEntry; import org.apache.commons.compress.archivers.ArchiveInputStream; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; @@ -48,10 +48,10 @@ public class CompressedApplicationInputStream implements AutoCloseable { private static ArchiveInputStream getArchiveInputStream(InputStream is, String contentTypeHeader) throws IOException { ArchiveInputStream ais; switch (contentTypeHeader) { - case SessionCreateHandler.APPLICATION_X_GZIP: + case ApplicationApiHandler.APPLICATION_X_GZIP: ais = new TarArchiveInputStream(new GZIPInputStream(is)); break; - case SessionCreateHandler.APPLICATION_ZIP: + case ApplicationApiHandler.APPLICATION_ZIP: ais = new ZipArchiveInputStream(is); break; default: diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandler.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandler.java index b0c251f477c..eb81ce15b3a 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandler.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandler.java @@ -1,7 +1,6 @@ // Copyright 2017 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.common.io.Files; import com.google.inject.Inject; import com.yahoo.cloud.config.ConfigserverConfig; import com.yahoo.config.application.api.DeployLogger; @@ -9,15 +8,11 @@ import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.TenantName; import com.yahoo.container.jdisc.HttpRequest; import com.yahoo.container.jdisc.HttpResponse; -import com.yahoo.container.logging.AccessLog; -import com.yahoo.io.IOUtils; import com.yahoo.jdisc.application.UriPattern; import com.yahoo.log.LogLevel; import com.yahoo.slime.Slime; import com.yahoo.vespa.config.server.ApplicationRepository; import com.yahoo.vespa.config.server.deploy.DeployHandlerLogger; -import com.yahoo.vespa.config.server.http.CompressedApplicationInputStream; -import com.yahoo.vespa.config.server.http.InternalServerException; import com.yahoo.vespa.config.server.tenant.Tenant; import com.yahoo.vespa.config.server.tenant.Tenants; import com.yahoo.vespa.config.server.TimeoutBudget; @@ -25,11 +20,8 @@ import com.yahoo.vespa.config.server.http.BadRequestException; import com.yahoo.vespa.config.server.http.SessionHandler; import com.yahoo.vespa.config.server.http.Utils; -import java.io.File; -import java.io.IOException; import java.net.URI; import java.time.Duration; -import java.util.concurrent.Executor; /** * A handler that is able to create a session from an application package, @@ -37,12 +29,9 @@ import java.util.concurrent.Executor; * Handles /application/v2/ requests * * @author hmusum - * @since 5.1 */ public class SessionCreateHandler extends SessionHandler { - public final static String APPLICATION_X_GZIP = "application/x-gzip"; - public final static String APPLICATION_ZIP = "application/zip"; - public final static String contentTypeHeader = "Content-Type"; + private static final String fromPattern = "http://*/application/v2/tenant/*/application/*/environment/*/region/*/instance/*"; private final Tenants tenants; @@ -72,11 +61,8 @@ public class SessionCreateHandler extends SessionHandler { sessionId = applicationRepository.createSessionFromExisting(tenant, logger, timeoutBudget, applicationId); } else { validateDataAndHeader(request); - File tempDir = Files.createTempDir(); - File applicationDirectory = decompressApplication(request, tempDir); String name = getNameProperty(request, logger); - sessionId = applicationRepository.createSession(tenant, timeoutBudget, applicationDirectory, name); - cleanupApplicationDirectory(tempDir, logger); + sessionId = applicationRepository.createSession(tenant, timeoutBudget, request.getData(), request.getHeader(ApplicationApiHandler.contentTypeHeader), name); } return createResponse(request, tenantName, deployLog, sessionId); } @@ -100,12 +86,12 @@ public class SessionCreateHandler extends SessionHandler { .instanceName(match.group(6)).build(); } - private DeployHandlerLogger createLogger(HttpRequest request, Slime deployLog, TenantName tenant) { + static DeployHandlerLogger createLogger(HttpRequest request, Slime deployLog, TenantName tenant) { return SessionHandler.createLogger(deployLog, request, new ApplicationId.Builder().tenant(tenant).applicationName("-").build()); } - private String getNameProperty(HttpRequest request, DeployLogger logger) { + static String getNameProperty(HttpRequest request, DeployLogger logger) { String name = request.getProperty("name"); // TODO: Do we need validation of this parameter? if (name == null) { @@ -115,32 +101,16 @@ public class SessionCreateHandler extends SessionHandler { return name; } - private File decompressApplication(HttpRequest request, File tempDir) { - try (CompressedApplicationInputStream application = CompressedApplicationInputStream.createFromCompressedStream(request.getData(), request - .getHeader(contentTypeHeader))) { - return application.decompress(tempDir); - } catch (IOException e) { - throw new InternalServerException("Unable to decompress data in body", e); - } - } - - private void cleanupApplicationDirectory(File tempDir, DeployLogger logger) { - logger.log(LogLevel.DEBUG, "Deleting tmp dir '" + tempDir + "'"); - if (!IOUtils.recursiveDeleteDir(tempDir)) { - logger.log(LogLevel.WARNING, "Not able to delete tmp dir '" + tempDir + "'"); - } - } - - private static void validateDataAndHeader(HttpRequest request) { + static void validateDataAndHeader(HttpRequest request) { if (request.getData() == null) { throw new BadRequestException("Request contains no data"); } - String header = request.getHeader(contentTypeHeader); + String header = request.getHeader(ApplicationApiHandler.contentTypeHeader); if (header == null) { - throw new BadRequestException("Request contains no " + contentTypeHeader + " header"); - } else if (!(header.equals(APPLICATION_X_GZIP) || header.equals(APPLICATION_ZIP))) { - throw new BadRequestException("Request contains invalid " + contentTypeHeader + " header, only '" + - APPLICATION_X_GZIP + "' and '" + APPLICATION_ZIP + "' are supported"); + throw new BadRequestException("Request contains no " + ApplicationApiHandler.contentTypeHeader + " header"); + } else if (!(header.equals(ApplicationApiHandler.APPLICATION_X_GZIP) || header.equals(ApplicationApiHandler.APPLICATION_ZIP))) { + throw new BadRequestException("Request contains invalid " + ApplicationApiHandler.contentTypeHeader + " header, only '" + + ApplicationApiHandler.APPLICATION_X_GZIP + "' and '" + ApplicationApiHandler.APPLICATION_ZIP + "' are supported"); } } 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 deleted file mode 100644 index dc88a6ddddc..00000000000 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/SessionPrepareAndActivateHandler.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.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/resources/configserver-app/services.xml b/configserver/src/main/resources/configserver-app/services.xml index bf38eedc97f..b3f0a58ea90 100644 --- a/configserver/src/main/resources/configserver-app/services.xml +++ b/configserver/src/main/resources/configserver-app/services.xml @@ -106,7 +106,7 @@ <binding>http://*/application/v2/tenant/*/session/*/active</binding> <binding>https://*/application/v2/tenant/*/session/*/active</binding> </handler> - <handler id='com.yahoo.vespa.config.server.http.v2.SessionPrepareAndActivateHandler' bundle='configserver'> + <handler id='com.yahoo.vespa.config.server.http.v2.ApplicationApiHandler' bundle='configserver'> <binding>http://*/application/v2/tenant/*/session/*/prepareandactivate</binding> <binding>https://*/application/v2/tenant/*/session/*/prepareandactivate</binding> </handler> 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 index 52c711e3157..ecc3a8a0a54 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java @@ -4,7 +4,9 @@ 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.CompressedApplicationInputStreamTest; import com.yahoo.vespa.config.server.http.SessionHandlerTest; +import com.yahoo.vespa.config.server.http.v2.ApplicationApiHandler; 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; @@ -15,6 +17,7 @@ import org.junit.Before; import org.junit.Test; import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.time.Clock; import java.time.Duration; @@ -65,12 +68,27 @@ public class ApplicationRepositoryTest { assertFalse(result.configChangeActions().getRestartActions().isEmpty()); } + @Test + public void createAndPrepareAndActivate() throws IOException { + PrepareResult result = createAndPrepareAndActivateApp(); + assertTrue(result.configChangeActions().getRefeedActions().isEmpty()); + assertTrue(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 PrepareResult createAndPrepareAndActivateApp() throws IOException { + File file = CompressedApplicationInputStreamTest.createTarFile(); + return applicationRepository.createSessionAndPrepareAndActivate(tenant, new FileInputStream(file), + ApplicationApiHandler.APPLICATION_X_GZIP, + timeoutBudget, "testapp", prepareParams(), + false, false, Instant.now()); + } + private PrepareParams prepareParams() { return new PrepareParams.Builder().build(); } diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandlerTest.java index fc9264a6ef5..d5d76ebe549 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandlerTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandlerTest.java @@ -7,7 +7,6 @@ import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.TenantName; 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.application.MemoryTenantApplications; import com.yahoo.vespa.config.server.application.TenantApplications; @@ -61,7 +60,7 @@ public class SessionCreateHandlerTest extends SessionHandlerTest { private TenantApplications applicationRepo; static { - postHeaders.put(SessionCreateHandler.contentTypeHeader, SessionCreateHandler.APPLICATION_X_GZIP); + postHeaders.put(ApplicationApiHandler.contentTypeHeader, ApplicationApiHandler.APPLICATION_X_GZIP); } @Before diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionFactoryTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionFactoryTest.java index 4ce34c11a22..7434bf411dd 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionFactoryTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionFactoryTest.java @@ -10,7 +10,7 @@ import com.yahoo.vespa.config.server.*; import com.yahoo.vespa.config.server.http.CompressedApplicationInputStream; import com.yahoo.vespa.config.server.http.CompressedApplicationInputStreamTest; -import com.yahoo.vespa.config.server.http.v2.SessionCreateHandler; +import com.yahoo.vespa.config.server.http.v2.ApplicationApiHandler; import com.yahoo.vespa.config.server.tenant.TestWithTenant; import org.json.JSONException; import org.json.JSONObject; @@ -77,7 +77,7 @@ public class SessionFactoryTest extends TestWithTenant { private LocalSession getLocalSession(String appName) throws IOException { CompressedApplicationInputStream app = CompressedApplicationInputStream.createFromCompressedStream( - new FileInputStream(CompressedApplicationInputStreamTest.createTarFile()), SessionCreateHandler.APPLICATION_X_GZIP); + new FileInputStream(CompressedApplicationInputStreamTest.createTarFile()), ApplicationApiHandler.APPLICATION_X_GZIP); return factory.createSession(app.decompress(Files.createTempDir()), appName, TimeoutBudgetTest.day()); } } |