diff options
author | Harald Musum <musum@yahoo-inc.com> | 2017-01-06 08:44:09 +0100 |
---|---|---|
committer | Harald Musum <musum@yahoo-inc.com> | 2017-01-06 08:44:09 +0100 |
commit | 0b0b72c9dff286d3cc0c7fd79f1bea9bd2c6f0a7 (patch) | |
tree | 06122cccf108ad5899aaff8c345b2e398c23543e | |
parent | e22014851a75fd89b78e05aa860ba5532883027c (diff) |
Refactor content handlers, first step
* Move most of logic into ApplicationRepository
* Remove unnneded base class
8 files changed, 194 insertions, 188 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 8ba71d7533e..8e706b7056a 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,16 +1,16 @@ // Copyright 2016 Yahoo Inc. 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.application.api.ApplicationFile; import com.yahoo.config.application.api.ApplicationMetaData; import com.yahoo.config.application.api.DeployLogger; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.HostFilter; import com.yahoo.config.provision.Provisioner; 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.log.LogLevel; +import com.yahoo.path.Path; import com.yahoo.transaction.NestedTransaction; import com.yahoo.vespa.config.server.application.Application; import com.yahoo.vespa.config.server.application.ApplicationConvergenceChecker; @@ -19,8 +19,6 @@ import com.yahoo.vespa.config.server.application.LogServerLogGrabber; import com.yahoo.vespa.config.server.application.TenantApplications; import com.yahoo.vespa.config.server.configchange.ConfigChangeActions; import com.yahoo.vespa.config.server.deploy.Deployment; -import com.yahoo.vespa.config.server.http.ContentHandler; -import com.yahoo.vespa.config.server.http.v2.ApplicationContentRequest; import com.yahoo.vespa.config.server.provision.HostProvisionerProvider; import com.yahoo.vespa.config.server.session.LocalSession; import com.yahoo.vespa.config.server.session.LocalSessionRepo; @@ -60,7 +58,6 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye private final Curator curator; private final LogServerLogGrabber logServerLogGrabber; private final ApplicationConvergenceChecker convergeChecker; - private final ContentHandler contentHandler = new ContentHandler(); private final Clock clock; private final DeployLogger logger = new SilentDeployLogger(); @@ -141,8 +138,7 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye transaction.add(tenantApplications.deleteApplication(applicationId)); - if (hostProvisioner.isPresent()) - hostProvisioner.get().remove(transaction, applicationId); + hostProvisioner.ifPresent(provisioner -> provisioner.remove(transaction, applicationId)); transaction.onCommitted(() -> log.log(LogLevel.INFO, "Deleted " + applicationId)); transaction.commit(); @@ -173,25 +169,24 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye return getApplication(tenant, applicationId).getApplicationGeneration(); } - public HttpResponse getContent(Tenant tenant, ApplicationId applicationId, Zone zone, HttpRequest request) { - LocalSession session = getLocalSession(tenant, tenant.getApplicationRepo().getSessionIdForApplication(applicationId)); - return contentHandler.get(ApplicationContentRequest.create(request, session, applicationId, zone)); - } - private Application getApplication(Tenant tenant, ApplicationId applicationId) { - long sessionId = tenant.getApplicationRepo().getSessionIdForApplication(applicationId); + long sessionId = getSessionIdForApplication(tenant, applicationId); RemoteSession session = tenant.getRemoteSessionRepo().getSession(sessionId, 0); return session.ensureApplicationLoaded().getForVersionOrLatest(Optional.empty()); } - public LocalSession getLocalSession(Tenant tenant, long sessionId) { + public long getSessionIdForApplication(Tenant tenant, ApplicationId applicationId) { + return tenant.getApplicationRepo().getSessionIdForApplication(applicationId); + } + + private LocalSession getLocalSession(Tenant tenant, long sessionId) { LocalSession session = tenant.getLocalSessionRepo().getSession(sessionId); if (session == null) throw new NotFoundException("Session " + sessionId + " was not found"); return session; } - public RemoteSession getRemoteSession(Tenant tenant, long sessionId) { + private RemoteSession getRemoteSession(Tenant tenant, long sessionId) { RemoteSession session = tenant.getRemoteSessionRepo().getSession(sessionId); if (session == null) throw new NotFoundException("Session " + sessionId + " was not found"); @@ -199,8 +194,7 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye } public void restart(ApplicationId applicationId, HostFilter hostFilter) { - if (hostProvisioner.isPresent()) - hostProvisioner.get().restart(applicationId, hostFilter); + hostProvisioner.ifPresent(provisioner -> provisioner.restart(applicationId, hostFilter)); } public Tenant verifyTenantAndApplication(ApplicationId applicationId) { @@ -310,6 +304,11 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye return session.getSessionId(); } + public ApplicationFile getApplicationFileFromSession(TenantName tenantName, long sessionId, String path, LocalSession.Mode mode) { + Tenant tenant = tenants.getTenant(tenantName); + return getLocalSession(tenant, sessionId).getApplicationFile(Path.fromString(path), mode); + } + private LocalSession getExistingSession(Tenant tenant, ApplicationId applicationId) { TenantApplications applicationRepo = tenant.getApplicationRepo(); return getLocalSession(tenant, applicationRepo.getSessionIdForApplication(applicationId)); diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/ContentRequest.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/ContentRequest.java index d71987449bf..3446d84bdf1 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/ContentRequest.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/ContentRequest.java @@ -3,11 +3,11 @@ package com.yahoo.vespa.config.server.http; import com.yahoo.config.application.api.ApplicationFile; import com.yahoo.container.jdisc.HttpRequest; -import com.yahoo.path.Path; -import com.yahoo.vespa.config.server.session.LocalSession; import java.io.InputStream; +import static com.yahoo.vespa.config.server.session.LocalSession.Mode; + /** * Represents a {@link ContentRequest}, and contains common functionality for content requests for all content handlers. * @@ -24,20 +24,20 @@ public abstract class ContentRequest { private final ApplicationFile file; private final HttpRequest request; - protected ContentRequest(HttpRequest request, LocalSession session) { + protected ContentRequest(HttpRequest request, long sessionId, String path, ApplicationFile applicationFile) { this.request = request; - this.sessionId = session.getSessionId(); - this.path = getContentPath(request); - this.file = session.getApplicationFile(Path.fromString(path), getApplicationFileMode(request.getMethod())); + this.sessionId = sessionId; + this.path = path; + this.file = applicationFile; } - private LocalSession.Mode getApplicationFileMode(com.yahoo.jdisc.http.HttpRequest.Method method) { + public static Mode getApplicationFileMode(com.yahoo.jdisc.http.HttpRequest.Method method) { switch (method) { case GET: case OPTIONS: - return LocalSession.Mode.READ; + return Mode.READ; default: - return LocalSession.Mode.WRITE; + return Mode.WRITE; } } @@ -59,7 +59,6 @@ public abstract class ContentRequest { } protected abstract String getPathPrefix(); - protected abstract String getContentPath(HttpRequest request); String getUrlBase(String appendStr) { return Utils.getUrlBase(request, getPathPrefix() + appendStr); diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationContentRequest.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationContentRequest.java index 2b5bc4b3d35..823300a3f85 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationContentRequest.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationContentRequest.java @@ -1,13 +1,13 @@ // Copyright 2016 Yahoo Inc. 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.application.api.ApplicationFile; import com.yahoo.config.provision.Zone; import com.yahoo.container.jdisc.HttpRequest; import com.yahoo.jdisc.application.BindingMatch; import com.yahoo.config.provision.ApplicationId; import com.yahoo.vespa.config.server.http.ContentRequest; import com.yahoo.vespa.config.server.http.Utils; -import com.yahoo.vespa.config.server.session.LocalSession; /** * Represents a content request for an application. @@ -21,18 +21,18 @@ public class ApplicationContentRequest extends ContentRequest { private final ApplicationId applicationId; private final Zone zone; - private ApplicationContentRequest(HttpRequest request, LocalSession session, ApplicationId applicationId, Zone zone) { - super(request, session); + ApplicationContentRequest(HttpRequest request, + long sessionId, + ApplicationId applicationId, + Zone zone, + String contentPath, + ApplicationFile applicationFile) { + super(request, sessionId, contentPath, applicationFile); this.applicationId = applicationId; this.zone = zone; } - public static ContentRequest create(HttpRequest request, LocalSession session, ApplicationId applicationId, Zone zone) { - return new ApplicationContentRequest(request, session, applicationId, zone); - } - - @Override - protected String getContentPath(HttpRequest request) { + static String getContentPath(HttpRequest request) { BindingMatch<?> bm = Utils.getBindingMatch(request, uriPattern); return bm.group(7); } diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java index 21e97cf0fa8..b51eede04bb 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java @@ -1,6 +1,7 @@ // Copyright 2016 Yahoo Inc. 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.application.api.ApplicationFile; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.ApplicationName; import com.yahoo.config.provision.HostFilter; @@ -11,6 +12,8 @@ import com.yahoo.container.jdisc.HttpResponse; import com.yahoo.container.logging.AccessLog; import com.yahoo.jdisc.Response; import com.yahoo.jdisc.application.BindingMatch; +import com.yahoo.vespa.config.server.http.ContentHandler; +import com.yahoo.vespa.config.server.http.ContentRequest; import com.yahoo.vespa.config.server.http.HttpConfigResponse; import com.yahoo.vespa.config.server.tenant.Tenant; import com.yahoo.vespa.config.server.TimeoutBudget; @@ -69,7 +72,20 @@ public class ApplicationHandler extends HttpHandler { return applicationRepository.nodeConvergenceCheck(tenant, applicationId, getHostFromRequest(request), request.getUri()); } if (isContentRequest(request)) { - return applicationRepository.getContent(tenant, applicationId, zone, request); + long sessionId = applicationRepository.getSessionIdForApplication(tenant, applicationId); + String contentPath = ApplicationContentRequest.getContentPath(request); + ApplicationFile applicationFile = + applicationRepository.getApplicationFileFromSession(tenant.getName(), + sessionId, + contentPath, + ContentRequest.getApplicationFileMode(request.getMethod())); + ApplicationContentRequest contentRequest = new ApplicationContentRequest(request, + sessionId, + applicationId, + zone, + contentPath, + applicationFile); + return new ContentHandler().get(contentRequest); } // TODO: Remove this once the config convergence logic is moved to client and is live for all clusters. diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/SessionContentHandler.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/SessionContentHandler.java index 7b55fdea11a..f776062d194 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/SessionContentHandler.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/SessionContentHandler.java @@ -2,17 +2,17 @@ package com.yahoo.vespa.config.server.http.v2; import com.google.inject.Inject; +import com.yahoo.config.application.api.ApplicationFile; 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.tenant.Tenant; +import com.yahoo.vespa.config.server.http.ContentRequest; import com.yahoo.vespa.config.server.tenant.Tenants; import com.yahoo.vespa.config.server.http.ContentHandler; import com.yahoo.vespa.config.server.http.SessionHandler; import com.yahoo.vespa.config.server.http.Utils; -import com.yahoo.vespa.config.server.session.LocalSession; import java.util.concurrent.Executor; @@ -38,27 +38,34 @@ public class SessionContentHandler extends SessionHandler { @Override public HttpResponse handleGET(HttpRequest request) { - LocalSession session = validateRequestAndGetSession(request); - return contentHandler.get(SessionContentRequestV2.create(request, session)); + return contentHandler.get(getContentRequest(request)); } @Override public HttpResponse handlePUT(HttpRequest request) { - LocalSession session = validateRequestAndGetSession(request); - return contentHandler.put(SessionContentRequestV2.create(request, session)); + return contentHandler.put(getContentRequest(request)); } @Override public HttpResponse handleDELETE(HttpRequest request) { - LocalSession session = validateRequestAndGetSession(request); - return contentHandler.delete(SessionContentRequestV2.create(request, session)); + return contentHandler.delete(getContentRequest(request)); } - private LocalSession validateRequestAndGetSession(HttpRequest request) { - final TenantName tenantName = Utils.getTenantNameFromSessionRequest(request); + private void validateRequest(TenantName tenantName) { Utils.checkThatTenantExists(tenants, tenantName); - Tenant tenant = tenants.getTenant(tenantName); - return applicationRepository.getLocalSession(tenant, getSessionIdV2(request)); + } + + private SessionContentRequestV2 getContentRequest(HttpRequest request) { + final TenantName tenantName = Utils.getTenantNameFromSessionRequest(request); + validateRequest(tenantName); + long sessionId = getSessionIdV2(request); + String contentPath = SessionContentRequestV2.getContentPath(request); + ApplicationFile applicationFile = + applicationRepository.getApplicationFileFromSession(tenantName, + sessionId, + contentPath, + ContentRequest.getApplicationFileMode(request.getMethod())); + return new SessionContentRequestV2(request, sessionId, tenantName, contentPath, applicationFile); } } diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/SessionContentRequestV2.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/SessionContentRequestV2.java index 30af00dd4d1..af69d67cf22 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/SessionContentRequestV2.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/SessionContentRequestV2.java @@ -1,12 +1,12 @@ // Copyright 2016 Yahoo Inc. 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.application.api.ApplicationFile; import com.yahoo.config.provision.TenantName; import com.yahoo.container.jdisc.HttpRequest; import com.yahoo.jdisc.application.BindingMatch; import com.yahoo.vespa.config.server.http.ContentRequest; import com.yahoo.vespa.config.server.http.Utils; -import com.yahoo.vespa.config.server.session.LocalSession; /** * Requests for content and content status (v2) @@ -15,19 +15,19 @@ import com.yahoo.vespa.config.server.session.LocalSession; * @author hmusum * @since 5.3 */ -class SessionContentRequestV2 extends ContentRequest { +public class SessionContentRequestV2 extends ContentRequest { private static final String uriPattern = "http://*/application/v2/tenant/*/session/*/content/*"; private final TenantName tenantName; private final long sessionId; - private SessionContentRequestV2(HttpRequest request, LocalSession session, TenantName tenantName) { - super(request, session); + SessionContentRequestV2(HttpRequest request, + long sessionId, + TenantName tenantName, + String path, + ApplicationFile applicationFile) { + super(request, sessionId, path, applicationFile); this.tenantName = tenantName; - this.sessionId = session.getSessionId(); - } - - static ContentRequest create(HttpRequest request, LocalSession session) { - return new SessionContentRequestV2(request, session, Utils.getTenantNameFromSessionRequest(request)); + this.sessionId = sessionId; } @Override @@ -35,8 +35,7 @@ class SessionContentRequestV2 extends ContentRequest { return "/application/v2/tenant/" + tenantName.value() + "/session/" + sessionId; } - @Override - protected String getContentPath(HttpRequest request) { + static String getContentPath(HttpRequest request) { BindingMatch<?> bm = Utils.getBindingMatch(request, uriPattern); return bm.group(4); } diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/SessionContentHandlerTestBase.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/SessionContentHandlerTestBase.java deleted file mode 100644 index 43fdb2d747a..00000000000 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/SessionContentHandlerTestBase.java +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.config.server.http; - -import com.google.common.io.Files; -import com.yahoo.container.jdisc.HttpResponse; -import com.yahoo.jdisc.Response; -import com.yahoo.jdisc.http.HttpRequest; -import com.yahoo.text.Utf8; -import org.apache.commons.io.FileUtils; -import org.junit.Ignore; -import org.junit.Test; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; - -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertThat; - -public abstract class SessionContentHandlerTestBase extends ContentHandlerTestBase { - - @Test - public void require_that_directories_can_be_created() throws IOException { - assertMkdir("/bar/"); - assertMkdir("/bar/brask/"); - assertMkdir("/bar/brask/"); - assertMkdir("/bar/brask/bram/"); - assertMkdir("/brask/og/bram/"); - }// TODO: Enable when we have a predictable way of checking request body existence. - - @Test - @Ignore - public void require_that_mkdir_with_body_is_illegal() throws IOException { - HttpResponse response = put("/foobio/", "foo"); - assertNotNull(response); - assertThat(response.getStatus(), is(Response.Status.BAD_REQUEST)); - } - - @Test - public void require_that_nonexistant_session_returns_not_found() throws IOException { - HttpResponse response = doRequest(HttpRequest.Method.GET, "/test.txt", 2l); - assertNotNull(response); - assertThat(response.getStatus(), is(Response.Status.NOT_FOUND)); - } - - protected HttpResponse put(String path, String content) { - ByteArrayInputStream data = new ByteArrayInputStream(Utf8.toBytes(content)); - return doRequest(HttpRequest.Method.PUT, path, data); - } - - @Test - public void require_that_file_write_without_body_is_illegal() throws IOException { - HttpResponse response = doRequest(HttpRequest.Method.PUT, "/foobio.txt"); - assertNotNull(response); - assertThat(response.getStatus(), is(Response.Status.BAD_REQUEST)); - } - - @Test - public void require_that_files_can_be_written() throws IOException { - assertWriteFile("/foo/minfil.txt", "Mycontent"); - assertWriteFile("/foo/minfil.txt", "Differentcontent"); - } - - @Test - public void require_that_nonexistant_file_returs_not_found_when_deleted() throws IOException { - assertDeleteFile(Response.Status.NOT_FOUND, "/test2.txt", "{\"error-code\":\"NOT_FOUND\",\"message\":\"Session 1 does not contain a file 'test2.txt'\"}"); - } - - @Test - public void require_that_files_can_be_deleted() throws IOException { - assertDeleteFile(Response.Status.OK, "/test.txt"); - assertDeleteFile(Response.Status.NOT_FOUND, "/test.txt", "{\"error-code\":\"NOT_FOUND\",\"message\":\"Session 1 does not contain a file 'test.txt'\"}"); - assertDeleteFile(Response.Status.BAD_REQUEST, "/newtest", "{\"error-code\":\"BAD_REQUEST\",\"message\":\"File 'newtest' is not an empty directory\"}"); - assertDeleteFile(Response.Status.OK, "/newtest/testfile.txt"); - assertDeleteFile(Response.Status.OK, "/newtest"); - } - - @Test - public void require_that_status_is_given_for_new_files() throws IOException { - assertStatus("/test.txt?return=status", - "{\"status\":\"new\",\"md5\":\"d3b07384d113edec49eaa6238ad5ff00\",\"name\":\"http://foo:1337" + pathPrefix + "1/content/test.txt\"}"); - assertWriteFile("/test.txt", "Mycontent"); - assertStatus("/test.txt?return=status", - "{\"status\":\"changed\",\"md5\":\"01eabd73c69d78d0009ec93cd62d7f77\",\"name\":\"http://foo:1337" + pathPrefix + "1/content/test.txt\"}"); - } - - private void assertWriteFile(String path, String content) throws IOException { - HttpResponse response = put(path, content); - assertNotNull(response); - assertThat(response.getStatus(), is(Response.Status.OK)); - assertContent(path, content); - assertThat(SessionHandlerTest.getRenderedString(response), - is("{\"prepared\":\"http://foo:1337" + pathPrefix + "1/prepared\"}")); - } - - private void assertDeleteFile(int statusCode, String filePath) throws IOException { - assertDeleteFile(statusCode, filePath, "{\"prepared\":\"http://foo:1337" + pathPrefix + "1/prepared\"}"); - } - - private void assertDeleteFile(int statusCode, String filePath, String expectedResponse) throws IOException { - HttpResponse response = doRequest(HttpRequest.Method.DELETE, filePath); - assertNotNull(response); - assertThat(response.getStatus(), is(statusCode)); - assertThat(SessionHandlerTest.getRenderedString(response), is(expectedResponse)); - } - - private void assertMkdir(String path) throws IOException { - HttpResponse response = doRequest(HttpRequest.Method.PUT, path); - assertNotNull(response); - assertThat(response.getStatus(), is(Response.Status.OK)); - assertThat(SessionHandlerTest.getRenderedString(response), - is("{\"prepared\":\"http://foo:1337" + pathPrefix + "1/prepared\"}")); - } - - protected File createTestApp() throws IOException { - File testApp = Files.createTempDir(); - FileUtils.copyDirectory(new File("src/test/apps/content"), testApp); - return testApp; - } - - protected abstract HttpResponse doRequest(HttpRequest.Method method, String path, long sessionId); - protected abstract HttpResponse doRequest(HttpRequest.Method method, String path, InputStream data); - protected abstract HttpResponse doRequest(HttpRequest.Method method, String path, long sessionId, InputStream data); -}
\ No newline at end of file diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionContentHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionContentHandlerTest.java index 0f6a400ca1e..224cd5d28d1 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionContentHandlerTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionContentHandlerTest.java @@ -1,28 +1,41 @@ // Copyright 2016 Yahoo Inc. 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.yahoo.config.model.application.provider.FilesApplicationPackage; import com.yahoo.config.provision.TenantName; import com.yahoo.container.jdisc.HttpResponse; import com.yahoo.container.logging.AccessLog; +import com.yahoo.jdisc.Response; import com.yahoo.jdisc.http.HttpRequest; +import com.yahoo.text.Utf8; import com.yahoo.vespa.config.server.ApplicationRepository; import com.yahoo.vespa.config.server.application.ApplicationConvergenceChecker; import com.yahoo.vespa.config.server.application.LogServerLogGrabber; -import com.yahoo.vespa.config.server.http.SessionContentHandlerTestBase; +import com.yahoo.vespa.config.server.http.ContentHandlerTestBase; import com.yahoo.vespa.config.server.http.SessionHandlerTest; import com.yahoo.vespa.config.server.provision.HostProvisionerProvider; import com.yahoo.vespa.curator.mock.MockCurator; +import org.apache.commons.io.FileUtils; import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; import java.io.InputStream; import java.util.concurrent.Executor; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; + /** * @author lulf * @since 5.1 */ -public class SessionContentHandlerTest extends SessionContentHandlerTestBase { +public class SessionContentHandlerTest extends ContentHandlerTestBase { private static final TenantName tenant = TenantName.from("contenttest"); private SessionContentHandler handler = null; @@ -33,6 +46,105 @@ public class SessionContentHandlerTest extends SessionContentHandlerTestBase { baseUrl = "http://foo:1337/application/v2/tenant/" + tenant + "/session/1/content/"; } + @Test + public void require_that_directories_can_be_created() throws IOException { + assertMkdir("/bar/"); + assertMkdir("/bar/brask/"); + assertMkdir("/bar/brask/"); + assertMkdir("/bar/brask/bram/"); + assertMkdir("/brask/og/bram/"); + }// TODO: Enable when we have a predictable way of checking request body existence. + + @Test + @Ignore + public void require_that_mkdir_with_body_is_illegal() throws IOException { + HttpResponse response = put("/foobio/", "foo"); + assertNotNull(response); + assertThat(response.getStatus(), is(Response.Status.BAD_REQUEST)); + } + + @Test + public void require_that_nonexistant_session_returns_not_found() throws IOException { + HttpResponse response = doRequest(HttpRequest.Method.GET, "/test.txt", 2l); + assertNotNull(response); + assertThat(response.getStatus(), is(Response.Status.NOT_FOUND)); + } + + protected HttpResponse put(String path, String content) { + ByteArrayInputStream data = new ByteArrayInputStream(Utf8.toBytes(content)); + return doRequest(HttpRequest.Method.PUT, path, data); + } + + @Test + public void require_that_file_write_without_body_is_illegal() throws IOException { + HttpResponse response = doRequest(HttpRequest.Method.PUT, "/foobio.txt"); + assertNotNull(response); + assertThat(response.getStatus(), is(Response.Status.BAD_REQUEST)); + } + + @Test + public void require_that_files_can_be_written() throws IOException { + assertWriteFile("/foo/minfil.txt", "Mycontent"); + assertWriteFile("/foo/minfil.txt", "Differentcontent"); + } + + @Test + public void require_that_nonexistant_file_returs_not_found_when_deleted() throws IOException { + assertDeleteFile(Response.Status.NOT_FOUND, "/test2.txt", "{\"error-code\":\"NOT_FOUND\",\"message\":\"Session 1 does not contain a file 'test2.txt'\"}"); + } + + @Test + public void require_that_files_can_be_deleted() throws IOException { + assertDeleteFile(Response.Status.OK, "/test.txt"); + assertDeleteFile(Response.Status.NOT_FOUND, "/test.txt", "{\"error-code\":\"NOT_FOUND\",\"message\":\"Session 1 does not contain a file 'test.txt'\"}"); + assertDeleteFile(Response.Status.BAD_REQUEST, "/newtest", "{\"error-code\":\"BAD_REQUEST\",\"message\":\"File 'newtest' is not an empty directory\"}"); + assertDeleteFile(Response.Status.OK, "/newtest/testfile.txt"); + assertDeleteFile(Response.Status.OK, "/newtest"); + } + + @Test + public void require_that_status_is_given_for_new_files() throws IOException { + assertStatus("/test.txt?return=status", + "{\"status\":\"new\",\"md5\":\"d3b07384d113edec49eaa6238ad5ff00\",\"name\":\"http://foo:1337" + pathPrefix + "1/content/test.txt\"}"); + assertWriteFile("/test.txt", "Mycontent"); + assertStatus("/test.txt?return=status", + "{\"status\":\"changed\",\"md5\":\"01eabd73c69d78d0009ec93cd62d7f77\",\"name\":\"http://foo:1337" + pathPrefix + "1/content/test.txt\"}"); + } + + private void assertWriteFile(String path, String content) throws IOException { + HttpResponse response = put(path, content); + assertNotNull(response); + assertThat(response.getStatus(), is(Response.Status.OK)); + assertContent(path, content); + assertThat(SessionHandlerTest.getRenderedString(response), + is("{\"prepared\":\"http://foo:1337" + pathPrefix + "1/prepared\"}")); + } + + private void assertDeleteFile(int statusCode, String filePath) throws IOException { + assertDeleteFile(statusCode, filePath, "{\"prepared\":\"http://foo:1337" + pathPrefix + "1/prepared\"}"); + } + + private void assertDeleteFile(int statusCode, String filePath, String expectedResponse) throws IOException { + HttpResponse response = doRequest(HttpRequest.Method.DELETE, filePath); + assertNotNull(response); + assertThat(response.getStatus(), is(statusCode)); + assertThat(SessionHandlerTest.getRenderedString(response), is(expectedResponse)); + } + + private void assertMkdir(String path) throws IOException { + HttpResponse response = doRequest(HttpRequest.Method.PUT, path); + assertNotNull(response); + assertThat(response.getStatus(), is(Response.Status.OK)); + assertThat(SessionHandlerTest.getRenderedString(response), + is("{\"prepared\":\"http://foo:1337" + pathPrefix + "1/prepared\"}")); + } + + protected File createTestApp() throws IOException { + File testApp = Files.createTempDir(); + FileUtils.copyDirectory(new File("src/test/apps/content"), testApp); + return testApp; + } + protected HttpResponse doRequest(HttpRequest.Method method, String path) { return doRequest(method, path, 1l); } |