Merge pull request #656 from aplowe/openstack-nova2

Openstack glance: adding create, reserve, update, upload and delete methods to ImageClient
This commit is contained in:
Adrian Cole 2012-05-31 10:14:58 -07:00
commit adf2b5996a
16 changed files with 1362 additions and 105 deletions

View File

@ -81,7 +81,7 @@ public class BaseListOptions extends BaseHttpRequestOptions {
}
/**
* @see BaseListOptions#maxResults(long)
* @see BaseListOptions#maxResults
*/
public static BaseListOptions maxResults(int maxKeys) {
BaseListOptions options = new BaseListOptions();

View File

@ -472,8 +472,7 @@ public class RestAnnotationProcessor<T> {
}
Payload payload = null;
HttpRequestOptions options = findOptionsIn(method, args);
if (options != null) {
for(HttpRequestOptions options : findOptionsIn(method, args)) {
injector.injectMembers(options);// TODO test case
for (Entry<String, String> header : options.buildRequestHeaders().entries()) {
headers.put(header.getKey(), Strings2.replaceTokens(header.getValue(), tokenValues.entries()));
@ -1051,30 +1050,26 @@ public class RestAnnotationProcessor<T> {
}
//TODO: change to LoadingCache<ClassMethodArgs, HttpRequestOptions and move this logic to the CacheLoader.
private HttpRequestOptions findOptionsIn(Method method, Object... args) throws ExecutionException {
for (int index : methodToIndexesOfOptions.get(method)) {
private Set<HttpRequestOptions> findOptionsIn(Method method, Object... args) throws ExecutionException {
ImmutableSet.Builder<HttpRequestOptions> result = ImmutableSet.builder();
for (int index : methodToIndexesOfOptions.get(method)) {
if (args.length >= index + 1) {// accomodate varargs
if (args[index] instanceof Object[]) {
Object[] options = (Object[]) args[index];
if (options.length == 0) {
} else if (options.length == 1) {
if (options[0] instanceof HttpRequestOptions) {
HttpRequestOptions binder = (HttpRequestOptions) options[0];
injector.injectMembers(binder);
return binder;
}
} else {
if (options[0] instanceof HttpRequestOptions) {
throw new IllegalArgumentException("we currently do not support multiple varargs options in: "
+ method.getName());
for (Object option : (Object[]) args[index]) {
if (option instanceof HttpRequestOptions) {
result.add((HttpRequestOptions) option);
}
}
} else {
return (HttpRequestOptions) args[index];
for (; index < args.length; index++) {
if (args[index] instanceof HttpRequestOptions) {
result.add((HttpRequestOptions) args[index]);
}
}
}
}
}
return null;
return result.build();
}
public Multimap<String, String> buildHeaders(Collection<Entry<String, String>> tokenValues, Method method,

View File

@ -511,6 +511,9 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest {
@POST
public void varargs(HttpRequestOptions... options);
@POST
public void varargsWithReq(String required, HttpRequestOptions... options);
@POST
public void post(HttpRequestOptions options);
@ -530,6 +533,12 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest {
assertNonPayloadHeadersEqual(request, "");
assertPayloadEquals(request, "", "application/octet-stream", false);
}
private class TestHttpRequestOptions extends BaseHttpRequestOptions {
TestHttpRequestOptions payload(String payload) { this.payload = payload; return this; }
TestHttpRequestOptions headerParams(Multimap<String, String> headers) { this.headers.putAll(headers); return this; }
TestHttpRequestOptions queryParams(Multimap<String, String> params) { this.queryParameters.putAll(params); return this; }
}
public void testHttpRequestOptionsPayloadParam() throws SecurityException, NoSuchMethodException, IOException {
Method method = TestPayloadParamVarargs.class.getMethod("post", Payload.class);
@ -541,48 +550,51 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest {
public void testHttpRequestWithOnlyContentType() throws SecurityException, NoSuchMethodException, IOException {
Method method = TestPayloadParamVarargs.class.getMethod("post", HttpRequestOptions.class);
verifyTestPostOptions(method);
}
public void testPayloadParamVarargs() throws SecurityException, NoSuchMethodException, IOException {
Method method = TestPayloadParamVarargs.class.getMethod("varargs", Array.newInstance(HttpRequestOptions.class, 0)
.getClass());
verifyTestPostOptions(method);
}
private void verifyTestPostOptions(Method method) throws IOException {
HttpRequest request = factory(TestPayloadParamVarargs.class).createRequest(method, new HttpRequestOptions() {
public Multimap<String, String> buildMatrixParameters() {
return LinkedHashMultimap.create();
}
public String buildPathSuffix() {
return null;
}
public Multimap<String, String> buildQueryParameters() {
return LinkedHashMultimap.create();
}
public Multimap<String, String> buildFormParameters() {
return LinkedHashMultimap.create();
}
public Multimap<String, String> buildRequestHeaders() {
return LinkedHashMultimap.create();
}
public String buildStringPayload() {
return "fooya";
}
});
HttpRequest request = factory(TestPayloadParamVarargs.class).createRequest(method, new TestHttpRequestOptions().payload("fooya"));
assertRequestLineEquals(request, "POST http://localhost:9999 HTTP/1.1");
assertNonPayloadHeadersEqual(request, "");
assertPayloadEquals(request, "fooya", "application/unknown", false);
}
public void testHeaderAndQueryVarargs() throws SecurityException, NoSuchMethodException, IOException {
Method method = TestPayloadParamVarargs.class.getMethod("varargs", Array.newInstance(HttpRequestOptions.class, 0)
.getClass());
HttpRequest request = factory(TestPayloadParamVarargs.class).createRequest(method,
new TestHttpRequestOptions().payload("fooya"),
new TestHttpRequestOptions().headerParams(ImmutableMultimap.of("X-header-1", "fooya")),
new TestHttpRequestOptions().queryParams(ImmutableMultimap.of("key", "value")));
assertRequestLineEquals(request, "POST http://localhost:9999?key=value HTTP/1.1");
assertNonPayloadHeadersEqual(request, "X-header-1: fooya\n");
assertPayloadEquals(request, "fooya", "application/unknown", false);
}
public void testHeaderAndQueryVarargsPlusReq() throws SecurityException, NoSuchMethodException, IOException {
Method method = TestPayloadParamVarargs.class.getMethod("varargsWithReq", String.class, Array.newInstance(HttpRequestOptions.class, 0)
.getClass());
HttpRequest request = factory(TestPayloadParamVarargs.class).createRequest(method, "required param",
new Object[]{ new TestHttpRequestOptions().payload("fooya"),
new TestHttpRequestOptions().headerParams(ImmutableMultimap.of("X-header-1", "fooya")),
new TestHttpRequestOptions().queryParams(ImmutableMultimap.of("key", "value"))});
assertRequestLineEquals(request, "POST http://localhost:9999?key=value HTTP/1.1");
assertNonPayloadHeadersEqual(request, "X-header-1: fooya\n");
assertPayloadEquals(request, "fooya", "application/unknown", false);
}
public void testDuplicateHeaderAndQueryVarargs() throws SecurityException, NoSuchMethodException, IOException {
Method method = TestPayloadParamVarargs.class.getMethod("varargs", Array.newInstance(HttpRequestOptions.class, 0)
.getClass());
HttpRequest request = factory(TestPayloadParamVarargs.class).createRequest(method,
new TestHttpRequestOptions().queryParams(ImmutableMultimap.of("key", "value")),
new TestHttpRequestOptions().payload("fooya"),
new TestHttpRequestOptions().headerParams(ImmutableMultimap.of("X-header-1", "fooya")),
new TestHttpRequestOptions().queryParams(ImmutableMultimap.of("key", "anothervalue")),
new TestHttpRequestOptions().headerParams(ImmutableMultimap.of("X-header-1", "fooya again!")),
new TestHttpRequestOptions().payload("last_payload_wins!"));
assertRequestLineEquals(request, "POST http://localhost:9999?key=value&key=anothervalue HTTP/1.1");
assertNonPayloadHeadersEqual(request, "X-header-1: fooya\nX-header-1: fooya again!\n");
assertPayloadEquals(request, "last_payload_wins!", "application/unknown", false);
}
public class TestCustomMethod {
@FOO
public void foo() {

View File

@ -246,6 +246,10 @@ public class ImageDetails extends Image {
return this.location;
}
public Optional<String> getOwner() {
return owner;
}
public Date getUpdatedAt() {
return this.updatedAt;
}

View File

@ -0,0 +1,57 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds 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.glance.v1_0.domain;
/**
* Backing store types for glance images
*
* @author Adam Lowe
* @see <a href= "http://glance.openstack.org/architecture.html#what-is-a-store" />
*/
public enum StoreType {
/**
* Filesystem store
*/
FILE,
/**
* S3 store
*/
S3,
/**
* Openstack swift store
*/
SWIFT,
/**
* RADOS (Reliable Autonomic Distributed Object Store) Block Device store
*/
RBD,
/**
* HTTP (read-only) store
*/
HTTP;
public String value() {
return name().toLowerCase().replace("_", "+");
}
@Override
public String toString() {
return value();
}
}

View File

@ -21,23 +21,24 @@ package org.jclouds.openstack.glance.v1_0.features;
import java.io.InputStream;
import java.util.Set;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.HEAD;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import org.jclouds.io.Payload;
import org.jclouds.openstack.filters.AuthenticateRequest;
import org.jclouds.openstack.glance.v1_0.domain.Image;
import org.jclouds.openstack.glance.v1_0.domain.ImageDetails;
import org.jclouds.openstack.glance.v1_0.functions.ParseImageDetailsFromHeaders;
import org.jclouds.openstack.glance.v1_0.options.CreateImageOptions;
import org.jclouds.openstack.glance.v1_0.options.ListImageOptions;
import org.jclouds.openstack.glance.v1_0.options.UpdateImageOptions;
import org.jclouds.rest.annotations.ExceptionParser;
import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.ResponseParser;
import org.jclouds.rest.annotations.SelectJson;
import org.jclouds.rest.annotations.SkipEncoding;
import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnFalseOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
import com.google.common.util.concurrent.ListenableFuture;
@ -47,6 +48,7 @@ import com.google.common.util.concurrent.ListenableFuture;
*
* @see ImageClient
* @author Adrian Cole
* @author Adam Lowe
* @see <a href="http://glance.openstack.org/glanceapi.html">api doc</a>
* @see <a href="https://github.com/openstack/glance/blob/master/glance/api/v1/images.py">api src</a>
*/
@ -62,7 +64,7 @@ public interface ImageAsyncClient {
@Consumes(MediaType.APPLICATION_JSON)
@Path("/images")
@ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
ListenableFuture<Set<Image>> list();
ListenableFuture<Set<Image>> list(ListImageOptions... options);
/**
* @see ImageClient#listInDetail
@ -72,7 +74,7 @@ public interface ImageAsyncClient {
@Consumes(MediaType.APPLICATION_JSON)
@Path("/images/detail")
@ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
ListenableFuture<Set<ImageDetails>> listInDetail();
ListenableFuture<Set<ImageDetails>> listInDetail(ListImageOptions... options);
/**
* @see ImageClient#show
@ -91,9 +93,49 @@ public interface ImageAsyncClient {
@ExceptionParser(ReturnNullOnNotFoundOr404.class)
ListenableFuture<InputStream> getAsStream(@PathParam("id") String id);
// POST /images -- Store image data and return metadata about the
// newly-stored image
// PUT /images/<ID> -- Update image metadata and/or upload image
// data for a previously-reserved image
// DELETE /images/<ID> -- Delete the image with id <ID>
/**
* @see ImageClient#create
*/
@POST
@Path("/images")
@Produces(MediaType.APPLICATION_OCTET_STREAM)
@SelectJson("image")
@Consumes(MediaType.APPLICATION_JSON)
ListenableFuture<ImageDetails> create(@HeaderParam("x-image-meta-name") String name, Payload payload, CreateImageOptions... options);
/**
* @see ImageClient#reserve
*/
@POST
@Path("/images")
@SelectJson("image")
@Consumes(MediaType.APPLICATION_JSON)
ListenableFuture<ImageDetails> reserve(@HeaderParam("x-image-meta-name") String name, CreateImageOptions... options);
/**
* @see ImageClient#upload
*/
@PUT
@Path("/images/{id}")
@Produces(MediaType.APPLICATION_OCTET_STREAM)
@SelectJson("image")
@Consumes(MediaType.APPLICATION_JSON)
ListenableFuture<ImageDetails> upload(@PathParam("id") String id, Payload imageData, UpdateImageOptions... options);
/**
* @see ImageClient#update
*/
@PUT
@Path("/images/{id}")
@SelectJson("image")
@Consumes(MediaType.APPLICATION_JSON)
ListenableFuture<ImageDetails> update(@PathParam("id") String id, UpdateImageOptions... options);
/**
* @see ImageClient#delete
*/
@DELETE
@Path("/images/{id}")
@ExceptionParser(ReturnFalseOnNotFoundOr404.class)
ListenableFuture<Boolean> delete(@PathParam("id") String id);
}

View File

@ -23,15 +23,21 @@ import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.jclouds.concurrent.Timeout;
import org.jclouds.io.Payload;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.openstack.glance.v1_0.domain.DiskFormat;
import org.jclouds.openstack.glance.v1_0.domain.Image;
import org.jclouds.openstack.glance.v1_0.domain.ImageDetails;
import org.jclouds.openstack.glance.v1_0.options.CreateImageOptions;
import org.jclouds.openstack.glance.v1_0.options.UpdateImageOptions;
import org.jclouds.openstack.glance.v1_0.options.ListImageOptions;
/**
* Image Services
*
* @see ImageAsyncClient
*
* @author Adrian Cole
* @author Adam Lowe
* @see ImageAsyncClient
* @see <a href="http://glance.openstack.org/glanceapi.html">api doc</a>
*/
@Timeout(duration = 180, timeUnit = TimeUnit.SECONDS)
@ -39,13 +45,13 @@ public interface ImageClient {
/**
* Returns a set of brief metadata about images
*/
Set<Image> list();
Set<Image> list(ListImageOptions... options);
/**
* Returns a set of detailed metadata about images
*/
Set<ImageDetails> listInDetail();
Set<ImageDetails> listInDetail(ListImageOptions... options);
/**
* Return metadata about an image with id
*/
@ -53,14 +59,51 @@ public interface ImageClient {
ImageDetails show(String id);
/**
* Return image data for image with id
* Return image data for image with id
*/
@Nullable
InputStream getAsStream(String id);
// POST /images -- Store image data and return metadata about the
// newly-stored image
// PUT /images/<ID> -- Update image metadata and/or upload image
// data for a previously-reserved image
// DELETE /images/<ID> -- Delete the image with id <ID>
/**
* Create a new image
*
* @return detailed metadata about the newly stored image
*/
ImageDetails create(String name, Payload imageData, CreateImageOptions... options);
/**
* Reserve a new image to be uploaded later
*
* @return detailed metadata about the newly stored image
* @see #upload
*/
ImageDetails reserve(String name, CreateImageOptions... options);
/**
* Adjust the metadata stored for an existing image
*
* @return detailed metadata about the updated image
*/
ImageDetails update(String id, UpdateImageOptions... options);
/**
* Upload image data for a previously-reserved image
* <p/>
* If an image was previously reserved, and thus is in the queued state, then image data can be added using this method.
* If the image already as data associated with it (e.g. not in the queued state), then you will receive a 409
* Conflict exception.
*
* @param imageData the new image to upload
* @param options can be used to adjust the metadata stored for the image in the same call
* @return detailed metadata about the updated image
* @see #reserve
*/
ImageDetails upload(String id, Payload imageData, UpdateImageOptions... options);
/**
* Delete the image with the specified id
*
* @return true if successful
*/
Boolean delete(String id);
}

View File

@ -18,16 +18,19 @@
*/
package org.jclouds.openstack.glance.v1_0.functions;
import static org.jclouds.openstack.glance.v1_0.options.ImageField.*;
import javax.inject.Inject;
import org.jclouds.date.DateService;
import org.jclouds.http.HttpResponse;
import org.jclouds.openstack.glance.v1_0.domain.ContainerFormat;
import org.jclouds.openstack.glance.v1_0.domain.DiskFormat;
import org.jclouds.openstack.glance.v1_0.domain.Image;
import org.jclouds.openstack.glance.v1_0.domain.ImageDetails;
import org.jclouds.openstack.glance.v1_0.domain.Image.Status;
import com.google.common.base.Function;
import com.google.common.base.Optional;
/**
* This parses {@link ImageDetails} from HTTP headers.
@ -44,23 +47,27 @@ public class ParseImageDetailsFromHeaders implements Function<HttpResponse, Imag
public ImageDetails apply(HttpResponse from) {
ImageDetails.Builder<?> builder = ImageDetails.builder()
.id(from.getFirstHeaderOrNull("X-Image-Meta-Id"))
.name(from.getFirstHeaderOrNull("X-Image-Meta-Name"))
.checksum(from.getFirstHeaderOrNull("X-Image-Meta-Checksum"))
.containerFormat(ContainerFormat.fromValue(from.getFirstHeaderOrNull("X-Image-Meta-Container_format")))
.diskFormat(DiskFormat.fromValue(from.getFirstHeaderOrNull("X-Image-Meta-Disk_format")))
.size(Long.parseLong(from.getFirstHeaderOrNull("X-Image-Meta-Size")))
.minDisk(Long.parseLong(from.getFirstHeaderOrNull("X-Image-Meta-Min_disk")))
.minRam(Long.parseLong(from.getFirstHeaderOrNull("X-Image-Meta-Min_ram")))
.isPublic(Boolean.parseBoolean(from.getFirstHeaderOrNull("X-Image-Meta-Is_public")))
.createdAt(dateService.iso8601SecondsDateParse(from.getFirstHeaderOrNull("X-Image-Meta-Created_at")))
.updatedAt(dateService.iso8601SecondsDateParse(from.getFirstHeaderOrNull("X-Image-Meta-Updated_at")))
.owner(from.getFirstHeaderOrNull("X-Image-Meta-Owner"))
.status(Image.Status.fromValue(from.getFirstHeaderOrNull("X-Image-Meta-Status")));
String deletedAt = from.getFirstHeaderOrNull("X-Image-Meta-Deleted_at");
if (deletedAt != null)
builder.deletedAt(dateService.iso8601SecondsDateParse(deletedAt));
.id(from.getFirstHeaderOrNull(ID.asHeader()))
.name(from.getFirstHeaderOrNull(NAME.asHeader()))
.checksum(Optional.fromNullable(from.getFirstHeaderOrNull(CHECKSUM.asHeader())))
.minDisk(Long.parseLong(from.getFirstHeaderOrNull(MIN_DISK.asHeader())))
.minRam(Long.parseLong(from.getFirstHeaderOrNull(MIN_RAM.asHeader())))
.isPublic(Boolean.parseBoolean(from.getFirstHeaderOrNull(IS_PUBLIC.asHeader())))
.createdAt(dateService.iso8601SecondsDateParse(from.getFirstHeaderOrNull(CREATED_AT.asHeader())))
.updatedAt(dateService.iso8601SecondsDateParse(from.getFirstHeaderOrNull(UPDATED_AT.asHeader())))
.owner(Optional.fromNullable(from.getFirstHeaderOrNull(OWNER.asHeader())))
.location(Optional.fromNullable(from.getFirstHeaderOrNull(LOCATION.asHeader())))
.status(Status.fromValue(from.getFirstHeaderOrNull(STATUS.asHeader())));
String containerFormat = from.getFirstHeaderOrNull(CONTAINER_FORMAT.asHeader());
String diskFormat = from.getFirstHeaderOrNull(DISK_FORMAT.asHeader());
String deletedAt = from.getFirstHeaderOrNull(DELETED_AT.asHeader());
String size = from.getFirstHeaderOrNull(SIZE.asHeader());
if (containerFormat != null) builder.containerFormat(ContainerFormat.fromValue(containerFormat));
if (diskFormat != null) builder.diskFormat(DiskFormat.fromValue(diskFormat));
if (deletedAt != null) builder.deletedAt(dateService.iso8601SecondsDateParse(deletedAt));
if (size != null) builder.size(Long.parseLong(size));
return builder.build();
}

View File

@ -0,0 +1,138 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds 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.glance.v1_0.options;
import org.jclouds.openstack.glance.v1_0.domain.ContainerFormat;
import org.jclouds.openstack.glance.v1_0.domain.DiskFormat;
import org.jclouds.openstack.glance.v1_0.domain.StoreType;
/**
*
* <h2></h2>Usage</h2> The recommended way to instantiate a CreateImageOptions object is to statically import
* CreateImageOptions.Builder.* and invoke a static creation method for each option as needed:
* <p/>
* <code>
* import static org.jclouds.openstack.glance.v1_0.options.CreateImageOptions.Builder.*
*
*
* // this will create an image with the name "imageName", minimum required disk of 10GB, etc.
* details = client.create("imageName", minDisk(10), isPublic(true), property("mykey", "somevalue"));
* <code>
* @author Adam Lowe
* @see <a href="http://glance.openstack.org/glanceapi.html"/>
*/
public class CreateImageOptions extends UpdateImageOptions {
/**
* When present, Glance will use the supplied identifier for the image instead of generating one. If the identifier
* already exists in that Glance node, then a 409 Conflict will be returned by Glance. The value of the header must
* be a uuid in hexadecimal string notation (i.e. 71c675ab-d94f-49cd-a114-e12490b328d9).
*/
public CreateImageOptions id(String id) {
headers.put(ImageField.ID.asHeader(), id);
return this;
}
public static class Builder {
/**
* @see org.jclouds.openstack.glance.v1_0.options.CreateImageOptions#id
*/
public static CreateImageOptions id(String id) {
return new CreateImageOptions().id(id);
}
/**
* @see org.jclouds.openstack.glance.v1_0.options.CreateImageOptions#storeType
*/
public static CreateImageOptions storeType(StoreType storeType) {
return CreateImageOptions.class.cast(new CreateImageOptions().storeType(storeType));
}
/**
* @see org.jclouds.openstack.glance.v1_0.options.CreateImageOptions#diskFormat
*/
public static CreateImageOptions diskFormat(DiskFormat diskFormat) {
return CreateImageOptions.class.cast(new CreateImageOptions().diskFormat(diskFormat));
}
/**
* @see org.jclouds.openstack.glance.v1_0.options.CreateImageOptions#containerFormat
*/
public static CreateImageOptions containerFormat(ContainerFormat containerFormat) {
return CreateImageOptions.class.cast(new CreateImageOptions().containerFormat(containerFormat));
}
/**
* @see org.jclouds.openstack.glance.v1_0.options.CreateImageOptions#size
*/
public static CreateImageOptions size(long size) {
return CreateImageOptions.class.cast(new CreateImageOptions().size(size));
}
/**
* @see org.jclouds.openstack.glance.v1_0.options.CreateImageOptions#checksum
*/
public static CreateImageOptions checksum(String checksum) {
return CreateImageOptions.class.cast(new CreateImageOptions().checksum(checksum));
}
/**
* @see org.jclouds.openstack.glance.v1_0.options.CreateImageOptions#isPublic
*/
public static CreateImageOptions isPublic(boolean isPublic) {
return CreateImageOptions.class.cast(new CreateImageOptions().isPublic(isPublic));
}
/**
* @see org.jclouds.openstack.glance.v1_0.options.CreateImageOptions#isProtected
*/
public static CreateImageOptions isProtected(boolean isProtected) {
return CreateImageOptions.class.cast(new CreateImageOptions().isProtected(isProtected));
}
/**
* @see org.jclouds.openstack.glance.v1_0.options.CreateImageOptions#minRam
*/
public static CreateImageOptions minRam(long ram) {
return CreateImageOptions.class.cast(new CreateImageOptions().minRam(ram));
}
/**
* @see org.jclouds.openstack.glance.v1_0.options.CreateImageOptions#minDisk
*/
public static CreateImageOptions minDisk(long disk) {
return CreateImageOptions.class.cast(new CreateImageOptions().minDisk(disk));
}
/**
* @see org.jclouds.openstack.glance.v1_0.options.CreateImageOptions#owner
*/
public static CreateImageOptions owner(String owner) {
return CreateImageOptions.class.cast(new CreateImageOptions().owner(owner));
}
/**
* @see org.jclouds.openstack.glance.v1_0.options.CreateImageOptions#property
*/
public static CreateImageOptions property(String key, String value) {
return CreateImageOptions.class.cast(new CreateImageOptions().property(key, value));
}
}
}

View File

@ -0,0 +1,45 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds 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.glance.v1_0.options;
import static com.google.common.base.CaseFormat.LOWER_UNDERSCORE;
import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
import org.jclouds.util.Strings2;
import com.google.common.base.CaseFormat;
import com.google.common.base.Strings;
/**
* Fields used in Glance options
*/
public enum ImageField {
ID, NAME, CHECKSUM, MIN_DISK, MIN_RAM, IS_PUBLIC, PROTECTED, CREATED_AT, UPDATED_AT, DELETED_AT,
OWNER, LOCATION, STATUS, DISK_FORMAT, CONTAINER_FORMAT, SIZE, SIZE_MIN, SIZE_MAX, STORE, PROPERTY;
public static final String HEADER_PREFIX = "X-Image-Meta-";
public String asParam() {
return name().toLowerCase();
}
public String asHeader() {
return HEADER_PREFIX + name().charAt(0) + name().substring(1).toLowerCase();
}
}

View File

@ -0,0 +1,265 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds 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.glance.v1_0.options;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static org.jclouds.openstack.glance.v1_0.options.ImageField.*;
import org.jclouds.http.options.BaseHttpRequestOptions;
import org.jclouds.openstack.glance.v1_0.domain.ContainerFormat;
import org.jclouds.openstack.glance.v1_0.domain.DiskFormat;
import org.jclouds.openstack.glance.v1_0.domain.Image.Status;
/**
* <h2></h2>Usage</h2> The recommended way to instantiate a ListImageOptions object is to statically import
* ListImageOptions.Builder.* and invoke a static creation method for each option as needed:
* <p/>
* <code>
* import static org.jclouds.openstack.glance.v1_0.options.ListImageOptions.Builder.*
*
*
* // this will list the first 10 images with the name "name", minimum required disk of 5GB.
* list = client.list(name("newName"), limit(10), minDisk(5));
* <code>
*
* @author Adam Lowe
* @see <a href="http://glance.openstack.org/glanceapi.html"/>
*/
public class ListImageOptions extends BaseHttpRequestOptions {
public static final ListImageOptions NONE = new ListImageOptions();
/**
* Given a string value x, return object names greater in value than the specified marker.
*/
public ListImageOptions marker(String marker) {
queryParameters.put("marker", checkNotNull(marker, "marker"));
return this;
}
/**
* For an integer value n, limits the number of results to n values.
*/
public ListImageOptions limit(int limit) {
checkState(limit >= 0, "limit must be >= 0");
checkState(limit <= 10000, "limit must be <= 10000");
queryParameters.put("limit", Integer.toString(limit));
return this;
}
/**
* Return only those images having a matching name attribute
*/
public ListImageOptions name(String name) {
queryParameters.put(NAME.asParam(), name);
return this;
}
/**
* Return only those images that have the requested status
*/
public ListImageOptions status(Status status) {
queryParameters.put(STATUS.asParam(), status.toString());
return this;
}
/**
* Return only those images having a matching container format
*/
public ListImageOptions containerFormat(ContainerFormat containerFormat) {
queryParameters.put(CONTAINER_FORMAT.asParam(), containerFormat.toString());
return this;
}
/**
* Return only those images having a matching disk format
*/
public ListImageOptions diskFormat(DiskFormat diskFormat) {
queryParameters.put(DISK_FORMAT.asParam(), diskFormat.toString());
return this;
}
/**
* Return only those images having a matching min ram size
*/
public ListImageOptions minRam(long ram) {
queryParameters.put(MIN_RAM.asParam(), Long.toString(ram));
return this;
}
/**
* Return only those images having a matching min disk size
*/
public ListImageOptions minDisk(long disk) {
queryParameters.put(MIN_DISK.asParam(), Long.toString(disk));
return this;
}
/**
* Return those images that have a size attribute greater than or equal to size
*/
public ListImageOptions minSize(long size) {
queryParameters.put(SIZE_MIN.asParam(), Long.toString(size));
return this;
}
/**
* Return those images that have a size attribute less than or equal to size
*/
public ListImageOptions maxSize(long size) {
queryParameters.put(SIZE_MAX.asParam(), Long.toString(size));
return this;
}
/**
* Return only public images or only private images
*/
public ListImageOptions isPublic(boolean isPublic) {
queryParameters.put(IS_PUBLIC.asParam(), Boolean.toString(isPublic));
return this;
}
/**
* Filter to only protected or unprotected images
*/
public ListImageOptions isProtected(boolean isProtected) {
queryParameters.put(PROTECTED.asParam(), Boolean.toString(isProtected));
return this;
}
/**
* Results will be ordered by the specified image attribute.
*/
public ListImageOptions sortBy(ImageField key) {
queryParameters.put("sort_key", key.asParam());
return this;
}
/**
* Ascending sort order (smallest first).
* <p/>
* NOTE: default behavior is to sort descending (largest first)
*/
public ListImageOptions sortAscending() {
queryParameters.put("sort_dir", "asc");
return this;
}
public static class Builder {
/**
* @see ListImageOptions#name
*/
public static ListImageOptions name(String name) {
return new ListImageOptions().name(name);
}
/**
* @see ListImageOptions#diskFormat
*/
public static ListImageOptions diskFormat(DiskFormat diskFormat) {
return new ListImageOptions().diskFormat(diskFormat);
}
/**
* @see ListImageOptions#containerFormat
*/
public static ListImageOptions containerFormat(ContainerFormat containerFormat) {
return new ListImageOptions().containerFormat(containerFormat);
}
/**
* @see ListImageOptions#minRam
*/
public static ListImageOptions minRam(long size) {
return new ListImageOptions().minRam(size);
}
/**
* @see ListImageOptions#minDisk
*/
public static ListImageOptions minDisk(long size) {
return new ListImageOptions().minDisk(size);
}
/**
* @see ListImageOptions#minSize
*/
public static ListImageOptions minSize(long size) {
return new ListImageOptions().minSize(size);
}
/**
* @see ListImageOptions#maxSize
*/
public static ListImageOptions maxSize(long size) {
return new ListImageOptions().maxSize(size);
}
/**
* @see ListImageOptions#sortBy
*/
public static ListImageOptions status(Status status) {
return new ListImageOptions().status(status);
}
/**
* @see ListImageOptions#sortBy
*/
public static ListImageOptions sortBy(ImageField sortKey) {
return new ListImageOptions().sortBy(sortKey);
}
/**
* @see ListImageOptions#sortAscending
*/
public static ListImageOptions sortAscending() {
return new ListImageOptions().sortAscending();
}
/**
* @see ListImageOptions#isPublic
*/
public static ListImageOptions isPublic(boolean isPublic) {
return ListImageOptions.class.cast(new ListImageOptions().isPublic(isPublic));
}
/**
* @see ListImageOptions#isProtected
*/
public static ListImageOptions isProtected(boolean isProtected) {
return ListImageOptions.class.cast(new ListImageOptions().isProtected(isProtected));
}
/**
* @see ListImageOptions#limit
*/
public static ListImageOptions limit(int limit) {
return new ListImageOptions().limit(limit);
}
/**
* @see ListImageOptions#marker
*/
public static ListImageOptions marker(String marker) {
ListImageOptions options = new ListImageOptions();
return options.marker(marker);
}
}
}

View File

@ -0,0 +1,243 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds 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.glance.v1_0.options;
import static org.jclouds.openstack.glance.v1_0.options.ImageField.*;
import org.jclouds.http.options.BaseHttpRequestOptions;
import org.jclouds.openstack.glance.v1_0.domain.ContainerFormat;
import org.jclouds.openstack.glance.v1_0.domain.DiskFormat;
import org.jclouds.openstack.glance.v1_0.domain.StoreType;
/**
* <h2></h2>Usage</h2> The recommended way to instantiate a UpdateImageOptions object is to statically import
* UpdateImageOptions.Builder.* and invoke a static creation method for each option as needed:
* <p/>
* <code>
* import static org.jclouds.openstack.glance.v1_0.options.UpdateImageOptions.Builder.*
*
*
* // this will adjust the image with id 'id' the name "newName", minimum required disk of 5GB, etc.
* details = client.update(id, name("newName"), minDisk(5), isPublic(true), property("mykey", "somevalue"));
* <code>
* @author Adam Lowe
* @see <a href="http://glance.openstack.org/glanceapi.html"/>
*/
public class UpdateImageOptions extends BaseHttpRequestOptions {
/**
* Adjust the name of the image
*/
public UpdateImageOptions name(String name) {
headers.put(NAME.asHeader(), name);
return this;
}
/**
* When present, Glance will attempt to store the disk image data in the backing store indicated by the value of the
* header. If the Glance node does not support the backing store, Glance will return a 400 Bad Request.
*/
public UpdateImageOptions storeType(StoreType storeType) {
headers.put(STORE.asHeader(), storeType.toString());
return this;
}
public UpdateImageOptions diskFormat(DiskFormat diskFormat) {
headers.put(DISK_FORMAT.asHeader(), diskFormat.toString());
return this;
}
public UpdateImageOptions containerFormat(ContainerFormat containerFormat) {
headers.put(CONTAINER_FORMAT.asHeader(), containerFormat.toString());
return this;
}
/**
* When present, Glance assumes that the expected size of the request body will be the value of this header. If the
* length in bytes of the request body does not match the value of this header, Glance will return a 400 Bad Request.
*/
public UpdateImageOptions size(long size) {
headers.put(SIZE.asHeader(), Long.toString(size));
return this;
}
/**
* MD5 checksum of the image
* <p/>
* When present, Glance will verify the checksum generated from the backend store when storing your image against
* this value and return a 400 Bad Request if the values do not match.
*/
public UpdateImageOptions checksum(String checksum) {
headers.put(CHECKSUM.asHeader(), checksum);
return this;
}
public UpdateImageOptions location(String location) {
headers.put(LOCATION.asHeader(), location);
return this;
}
/**
* Mark the image as public, meaning that any user may view its metadata and may read the disk image
* from Glance.
*/
public UpdateImageOptions isPublic(boolean isPublic) {
headers.put(IS_PUBLIC.asHeader(), Boolean.toString(isPublic));
return this;
}
/**
* Mark the image as protected - if set to true the image cannot be deleted till it is unset.
*/
public UpdateImageOptions isProtected(boolean isProtected) {
headers.put(PROTECTED.asHeader(), Boolean.toString(isProtected));
return this;
}
/**
* The expected minimum ram required in megabytes to run this image on a server (default 0).
*/
public UpdateImageOptions minRam(long ram) {
headers.put(MIN_RAM.asHeader(), Long.toString(ram));
return this;
}
/**
* The expected minimum disk required in gigabytes to run this image on a server (default 0).
*/
public UpdateImageOptions minDisk(long disk) {
headers.put(MIN_DISK.asHeader(), Long.toString(disk));
return this;
}
/**
* Glance normally sets the owner of an image to be the tenant or user (depending on the owner_is_tenant
* configuration option) of the authenticated user issuing the request. However, if the authenticated user has the
* Admin role, this default may be overridden by setting this header to null or to a string identifying the owner of
* the image.
*/
public UpdateImageOptions owner(String owner) {
headers.put(OWNER.asHeader(), owner);
return this;
}
/**
* Custom, free-form image properties stored with the image.
*/
public UpdateImageOptions property(String key, String value) {
if (!key.toLowerCase().startsWith(PROPERTY.asHeader() + "-")) {
key = PROPERTY.asHeader() + "-" + key;
}
headers.put(key, value);
return this;
}
public static class Builder {
/**
* @see UpdateImageOptions#name
*/
public static UpdateImageOptions name(String name) {
return new UpdateImageOptions().name(name);
}
/**
* @see UpdateImageOptions#storeType
*/
public static UpdateImageOptions storeType(StoreType storeType) {
return new UpdateImageOptions().storeType(storeType);
}
/**
* @see UpdateImageOptions#diskFormat
*/
public static UpdateImageOptions diskFormat(DiskFormat diskFormat) {
return new UpdateImageOptions().diskFormat(diskFormat);
}
/**
* @see UpdateImageOptions#containerFormat
*/
public static UpdateImageOptions containerFormat(ContainerFormat containerFormat) {
return new UpdateImageOptions().containerFormat(containerFormat);
}
/**
* @see UpdateImageOptions#size
*/
public static UpdateImageOptions size(long size) {
return new UpdateImageOptions().size(size);
}
/**
* @see UpdateImageOptions#checksum
*/
public static UpdateImageOptions checksum(String checksum) {
return new UpdateImageOptions().checksum(checksum);
}
/**
* @see UpdateImageOptions#location
*/
public static UpdateImageOptions location(String location) {
return new UpdateImageOptions().location(location);
}
/**
* @see UpdateImageOptions#isPublic
*/
public static UpdateImageOptions isPublic(boolean isPublic) {
return new UpdateImageOptions().isPublic(isPublic);
}
/**
* @see UpdateImageOptions#isProtected
*/
public static UpdateImageOptions isProtected(boolean isProtected) {
return new UpdateImageOptions().isProtected(isProtected);
}
/**
* @see UpdateImageOptions#minRam
*/
public static UpdateImageOptions minRam(long ram) {
return new UpdateImageOptions().minRam(ram);
}
/**
* @see UpdateImageOptions#minDisk
*/
public static UpdateImageOptions minDisk(long disk) {
return new UpdateImageOptions().minDisk(disk);
}
/**
* @see UpdateImageOptions#owner
*/
public static UpdateImageOptions owner(String owner) {
return new UpdateImageOptions().owner(owner);
}
/**
* @see UpdateImageOptions#property
*/
public static UpdateImageOptions property(String key, String value) {
return new UpdateImageOptions().property(key, value);
}
}
}

View File

@ -19,19 +19,28 @@
package org.jclouds.openstack.glance.v1_0.features;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
import java.net.URI;
import javax.ws.rs.core.MediaType;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.io.Payloads;
import org.jclouds.io.payloads.StringPayload;
import org.jclouds.openstack.glance.v1_0.GlanceClient;
import org.jclouds.openstack.glance.v1_0.functions.ParseImageDetailsFromHeadersTest;
import org.jclouds.openstack.glance.v1_0.internal.BaseGlanceClientExpectTest;
import org.jclouds.openstack.glance.v1_0.options.UpdateImageOptions;
import org.jclouds.openstack.glance.v1_0.parse.ParseImageDetailsTest;
import org.jclouds.openstack.glance.v1_0.parse.ParseImagesInDetailTest;
import org.jclouds.openstack.glance.v1_0.parse.ParseImagesTest;
import org.jclouds.rest.AuthorizationException;
import org.jclouds.rest.ResourceNotFoundException;
import org.jclouds.util.Strings2;
import org.testng.annotations.Test;
@ -40,6 +49,7 @@ import com.google.common.collect.ImmutableSet;
/**
* @author Adrian Cole
* @author Adam Lowe
*/
@Test(groups = "unit", testName = "ImageClientExpectTest")
public class ImageClientExpectTest extends BaseGlanceClientExpectTest {
@ -198,5 +208,273 @@ public class ImageClientExpectTest extends BaseGlanceClientExpectTest {
assertNull(clientWhenNoExist.getImageClientForRegion("az-1.region-a.geo-1").getAsStream("fcc451d0-f6e4-4824-ad8f-70ec12326d07"));
}
public void testCreateWhenResponseIs2xx() throws Exception {
HttpRequest get = HttpRequest
.builder()
.method("POST")
.endpoint(URI.create("https://glance.jclouds.org:9292/v1.0/images"))
.headers(
ImmutableMultimap.<String, String>builder()
.put("x-image-meta-name", "test").put("Accept", MediaType.APPLICATION_JSON).put("X-Auth-Token", authToken).build())
.payload(payloadFromStringWithContentType("somedata", MediaType.APPLICATION_OCTET_STREAM))
.build();
HttpResponse createResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResource("/image.json")).build();
GlanceClient clientWhenExist = requestsSendResponses(keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, get, createResponse);
assertEquals(clientWhenExist.getConfiguredRegions(), ImmutableSet.of("az-1.region-a.geo-1"));
assertEquals(clientWhenExist.getImageClientForRegion("az-1.region-a.geo-1").create("test", new StringPayload("somedata")),
new ParseImageDetailsTest().expected());
}
@Test(expectedExceptions = AuthorizationException.class)
public void testCreateWhenResponseIs4xx() throws Exception {
HttpRequest get = HttpRequest
.builder()
.method("POST")
.endpoint(URI.create("https://glance.jclouds.org:9292/v1.0/images"))
.headers(
ImmutableMultimap.<String, String>builder()
.put("x-image-meta-name", "test").put("Accept", MediaType.APPLICATION_JSON).put("X-Auth-Token", authToken).build())
.payload(payloadFromStringWithContentType("somedata", MediaType.APPLICATION_OCTET_STREAM))
.build();
HttpResponse createResponse = HttpResponse.builder().statusCode(401)
.payload(payloadFromResource("/image.json")).build();
GlanceClient clientWhenExist = requestsSendResponses(keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, get, createResponse);
assertEquals(clientWhenExist.getConfiguredRegions(), ImmutableSet.of("az-1.region-a.geo-1"));
clientWhenExist.getImageClientForRegion("az-1.region-a.geo-1").create("test", new StringPayload("somedata"));
}
public void testReserveWhenResponseIs2xx() throws Exception {
HttpRequest get = HttpRequest
.builder()
.method("POST")
.endpoint(URI.create("https://glance.jclouds.org:9292/v1.0/images"))
.headers(
ImmutableMultimap.<String, String>builder()
.put("x-image-meta-name", "test").put("Accept", MediaType.APPLICATION_JSON).put("X-Auth-Token", authToken).build())
.build();
HttpResponse createResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResource("/image.json")).build();
GlanceClient clientWhenExist = requestsSendResponses(keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, get, createResponse);
assertEquals(clientWhenExist.getConfiguredRegions(), ImmutableSet.of("az-1.region-a.geo-1"));
assertEquals(clientWhenExist.getImageClientForRegion("az-1.region-a.geo-1").reserve("test"), new ParseImageDetailsTest().expected());
}
@Test(expectedExceptions = AuthorizationException.class)
public void testReserveWhenResponseIs4xx() throws Exception {
HttpRequest get = HttpRequest
.builder()
.method("POST")
.endpoint(URI.create("https://glance.jclouds.org:9292/v1.0/images"))
.headers(
ImmutableMultimap.<String, String>builder()
.put("x-image-meta-name", "test").put("Accept", MediaType.APPLICATION_JSON).put("X-Auth-Token", authToken).build())
.build();
HttpResponse createResponse = HttpResponse.builder().statusCode(401)
.payload(payloadFromResource("/image.json")).build();
GlanceClient clientWhenExist = requestsSendResponses(keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, get, createResponse);
assertEquals(clientWhenExist.getConfiguredRegions(), ImmutableSet.of("az-1.region-a.geo-1"));
clientWhenExist.getImageClientForRegion("az-1.region-a.geo-1").reserve("test");
}
public void testUpdateMetadataWhenResponseIs2xx() throws Exception {
HttpRequest get = HttpRequest
.builder()
.method("PUT")
.endpoint(URI.create("https://glance.jclouds.org:9292/v1.0/images/fcc451d0-f6e4-4824-ad8f-70ec12326d07"))
.headers(
ImmutableMultimap.<String, String>builder()
.put("Accept", MediaType.APPLICATION_JSON)
.put("X-Image-Meta-Name", "newname")
.put("X-Image-Meta-Is_public", "true")
.put("X-Image-Meta-Protected", "true")
.put("X-Image-Meta-Checksum", "XXXX")
.put("X-Image-Meta-Location", "somewhere")
.put("X-Image-Meta-Min_disk", "10")
.put("X-Image-Meta-Min_ram", "2048")
.put("X-Auth-Token", authToken).build())
.build();
HttpResponse updateResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResource("/image.json")).build();
GlanceClient clientWhenExist = requestsSendResponses(keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, get, updateResponse);
assertEquals(clientWhenExist.getConfiguredRegions(), ImmutableSet.of("az-1.region-a.geo-1"));
assertEquals(clientWhenExist.getImageClientForRegion("az-1.region-a.geo-1")
.update("fcc451d0-f6e4-4824-ad8f-70ec12326d07",
UpdateImageOptions.Builder.name("newname"),
UpdateImageOptions.Builder.isPublic(true),
UpdateImageOptions.Builder.isProtected(true),
UpdateImageOptions.Builder.checksum("XXXX"),
UpdateImageOptions.Builder.location("somewhere"),
UpdateImageOptions.Builder.minDisk(10),
UpdateImageOptions.Builder.minRam(2048)),
new ParseImageDetailsTest().expected());
}
@Test(expectedExceptions = ResourceNotFoundException.class)
public void testUpdateMetadataWhenResponseIs4xx() throws Exception {
HttpRequest get = HttpRequest
.builder()
.method("PUT")
.endpoint(URI.create("https://glance.jclouds.org:9292/v1.0/images/fcc451d0-f6e4-4824-ad8f-70ec12326d07"))
.headers(
ImmutableMultimap.<String, String>builder()
.put("Accept", MediaType.APPLICATION_JSON)
.put("X-Image-Meta-Name", "newname")
.put("X-Image-Meta-Is_public", "true")
.put("X-Auth-Token", authToken).build())
.build();
HttpResponse updateResponse = HttpResponse.builder().statusCode(404).build();
GlanceClient clientWhenExist = requestsSendResponses(keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, get, updateResponse);
assertEquals(clientWhenExist.getConfiguredRegions(), ImmutableSet.of("az-1.region-a.geo-1"));
clientWhenExist.getImageClientForRegion("az-1.region-a.geo-1")
.update("fcc451d0-f6e4-4824-ad8f-70ec12326d07",
UpdateImageOptions.Builder.name("newname"),
UpdateImageOptions.Builder.isPublic(true));
}
public void testUpdateImageWhenResponseIs2xx() throws Exception {
HttpRequest get = HttpRequest
.builder()
.method("PUT")
.endpoint(URI.create("https://glance.jclouds.org:9292/v1.0/images/fcc451d0-f6e4-4824-ad8f-70ec12326d07"))
.headers(
ImmutableMultimap.<String, String>builder()
.put("Accept", MediaType.APPLICATION_JSON).put("X-Auth-Token", authToken).build())
.payload(payloadFromStringWithContentType("somenewdata", MediaType.APPLICATION_OCTET_STREAM))
.build();
HttpResponse updateResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResource("/image.json")).build();
GlanceClient clientWhenExist = requestsSendResponses(keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, get, updateResponse);
assertEquals(clientWhenExist.getConfiguredRegions(), ImmutableSet.of("az-1.region-a.geo-1"));
assertEquals(clientWhenExist.getImageClientForRegion("az-1.region-a.geo-1").upload("fcc451d0-f6e4-4824-ad8f-70ec12326d07",
new StringPayload("somenewdata")), new ParseImageDetailsTest().expected());
}
public void testUpdateNameAndImageWhenResponseIs2xx() throws Exception {
HttpRequest get = HttpRequest
.builder()
.method("PUT")
.endpoint(URI.create("https://glance.jclouds.org:9292/v1.0/images/fcc451d0-f6e4-4824-ad8f-70ec12326d07"))
.headers(
ImmutableMultimap.<String, String>builder()
.put("Accept", MediaType.APPLICATION_JSON)
.put("X-Image-Meta-Name", "anothernewname")
.put("X-Auth-Token", authToken).build())
.payload(payloadFromStringWithContentType("somenewdata", MediaType.APPLICATION_OCTET_STREAM))
.build();
HttpResponse updateResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResource("/image.json")).build();
GlanceClient clientWhenExist = requestsSendResponses(keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, get, updateResponse);
assertEquals(clientWhenExist.getConfiguredRegions(), ImmutableSet.of("az-1.region-a.geo-1"));
assertEquals(clientWhenExist.getImageClientForRegion("az-1.region-a.geo-1").upload("fcc451d0-f6e4-4824-ad8f-70ec12326d07",
new StringPayload("somenewdata"), UpdateImageOptions.Builder.name("anothernewname")), new ParseImageDetailsTest().expected());
}
@Test(expectedExceptions = AuthorizationException.class)
public void testUpdateNameAndImageWhenResponseIs4xx() throws Exception {
HttpRequest get = HttpRequest
.builder()
.method("PUT")
.endpoint(URI.create("https://glance.jclouds.org:9292/v1.0/images/fcc451d0-f6e4-4824-ad8f-70ec12326d07"))
.headers(
ImmutableMultimap.<String, String>builder()
.put("Accept", MediaType.APPLICATION_JSON)
.put("X-Image-Meta-Name", "anothernewname")
.put("X-Auth-Token", authToken).build())
.payload(payloadFromStringWithContentType("somenewdata", MediaType.APPLICATION_OCTET_STREAM))
.build();
HttpResponse updateResponse = HttpResponse.builder().statusCode(403).build();
GlanceClient clientWhenExist = requestsSendResponses(keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, get, updateResponse);
assertEquals(clientWhenExist.getConfiguredRegions(), ImmutableSet.of("az-1.region-a.geo-1"));
clientWhenExist.getImageClientForRegion("az-1.region-a.geo-1").upload("fcc451d0-f6e4-4824-ad8f-70ec12326d07",
new StringPayload("somenewdata"), UpdateImageOptions.Builder.name("anothernewname"));
}
public void testDeleteWhenResponseIs2xx() throws Exception {
HttpRequest get = HttpRequest
.builder()
.method("DELETE")
.endpoint(URI.create("https://glance.jclouds.org:9292/v1.0/images/fcc451d0-f6e4-4824-ad8f-70ec12326d07"))
.headers(
ImmutableMultimap.<String, String>builder()
.put("X-Auth-Token", authToken).build())
.build();
HttpResponse getResponse = HttpResponse.builder().statusCode(200).build();
GlanceClient clientWhenExist = requestsSendResponses(keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, get, getResponse);
assertEquals(clientWhenExist.getConfiguredRegions(), ImmutableSet.of("az-1.region-a.geo-1"));
assertTrue(clientWhenExist.getImageClientForRegion("az-1.region-a.geo-1").delete("fcc451d0-f6e4-4824-ad8f-70ec12326d07"));
}
public void testDeleteWhenResponseIs4xx() throws Exception {
HttpRequest get = HttpRequest
.builder()
.method("DELETE")
.endpoint(URI.create("https://glance.jclouds.org:9292/v1.0/images/fcc451d0-f6e4-4824-ad8f-70ec12326d07"))
.headers(
ImmutableMultimap.<String, String>builder()
.put("X-Auth-Token", authToken).build())
.build();
HttpResponse getResponse = HttpResponse.builder().statusCode(404).build();
GlanceClient clientWhenExist = requestsSendResponses(keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, get, getResponse);
assertEquals(clientWhenExist.getConfiguredRegions(), ImmutableSet.of("az-1.region-a.geo-1"));
assertFalse(clientWhenExist.getImageClientForRegion("az-1.region-a.geo-1").delete("fcc451d0-f6e4-4824-ad8f-70ec12326d07"));
}
}

View File

@ -18,17 +18,29 @@
*/
package org.jclouds.openstack.glance.v1_0.features;
import static org.jclouds.openstack.glance.v1_0.options.CreateImageOptions.Builder.containerFormat;
import static org.jclouds.openstack.glance.v1_0.options.CreateImageOptions.Builder.diskFormat;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import java.util.Set;
import org.jclouds.io.payloads.StringPayload;
import org.jclouds.openstack.glance.v1_0.domain.ContainerFormat;
import org.jclouds.openstack.glance.v1_0.domain.DiskFormat;
import org.jclouds.openstack.glance.v1_0.domain.Image;
import org.jclouds.openstack.glance.v1_0.domain.ImageDetails;
import org.jclouds.openstack.glance.v1_0.internal.BaseGlanceClientLiveTest;
import org.jclouds.openstack.glance.v1_0.options.ListImageOptions;
import org.jclouds.openstack.glance.v1_0.options.UpdateImageOptions;
import org.testng.annotations.Test;
import com.google.common.collect.Iterables;
/**
* @author Adrian Cole
* @author Adam Lowe
*/
@Test(groups = "live", testName = "ImageClientLiveTest")
public class ImageClientLiveTest extends BaseGlanceClientLiveTest {
@ -37,7 +49,7 @@ public class ImageClientLiveTest extends BaseGlanceClientLiveTest {
public void testList() throws Exception {
for (String zoneId : glanceContext.getApi().getConfiguredRegions()) {
ImageClient client = glanceContext.getApi().getImageClientForRegion(zoneId);
Set<Image> response = client.list();
Set<Image> response = client.list(ListImageOptions.Builder.limit(100));
assert null != response;
for (Image image : response) {
checkImage(image);
@ -47,10 +59,8 @@ public class ImageClientLiveTest extends BaseGlanceClientLiveTest {
private void checkImage(Image image) {
assert image.getId() != null : image;
assert image.getSize().isPresent() : image;
assert image.getChecksum().isPresent() : image;
assert image.getContainerFormat().isPresent() : image;
assert image.getContainerFormat().isPresent() : image;
assert image.getName() != null : image;
assert image.getLinks() != null : image;
}
@Test
@ -69,7 +79,9 @@ public class ImageClientLiveTest extends BaseGlanceClientLiveTest {
}
private void checkImageDetails(ImageDetails image) {
//TODO
checkImage(image);
assertTrue(image.getMinDisk() >= 0);
assertTrue(image.getMinRam() >= 0);
}
private void checkImageDetailsEqual(ImageDetails image, ImageDetails newDetails) {
@ -78,4 +90,54 @@ public class ImageClientLiveTest extends BaseGlanceClientLiveTest {
assertEquals(newDetails.getLinks(), image.getLinks());
}
@Test
public void testCreateUpdateAndDeleteImage() {
StringPayload imageData = new StringPayload("This isn't really an image!");
for (String zoneId : glanceContext.getApi().getConfiguredRegions()) {
ImageClient client = glanceContext.getApi().getImageClientForRegion(zoneId);
ImageDetails details = client.create("jclouds-live-test", imageData, diskFormat(DiskFormat.RAW), containerFormat(ContainerFormat.BARE));
assertEquals(details.getName(), "jclouds-live-test");
assertEquals(details.getSize().get().longValue(), imageData.getRawContent().length());
details = client.update(details.getId(), UpdateImageOptions.Builder.name("jclouds-live-test2"), UpdateImageOptions.Builder.minDisk(10));
assertEquals(details.getName(), "jclouds-live-test2");
assertEquals(details.getMinDisk(), 10);
Image fromListing = Iterables.getOnlyElement(client.list(ListImageOptions.Builder.name("jclouds-live-test2"), ListImageOptions.Builder.limit(2), ListImageOptions.Builder.containerFormat(ContainerFormat.BARE)));
assertEquals(fromListing.getId(), details.getId());
assertEquals(fromListing.getSize(), details.getSize());
assertEquals(Iterables.getOnlyElement(client.listInDetail(ListImageOptions.Builder.name("jclouds-live-test2"))), details);
assertTrue(client.delete(details.getId()));
assertTrue(client.list(ListImageOptions.Builder.name("jclouds-live-test2")).isEmpty());
}
}
@Test
public void testReserveUploadAndDeleteImage() {
StringPayload imageData = new StringPayload("This isn't an image!");
for (String zoneId : glanceContext.getApi().getConfiguredRegions()) {
ImageClient client = glanceContext.getApi().getImageClientForRegion(zoneId);
ImageDetails details = client.reserve("jclouds-live-res-test", diskFormat(DiskFormat.RAW), containerFormat(ContainerFormat.BARE));
assertEquals(details.getName(), "jclouds-live-res-test");
details = client.upload(details.getId(), imageData, UpdateImageOptions.Builder.name("jclouds-live-res-test2"), UpdateImageOptions.Builder.minDisk(10));
assertEquals(details.getName(), "jclouds-live-res-test2");
assertEquals(details.getSize().get().longValue(), imageData.getRawContent().length());
assertEquals(details.getMinDisk(), 10);
Image fromListing = Iterables.getOnlyElement(client.list(ListImageOptions.Builder.name("jclouds-live-res-test2"), ListImageOptions.Builder.limit(2), ListImageOptions.Builder.containerFormat(ContainerFormat.BARE)));
assertEquals(fromListing.getId(), details.getId());
assertEquals(fromListing.getSize(), details.getSize());
assertEquals(Iterables.getOnlyElement(client.listInDetail(ListImageOptions.Builder.name("jclouds-live-res-test2"))), details);
assertTrue(client.delete(details.getId()));
assertTrue(client.list(ListImageOptions.Builder.name("jclouds-live-res-test2")).isEmpty());
}
}
}

View File

@ -0,0 +1,65 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds 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.glance.v1_0.parse;
import javax.ws.rs.Consumes;
import javax.ws.rs.core.MediaType;
import org.jclouds.date.internal.SimpleDateFormatDateService;
import org.jclouds.json.BaseItemParserTest;
import org.jclouds.openstack.glance.v1_0.domain.ContainerFormat;
import org.jclouds.openstack.glance.v1_0.domain.DiskFormat;
import org.jclouds.openstack.glance.v1_0.domain.Image;
import org.jclouds.openstack.glance.v1_0.domain.ImageDetails;
import org.jclouds.rest.annotations.SelectJson;
import org.testng.annotations.Test;
/**
*
* @author Adam Lowe
*/
@Test(groups = "unit", testName = "ParseImageDetailTest")
public class ParseImageDetailsTest extends BaseItemParserTest<ImageDetails> {
@Override
public String resource() {
return "/image.json";
}
@Override
@SelectJson("image")
@Consumes(MediaType.APPLICATION_JSON)
public ImageDetails expected() {
return ImageDetails
.builder()
.id("02fa0378-f305-43cf-8058-8572fe1da795")
.name("jclouds-live-test")
.containerFormat(ContainerFormat.BARE)
.diskFormat(DiskFormat.RAW)
.checksum("6ae4e0fdc3c108a1bfe10ef5e436f4f4")
.size(27)
.status(Image.Status.ACTIVE)
.owner("68a7c7abb7bf45ada1536dfa28ec2115")
.isPublic(false)
.createdAt(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-05-31T10:13:47"))
.updatedAt(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-05-31T10:13:47"))
.build();
}
}

View File

@ -0,0 +1 @@
{"image": {"status": "active", "name": "jclouds-live-test", "deleted": false, "container_format": "bare", "created_at": "2012-05-31T10:13:47", "disk_format": "raw", "updated_at": "2012-05-31T10:13:47", "properties": {}, "min_disk": 0, "protected": false, "id": "02fa0378-f305-43cf-8058-8572fe1da795", "checksum": "6ae4e0fdc3c108a1bfe10ef5e436f4f4", "owner": "68a7c7abb7bf45ada1536dfa28ec2115", "is_public": false, "deleted_at": null, "min_ram": 0, "size": 27}}