Issue 77: rackspace server commands

git-svn-id: http://jclouds.googlecode.com/svn/trunk@1651 3d8758e0-26b5-11de-8745-db77d3ebf521
This commit is contained in:
adrian.f.cole 2009-07-18 22:34:35 +00:00
parent 86475e60e7
commit 02b35f2cb0
14 changed files with 757 additions and 56 deletions

View File

@ -42,7 +42,6 @@ import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.aws.s3.filters.RequestAuthorizeSignature;
import org.jclouds.aws.s3.functions.ParseMetadataFromHeaders;
import org.jclouds.aws.s3.functions.ParseObjectFromHeadersAndHttpContent;
import org.jclouds.aws.s3.functions.ReturnFalseOn404;
import org.jclouds.aws.s3.functions.ReturnNotFoundIfBucketDoesntExist;
import org.jclouds.aws.s3.functions.ReturnNotFoundIfObjectDoesntExist;
import org.jclouds.aws.s3.functions.ReturnS3BucketNotFoundOn404;
@ -60,6 +59,7 @@ import org.jclouds.aws.s3.xml.CopyObjectHandler;
import org.jclouds.aws.s3.xml.ListAllMyBucketsHandler;
import org.jclouds.aws.s3.xml.ListBucketHandler;
import org.jclouds.http.functions.ParseETagHeader;
import org.jclouds.http.functions.ReturnFalseOn404;
import org.jclouds.http.options.GetOptions;
import org.jclouds.rest.EntityParam;
import org.jclouds.rest.ExceptionParser;

View File

@ -32,7 +32,6 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.Arrays;
import java.util.Collection;

View File

