openstack-glance: Adding create, reserve, update, upload and delete methods to ImageClient

This commit is contained in:
Adam Lowe 2012-05-31 13:36:42 +01:00
parent 76531d62e0
commit 03267c6e33
14 changed files with 1281 additions and 49 deletions

View File

@ -48,6 +48,10 @@ public class BaseListOptions extends BaseHttpRequestOptions {
* Indicates where to begin listing. The list will only include objects that occur after the * Indicates where to begin listing. The list will only include objects that occur after the
* offset. This is convenient for pagination: To get the next page of results use the last result * offset. This is convenient for pagination: To get the next page of results use the last result
* number of the current page + current page offset as the offset. * number of the current page + current page offset as the offset.
* <p/>
* This isn't supported by newer openstack API implementations
*
* @see #marker(String) for the new mechanism to set the page offset
*/ */
public BaseListOptions startAt(long offset) { public BaseListOptions startAt(long offset) {
checkState(offset >= 0, "offset must be >= 0"); checkState(offset >= 0, "offset must be >= 0");
@ -55,6 +59,19 @@ public class BaseListOptions extends BaseHttpRequestOptions {
return this; return this;
} }
/**
* The marker parameter is the ID of the last item in the previous list
* (i.e. return the page of items after the marker).
* <p/>
* This is only supported by newer openstack API implementations
*
* @see #startAt for the old mechanism to set the page offset
*/
public BaseListOptions marker(String marker) {
queryParameters.put("marker", checkNotNull(marker, "marker"));
return this;
}
/** /**
* To reduce load on the service, list operations will return a maximum of 1,000 items at a time. * To reduce load on the service, list operations will return a maximum of 1,000 items at a time.
* To navigate the collection, the parameters limit and offset can be set in the URI * To navigate the collection, the parameters limit and offset can be set in the URI
@ -81,7 +98,15 @@ public class BaseListOptions extends BaseHttpRequestOptions {
} }
/** /**
* @see BaseListOptions#maxResults(long) * @see BaseListOptions#marker
*/
public static BaseListOptions marker(String marker) {
BaseListOptions options = new BaseListOptions();
return options.marker(marker);
}
/**
* @see BaseListOptions#maxResults
*/ */
public static BaseListOptions maxResults(int maxKeys) { public static BaseListOptions maxResults(int maxKeys) {
BaseListOptions options = new BaseListOptions(); BaseListOptions options = new BaseListOptions();

View File

@ -246,6 +246,10 @@ public class ImageDetails extends Image {
return this.location; return this.location;
} }
public Optional<String> getOwner() {
return owner;
}
public Date getUpdatedAt() { public Date getUpdatedAt() {
return this.updatedAt; 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.io.InputStream;
import java.util.Set; import java.util.Set;
import javax.ws.rs.Consumes; import javax.ws.rs.*;
import javax.ws.rs.GET;
import javax.ws.rs.HEAD;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import org.jclouds.io.Payload;
import org.jclouds.openstack.filters.AuthenticateRequest; import org.jclouds.openstack.filters.AuthenticateRequest;
import org.jclouds.openstack.glance.v1_0.domain.Image; 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.ImageDetails;
import org.jclouds.openstack.glance.v1_0.functions.ParseImageDetailsFromHeaders; 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.ExceptionParser;
import org.jclouds.rest.annotations.RequestFilters; import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.ResponseParser; import org.jclouds.rest.annotations.ResponseParser;
import org.jclouds.rest.annotations.SelectJson; import org.jclouds.rest.annotations.SelectJson;
import org.jclouds.rest.annotations.SkipEncoding; import org.jclouds.rest.annotations.SkipEncoding;
import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404; import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnFalseOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
@ -47,6 +48,7 @@ import com.google.common.util.concurrent.ListenableFuture;
* *
* @see ImageClient * @see ImageClient
* @author Adrian Cole * @author Adrian Cole
* @author Adam Lowe
* @see <a href="http://glance.openstack.org/glanceapi.html">api doc</a> * @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> * @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) @Consumes(MediaType.APPLICATION_JSON)
@Path("/images") @Path("/images")
@ExceptionParser(ReturnEmptySetOnNotFoundOr404.class) @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
ListenableFuture<Set<Image>> list(); ListenableFuture<Set<Image>> list(ListImageOptions... options);
/** /**
* @see ImageClient#listInDetail * @see ImageClient#listInDetail
@ -72,7 +74,7 @@ public interface ImageAsyncClient {
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@Path("/images/detail") @Path("/images/detail")
@ExceptionParser(ReturnEmptySetOnNotFoundOr404.class) @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
ListenableFuture<Set<ImageDetails>> listInDetail(); ListenableFuture<Set<ImageDetails>> listInDetail(ListImageOptions... options);
/** /**
* @see ImageClient#show * @see ImageClient#show
@ -91,9 +93,49 @@ public interface ImageAsyncClient {
@ExceptionParser(ReturnNullOnNotFoundOr404.class) @ExceptionParser(ReturnNullOnNotFoundOr404.class)
ListenableFuture<InputStream> getAsStream(@PathParam("id") String id); ListenableFuture<InputStream> getAsStream(@PathParam("id") String id);
// POST /images -- Store image data and return metadata about the /**
// newly-stored image * @see ImageClient#create
// PUT /images/<ID> -- Update image metadata and/or upload image */
// data for a previously-reserved image @POST
// DELETE /images/<ID> -- Delete the image with id <ID> @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 java.util.concurrent.TimeUnit;
import org.jclouds.concurrent.Timeout; import org.jclouds.concurrent.Timeout;
import org.jclouds.io.Payload;
import org.jclouds.javax.annotation.Nullable; 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.Image;
import org.jclouds.openstack.glance.v1_0.domain.ImageDetails; 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 * Image Services
* *
* @see ImageAsyncClient
* @author Adrian Cole * @author Adrian Cole
* @author Adam Lowe
* @see ImageAsyncClient
* @see <a href="http://glance.openstack.org/glanceapi.html">api doc</a> * @see <a href="http://glance.openstack.org/glanceapi.html">api doc</a>
*/ */
@Timeout(duration = 180, timeUnit = TimeUnit.SECONDS) @Timeout(duration = 180, timeUnit = TimeUnit.SECONDS)
@ -39,12 +45,12 @@ public interface ImageClient {
/** /**
* Returns a set of brief metadata about images * Returns a set of brief metadata about images
*/ */
Set<Image> list(); Set<Image> list(ListImageOptions... options);
/** /**
* Returns a set of detailed metadata about images * Returns a set of detailed metadata about images
*/ */
Set<ImageDetails> listInDetail(); Set<ImageDetails> listInDetail(ListImageOptions... options);
/** /**
* Return metadata about an image with id * Return metadata about an image with id
@ -58,9 +64,46 @@ public interface ImageClient {
@Nullable @Nullable
InputStream getAsStream(String id); InputStream getAsStream(String id);
// POST /images -- Store image data and return metadata about the /**
// newly-stored image * Create a new image
// PUT /images/<ID> -- Update image metadata and/or upload image *
// data for a previously-reserved image * @return detailed metadata about the newly stored image
// DELETE /images/<ID> -- Delete the image with id <ID> */
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; package org.jclouds.openstack.glance.v1_0.functions;
import static org.jclouds.openstack.glance.v1_0.options.ImageField.*;
import javax.inject.Inject; import javax.inject.Inject;
import org.jclouds.date.DateService; import org.jclouds.date.DateService;
import org.jclouds.http.HttpResponse; import org.jclouds.http.HttpResponse;
import org.jclouds.openstack.glance.v1_0.domain.ContainerFormat; 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.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.ImageDetails;
import org.jclouds.openstack.glance.v1_0.domain.Image.Status;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Optional;
/** /**
* This parses {@link ImageDetails} from HTTP headers. * This parses {@link ImageDetails} from HTTP headers.
@ -44,23 +47,27 @@ public class ParseImageDetailsFromHeaders implements Function<HttpResponse, Imag
public ImageDetails apply(HttpResponse from) { public ImageDetails apply(HttpResponse from) {
ImageDetails.Builder<?> builder = ImageDetails.builder() ImageDetails.Builder<?> builder = ImageDetails.builder()
.id(from.getFirstHeaderOrNull("X-Image-Meta-Id")) .id(from.getFirstHeaderOrNull(ID.asHeader()))
.name(from.getFirstHeaderOrNull("X-Image-Meta-Name")) .name(from.getFirstHeaderOrNull(NAME.asHeader()))
.checksum(from.getFirstHeaderOrNull("X-Image-Meta-Checksum")) .checksum(Optional.fromNullable(from.getFirstHeaderOrNull(CHECKSUM.asHeader())))
.containerFormat(ContainerFormat.fromValue(from.getFirstHeaderOrNull("X-Image-Meta-Container_format"))) .minDisk(Long.parseLong(from.getFirstHeaderOrNull(MIN_DISK.asHeader())))
.diskFormat(DiskFormat.fromValue(from.getFirstHeaderOrNull("X-Image-Meta-Disk_format"))) .minRam(Long.parseLong(from.getFirstHeaderOrNull(MIN_RAM.asHeader())))
.size(Long.parseLong(from.getFirstHeaderOrNull("X-Image-Meta-Size"))) .isPublic(Boolean.parseBoolean(from.getFirstHeaderOrNull(IS_PUBLIC.asHeader())))
.minDisk(Long.parseLong(from.getFirstHeaderOrNull("X-Image-Meta-Min_disk"))) .createdAt(dateService.iso8601SecondsDateParse(from.getFirstHeaderOrNull(CREATED_AT.asHeader())))
.minRam(Long.parseLong(from.getFirstHeaderOrNull("X-Image-Meta-Min_ram"))) .updatedAt(dateService.iso8601SecondsDateParse(from.getFirstHeaderOrNull(UPDATED_AT.asHeader())))
.isPublic(Boolean.parseBoolean(from.getFirstHeaderOrNull("X-Image-Meta-Is_public"))) .owner(Optional.fromNullable(from.getFirstHeaderOrNull(OWNER.asHeader())))
.createdAt(dateService.iso8601SecondsDateParse(from.getFirstHeaderOrNull("X-Image-Meta-Created_at"))) .location(Optional.fromNullable(from.getFirstHeaderOrNull(LOCATION.asHeader())))
.updatedAt(dateService.iso8601SecondsDateParse(from.getFirstHeaderOrNull("X-Image-Meta-Updated_at"))) .status(Status.fromValue(from.getFirstHeaderOrNull(STATUS.asHeader())));
.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"); String containerFormat = from.getFirstHeaderOrNull(CONTAINER_FORMAT.asHeader());
if (deletedAt != null) String diskFormat = from.getFirstHeaderOrNull(DISK_FORMAT.asHeader());
builder.deletedAt(dateService.iso8601SecondsDateParse(deletedAt)); 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(); return builder.build();
} }

View File

@ -0,0 +1,126 @@
/**
* 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;
/**
* @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,240 @@
/**
* 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 java.util.Date;
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;
import org.jclouds.openstack.options.BaseListOptions;
/**
* @author Adam Lowe
* @see <a href="http://glance.openstack.org/glanceapi.html"/>
*/
public class ListImageOptions extends BaseListOptions {
/**
* 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 BaseListOptions#maxResults
*/
public static ListImageOptions maxResults(int limit) {
return ListImageOptions.class.cast(new ListImageOptions().maxResults(limit));
}
/**
* @see BaseListOptions#marker
*/
public static ListImageOptions marker(String marker) {
return ListImageOptions.class.cast(new ListImageOptions().marker(marker));
}
/**
* @see BaseListOptions#changesSince
*/
public static ListImageOptions changesSince(Date ifModifiedSince) {
return ListImageOptions.class.cast(new BaseListOptions().changesSince(ifModifiedSince));
}
}
}

View File

@ -0,0 +1,233 @@
/**
* 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;
/**
* @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; package org.jclouds.openstack.glance.v1_0.features;
import static org.testng.Assert.assertEquals; 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.assertNull;
import static org.testng.Assert.assertTrue; import static org.testng.Assert.assertTrue;
import java.net.URI; import java.net.URI;
import javax.ws.rs.core.MediaType;
import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse; import org.jclouds.http.HttpResponse;
import org.jclouds.io.Payloads; 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.GlanceClient;
import org.jclouds.openstack.glance.v1_0.functions.ParseImageDetailsFromHeadersTest; 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.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.ParseImagesInDetailTest;
import org.jclouds.openstack.glance.v1_0.parse.ParseImagesTest; 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.jclouds.util.Strings2;
import org.testng.annotations.Test; import org.testng.annotations.Test;
@ -40,6 +49,7 @@ import com.google.common.collect.ImmutableSet;
/** /**
* @author Adrian Cole * @author Adrian Cole
* @author Adam Lowe
*/ */
@Test(groups = "unit", testName = "ImageClientExpectTest") @Test(groups = "unit", testName = "ImageClientExpectTest")
public class ImageClientExpectTest extends BaseGlanceClientExpectTest { public class ImageClientExpectTest extends BaseGlanceClientExpectTest {
@ -199,4 +209,272 @@ public class ImageClientExpectTest extends BaseGlanceClientExpectTest {
assertNull(clientWhenNoExist.getImageClientForRegion("az-1.region-a.geo-1").getAsStream("fcc451d0-f6e4-4824-ad8f-70ec12326d07")); 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,34 @@
*/ */
package org.jclouds.openstack.glance.v1_0.features; package org.jclouds.openstack.glance.v1_0.features;
import static org.testng.Assert.assertEquals;
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 static org.testng.Assert.fail;
import java.io.File;
import java.util.Set; import java.util.Set;
import org.jclouds.io.Payload;
import org.jclouds.io.payloads.FilePayload;
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.Image;
import org.jclouds.openstack.glance.v1_0.domain.ImageDetails; 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.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 org.testng.annotations.Test;
import com.google.common.base.Optional;
import com.google.common.collect.Iterables;
/** /**
* @author Adrian Cole * @author Adrian Cole
* @author Adam Lowe
*/ */
@Test(groups = "live", testName = "ImageClientLiveTest") @Test(groups = "live", testName = "ImageClientLiveTest")
public class ImageClientLiveTest extends BaseGlanceClientLiveTest { public class ImageClientLiveTest extends BaseGlanceClientLiveTest {
@ -37,7 +54,7 @@ public class ImageClientLiveTest extends BaseGlanceClientLiveTest {
public void testList() throws Exception { public void testList() throws Exception {
for (String zoneId : glanceContext.getApi().getConfiguredRegions()) { for (String zoneId : glanceContext.getApi().getConfiguredRegions()) {
ImageClient client = glanceContext.getApi().getImageClientForRegion(zoneId); ImageClient client = glanceContext.getApi().getImageClientForRegion(zoneId);
Set<Image> response = client.list(); Set<Image> response = client.list(ListImageOptions.Builder.maxResults(100));
assert null != response; assert null != response;
for (Image image : response) { for (Image image : response) {
checkImage(image); checkImage(image);
@ -49,8 +66,6 @@ public class ImageClientLiveTest extends BaseGlanceClientLiveTest {
assert image.getId() != null : image; assert image.getId() != null : image;
assert image.getSize().isPresent() : image; assert image.getSize().isPresent() : image;
assert image.getChecksum().isPresent() : image; assert image.getChecksum().isPresent() : image;
assert image.getContainerFormat().isPresent() : image;
assert image.getContainerFormat().isPresent() : image;
} }
@Test @Test
@ -69,6 +84,7 @@ public class ImageClientLiveTest extends BaseGlanceClientLiveTest {
} }
private void checkImageDetails(ImageDetails image) { private void checkImageDetails(ImageDetails image) {
checkImage(image);
// TODO // TODO
} }
@ -78,4 +94,54 @@ public class ImageClientLiveTest extends BaseGlanceClientLiveTest {
assertEquals(newDetails.getLinks(), image.getLinks()); 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.maxResults(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.maxResults(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}}