Updated Branches: refs/heads/master 6fbc1932e -> cc1e2ae4d
JCLOUDS-307. Support Swift Static Large Object Project: http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/commit/cc1e2ae4 Tree: http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/tree/cc1e2ae4 Diff: http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/diff/cc1e2ae4 Branch: refs/heads/master Commit: cc1e2ae4d7f6db038e7b4b9f7dd228b2321a6cd6 Parents: d7b6e87 Author: Adrian Cole <[email protected]> Authored: Sun Sep 29 13:23:57 2013 -0700 Committer: Adrian Cole <[email protected]> Committed: Sun Sep 29 13:36:20 2013 -0700 ---------------------------------------------------------------------- .../jclouds/openstack/swift/v1/SwiftApi.java | 7 + .../openstack/swift/v1/domain/Segment.java | 129 +++++++++++++++++++ .../openstack/swift/v1/domain/SwiftObject.java | 2 +- .../swift/v1/features/StaticLargeObjectApi.java | 87 +++++++++++++ .../swift/v1/functions/ETagHeader.java | 3 +- .../swift/v1/features/BulkApiLiveTest.java | 26 +--- .../features/StaticLargeObjectApiLiveTest.java | 128 ++++++++++++++++++ .../features/StaticLargeObjectApiMockTest.java | 109 ++++++++++++++++ .../swift/v1/internal/BaseSwiftApiLiveTest.java | 23 ++++ 9 files changed, 490 insertions(+), 24 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/cc1e2ae4/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftApi.java ---------------------------------------------------------------------- diff --git a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftApi.java b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftApi.java index b18a521..57ebc9c 100644 --- a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftApi.java +++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftApi.java @@ -29,6 +29,7 @@ import org.jclouds.openstack.swift.v1.features.AccountApi; import org.jclouds.openstack.swift.v1.features.BulkApi; import org.jclouds.openstack.swift.v1.features.ContainerApi; import org.jclouds.openstack.swift.v1.features.ObjectApi; +import org.jclouds.openstack.swift.v1.features.StaticLargeObjectApi; import org.jclouds.rest.annotations.Delegate; import org.jclouds.rest.annotations.EndpointParam; @@ -60,4 +61,10 @@ public interface SwiftApi extends Closeable { @Path("/{containerName}") ObjectApi objectApiInRegionForContainer(@EndpointParam(parser = RegionToEndpoint.class) @Nullable String region, @PathParam("containerName") String containerName); + + @Delegate + @Path("/{containerName}") + StaticLargeObjectApi staticLargeObjectApiInRegionForContainer( + @EndpointParam(parser = RegionToEndpoint.class) @Nullable String region, + @PathParam("containerName") String containerName); } http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/cc1e2ae4/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/Segment.java ---------------------------------------------------------------------- diff --git a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/Segment.java b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/Segment.java new file mode 100644 index 0000000..3d24b05 --- /dev/null +++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/Segment.java @@ -0,0 +1,129 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.openstack.swift.v1.domain; + +import static com.google.common.base.Objects.equal; +import static com.google.common.base.Objects.toStringHelper; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.Objects; + +/** + * One piece of a multi-part upload. + * + * @see <a + * href="http://docs.openstack.org/api/openstack-object-storage/1.0/content/static-large-objects.html">api + * doc</a> + */ +public class Segment { + + private final String path; + private final String etag; + private final long size_bytes; + + private Segment(String path, String etag, long sizeBytes) { + this.path = checkNotNull(path, "path"); + this.etag = checkNotNull(etag, "etag of %s", path); + this.size_bytes = checkNotNull(sizeBytes, "sizeBytes of %s", path); + } + + /** + * {@code /container/objectName} which corresponds to the path of + * {@link SwiftObject#uri()}. + */ + public String path() { + return path; + } + + /** + * Corresponds to the {@code ETag} header of the response, and is usually the + * MD5 checksum of the object + */ + public String etag() { + return etag; + } + + public long sizeBytes() { + return size_bytes; + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object instanceof Segment) { + Segment that = Segment.class.cast(object); + return equal(path(), that.path()) // + && equal(etag(), that.etag()) // + && equal(sizeBytes(), that.sizeBytes()); + } else { + return false; + } + } + + @Override + public int hashCode() { + return Objects.hashCode(path(), etag(), sizeBytes()); + } + + @Override + public String toString() { + return toStringHelper("") // + .add("path", path()) // + .add("etag", etag()) // + .add("sizeBytes", sizeBytes()).toString(); + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + protected String path; + protected String etag; + protected long sizeBytes; + + /** + * @see Segment#path() + */ + public Builder path(String path) { + this.path = path; + return this; + } + + /** + * @see Segment#etag() + */ + public Builder etag(String etag) { + this.etag = etag; + return this; + } + + /** + * @see Segment#sizeBytes() + */ + public Builder sizeBytes(long sizeBytes) { + this.sizeBytes = sizeBytes; + return this; + } + + public Segment build() { + return new Segment(path, etag, sizeBytes); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/cc1e2ae4/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/SwiftObject.java ---------------------------------------------------------------------- diff --git a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/SwiftObject.java b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/SwiftObject.java index e94fde4..81f690c 100644 --- a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/SwiftObject.java +++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/SwiftObject.java @@ -51,7 +51,7 @@ public class SwiftObject implements Comparable<SwiftObject> { Payload payload) { this.name = checkNotNull(name, "name"); this.uri = checkNotNull(uri, "uri of %s", uri); - this.etag = checkNotNull(etag, "etag of %s", name); + this.etag = checkNotNull(etag, "etag of %s", name).replace("\"", ""); this.lastModified = checkNotNull(lastModified, "lastModified of %s", name); this.metadata = metadata == null ? ImmutableMap.<String, String> of() : metadata; this.payload = checkNotNull(payload, "payload of %s", name); http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/cc1e2ae4/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/StaticLargeObjectApi.java ---------------------------------------------------------------------- diff --git a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/StaticLargeObjectApi.java b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/StaticLargeObjectApi.java new file mode 100644 index 0000000..cc651cd --- /dev/null +++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/StaticLargeObjectApi.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.openstack.swift.v1.features; + +import static javax.ws.rs.core.MediaType.APPLICATION_JSON; + +import java.util.List; +import java.util.Map; + +import javax.inject.Named; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; + +import org.jclouds.Fallbacks.VoidOnNotFoundOr404; +import org.jclouds.openstack.keystone.v2_0.filters.AuthenticateRequest; +import org.jclouds.openstack.swift.v1.binders.BindMetadataToHeaders.BindObjectMetadataToHeaders; +import org.jclouds.openstack.swift.v1.domain.Segment; +import org.jclouds.openstack.swift.v1.domain.SwiftObject; +import org.jclouds.openstack.swift.v1.functions.ETagHeader; +import org.jclouds.rest.annotations.BinderParam; +import org.jclouds.rest.annotations.Fallback; +import org.jclouds.rest.annotations.QueryParams; +import org.jclouds.rest.annotations.RequestFilters; +import org.jclouds.rest.annotations.ResponseParser; +import org.jclouds.rest.binders.BindToJsonPayload; + +/** + * @see <a + * href="http://docs.openstack.org/api/openstack-object-storage/1.0/content/static-large-objects.html"> + * Static Large Objects API</a> + */ +@RequestFilters(AuthenticateRequest.class) +@Consumes(APPLICATION_JSON) +public interface StaticLargeObjectApi { + + /** + * Creates or updates a static large object's manifest. + * + * @param objectName + * corresponds to {@link SwiftObject#name()}. + * @param segments + * ordered parts which will be concatenated upon download. + * @param metadata + * corresponds to {@link SwiftObject#metadata()}. + * + * @return {@link SwiftObject#etag()} of the object, which is the MD5 + * checksum of the concatenated ETag values of the {@code segments}. + */ + @Named("CreateOrUpdateStaticLargeObjectManifest") + @PUT + @ResponseParser(ETagHeader.class) + @Path("/{objectName}") + @QueryParams(keys = "multipart-manifest", values = "put") + String replaceManifest(@PathParam("objectName") String objectName, + @BinderParam(BindToJsonPayload.class) List<Segment> segments, + @BinderParam(BindObjectMetadataToHeaders.class) Map<String, String> metadata); + + /** + * Deletes a static large object, if present, including all of its segments. + * + * @param objectName + * corresponds to {@link SwiftObject#name()}. + */ + @Named("DeleteStaticLargeObject") + @DELETE + @Fallback(VoidOnNotFoundOr404.class) + @Path("/{objectName}") + @QueryParams(keys = "multipart-manifest", values = "delete") + void delete(@PathParam("objectName") String objectName); +} http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/cc1e2ae4/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/functions/ETagHeader.java ---------------------------------------------------------------------- diff --git a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/functions/ETagHeader.java b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/functions/ETagHeader.java index f5c9dd2..25a749b 100644 --- a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/functions/ETagHeader.java +++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/functions/ETagHeader.java @@ -26,6 +26,7 @@ public class ETagHeader implements Function<HttpResponse, String> { @Override public String apply(HttpResponse from) { - return from.getFirstHeaderOrNull(ETAG); + String etag = from.getFirstHeaderOrNull(ETAG); + return etag != null ? etag.replace("\"", "") : null; } } http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/cc1e2ae4/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/BulkApiLiveTest.java ---------------------------------------------------------------------- diff --git a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/BulkApiLiveTest.java b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/BulkApiLiveTest.java index 26d2045..70f5c2f 100644 --- a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/BulkApiLiveTest.java +++ b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/BulkApiLiveTest.java @@ -16,7 +16,6 @@ */ package org.jclouds.openstack.swift.v1.features; -import static com.google.common.base.Preconditions.checkState; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; @@ -31,15 +30,12 @@ import org.jboss.shrinkwrap.api.exporter.TarGzExporter; import org.jclouds.io.Payloads; import org.jclouds.openstack.swift.v1.domain.BulkDeleteResponse; import org.jclouds.openstack.swift.v1.domain.ExtractArchiveResponse; -import org.jclouds.openstack.swift.v1.domain.SwiftObject; import org.jclouds.openstack.swift.v1.internal.BaseSwiftApiLiveTest; import org.jclouds.openstack.swift.v1.options.CreateContainerOptions; -import org.jclouds.openstack.swift.v1.options.ListContainerOptions; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; -import com.google.common.base.Function; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; @@ -71,7 +67,8 @@ public class BulkApiLiveTest extends BaseSwiftApiLiveTest { assertEquals(api.containerApiInRegion(regionId).get(containerName).objectCount(), OBJECT_COUNT); // repeat the command - extractResponse = api.bulkApiInRegion(regionId).extractArchive(containerName, Payloads.newPayload(tarGz), "tar.gz"); + extractResponse = api.bulkApiInRegion(regionId).extractArchive(containerName, Payloads.newPayload(tarGz), + "tar.gz"); assertEquals(extractResponse.created(), OBJECT_COUNT); assertTrue(extractResponse.errors().isEmpty()); } @@ -99,7 +96,7 @@ public class BulkApiLiveTest extends BaseSwiftApiLiveTest { boolean created = api.containerApiInRegion(regionId).createIfAbsent(containerName, new CreateContainerOptions()); if (!created) { - deleteAllObjectsInContainer(regionId); + deleteAllObjectsInContainer(regionId, containerName); } } GenericArchive files = ShrinkWrap.create(GenericArchive.class, "files.tar.gz"); @@ -119,24 +116,9 @@ public class BulkApiLiveTest extends BaseSwiftApiLiveTest { @AfterClass(groups = "live") public void tearDown() { for (String regionId : api.configuredRegions()) { - deleteAllObjectsInContainer(regionId); + deleteAllObjectsInContainer(regionId, containerName); api.containerApiInRegion(regionId).deleteIfEmpty(containerName); } super.tearDown(); } - - void deleteAllObjectsInContainer(String regionId) { - ImmutableList<String> pathsToDelete = api.objectApiInRegionForContainer(regionId, containerName) - .list(new ListContainerOptions()).transform(new Function<SwiftObject, String>() { - - public String apply(SwiftObject input) { - return containerName + "/" + input.name(); - } - - }).toList(); - if (!pathsToDelete.isEmpty()) { - BulkDeleteResponse response = api.bulkApiInRegion(regionId).bulkDelete(pathsToDelete); - checkState(response.errors().isEmpty(), "Errors deleting paths %s: %s", pathsToDelete, response); - } - } } http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/cc1e2ae4/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/StaticLargeObjectApiLiveTest.java ---------------------------------------------------------------------- diff --git a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/StaticLargeObjectApiLiveTest.java b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/StaticLargeObjectApiLiveTest.java new file mode 100644 index 0000000..36af318 --- /dev/null +++ b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/StaticLargeObjectApiLiveTest.java @@ -0,0 +1,128 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.openstack.swift.v1.features; + +import static java.lang.String.format; +import static org.jclouds.io.Payloads.newPayload; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotEquals; +import static org.testng.Assert.assertNotNull; + +import java.util.List; +import java.util.UUID; + +import org.jclouds.openstack.swift.v1.domain.Segment; +import org.jclouds.openstack.swift.v1.domain.SwiftObject; +import org.jclouds.openstack.swift.v1.internal.BaseSwiftApiLiveTest; +import org.jclouds.openstack.swift.v1.options.CreateContainerOptions; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +@Test(groups = "live", testName = "StaticLargeObjectApiLiveTest") +public class StaticLargeObjectApiLiveTest extends BaseSwiftApiLiveTest { + + private String name = getClass().getSimpleName(); + private String containerName = getClass().getSimpleName() + "Container"; + private byte[] megOf1s; + private byte[] megOf2s; + + public void notPresentWhenDeleting() throws Exception { + for (String regionId : api.configuredRegions()) { + api.staticLargeObjectApiInRegionForContainer(regionId, containerName).delete(UUID.randomUUID().toString()); + } + } + + public void replaceManifest() throws Exception { + for (String regionId : api.configuredRegions()) { + ObjectApi objectApi = api.objectApiInRegionForContainer(regionId, containerName); + String etag1s = objectApi.replace(name + "/1", newPayload(megOf1s), ImmutableMap.<String, String> of()); + assertMegabyteAndETagMatches(regionId, name + "/1", etag1s); + + String etag2s = objectApi.replace(name + "/2", newPayload(megOf2s), ImmutableMap.<String, String> of()); + assertMegabyteAndETagMatches(regionId, name + "/2", etag2s); + + List<Segment> segments = ImmutableList.<Segment> builder() + .add(Segment.builder() + .path(format("%s/%s/1", containerName, name)).etag(etag1s).sizeBytes(1024 * 1024) + .build()) + .add(Segment.builder() + .path(format("%s/%s/2", containerName, name)).etag(etag2s).sizeBytes(1024 * 1024) + .build()) + .build(); + + String etagOfEtags = api.staticLargeObjectApiInRegionForContainer(regionId, containerName).replaceManifest( + name, segments, ImmutableMap.of("myfoo", "Bar")); + + assertNotNull(etagOfEtags); + + SwiftObject bigObject = api.objectApiInRegionForContainer(regionId, containerName).head(name); + assertNotEquals(bigObject.etag(), etagOfEtags); + assertEquals(bigObject.payload().getContentMetadata().getContentLength(), new Long(2 * 1024 * 1024)); + assertEquals(bigObject.metadata(), ImmutableMap.of("myfoo", "Bar")); + + // segments are visible + assertEquals(api.containerApiInRegion(regionId).get(containerName).objectCount(), 3); + } + } + + @Test(dependsOnMethods = "replaceManifest") + public void delete() throws Exception { + for (String regionId : api.configuredRegions()) { + api.staticLargeObjectApiInRegionForContainer(regionId, containerName).delete(name); + assertEquals(api.containerApiInRegion(regionId).get(containerName).objectCount(), 0); + } + } + + @Override + @BeforeClass(groups = "live") + public void setup() { + super.setup(); + for (String regionId : api.configuredRegions()) { + boolean created = api.containerApiInRegion(regionId).createIfAbsent(containerName, + new CreateContainerOptions()); + if (!created) { + deleteAllObjectsInContainer(regionId, containerName); + } + } + megOf1s = new byte[1024 * 1024]; + megOf2s = new byte[1024 * 1024]; + for (int i = 0; i < 1024 * 1024; i++) { + megOf1s[i] = 1; + megOf2s[i] = 2; + } + } + + @Override + @AfterClass(groups = "live") + public void tearDown() { + for (String regionId : api.configuredRegions()) { + deleteAllObjectsInContainer(regionId, containerName); + api.containerApiInRegion(regionId).deleteIfEmpty(containerName); + } + super.tearDown(); + } + + protected void assertMegabyteAndETagMatches(String regionId, String name, String etag1s) { + SwiftObject object1s = api.objectApiInRegionForContainer(regionId, containerName).head(name); + assertEquals(object1s.etag(), etag1s); + assertEquals(object1s.payload().getContentMetadata().getContentLength(), new Long(1024 * 1024)); + } +} http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/cc1e2ae4/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/StaticLargeObjectApiMockTest.java ---------------------------------------------------------------------- diff --git a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/StaticLargeObjectApiMockTest.java b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/StaticLargeObjectApiMockTest.java new file mode 100644 index 0000000..a44e2d1 --- /dev/null +++ b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/StaticLargeObjectApiMockTest.java @@ -0,0 +1,109 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.openstack.swift.v1.features; + +import static org.testng.Assert.assertEquals; + +import org.jclouds.openstack.swift.v1.SwiftApi; +import org.jclouds.openstack.swift.v1.domain.Segment; +import org.jclouds.openstack.swift.v1.internal.BaseSwiftMockTest; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.net.HttpHeaders; +import com.squareup.okhttp.mockwebserver.MockResponse; +import com.squareup.okhttp.mockwebserver.MockWebServer; +import com.squareup.okhttp.mockwebserver.RecordedRequest; + +@Test +public class StaticLargeObjectApiMockTest extends BaseSwiftMockTest { + + public void replaceManifest() throws Exception { + MockWebServer server = mockSwiftServer(); + server.enqueue(new MockResponse().setBody(access)); + server.enqueue(new MockResponse().addHeader(HttpHeaders.ETAG, "\"abcd\"")); + + try { + SwiftApi api = swiftApi(server.getUrl("/").toString()); + assertEquals( + api.staticLargeObjectApiInRegionForContainer("DFW", "myContainer").replaceManifest( + "myObject", + ImmutableList + .<Segment> builder() + .add(Segment.builder().path("/mycontainer/objseg1").etag("0228c7926b8b642dfb29554cd1f00963") + .sizeBytes(1468006).build()) + .add(Segment.builder().path("/mycontainer/pseudodir/seg-obj2") + .etag("5bfc9ea51a00b790717eeb934fb77b9b").sizeBytes(1572864).build()) + .add(Segment.builder().path("/other-container/seg-final") + .etag("b9c3da507d2557c1ddc51f27c54bae51").sizeBytes(256).build()).build(), + ImmutableMap.of("MyFoo", "Bar")), "abcd"); + + assertEquals(server.getRequestCount(), 2); + assertEquals(server.takeRequest().getRequestLine(), "POST /tokens HTTP/1.1"); + RecordedRequest replaceRequest = server.takeRequest(); + assertEquals(replaceRequest.getRequestLine(), + "PUT /v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/myContainer/myObject?multipart-manifest=put HTTP/1.1"); + assertEquals(replaceRequest.getHeader("x-object-meta-myfoo"), "Bar"); + assertEquals( + new String(replaceRequest.getBody()), + "[{\"path\":\"/mycontainer/objseg1\",\"etag\":\"0228c7926b8b642dfb29554cd1f00963\",\"size_bytes\":1468006}," + + "{\"path\":\"/mycontainer/pseudodir/seg-obj2\",\"etag\":\"5bfc9ea51a00b790717eeb934fb77b9b\",\"size_bytes\":1572864}," + + "{\"path\":\"/other-container/seg-final\",\"etag\":\"b9c3da507d2557c1ddc51f27c54bae51\",\"size_bytes\":256}]"); + } finally { + server.shutdown(); + } + } + + public void delete() throws Exception { + MockWebServer server = mockSwiftServer(); + server.enqueue(new MockResponse().setBody(access)); + server.enqueue(new MockResponse().setResponseCode(204)); + + try { + SwiftApi api = swiftApi(server.getUrl("/").toString()); + api.staticLargeObjectApiInRegionForContainer("DFW", "myContainer").delete("myObject"); + + assertEquals(server.getRequestCount(), 2); + assertEquals(server.takeRequest().getRequestLine(), "POST /tokens HTTP/1.1"); + RecordedRequest deleteRequest = server.takeRequest(); + assertEquals(deleteRequest.getRequestLine(), + "DELETE /v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/myContainer/myObject?multipart-manifest=delete HTTP/1.1"); + } finally { + server.shutdown(); + } + } + + public void alreadyDeleted() throws Exception { + MockWebServer server = mockSwiftServer(); + server.enqueue(new MockResponse().setBody(access)); + server.enqueue(new MockResponse().setResponseCode(404)); + + try { + SwiftApi api = swiftApi(server.getUrl("/").toString()); + api.staticLargeObjectApiInRegionForContainer("DFW", "myContainer").delete("myObject"); + + assertEquals(server.getRequestCount(), 2); + assertEquals(server.takeRequest().getRequestLine(), "POST /tokens HTTP/1.1"); + RecordedRequest deleteRequest = server.takeRequest(); + assertEquals(deleteRequest.getRequestLine(), + "DELETE /v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/myContainer/myObject?multipart-manifest=delete HTTP/1.1"); + } finally { + server.shutdown(); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/cc1e2ae4/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/internal/BaseSwiftApiLiveTest.java ---------------------------------------------------------------------- diff --git a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/internal/BaseSwiftApiLiveTest.java b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/internal/BaseSwiftApiLiveTest.java index fc01490..491a5f6 100644 --- a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/internal/BaseSwiftApiLiveTest.java +++ b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/internal/BaseSwiftApiLiveTest.java @@ -16,11 +16,19 @@ */ package org.jclouds.openstack.swift.v1.internal; +import static com.google.common.base.Preconditions.checkState; + import java.util.Properties; import org.jclouds.apis.BaseApiLiveTest; import org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties; import org.jclouds.openstack.swift.v1.SwiftApi; +import org.jclouds.openstack.swift.v1.domain.BulkDeleteResponse; +import org.jclouds.openstack.swift.v1.domain.SwiftObject; +import org.jclouds.openstack.swift.v1.options.ListContainerOptions; + +import com.google.common.base.Function; +import com.google.common.collect.ImmutableList; public class BaseSwiftApiLiveTest extends BaseApiLiveTest<SwiftApi> { @@ -34,4 +42,19 @@ public class BaseSwiftApiLiveTest extends BaseApiLiveTest<SwiftApi> { setIfTestSystemPropertyPresent(props, KeystoneProperties.CREDENTIAL_TYPE); return props; } + + protected void deleteAllObjectsInContainer(String regionId, final String containerName) { + ImmutableList<String> pathsToDelete = api.objectApiInRegionForContainer(regionId, containerName) + .list(new ListContainerOptions()).transform(new Function<SwiftObject, String>() { + + public String apply(SwiftObject input) { + return containerName + "/" + input.name(); + } + + }).toList(); + if (!pathsToDelete.isEmpty()) { + BulkDeleteResponse response = api.bulkApiInRegion(regionId).bulkDelete(pathsToDelete); + checkState(response.errors().isEmpty(), "Errors deleting paths %s: %s", pathsToDelete, response); + } + } }
