From 3fd5333732106cc4fa718ad70657048ae5dd18af Mon Sep 17 00:00:00 2001 From: "adrian.f.cole" Date: Fri, 17 Jul 2009 14:56:55 +0000 Subject: [PATCH] Issue 77: added list Image support git-svn-id: http://jclouds.googlecode.com/svn/trunk@1636 3d8758e0-26b5-11de-8745-db77d3ebf521 --- .../cloudservers/CloudServersConnection.java | 30 ++++++ .../rackspace/cloudservers/domain/Image.java | 85 ++++++++++++++++- .../cloudservers/domain/ImageStatus.java | 28 +++--- .../ParseImageListFromGsonResponse.java | 62 +++++++++++++ .../CloudServersConnectionLiveTest.java | 51 +++++++++-- .../CloudServersConnectionTest.java | 34 +++++++ .../ParseImageListFromGsonResponseTest.java | 91 +++++++++++++++++++ .../src/test/resources/test_list_images.json | 12 +++ .../resources/test_list_images_detail.json | 20 ++++ 9 files changed, 386 insertions(+), 27 deletions(-) create mode 100644 rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/functions/ParseImageListFromGsonResponse.java create mode 100644 rackspace/cloudservers/core/src/test/java/org/jclouds/rackspace/cloudservers/functions/ParseImageListFromGsonResponseTest.java create mode 100644 rackspace/cloudservers/core/src/test/resources/test_list_images.json create mode 100644 rackspace/cloudservers/core/src/test/resources/test_list_images_detail.json diff --git a/rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/CloudServersConnection.java b/rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/CloudServersConnection.java index b087886428..78270819aa 100755 --- a/rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/CloudServersConnection.java +++ b/rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/CloudServersConnection.java @@ -31,8 +31,10 @@ import javax.ws.rs.GET; import javax.ws.rs.Path; import org.jclouds.rackspace.cloudservers.domain.Flavor; +import org.jclouds.rackspace.cloudservers.domain.Image; import org.jclouds.rackspace.cloudservers.domain.Server; import org.jclouds.rackspace.cloudservers.functions.ParseFlavorListFromGsonResponse; +import org.jclouds.rackspace.cloudservers.functions.ParseImageListFromGsonResponse; import org.jclouds.rackspace.cloudservers.functions.ParseServerListFromGsonResponse; import org.jclouds.rackspace.filters.AuthenticateRequest; import org.jclouds.rest.Query; @@ -106,5 +108,33 @@ public interface CloudServersConnection { // TODO: cloudServersFault (400, 500), serviceUnavailable (503), unauthorized (401), badRequest // (400) List listFlavorDetails(); + + + + /** + * + * List available images (IDs and names only) + * + * @see #listImageDetails() + */ + @GET + @ResponseParser(ParseImageListFromGsonResponse.class) + @Query(key = "format", value = "json") + @Path("/images") + // TODO: cloudServersFault (400, 500), serviceUnavailable (503), unauthorized (401), badRequest (400) + List listImages(); + + /** + * + * This operation will list all images visible by the account. + * + * @see Image + */ + @GET + @ResponseParser(ParseImageListFromGsonResponse.class) + @Query(key = "format", value = "json") + @Path("/images/detail") + // TODO: cloudServersFault (400, 500), serviceUnavailable (503), unauthorized (401), badRequest (400) + List listImageDetails(); } diff --git a/rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/domain/Image.java b/rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/domain/Image.java index 1d4ecbadd4..72d7a688b4 100644 --- a/rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/domain/Image.java +++ b/rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/domain/Image.java @@ -25,18 +25,28 @@ package org.jclouds.rackspace.cloudservers.domain; import org.joda.time.DateTime; +/** + * An image is a collection of files you use to create or rebuild a server. Rackspace provides + * pre-built OS images by default. You may also create custom images. + * + * @author Adrian Cole + */ public class Image { private DateTime created; - private Integer id; + private int id; private String name; private Integer progress; private Integer serverId; private ImageStatus status; private DateTime updated; - public Image(String name) { - this.setName(name); + public Image() { + } + + public Image(int id, String name) { + this.id = id; + this.name = name; } public void setCreated(DateTime created) { @@ -47,11 +57,11 @@ public class Image { return created; } - public void setId(Integer id) { + public void setId(int id) { this.id = id; } - public Integer getId() { + public int getId() { return id; } @@ -95,4 +105,69 @@ public class Image { return updated; } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((created == null) ? 0 : created.hashCode()); + result = prime * result + id; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((progress == null) ? 0 : progress.hashCode()); + result = prime * result + ((serverId == null) ? 0 : serverId.hashCode()); + result = prime * result + ((status == null) ? 0 : status.hashCode()); + result = prime * result + ((updated == null) ? 0 : updated.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Image other = (Image) obj; + if (created == null) { + if (other.created != null) + return false; + } else if (!created.equals(other.created)) + return false; + if (id != other.id) + return false; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + if (progress == null) { + if (other.progress != null) + return false; + } else if (!progress.equals(other.progress)) + return false; + if (serverId == null) { + if (other.serverId != null) + return false; + } else if (!serverId.equals(other.serverId)) + return false; + if (status == null) { + if (other.status != null) + return false; + } else if (!status.equals(other.status)) + return false; + if (updated == null) { + if (other.updated != null) + return false; + } else if (!updated.equals(other.updated)) + return false; + return true; + } + + @Override + public String toString() { + return "Image [created=" + created + ", id=" + id + ", name=" + name + ", progress=" + + progress + ", serverId=" + serverId + ", status=" + status + ", updated=" + + updated + "]"; + } + } diff --git a/rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/domain/ImageStatus.java b/rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/domain/ImageStatus.java index 2318a476e6..2e91a966a3 100644 --- a/rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/domain/ImageStatus.java +++ b/rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/domain/ImageStatus.java @@ -24,22 +24,24 @@ package org.jclouds.rackspace.cloudservers.domain; - +/** + * In-flight images will have the status attribute set to SAVING and the conditional progress + * element (0- 100% completion) will also be returned. Other possible values for the status + * attribute include: UNKNOWN, PREPARING, ACTIVE QUEUED, FAILED. Images with an ACTIVE status are + * available for install. + * + * @author Adrian Cole + */ public enum ImageStatus { - UNKNOWN, - ACTIVE, - SAVING, - PREPARING, - QUEUED, - FAILED; + UNKNOWN, ACTIVE, SAVING, PREPARING, QUEUED, FAILED; - public String value() { - return name(); - } + public String value() { + return name(); + } - public static ImageStatus fromValue(String v) { - return valueOf(v); - } + public static ImageStatus fromValue(String v) { + return valueOf(v); + } } diff --git a/rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/functions/ParseImageListFromGsonResponse.java b/rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/functions/ParseImageListFromGsonResponse.java new file mode 100644 index 0000000000..3832462033 --- /dev/null +++ b/rackspace/cloudservers/core/src/main/java/org/jclouds/rackspace/cloudservers/functions/ParseImageListFromGsonResponse.java @@ -0,0 +1,62 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + */ +package org.jclouds.rackspace.cloudservers.functions; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.util.List; + +import org.jclouds.http.functions.ParseJson; +import org.jclouds.rackspace.cloudservers.domain.Image; + +import com.google.gson.Gson; +import com.google.inject.Inject; +import com.google.inject.internal.Lists; + +/** + * This parses {@link Image} from a gson string. + * + * @author Adrian Cole + */ +public class ParseImageListFromGsonResponse extends ParseJson> { + + @Inject + public ParseImageListFromGsonResponse(Gson gson) { + super(gson); + } + + private static class ImageListResponse { + List images = Lists.newArrayList(); + } + + public List apply(InputStream stream) { + + try { + return gson.fromJson(new InputStreamReader(stream, "UTF-8"), ImageListResponse.class).images; + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("jclouds requires UTF-8 encoding", e); + } + } +} \ No newline at end of file diff --git a/rackspace/cloudservers/core/src/test/java/org/jclouds/rackspace/cloudservers/CloudServersConnectionLiveTest.java b/rackspace/cloudservers/core/src/test/java/org/jclouds/rackspace/cloudservers/CloudServersConnectionLiveTest.java index 031f17ffd9..bd5c45c6f5 100755 --- a/rackspace/cloudservers/core/src/test/java/org/jclouds/rackspace/cloudservers/CloudServersConnectionLiveTest.java +++ b/rackspace/cloudservers/core/src/test/java/org/jclouds/rackspace/cloudservers/CloudServersConnectionLiveTest.java @@ -25,12 +25,12 @@ package org.jclouds.rackspace.cloudservers; import static org.jclouds.rackspace.reference.RackspaceConstants.PROPERTY_RACKSPACE_KEY; import static org.jclouds.rackspace.reference.RackspaceConstants.PROPERTY_RACKSPACE_USER; -import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertTrue; import java.util.List; import org.jclouds.rackspace.cloudservers.domain.Flavor; +import org.jclouds.rackspace.cloudservers.domain.Image; import org.jclouds.rackspace.cloudservers.domain.Server; import org.testng.annotations.Test; @@ -50,7 +50,7 @@ public class CloudServersConnectionLiveTest { CloudServersConnection connection = CloudServersContextBuilder.newBuilder(sysRackspaceUser, sysRackspaceKey).withJsonDebug().buildContext().getConnection(); List response = connection.listServers(); - assertNotNull(response); + assert null != response; long initialContainerCount = response.size(); assertTrue(initialContainerCount >= 0); @@ -61,7 +61,7 @@ public class CloudServersConnectionLiveTest { CloudServersConnection connection = CloudServersContextBuilder.newBuilder(sysRackspaceUser, sysRackspaceKey).withJsonDebug().buildContext().getConnection(); List response = connection.listServerDetails(); - assertNotNull(response); + assert null != response; long initialContainerCount = response.size(); assertTrue(initialContainerCount >= 0); } @@ -71,12 +71,12 @@ public class CloudServersConnectionLiveTest { CloudServersConnection connection = CloudServersContextBuilder.newBuilder(sysRackspaceUser, sysRackspaceKey).withJsonDebug().buildContext().getConnection(); List response = connection.listFlavors(); - assertNotNull(response); + assert null != response; long flavorCount = response.size(); assertTrue(flavorCount >= 1); for (Flavor flavor : response) { assertTrue(flavor.getId() >= 0); - assertNotNull(flavor.getName()); + assert null != flavor.getName() : flavor; } } @@ -86,14 +86,47 @@ public class CloudServersConnectionLiveTest { CloudServersConnection connection = CloudServersContextBuilder.newBuilder(sysRackspaceUser, sysRackspaceKey).withJsonDebug().buildContext().getConnection(); List response = connection.listFlavorDetails(); - assertNotNull(response); + assert null != response; long flavorCount = response.size(); assertTrue(flavorCount >= 0); for (Flavor flavor : response) { assertTrue(flavor.getId() >= 1); - assertNotNull(flavor.getName()); - assertNotNull(flavor.getDisk()); - assertNotNull(flavor.getRam()); + assert null != flavor.getName() : flavor; + assert null != flavor.getDisk() : flavor; + assert null != flavor.getRam() : flavor; + } + } + + @Test + public void testListImages() throws Exception { + CloudServersConnection connection = CloudServersContextBuilder.newBuilder(sysRackspaceUser, + sysRackspaceKey).withJsonDebug().buildContext().getConnection(); + List response = connection.listImages(); + assert null != response; + long imageCount = response.size(); + assertTrue(imageCount >= 1); + for (Image image : response) { + assertTrue(image.getId() >= 0); + assert null != image.getName() : image; + } + + } + + @Test + public void testListImagesDetail() throws Exception { + CloudServersConnection connection = CloudServersContextBuilder.newBuilder(sysRackspaceUser, + sysRackspaceKey).withJsonDebug().buildContext().getConnection(); + List response = connection.listImageDetails(); + assert null != response; + long imageCount = response.size(); + assertTrue(imageCount >= 0); + for (Image image : response) { + assertTrue(image.getId() >= 1); + assert null != image.getName() : image; + // TODO: Web Hosting #118779 + // assert null != image.getCreated() : image; + // assert null != image.getUpdated() : image; + assert null != image.getStatus() : image; } } } diff --git a/rackspace/cloudservers/core/src/test/java/org/jclouds/rackspace/cloudservers/CloudServersConnectionTest.java b/rackspace/cloudservers/core/src/test/java/org/jclouds/rackspace/cloudservers/CloudServersConnectionTest.java index 43ec4033ea..3f0c93d2d4 100755 --- a/rackspace/cloudservers/core/src/test/java/org/jclouds/rackspace/cloudservers/CloudServersConnectionTest.java +++ b/rackspace/cloudservers/core/src/test/java/org/jclouds/rackspace/cloudservers/CloudServersConnectionTest.java @@ -35,6 +35,7 @@ import org.jclouds.http.HttpRequest; import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule; import org.jclouds.rackspace.Authentication; import org.jclouds.rackspace.cloudservers.functions.ParseFlavorListFromGsonResponse; +import org.jclouds.rackspace.cloudservers.functions.ParseImageListFromGsonResponse; import org.jclouds.rackspace.cloudservers.functions.ParseServerListFromGsonResponse; import org.jclouds.rest.JaxrsAnnotationProcessor; import org.jclouds.rest.config.JaxrsModule; @@ -119,6 +120,39 @@ public class CloudServersConnectionTest { } + + public void testListImages() throws SecurityException, NoSuchMethodException { + Method method = CloudServersConnection.class.getMethod("listImages"); + URI endpoint = URI.create("http://localhost"); + HttpRequest httpMethod = factory.create(CloudServersConnection.class).createRequest(endpoint, + method, new Object[] {}); + assertEquals(httpMethod.getEndpoint().getHost(), "localhost"); + assertEquals(httpMethod.getEndpoint().getPath(), "/images"); + assertEquals(httpMethod.getEndpoint().getQuery(), "format=json"); + assertEquals(httpMethod.getMethod(), HttpMethod.GET); + assertEquals(httpMethod.getHeaders().size(), 0); + factory.create(CloudServersConnection.class); + assertEquals(JaxrsAnnotationProcessor.getParserOrThrowException(method), + ParseImageListFromGsonResponse.class); + + } + + public void testListImagesDetail() throws SecurityException, NoSuchMethodException { + Method method = CloudServersConnection.class.getMethod("listImageDetails"); + URI endpoint = URI.create("http://localhost"); + HttpRequest httpMethod = factory.create(CloudServersConnection.class).createRequest(endpoint, + method, new Object[] {}); + assertEquals(httpMethod.getEndpoint().getHost(), "localhost"); + assertEquals(httpMethod.getEndpoint().getPath(), "/images/detail"); + assertEquals(httpMethod.getEndpoint().getQuery(), "format=json"); + assertEquals(httpMethod.getMethod(), HttpMethod.GET); + assertEquals(httpMethod.getHeaders().size(), 0); + factory.create(CloudServersConnection.class); + assertEquals(JaxrsAnnotationProcessor.getParserOrThrowException(method), + ParseImageListFromGsonResponse.class); + + } + @BeforeClass void setupFactory() { factory = Guice.createInjector(new AbstractModule() { diff --git a/rackspace/cloudservers/core/src/test/java/org/jclouds/rackspace/cloudservers/functions/ParseImageListFromGsonResponseTest.java b/rackspace/cloudservers/core/src/test/java/org/jclouds/rackspace/cloudservers/functions/ParseImageListFromGsonResponseTest.java new file mode 100644 index 0000000000..887eecbcd9 --- /dev/null +++ b/rackspace/cloudservers/core/src/test/java/org/jclouds/rackspace/cloudservers/functions/ParseImageListFromGsonResponseTest.java @@ -0,0 +1,91 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + */ +package org.jclouds.rackspace.cloudservers.functions; + +import static org.testng.Assert.assertEquals; + +import java.io.InputStream; +import java.net.UnknownHostException; +import java.util.List; + +import org.jclouds.http.functions.config.ParserModule; +import org.jclouds.rackspace.cloudservers.domain.Image; +import org.jclouds.rackspace.cloudservers.domain.ImageStatus; +import org.jclouds.util.DateService; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableList; +import com.google.gson.Gson; +import com.google.inject.Guice; +import com.google.inject.Injector; + +/** + * Tests behavior of {@code ParseImageListFromGsonResponseTest} + * + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "cloudImages.ParseImageListFromGsonResponseTest") +public class ParseImageListFromGsonResponseTest { + + Injector i = Guice.createInjector(new ParserModule()); + DateService dateService = new DateService(); + + public void testApplyInputStream() { + InputStream is = getClass().getResourceAsStream("/test_list_images.json"); + + List expects = ImmutableList.of(new Image(2, "CentOS 5.2"), new Image(743, + "My Server Backup")); + ParseImageListFromGsonResponse parser = new ParseImageListFromGsonResponse(i + .getInstance(Gson.class)); + assertEquals(parser.apply(is), expects); + } + + public void testApplyInputStreamDetails() throws UnknownHostException { + InputStream is = getClass().getResourceAsStream("/test_list_images_detail.json"); + + ParseImageListFromGsonResponse parser = new ParseImageListFromGsonResponse(i + .getInstance(Gson.class)); + List response = parser.apply(is); + assertEquals(response.get(0).getId(), 2); + assertEquals(response.get(0).getName(), "CentOS 5.2"); + // Web Hosting #118779 + assertEquals(null, response.get(0).getCreated()); + assertEquals(response.get(0).getProgress(), null); + assertEquals(response.get(0).getServerId(), null); + assertEquals(response.get(0).getStatus(), ImageStatus.ACTIVE); + assertEquals(response.get(0).getUpdated(), dateService + .iso8601DateParse("2010-10-10T12:00:00Z")); + + assertEquals(response.get(1).getId(), 743); + assertEquals(response.get(1).getName(), "My Server Backup"); + assertEquals(response.get(1).getCreated(), dateService + .iso8601DateParse("2010-08-10T12:00:00Z")); + assertEquals(response.get(1).getProgress(), new Integer(80)); + assertEquals(response.get(1).getServerId(), new Integer(12)); + assertEquals(response.get(1).getStatus(), ImageStatus.SAVING); + assertEquals(response.get(1).getUpdated(), dateService + .iso8601DateParse("2010-10-10T12:00:00Z")); + } + +} diff --git a/rackspace/cloudservers/core/src/test/resources/test_list_images.json b/rackspace/cloudservers/core/src/test/resources/test_list_images.json new file mode 100644 index 0000000000..0c5cb251ad --- /dev/null +++ b/rackspace/cloudservers/core/src/test/resources/test_list_images.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "id" : 2, + "name" : "CentOS 5.2" + }, + { + "id" : 743, + "name" : "My Server Backup" + } + ] +} diff --git a/rackspace/cloudservers/core/src/test/resources/test_list_images_detail.json b/rackspace/cloudservers/core/src/test/resources/test_list_images_detail.json new file mode 100644 index 0000000000..bdafc2ded3 --- /dev/null +++ b/rackspace/cloudservers/core/src/test/resources/test_list_images_detail.json @@ -0,0 +1,20 @@ +{ + "images" : [ + { + "id" : 2, + "name" : "CentOS 5.2", + "updated" : "2010-10-10T12:00:00Z", + "created" : "2010-08-10T12:00:00Z", + "status" : "ACTIVE" + }, + { + "id" : 743, + "name" : "My Server Backup", + "serverId" : 12, + "updated" : "2010-10-10T12:00:00Z", + "created" : "2009-07-07T09:56:16-05:00", + "status" : "SAVING", + "progress" : 80 + } + ] +}