@ -21,12 +21,17 @@
* under the License.
* ====================================================================
*/
package org.jclouds.aws.s3.functions;
package org.jclouds.http.functions;
import org.jclouds.http.HttpResponseException;
import com.google.common.base.Function;
/**
*
* @author Adrian Cole
* @since 4.0
*/
public class ReturnFalseOn404 implements Function<Exception, Boolean> {
public Boolean apply(Exception from) {

View File

@ -27,10 +27,13 @@ import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import org.jclouds.http.functions.ReturnFalseOn404;
import org.jclouds.rackspace.cloudservers.domain.Flavor;
import org.jclouds.rackspace.cloudservers.domain.Image;
import org.jclouds.rackspace.cloudservers.domain.Server;
@ -38,11 +41,16 @@ import org.jclouds.rackspace.cloudservers.functions.ParseFlavorFromGsonResponse;
import org.jclouds.rackspace.cloudservers.functions.ParseFlavorListFromGsonResponse;
import org.jclouds.rackspace.cloudservers.functions.ParseImageFromGsonResponse;
import org.jclouds.rackspace.cloudservers.functions.ParseImageListFromGsonResponse;
import org.jclouds.rackspace.cloudservers.functions.ParseServerFromGsonResponse;
import org.jclouds.rackspace.cloudservers.functions.ParseServerListFromGsonResponse;
import org.jclouds.rackspace.cloudservers.functions.ReturnFlavorNotFoundOn404;
import org.jclouds.rackspace.cloudservers.functions.ReturnImageNotFoundOn404;
import org.jclouds.rackspace.cloudservers.functions.ReturnServerNotFoundOn404;
import org.jclouds.rackspace.cloudservers.options.CreateServerOptions;
import org.jclouds.rackspace.filters.AuthenticateRequest;
import org.jclouds.rest.ExceptionParser;
import org.jclouds.rest.PostBinder;
import org.jclouds.rest.PostParam;
import org.jclouds.rest.Query;
import org.jclouds.rest.RequestFilters;
import org.jclouds.rest.ResponseParser;
@ -87,6 +95,55 @@ public interface CloudServersConnection {
// unauthorized (401), badRequest (400), overLimit (413)
List<Server> listServerDetails();
/**
*
* This operation returns details of the specified server.
*
* @see Server
*/
@GET
@ResponseParser(ParseServerFromGsonResponse.class)
@Query(key = "format", value = "json")
@ExceptionParser(ReturnServerNotFoundOn404.class)
@Path("/servers/{id}")
// TODO: cloudServersFault (400, 500), serviceUnavailable (503), unauthorized (401), badRequest
// (400)
Server getServerDetails(@PathParam("id") int id);
/**
*
* This operation deletes a cloud server instance from the system.
* <p/>
* Note: When a server is deleted, all images created from that server are also removed.
*
*
* @see Server
*/
@DELETE
@ExceptionParser(ReturnFalseOn404.class)
@Path("/servers/{id}")
// TODO:cloudServersFault (400, 500), serviceUnavailable (503), unauthorized (401), badRequest
// (400), itemNotFound (404), buildInProgress (409), overLimit (413)
boolean deleteServer(@PathParam("id") int id);
/**
* This operation asynchronously provisions a new server. The progress of this operation depends
* on several factors including location of the requested image, network i/o, host load, and the
* selected flavor. The progress of the request can be checked by performing a GET on /server/id,
* which will return a progress attribute (0-100% completion). A password will be randomly
* generated for you and returned in the response object. For security reasons, it will not be
* returned in subsequent GET calls against a given server ID.
*/
@POST
@ResponseParser(ParseServerFromGsonResponse.class)
@Query(key = "format", value = "json")
@Path("/servers")
@PostBinder(CreateServerOptions.class)
// TODO:cloudServersFault (400, 500), serviceUnavailable (503), unauthorized (401),
// badMediaType(415), badRequest (400), serverCapacityUnavailable (503), overLimit (413)
Server createServer(@PostParam("name") String name, @PostParam("imageId") int imageId,
@PostParam("flavorId") int flavorId);
/**
*
* List available flavors (IDs and names only)
@ -157,7 +214,7 @@ public interface CloudServersConnection {
// TODO: cloudServersFault (400, 500), serviceUnavailable (503), unauthorized (401), badRequest
// (400)
Image getImageDetails(@PathParam("id") int id);
/**
*
* This operation returns details of the specified flavor.

View File

@ -23,10 +23,8 @@
*/
package org.jclouds.rackspace.cloudservers.domain;
import java.util.List;
import java.util.Map;
import com.google.inject.internal.Lists;
import com.google.inject.internal.Maps;
/**
@ -37,11 +35,11 @@ import com.google.inject.internal.Maps;
* @since 4.0
*/
public class Server {
public static final Server NOT_FOUND = new Server(-1, "NOT_FOUND");
private int id;
private String name;
private Map<String, String> metadata = Maps.newHashMap();
private List<File> personality = Lists.newArrayList();
private Addresses addresses;
private String adminPass;
@ -77,14 +75,6 @@ public class Server {
return addresses;
}
public void setPersonality(List<File> personality) {
this.personality = personality;
}
public List<File> getPersonality() {
return personality;
}
public void setAdminPass(String adminPass) {
this.adminPass = adminPass;
}
@ -173,7 +163,6 @@ public class Server {
result = prime * result + ((imageId == null) ? 0 : imageId.hashCode());
result = prime * result + ((metadata == null) ? 0 : metadata.hashCode());
result = prime * result + ((getName() == null) ? 0 : getName().hashCode());
result = prime * result + ((personality == null) ? 0 : personality.hashCode());
result = prime * result + ((progress == null) ? 0 : progress.hashCode());
result = prime * result + ((sharedIpGroupId == null) ? 0 : sharedIpGroupId.hashCode());
result = prime * result + ((status == null) ? 0 : status.hashCode());
@ -226,11 +215,6 @@ public class Server {
return false;
} else if (!getName().equals(other.getName()))
return false;
if (personality == null) {
if (other.personality != null)
return false;
} else if (!personality.equals(other.personality))
return false;
if (progress == null) {
if (other.progress != null)
return false;
@ -251,11 +235,10 @@ public class Server {
@Override
public String toString() {
return "Server [addresses=" + addresses + ", adminPass=" + adminPass + ", flavorId="
+ flavorId + ", hostId=" + hostId + ", id=" + id + ", imageId=" + imageId
+ ", metadata=" + metadata + ", name=" + getName() + ", personality=" + personality
+ ", progress=" + progress + ", sharedIpGroupId=" + sharedIpGroupId + ", status="
+ status + "]";
return "Server [addresses=" + addresses + ", isAdminPassSet=" + (adminPass != null)
+ ", flavorId=" + flavorId + ", hostId=" + hostId + ", id=" + id + ", imageId="
+ imageId + ", metadata=" + metadata + ", name=" + getName() + ", progress="
+ progress + ", sharedIpGroupId=" + sharedIpGroupId + ", status=" + status + "]";
}
public void setName(String name) {

View File

@ -0,0 +1,60 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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 org.jclouds.http.functions.ParseJson;
import org.jclouds.rackspace.cloudservers.domain.Server;
import com.google.gson.Gson;
import com.google.inject.Inject;
/**
* This parses {@link Server} from a gson string.
*
* @author Adrian Cole
*/
public class ParseServerFromGsonResponse extends ParseJson<Server> {
@Inject
public ParseServerFromGsonResponse(Gson gson) {
super(gson);
}
private static class ServerListResponse {
Server server;
}
public Server apply(InputStream stream) {
try {
return gson.fromJson(new InputStreamReader(stream, "UTF-8"), ServerListResponse.class).server;
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("jclouds requires UTF-8 encoding", e);
}
}
}

View File

@ -21,27 +21,28 @@
* under the License.
* ====================================================================
*/
package org.jclouds.rackspace.cloudservers.domain;
package org.jclouds.rackspace.cloudservers.functions;
public class File {
import org.jclouds.http.HttpResponseException;
import org.jclouds.rackspace.cloudservers.domain.Server;
private byte[] value;
private String path;
import com.google.common.base.Function;
public byte[] getValue() {
return value;
}
/**
*
*
* @author Adrian Cole
*/
public class ReturnServerNotFoundOn404 implements Function<Exception, Server> {
public void setValue(byte[] value) {
this.value = ((byte[]) value);
}
public String getPath() {
return path;
}
public void setPath(String value) {
this.path = value;
public Server apply(Exception from) {
if (from instanceof HttpResponseException) {
HttpResponseException responseException = (HttpResponseException) from;
if (responseException.getResponse().getStatusCode() == 404) {
return Server.NOT_FOUND;
}
}
return null;
}
}

View File

@ -0,0 +1,211 @@
package org.jclouds.rackspace.cloudservers.options;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import java.net.InetAddress;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpUtils;
import org.jclouds.http.binders.JsonBinder;
import org.jclouds.rackspace.cloudservers.domain.Addresses;
import com.google.common.collect.ImmutableMap;
import com.google.inject.internal.Lists;
import com.google.inject.internal.Maps;
/**
*
* @author Adrian Cole
*
*/
public class CreateServerOptions extends JsonBinder {
static class File {
private final String path;
private final String contents;
public File(String path, byte[] contents) {
this.path = checkNotNull(path, "path");
this.contents = HttpUtils.toBase64String(checkNotNull(contents, "contents"));
checkArgument(path.getBytes().length < 255, String.format(
"maximum length of path is 255 bytes. Path specified %s is %d bytes", path, path
.getBytes().length));
checkArgument(contents.length < 10 * 1024, String.format(
"maximum size of the file is 10KB. Contents specified is %d bytes",
contents.length));
}
public String getContents() {
return contents;
}
public String getPath() {
return path;
}
}
@SuppressWarnings("unused")
private class ServerRequest {
final String name;
final int imageId;
final int flavorId;
Map<String, String> metadata;
List<File> personality;
Integer sharedIpGroupId;
Addresses addresses;
private ServerRequest(String name, int imageId, int flavorId) {
this.name = name;
this.imageId = imageId;
this.flavorId = flavorId;
}
}
private Map<String, String> metadata = Maps.newHashMap();
private List<File> files = Lists.newArrayList();
private Integer sharedIpGroupId;
private InetAddress publicIp;
@Override
public void addEntityToRequest(Map<String, String> postParams, HttpRequest request) {
ServerRequest server = new ServerRequest(checkNotNull(postParams.get("name"),
"name parameter not present"), Integer.parseInt(checkNotNull(postParams
.get("imageId"), "imageId parameter not present")), Integer.parseInt(checkNotNull(
postParams.get("flavorId"), "flavorId parameter not present")));
if (metadata.size() > 0)
server.metadata = metadata;
if (files.size() > 0)
server.personality = files;
if (sharedIpGroupId != null)
server.sharedIpGroupId = this.sharedIpGroupId;
if (publicIp != null) {
server.addresses = new Addresses();
server.addresses.setPublicAddresses(Collections.singletonList(publicIp));
server.addresses.setPrivateAddresses(null);
}
addEntityToRequest(ImmutableMap.of("server", server), request);
}
/**
* You may further customize a cloud server by injecting data into the file system of the cloud
* server itself. This is useful, for example, for inserting ssh keys, setting configuration
* files, or storing data that you want to retrieve from within the instance itself. It is
* intended to provide a minimal amount of launch-time personalization. If significant
* customization is required, a custom image should be created. The max size of the file path
* data is 255 bytes while the max size of the file contents is 10KB. Note that the file contents
* should be encoded as a Base64 string and the 10KB limit refers to the number of bytes in the
* decoded data not the number of characters in the encoded data. The maximum number of file
* path/content pairs that can be supplied is 5. Any existing files that match the specified file
* will be renamed to include the extension bak followed by a time stamp. For example, the file
* /etc/passwd will be backed up as /etc/passwd.bak.1246036261.5785. All files will have root and
* the root group as owner and group owner, respectively and will allow user and group read
* access only (-r--r-----).
*/
public CreateServerOptions withFile(String path, byte[] contents) {
checkState(files.size() < 5, "maximum number of files allowed is 5");
files.add(new File(path, contents));
return this;
}
/**
* Servers in the same shared IP group can share public IPs for various high availability and
* load balancing configurations. To launch an HA server, include the optional sharedIpGroupId
* element and the server will be launched into that shared IP group.
* <p />
*
* Note: sharedIpGroupId is an optional parameter and for optimal performance, should ONLY be
* specified when intending to share IPs between servers.
*
* @see #withSharedIp(InetAddress)
*/
public CreateServerOptions withSharedIpGroup(int id) {
checkArgument(id > 0, "id must be positive or zero. was: " + id);
this.sharedIpGroupId = id;
return this;
}
/**
* Custom cloud server metadata can also be supplied at launch time. This metadata is stored in
* the API system where it is retrievable by querying the API for server status. The maximum size
* of the metadata key and value is each 255 bytes and the maximum number of key-value pairs that
* can be supplied per server is 5.
*/
public CreateServerOptions withMetadata(Map<String, String> metadata) {
checkNotNull(metadata, "metadata");
checkArgument(metadata.size() <= 5,
"you cannot have more then 5 metadata values. You specified: " + metadata.size());
for (Entry<String, String> entry : metadata.entrySet()) {
checkArgument(entry.getKey().getBytes().length < 255, String.format(
"maximum length of metadata key is 255 bytes. Key specified %s is %d bytes",
entry.getKey(), entry.getKey().getBytes().length));
checkArgument(
entry.getKey().getBytes().length < 255,
String
.format(
"maximum length of metadata value is 255 bytes. Value specified for %s (%s) is %d bytes",
entry.getKey(), entry.getValue(),
entry.getValue().getBytes().length));
}
this.metadata = metadata;
return this;
}
/**
* If you intend to use a shared IP on the server being created and have no need for a separate
* public IP address, you may launch the server into a shared IP group and specify an IP address
* from that shared IP group to be used as its public IP. You can accomplish this by specifying
* the public shared IP address in your request. This is optional and is only valid if
* sharedIpGroupId is also supplied.
*/
public CreateServerOptions withSharedIp(InetAddress publicIp) {
checkState(sharedIpGroupId != null,
"sharedIp is invalid unless a shared ip group is specified.");
this.publicIp = checkNotNull(publicIp, "ip");
return this;
}
public static class Builder {
/**
* @see CreateServerOptions#withFile(String,byte [])
*/
public static CreateServerOptions withFile(String path, byte[] contents) {
CreateServerOptions options = new CreateServerOptions();
return options.withFile(path, contents);
}
/**
* @see CreateServerOptions#withSharedIpGroup(int)
*/
public static CreateServerOptions withSharedIpGroup(int id) {
CreateServerOptions options = new CreateServerOptions();
return options.withSharedIpGroup(id);
}
/**
* @see CreateServerOptions#withMetadata(Map<String, String>)
*/
public static CreateServerOptions withMetadata(Map<String, String> metadata) {
CreateServerOptions options = new CreateServerOptions();
return options.withMetadata(metadata);
}
/**
* @see CreateServerOptions#withSharedIp(InetAddress)
*/
public static CreateServerOptions withSharedIp(InetAddress publicIp) {
CreateServerOptions options = new CreateServerOptions();
return options.withSharedIp(publicIp);
}
}
}

View File

@ -25,17 +25,26 @@ 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.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
import java.io.InputStream;
import java.util.List;
import org.jclouds.logging.log4j.config.Log4JLoggingModule;
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.domain.ServerStatus;
import org.jclouds.ssh.SshConnection;
import org.jclouds.ssh.jsch.config.JschSshConnectionModule;
import org.jclouds.util.Utils;
import org.testng.annotations.BeforeGroups;
import org.testng.annotations.Test;
import com.google.inject.Injector;
/**
* Tests behavior of {@code CloudServersConnection}
*
@ -47,11 +56,16 @@ public class CloudServersConnectionLiveTest {
protected static final String sysRackspaceUser = System.getProperty(PROPERTY_RACKSPACE_USER);
protected static final String sysRackspaceKey = System.getProperty(PROPERTY_RACKSPACE_KEY);
CloudServersConnection connection;
SshConnection.Factory sshFactory;
@BeforeGroups(groups = { "live" })
public void setupConnection() {
connection = CloudServersContextBuilder.newBuilder(sysRackspaceUser, sysRackspaceKey)
.withModule(new Log4JLoggingModule()).withJsonDebug().buildContext().getConnection();
Injector injector = CloudServersContextBuilder.newBuilder(sysRackspaceUser, sysRackspaceKey)
.withModules(new Log4JLoggingModule(), new JschSshConnectionModule())
.withJsonDebug().buildInjector();
connection = injector.getInstance(CloudServersConnection.class);
sshFactory = injector.getInstance(SshConnection.Factory.class);
}
@Test
@ -137,10 +151,17 @@ public class CloudServersConnectionLiveTest {
assertTrue(imageCount >= 0);
for (Image image : response) {
Image newDetails = connection.getImageDetails(image.getId());
assert image.equals(newDetails) : String.format("%s doesn't equal %2", newDetails, image);
assertEquals(image, newDetails);
}
}
@Test(enabled = false)
// Rackspace Web Hosting issue #118856
public void testGetImageDetailsNotFound() throws Exception {
Image newDetails = connection.getImageDetails(12312987);
assertEquals(Image.NOT_FOUND, newDetails);
}
@Test
public void testGetFlavorDetails() throws Exception {
List<Flavor> response = connection.listFlavorDetails();
@ -149,8 +170,67 @@ public class CloudServersConnectionLiveTest {
assertTrue(flavorCount >= 0);
for (Flavor flavor : response) {
Flavor newDetails = connection.getFlavorDetails(flavor.getId());
assert flavor.equals(newDetails) : String
.format("%s doesn't equal %2", newDetails, flavor);
assertEquals(flavor, newDetails);
}
}
public void testGetFlavorDetailsNotFound() throws Exception {
Flavor newDetails = connection.getFlavorDetails(12312987);
assertEquals(Flavor.NOT_FOUND, newDetails);
}
public void testGetServerDetailsNotFound() throws Exception {
Server newDetails = connection.getServerDetails(12312987);
assertEquals(Server.NOT_FOUND, newDetails);
}
public void testGetServerDetails() throws Exception {
List<Server> response = connection.listServerDetails();
assert null != response;
long serverCount = response.size();
assertTrue(serverCount >= 0);
for (Server server : response) {
Server newDetails = connection.getServerDetails(server.getId());
assertEquals(server, newDetails);
}
}
private String serverPrefix = System.getProperty("user.name") + ".cs";
@Test(timeOut = 5 * 60 * 1000)
public void testCreateServer() throws Exception {
Server newDetails = connection.createServer(serverPrefix + "createserver", 2, 1);
System.err.print(newDetails);
assertNotNull(newDetails.getAdminPass());
assertNotNull(newDetails.getHostId());
assertEquals(newDetails.getStatus(), ServerStatus.BUILD);
assert newDetails.getProgress() >= 0 : "newDetails.getProgress()" + newDetails.getProgress();
assertEquals(new Integer(2), newDetails.getImageId());
assertEquals(new Integer(1), newDetails.getFlavorId());
assertNotNull(newDetails.getAddresses());
assertEquals(newDetails.getAddresses().getPublicAddresses().size(), 1);
assertEquals(newDetails.getAddresses().getPrivateAddresses().size(), 1);
int serverId = newDetails.getId();
ServerStatus currentStatus = newDetails.getStatus();
Server currentDetails = newDetails;
while (currentStatus != ServerStatus.ACTIVE) {
Thread.sleep(5 * 1000);
currentDetails = connection.getServerDetails(serverId);
System.out.println(currentDetails);
currentStatus = currentDetails.getStatus();
}
InputStream etcPasswd = sshFactory.create(
newDetails.getAddresses().getPublicAddresses().get(0), 22, "root",
newDetails.getAdminPass()).get("/etc/passwd");
String etcPasswdContents = Utils.toStringAndClose(etcPasswd);
assert etcPasswdContents.indexOf("root") >= 0 : etcPasswdContents;
connection.deleteServer(serverId);
currentDetails = connection.getServerDetails(serverId);
assertEquals(ServerStatus.DELETED, currentDetails.getStatus());
}
}

View File

@ -27,6 +27,10 @@ import static org.testng.Assert.assertEquals;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.Collections;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import org.jclouds.concurrent.WithinThreadExecutorService;
import org.jclouds.concurrent.config.ExecutorServiceModule;
@ -38,6 +42,7 @@ import org.jclouds.rackspace.cloudservers.functions.ParseFlavorFromGsonResponse;
import org.jclouds.rackspace.cloudservers.functions.ParseFlavorListFromGsonResponse;
import org.jclouds.rackspace.cloudservers.functions.ParseImageFromGsonResponse;
import org.jclouds.rackspace.cloudservers.functions.ParseImageListFromGsonResponse;
import org.jclouds.rackspace.cloudservers.functions.ParseServerFromGsonResponse;
import org.jclouds.rackspace.cloudservers.functions.ParseServerListFromGsonResponse;
import org.jclouds.rest.JaxrsAnnotationProcessor;
import org.jclouds.rest.config.JaxrsModule;
@ -58,6 +63,27 @@ public class CloudServersConnectionTest {
JaxrsAnnotationProcessor.Factory factory;
public void testCreateServer() throws SecurityException, NoSuchMethodException {
Method method = CloudServersConnection.class.getMethod("createServer", String.class,
int.class, int.class);
URI endpoint = URI.create("http://localhost");
HttpRequest httpMethod = factory.create(CloudServersConnection.class).createRequest(endpoint,
method, new Object[] { "ralphie", 2, 1 });
assertEquals(httpMethod.getEndpoint().getHost(), "localhost");
assertEquals(httpMethod.getEndpoint().getPath(), "/servers");
assertEquals(httpMethod.getEndpoint().getQuery(), "format=json");
assertEquals(httpMethod.getMethod(), HttpMethod.POST);
assertEquals("{\"server\":{\"name\":\"ralphie\",\"imageId\":2,\"flavorId\":1}}", httpMethod
.getEntity());
assertEquals(httpMethod.getHeaders().size(), 2);
assertEquals(httpMethod.getHeaders().get(HttpHeaders.CONTENT_LENGTH), Collections
.singletonList(httpMethod.getEntity().toString().getBytes().length + ""));
assertEquals(httpMethod.getHeaders().get(HttpHeaders.CONTENT_TYPE), Collections
.singletonList(MediaType.APPLICATION_JSON));
assertEquals(JaxrsAnnotationProcessor.getParserOrThrowException(method),
ParseServerFromGsonResponse.class);
}
public void testListServers() throws SecurityException, NoSuchMethodException {
Method method = CloudServersConnection.class.getMethod("listServers");
URI endpoint = URI.create("http://localhost");
@ -68,7 +94,6 @@ public class CloudServersConnectionTest {
assertEquals(httpMethod.getEndpoint().getQuery(), "format=json");
assertEquals(httpMethod.getMethod(), HttpMethod.GET);
assertEquals(httpMethod.getHeaders().size(), 0);
factory.create(CloudServersConnection.class);
assertEquals(JaxrsAnnotationProcessor.getParserOrThrowException(method),
ParseServerListFromGsonResponse.class);
@ -84,7 +109,6 @@ public class CloudServersConnectionTest {
assertEquals(httpMethod.getEndpoint().getQuery(), "format=json");
assertEquals(httpMethod.getMethod(), HttpMethod.GET);
assertEquals(httpMethod.getHeaders().size(), 0);
factory.create(CloudServersConnection.class);
assertEquals(JaxrsAnnotationProcessor.getParserOrThrowException(method),
ParseServerListFromGsonResponse.class);
@ -100,7 +124,6 @@ public class CloudServersConnectionTest {
assertEquals(httpMethod.getEndpoint().getQuery(), "format=json");
assertEquals(httpMethod.getMethod(), HttpMethod.GET);
assertEquals(httpMethod.getHeaders().size(), 0);
factory.create(CloudServersConnection.class);
assertEquals(JaxrsAnnotationProcessor.getParserOrThrowException(method),
ParseFlavorListFromGsonResponse.class);
@ -116,7 +139,6 @@ public class CloudServersConnectionTest {
assertEquals(httpMethod.getEndpoint().getQuery(), "format=json");
assertEquals(httpMethod.getMethod(), HttpMethod.GET);
assertEquals(httpMethod.getHeaders().size(), 0);
factory.create(CloudServersConnection.class);
assertEquals(JaxrsAnnotationProcessor.getParserOrThrowException(method),
ParseFlavorListFromGsonResponse.class);
@ -132,7 +154,6 @@ public class CloudServersConnectionTest {
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);
@ -148,7 +169,6 @@ public class CloudServersConnectionTest {
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);
@ -164,7 +184,6 @@ public class CloudServersConnectionTest {
assertEquals(httpMethod.getEndpoint().getQuery(), "format=json");
assertEquals(httpMethod.getMethod(), HttpMethod.GET);
assertEquals(httpMethod.getHeaders().size(), 0);
factory.create(CloudServersConnection.class);
assertEquals(JaxrsAnnotationProcessor.getParserOrThrowException(method),
ParseImageFromGsonResponse.class);
@ -180,12 +199,38 @@ public class CloudServersConnectionTest {
assertEquals(httpMethod.getEndpoint().getQuery(), "format=json");
assertEquals(httpMethod.getMethod(), HttpMethod.GET);
assertEquals(httpMethod.getHeaders().size(), 0);
factory.create(CloudServersConnection.class);
assertEquals(JaxrsAnnotationProcessor.getParserOrThrowException(method),
ParseFlavorFromGsonResponse.class);
}
public void testGetServerDetails() throws SecurityException, NoSuchMethodException {
Method method = CloudServersConnection.class.getMethod("getServerDetails", int.class);
URI endpoint = URI.create("http://localhost");
HttpRequest httpMethod = factory.create(CloudServersConnection.class).createRequest(endpoint,
method, new Object[] { 2 });
assertEquals(httpMethod.getEndpoint().getHost(), "localhost");
assertEquals(httpMethod.getEndpoint().getPath(), "/servers/2");
assertEquals(httpMethod.getEndpoint().getQuery(), "format=json");
assertEquals(httpMethod.getMethod(), HttpMethod.GET);
assertEquals(httpMethod.getHeaders().size(), 0);
assertEquals(JaxrsAnnotationProcessor.getParserOrThrowException(method),
ParseServerFromGsonResponse.class);
}
public void testDeleteServer() throws SecurityException, NoSuchMethodException {
Method method = CloudServersConnection.class.getMethod("deleteServer", int.class);
URI endpoint = URI.create("http://localhost");
HttpRequest httpMethod = factory.create(CloudServersConnection.class).createRequest(endpoint,
method, new Object[] { 2 });
assertEquals(httpMethod.getEndpoint().getHost(), "localhost");
assertEquals(httpMethod.getEndpoint().getPath(), "/servers/2");
assertEquals(httpMethod.getMethod(), HttpMethod.DELETE);
assertEquals(httpMethod.getHeaders().size(), 0);
}
@BeforeClass
void setupFactory() {
factory = Guice.createInjector(new AbstractModule() {

View File

@ -0,0 +1,79 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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.InetAddress;
import java.net.UnknownHostException;
import java.util.List;
import org.jclouds.http.functions.config.ParserModule;
import org.jclouds.rackspace.cloudservers.domain.Addresses;
import org.jclouds.rackspace.cloudservers.domain.Server;
import org.jclouds.rackspace.cloudservers.domain.ServerStatus;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.gson.Gson;
import com.google.inject.Guice;
import com.google.inject.Injector;
/**
* Tests behavior of {@code ParseServerFromGsonResponseTest}
*
* @author Adrian Cole
*/
@Test(groups = "unit", testName = "cloudservers.ParseServerFromGsonResponseTest")
public class ParseServerFromGsonResponseTest {
Injector i = Guice.createInjector(new ParserModule());
public void testApplyInputStreamDetails() throws UnknownHostException {
InputStream is = getClass().getResourceAsStream("/test_get_server_detail.json");
ParseServerFromGsonResponse parser = new ParseServerFromGsonResponse(i
.getInstance(Gson.class));
Server response = parser.apply(is);
assertEquals(response.getId(), 1234);
assertEquals(response.getName(), "sample-server");
assertEquals(response.getImageId(), new Integer(2));
assertEquals(response.getFlavorId(), new Integer(1));
assertEquals(response.getHostId(), "e4d909c290d0fb1ca068ffaddf22cbd0");
assertEquals(response.getStatus(), ServerStatus.BUILD);
assertEquals(response.getProgress(), new Integer(60));
List<InetAddress> publicAddresses = Lists.newArrayList(InetAddress.getByName("67.23.10.132"),
InetAddress.getByName("67.23.10.131"));
List<InetAddress> privateAddresses = Lists
.newArrayList(InetAddress.getByName("10.176.42.16"));
Addresses addresses1 = new Addresses(publicAddresses, privateAddresses);
assertEquals(response.getAddresses(), addresses1);
assertEquals(response.getMetadata(), ImmutableMap.of("Server Label", "Web Head 1",
"Image Version", "2.1"));
}
}

View File

@ -0,0 +1,150 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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.options;
import static org.testng.Assert.assertEquals;
import java.net.InetAddress;
import java.net.URI;
import java.net.UnknownHostException;
import org.jclouds.http.HttpMethod;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.functions.config.ParserModule;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMap;
import com.google.inject.Guice;
import com.google.inject.Injector;
import static org.jclouds.rackspace.cloudservers.options.CreateServerOptions.Builder.*;
/**
* Tests behavior of {@code ParseFlavorFromGsonResponse}
*
* @author Adrian Cole
*/
@Test(groups = "unit", testName = "cloudservers.CreateServerOptionsTest")
public class CreateServerOptionsTest {
Injector injector = Guice.createInjector(new ParserModule());
@Test
public void testAddEntityToRequestMapOfStringStringHttpRequest() {
CreateServerOptions options = new CreateServerOptions();
HttpRequest request = buildRequest(options);
assertEquals("{\"server\":{\"name\":\"foo\",\"imageId\":1,\"flavorId\":2}}", request
.getEntity());
}
private HttpRequest buildRequest(CreateServerOptions options) {
injector.injectMembers(options);
HttpRequest request = new HttpRequest(HttpMethod.POST, URI.create("/"));
options.addEntityToRequest(ImmutableMap.of("name", "foo", "imageId", "1", "flavorId", "2"),
request);
return request;
}
@Test
public void testWithFile() {
CreateServerOptions options = new CreateServerOptions();
options.withFile("/tmp/rhubarb", "foo".getBytes());
HttpRequest request = buildRequest(options);
assertFile(request);
}
@Test
public void testWithFileStatic() {
CreateServerOptions options = withFile("/tmp/rhubarb", "foo".getBytes());
HttpRequest request = buildRequest(options);
assertFile(request);
}
private void assertFile(HttpRequest request) {
assertEquals(
"{\"server\":{\"name\":\"foo\",\"imageId\":1,\"flavorId\":2,\"personality\":[{\"path\":\"/tmp/rhubarb\",\"contents\":\"Zm9v\"}]}}",
request.getEntity());
}
@Test
public void testWithSharedIpGroup() {
CreateServerOptions options = new CreateServerOptions();
options.withSharedIpGroup(3);
HttpRequest request = buildRequest(options);
assertSharedIpGroup(request);
}
@Test
public void testWithSharedIpGroupStatic() {
CreateServerOptions options = withSharedIpGroup(3);
HttpRequest request = buildRequest(options);
assertSharedIpGroup(request);
}
private void assertSharedIpGroup(HttpRequest request) {
assertEquals(
"{\"server\":{\"name\":\"foo\",\"imageId\":1,\"flavorId\":2,\"sharedIpGroupId\":3}}",
request.getEntity());
}
@Test
public void testWithMetadata() {
}
@Test
public void testWithSharedIp() throws UnknownHostException {
CreateServerOptions options = new CreateServerOptions();
options.withSharedIpGroup(3).withSharedIp(
InetAddress.getByAddress(new byte[] { 127, 0, 0, 1 }));
HttpRequest request = buildRequest(options);
assertSharedIp(request);
}
@Test
public void testWithSharedIpStatic() throws UnknownHostException {
CreateServerOptions options = withSharedIpGroup(3).withSharedIp(
InetAddress.getByAddress(new byte[] { 127, 0, 0, 1 }));
HttpRequest request = buildRequest(options);
assertSharedIp(request);
}
private void assertSharedIp(HttpRequest request) {
assertEquals(
"{\"server\":{\"name\":\"foo\",\"imageId\":1,\"flavorId\":2,\"sharedIpGroupId\":3,\"addresses\":{\"public\":[\"127.0.0.1\"]}}}",
request.getEntity());
}
@Test(expectedExceptions = IllegalStateException.class)
public void testWithSharedIpNoGroup() throws UnknownHostException {
CreateServerOptions options = new CreateServerOptions();
options.withSharedIp(InetAddress.getByAddress(new byte[] { 127, 0, 0, 1 }));
buildRequest(options);
}
@Test(expectedExceptions = IllegalStateException.class)
public void testWithSharedIpNoGroupStatic() throws UnknownHostException {
CreateServerOptions options = withSharedIp(InetAddress
.getByAddress(new byte[] { 127, 0, 0, 1 }));
buildRequest(options);
}
}

View File

@ -0,0 +1,25 @@
{
"server" : {
"id" : 1234,
"name" : "sample-server",
"imageId" : 2,
"flavorId" : 1,
"hostId" : "e4d909c290d0fb1ca068ffaddf22cbd0",
"status" : "BUILD",
"progress" : 60,
"addresses" : {
"public" : [
"67.23.10.132",
"67.23.10.131"
],
"private" : [
"10.176.42.16"
]
},
"metadata" : {
"Server Label" : "Web Head 1",
"Image Version" : "2.1"
}
}
}

View File

@ -40,6 +40,12 @@
<module>core</module>
</modules>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jclouds-jsch</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jclouds-rackspace-core</artifactId>