Issue 427: support all instance commands

This commit is contained in:
Adrian Cole 2010-12-22 18:03:51 +01:00
parent 62b5366f01
commit 55d4a579c0
16 changed files with 1188 additions and 28 deletions

View File

@ -71,6 +71,12 @@
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jclouds-jsch</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>

View File

@ -24,6 +24,7 @@ import java.util.Map;
import java.util.Set;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
@ -34,9 +35,12 @@ import org.jclouds.deltacloud.collections.DeltacloudCollection;
import org.jclouds.deltacloud.collections.Images;
import org.jclouds.deltacloud.collections.Instances;
import org.jclouds.deltacloud.domain.Image;
import org.jclouds.deltacloud.domain.Instance;
import org.jclouds.deltacloud.options.CreateInstanceOptions;
import org.jclouds.deltacloud.xml.ImageHandler;
import org.jclouds.deltacloud.xml.ImagesHandler;
import org.jclouds.deltacloud.xml.InstanceHandler;
import org.jclouds.deltacloud.xml.InstancesHandler;
import org.jclouds.deltacloud.xml.LinksHandler;
import org.jclouds.http.filters.BasicAuthentication;
import org.jclouds.rest.annotations.Endpoint;
@ -46,6 +50,7 @@ import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.XMLResponseParser;
import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404;
import com.google.common.util.concurrent.ListenableFuture;
@ -88,12 +93,47 @@ public interface DeltacloudAsyncClient {
@XMLResponseParser(ImageHandler.class)
ListenableFuture<Image> getImage(@EndpointParam URI imageHref);
/**
* @see DeltacloudClient#listInstances
*/
@GET
@Endpoint(Instances.class)
@Path("")
@ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
@XMLResponseParser(InstancesHandler.class)
ListenableFuture<? extends Set<Instance>> listInstances();
/**
* @see DeltacloudClient#getInstance
*/
@GET
@ExceptionParser(ReturnNullOnNotFoundOr404.class)
@Path("")
@XMLResponseParser(InstanceHandler.class)
ListenableFuture<Instance> getInstance(@EndpointParam URI instanceHref);
/**
* @see DeltacloudClient#createInstance
*/
@POST
@Endpoint(Instances.class)
@Path("")
ListenableFuture<String> createInstance(@FormParam("image_id") String imageId, CreateInstanceOptions... options);
@XMLResponseParser(InstanceHandler.class)
ListenableFuture<Instance> createInstance(@FormParam("image_id") String imageId, CreateInstanceOptions... options);
/**
* @see DeltacloudClient#performInstanceAction
*/
@POST
@Path("")
ListenableFuture<Void> performAction(@EndpointParam URI actionRef);
/**
* @see DeltacloudClient#deleteResource
*/
@DELETE
@ExceptionParser(ReturnVoidOnNotFoundOr404.class)
@Path("")
ListenableFuture<Void> deleteResource(@EndpointParam URI resourceHref);
}

View File

@ -27,7 +27,9 @@ import java.util.concurrent.TimeUnit;
import org.jclouds.concurrent.Timeout;
import org.jclouds.deltacloud.collections.DeltacloudCollection;
import org.jclouds.deltacloud.domain.Image;
import org.jclouds.deltacloud.domain.Instance;
import org.jclouds.deltacloud.options.CreateInstanceOptions;
import org.jclouds.rest.annotations.EndpointParam;
/**
* Provides synchronous access to deltacloud.
@ -61,6 +63,20 @@ public interface DeltacloudClient {
*/
Image getImage(URI imageHref);
/**
* The instances collection will return a set of all instances available to the current user.
*
* @return instances viewable to the user or empty set
*/
Set<? extends Instance> listInstances();
/**
*
* @param instanceHref
* @return instance or null, if not found
*/
Instance getInstance(URI instanceHref);
/**
* Create a new Instance
*
@ -76,5 +92,21 @@ public interface DeltacloudClient {
* includes realm, hardware profile, etc.
* @return newly-created instance including a URL to retrieve the instance in the future.
*/
String createInstance(String imageId, CreateInstanceOptions... options);
Instance createInstance(String imageId, CreateInstanceOptions... options);
/**
* perform a specific action.
*
* @param actionRef
* reference from {@link Instance#getActions()}
*/
void performAction(@EndpointParam URI actionRef);
/**
* delete a resource, such as {@link Instance}
*
* @param resourceHref
* reference from {@link Instance#getHref()}
*/
void deleteResource(@EndpointParam URI resourceHref);
}

View File

@ -0,0 +1,241 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed 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.deltacloud.domain;
import static com.google.common.base.Preconditions.checkNotNull;
import java.net.URI;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
/**
* An instance is a concrete machine realized from an image.
*
* @author Adrian Cole
*/
public class Instance {
private final URI href;
private final String id;
private final String ownerId;
@Nullable
private final String name;
private final URI image;
private final URI hardwareProfile;
private final URI realm;
private final InstanceState state;
private final Map<InstanceAction, URI> actions;
private final Set<String> publicAddresses;
private final Set<String> privateAddresses;
public Instance(URI href, String id, String ownerId, @Nullable String name, URI image, URI hardwareProfile,
URI realm, InstanceState state, Map<InstanceAction, URI> actions, Set<String> publicAddresses,
Set<String> privateAddresses) {
this.href = checkNotNull(href, "href");
this.id = checkNotNull(id, "id");
this.ownerId = checkNotNull(ownerId, "ownerId");
this.name = name;
this.image = checkNotNull(image, "image");
this.hardwareProfile = checkNotNull(hardwareProfile, "hardwareProfile");
this.realm = checkNotNull(realm, "realm");
this.state = checkNotNull(state, "state");
this.actions = ImmutableMap.copyOf(checkNotNull(actions, "actions"));
this.publicAddresses = ImmutableSet.copyOf(checkNotNull(publicAddresses, "publicAddresses"));
this.privateAddresses = ImmutableSet.copyOf(checkNotNull(privateAddresses, "privateAddresses"));
}
/**
*
* @return URL to manipulate a specific instance
*/
public URI getHref() {
return href;
}
/**
*
* @return A unique identifier for the instance
*/
public String getId() {
return id;
}
/**
*
* @return An opaque identifier which indicates the owner of an instance
*/
public String getOwnerId() {
return ownerId;
}
/**
*
* @return An optional short label describing the instance
*/
@Nullable
public String getName() {
return name;
}
/**
*
* @return
*/
public URI getImage() {
return image;
}
/**
*
* @return a link to the hardware profile in use by the instance
*/
public URI getHardwareProfile() {
return hardwareProfile;
}
/**
*
* @return a link to the realm where the instance is deployed
*/
public URI getRealm() {
return realm;
}
/**
*
* @return indicator of the instance's current state
*/
public InstanceState getState() {
return state;
}
/**
*
* @return valid actions for the instance, along with the URL which may be used to perform the
* action
*/
public Map<InstanceAction, URI> getActions() {
return actions;
}
/**
*
* @return publicly routable IP addresses or names for the instance
*/
public Set<String> getPublicAddresses() {
return publicAddresses;
}
/**
*
* @return Private network IP addresses or names for the instance
*/
public Set<String> getPrivateAddresses() {
return privateAddresses;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((actions == null) ? 0 : actions.hashCode());
result = prime * result + ((hardwareProfile == null) ? 0 : hardwareProfile.hashCode());
result = prime * result + ((href == null) ? 0 : href.hashCode());
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((image == null) ? 0 : image.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((privateAddresses == null) ? 0 : privateAddresses.hashCode());
result = prime * result + ((publicAddresses == null) ? 0 : publicAddresses.hashCode());
result = prime * result + ((realm == null) ? 0 : realm.hashCode());
result = prime * result + ((state == null) ? 0 : state.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;
Instance other = (Instance) obj;
if (actions == null) {
if (other.actions != null)
return false;
} else if (!actions.equals(other.actions))
return false;
if (hardwareProfile == null) {
if (other.hardwareProfile != null)
return false;
} else if (!hardwareProfile.equals(other.hardwareProfile))
return false;
if (href == null) {
if (other.href != null)
return false;
} else if (!href.equals(other.href))
return false;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (image == null) {
if (other.image != null)
return false;
} else if (!image.equals(other.image))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (privateAddresses == null) {
if (other.privateAddresses != null)
return false;
} else if (!privateAddresses.equals(other.privateAddresses))
return false;
if (publicAddresses == null) {
if (other.publicAddresses != null)
return false;
} else if (!publicAddresses.equals(other.publicAddresses))
return false;
if (realm == null) {
if (other.realm != null)
return false;
} else if (!realm.equals(other.realm))
return false;
if (state != other.state)
return false;
return true;
}
@Override
public String toString() {
return "[href=" + href + ", id=" + id + ", name=" + name + ", image=" + image + ", hardwareProfile="
+ hardwareProfile + ", realm=" + realm + ", state=" + state + ", actions=" + actions + ", publicAddresses="
+ publicAddresses + ", privateAddresses=" + privateAddresses + "]";
}
}

View File

@ -0,0 +1,57 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed 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.deltacloud.domain;
import static com.google.common.base.Preconditions.checkNotNull;
/**
*
* Indicator of the instance's current action
*
* @author Adrian Cole
*
*/
public enum InstanceAction {
REBOOT,
START,
STOP,
UNRECOGNIZED;
public String value() {
return name().toLowerCase();
}
@Override
public String toString() {
return value();
}
public static InstanceAction fromValue(String action) {
try {
return valueOf(checkNotNull(action, "action").toUpperCase());
} catch (IllegalArgumentException e) {
return UNRECOGNIZED;
}
}
}

View File

@ -0,0 +1,59 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed 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.deltacloud.domain;
import static com.google.common.base.Preconditions.checkNotNull;
/**
*
* Indicator of the instance's current state
*
* @author Adrian Cole
*
*/
public enum InstanceState {
/**
* the instance is in the process of being launched
*/
PENDING,
/**
* the instance is stopped
*/
STOPPED,
/**
* the instance launched (although the boot process might not be completed)
*/
RUNNING,
/**
* state returned as something besides the above.
*/
UNRECOGNIZED;
public static InstanceState fromValue(String state) {
try {
return valueOf(checkNotNull(state, "state"));
} catch (IllegalArgumentException e) {
return UNRECOGNIZED;
}
}
}

View File

@ -0,0 +1,135 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed 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.deltacloud.xml;
import java.net.URI;
import java.util.Map;
import java.util.Set;
import org.jclouds.deltacloud.domain.Instance;
import org.jclouds.deltacloud.domain.InstanceAction;
import org.jclouds.deltacloud.domain.InstanceState;
import org.jclouds.http.functions.ParseSax;
import org.jclouds.util.Utils;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
/**
* @author Adrian Cole
*/
public class InstanceHandler extends ParseSax.HandlerWithResult<Instance> {
private StringBuilder currentText = new StringBuilder();
private URI href;
private String id;
private String ownerId;
private String name;
private URI image;
private URI hardwareProfile;
private URI realm;
private InstanceState state;
private Map<InstanceAction, URI> actions = Maps.newLinkedHashMap();
private Set<String> publicAddresses = Sets.newLinkedHashSet();
private Set<String> privateAddresses = Sets.newLinkedHashSet();
private boolean inPublicAddresses;
private boolean inPrivateAddresses;
private Instance instance;
public Instance getResult() {
return instance;
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attrs) throws SAXException {
Map<String, String> attributes = Utils.cleanseAttributes(attrs);
if (qName.equals("public_addresses")) {
inPublicAddresses = true;
} else if (qName.equals("private_addresses")) {
inPrivateAddresses = true;
} else if (qName.equals("instance")) {
String href = attributes.get("href");
if (href != null) {
this.href = URI.create(href);
}
this.id = attributes.get("id");
} else if (qName.equals("link")) {
String rel = attributes.get("rel");
if (rel != null) {
InstanceAction action = InstanceAction.fromValue(rel);
if (action != InstanceAction.UNRECOGNIZED) {
actions.put(action, URI.create(attributes.get("href")));
}
}
} else if (attributes.containsKey("href")) {
URI href = URI.create(attributes.get("href"));
if (qName.equals("image"))
this.image = href;
else if (qName.equals("hardware_profile"))
this.hardwareProfile = href;
else if (qName.equals("realm"))
this.realm = href;
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if (qName.endsWith("public_addresses")) {
inPublicAddresses = false;
} else if (qName.endsWith("private_addresses")) {
inPrivateAddresses = false;
}
if (qName.equalsIgnoreCase("owner_id")) {
this.ownerId = currentText.toString().trim();
} else if (qName.equalsIgnoreCase("name")) {
this.name = currentText.toString().trim();
} else if (qName.equalsIgnoreCase("state")) {
this.state = InstanceState.fromValue(currentText.toString().trim());
} else if (qName.equalsIgnoreCase("address")) {
if (inPublicAddresses)
this.publicAddresses.add(currentText.toString().trim());
else if (inPrivateAddresses)
this.privateAddresses.add(currentText.toString().trim());
} else if (qName.equalsIgnoreCase("instance")) {
this.instance = new Instance(href, id, ownerId, name, image, hardwareProfile, realm, state, actions,
publicAddresses, privateAddresses);
this.href = null;
this.id = null;
this.ownerId = null;
this.name = null;
this.image = null;
this.hardwareProfile = null;
this.realm = null;
this.state = null;
this.actions = Maps.newLinkedHashMap();
this.publicAddresses = Sets.newLinkedHashSet();
this.privateAddresses = Sets.newLinkedHashSet();
}
currentText = new StringBuilder();
}
public void characters(char ch[], int start, int length) {
currentText.append(ch, start, length);
}
}

View File

@ -0,0 +1,69 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed 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.deltacloud.xml;
import java.util.Set;
import javax.inject.Inject;
import org.jclouds.deltacloud.domain.Instance;
import org.jclouds.http.functions.ParseSax;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import com.google.common.collect.Sets;
/**
* @author Adrian Cole
*/
public class InstancesHandler extends ParseSax.HandlerWithResult<Set<? extends Instance>> {
private StringBuilder currentText = new StringBuilder();
private Set<Instance> instances = Sets.newLinkedHashSet();
private final InstanceHandler instanceHandler;
@Inject
public InstancesHandler(InstanceHandler locationHandler) {
this.instanceHandler = locationHandler;
}
public Set<? extends Instance> getResult() {
return instances;
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
instanceHandler.startElement(uri, localName, qName, attributes);
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
instanceHandler.endElement(uri, localName, qName);
if (qName.equals("instance") && currentText.toString().trim().equals("")) {
this.instances.add(instanceHandler.getResult());
}
currentText = new StringBuilder();
}
public void characters(char ch[], int start, int length) {
instanceHandler.characters(ch, start, length);
currentText.append(ch, start, length);
}
}

View File

@ -32,18 +32,22 @@ import org.jclouds.deltacloud.config.DeltacloudRestClientModule;
import org.jclouds.deltacloud.options.CreateInstanceOptions;
import org.jclouds.deltacloud.xml.ImageHandler;
import org.jclouds.deltacloud.xml.ImagesHandler;
import org.jclouds.deltacloud.xml.InstanceHandler;
import org.jclouds.deltacloud.xml.InstancesHandler;
import org.jclouds.deltacloud.xml.LinksHandler;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.RequiresHttp;
import org.jclouds.http.filters.BasicAuthentication;
import org.jclouds.http.functions.ParseSax;
import org.jclouds.http.functions.ReturnStringIf2xx;
import org.jclouds.http.functions.ReleasePayloadAndReturn;
import org.jclouds.rest.ConfiguresRestClient;
import org.jclouds.rest.RestClientTest;
import org.jclouds.rest.RestContextFactory;
import org.jclouds.rest.RestContextSpec;
import org.jclouds.rest.functions.MapHttp4xxCodesToExceptions;
import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import org.jclouds.rest.internal.RestAnnotationProcessor;
import org.testng.annotations.Test;
@ -117,6 +121,36 @@ public class DeltacloudAsyncClientTest extends RestClientTest<DeltacloudAsyncCli
checkFilters(request);
}
public void testListInstances() throws IOException, SecurityException, NoSuchMethodException {
Method method = DeltacloudAsyncClient.class.getMethod("listInstances");
HttpRequest request = processor.createRequest(method);
assertRequestLineEquals(request, "GET http://localhost:3001/api/instances HTTP/1.1");
assertNonPayloadHeadersEqual(request, "Accept: application/xml\n");
assertPayloadEquals(request, null, null, false);
assertResponseParserClassEquals(method, request, ParseSax.class);
assertSaxResponseParserClassEquals(method, InstancesHandler.class);
assertExceptionParserClassEquals(method, ReturnEmptySetOnNotFoundOr404.class);
checkFilters(request);
}
public void testGetInstance() throws IOException, SecurityException, NoSuchMethodException {
Method method = DeltacloudAsyncClient.class.getMethod("getInstance", URI.class);
HttpRequest request = processor.createRequest(method, URI.create("https://delta/instance1"));
assertRequestLineEquals(request, "GET https://delta/instance1 HTTP/1.1");
assertNonPayloadHeadersEqual(request, "Accept: application/xml\n");
assertPayloadEquals(request, null, null, false);
assertResponseParserClassEquals(method, request, ParseSax.class);
assertSaxResponseParserClassEquals(method, InstanceHandler.class);
assertExceptionParserClassEquals(method, ReturnNullOnNotFoundOr404.class);
checkFilters(request);
}
public void testCreateInstance() throws SecurityException, NoSuchMethodException, IOException {
Method method = DeltacloudAsyncClient.class.getMethod("createInstance", String.class,
CreateInstanceOptions[].class);
@ -126,8 +160,8 @@ public class DeltacloudAsyncClientTest extends RestClientTest<DeltacloudAsyncCli
assertNonPayloadHeadersEqual(httpRequest, "Accept: application/xml\n");
assertPayloadEquals(httpRequest, "image_id=imageId-1", "application/x-www-form-urlencoded", false);
assertResponseParserClassEquals(method, httpRequest, ReturnStringIf2xx.class);
assertSaxResponseParserClassEquals(method, null);
assertResponseParserClassEquals(method, httpRequest, ParseSax.class);
assertSaxResponseParserClassEquals(method, InstanceHandler.class);
assertExceptionParserClassEquals(method, null);
checkFilters(httpRequest);
@ -144,14 +178,44 @@ public class DeltacloudAsyncClientTest extends RestClientTest<DeltacloudAsyncCli
assertNonPayloadHeadersEqual(httpRequest, "Accept: application/xml\n");
assertPayloadEquals(httpRequest, "image_id=imageId-1&name=foo", "application/x-www-form-urlencoded", false);
assertResponseParserClassEquals(method, httpRequest, ReturnStringIf2xx.class);
assertSaxResponseParserClassEquals(method, null);
assertResponseParserClassEquals(method, httpRequest, ParseSax.class);
assertSaxResponseParserClassEquals(method, InstanceHandler.class);
assertExceptionParserClassEquals(method, null);
checkFilters(httpRequest);
}
public void testPerformAction() throws IOException, SecurityException, NoSuchMethodException {
Method method = DeltacloudAsyncClient.class.getMethod("performAction", URI.class);
HttpRequest request = processor.createRequest(method, URI.create("https://delta/instance1/reboot"));
assertRequestLineEquals(request, "POST https://delta/instance1/reboot HTTP/1.1");
assertNonPayloadHeadersEqual(request, "Accept: application/xml\n");
assertPayloadEquals(request, null, null, false);
assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, MapHttp4xxCodesToExceptions.class);
checkFilters(request);
}
public void testDeleteResource() throws IOException, SecurityException, NoSuchMethodException {
Method method = DeltacloudAsyncClient.class.getMethod("deleteResource", URI.class);
HttpRequest request = processor.createRequest(method, URI.create("https://delta/instance1"));
assertRequestLineEquals(request, "DELETE https://delta/instance1 HTTP/1.1");
assertNonPayloadHeadersEqual(request, "Accept: application/xml\n");
assertPayloadEquals(request, null, null, false);
assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, ReturnVoidOnNotFoundOr404.class);
checkFilters(request);
}
@Override
protected void checkFilters(HttpRequest request) {
assertEquals(request.getFilters().size(), 1);

View File

@ -19,27 +19,44 @@
package org.jclouds.deltacloud;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
import java.io.IOException;
import java.net.URI;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import org.jclouds.Constants;
import org.jclouds.deltacloud.collections.DeltacloudCollection;
import org.jclouds.deltacloud.domain.Image;
import org.jclouds.deltacloud.domain.Instance;
import org.jclouds.deltacloud.domain.InstanceAction;
import org.jclouds.deltacloud.domain.InstanceState;
import org.jclouds.deltacloud.options.CreateInstanceOptions;
import org.jclouds.domain.Credentials;
import org.jclouds.logging.log4j.config.Log4JLoggingModule;
import org.jclouds.net.IPSocket;
import org.jclouds.predicates.InetSocketAddressConnect;
import org.jclouds.predicates.RetryablePredicate;
import org.jclouds.rest.RestContext;
import org.jclouds.rest.RestContextFactory;
import org.jclouds.ssh.ExecResponse;
import org.jclouds.ssh.SshClient;
import org.jclouds.ssh.jsch.config.JschSshClientModule;
import org.testng.annotations.AfterGroups;
import org.testng.annotations.BeforeGroups;
import org.testng.annotations.Test;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.gson.Gson;
import com.google.inject.Guice;
import com.google.inject.Module;
/**
@ -58,11 +75,12 @@ public class DeltacloudClientLiveTest {
protected String credential;
protected String endpoint;
protected String apiversion;
protected Predicate<IPSocket> socketTester;
protected void setupCredentials() {
identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
credential = System.getProperty("test." + provider + ".credential");
endpoint = System.getProperty("test." + provider + ".endpoint");
identity = System.getProperty("test." + provider + ".identity", "mockuser");
credential = System.getProperty("test." + provider + ".credential", "mockpassword");
endpoint = System.getProperty("test." + provider + ".endpoint", "http://localhost:3001/api");
apiversion = System.getProperty("test." + provider + ".apiversion");
}
@ -88,12 +106,7 @@ public class DeltacloudClientLiveTest {
overrides);
client = context.getApi();
}
@AfterGroups(groups = "live")
void tearDown() {
if (context != null)
context.close();
socketTester = new RetryablePredicate<IPSocket>(new InetSocketAddressConnect(), 180, 1, TimeUnit.SECONDS);
}
@Test
@ -115,14 +128,109 @@ public class DeltacloudClientLiveTest {
}
}
@Test
public void testCreateInstance() throws Exception {
// TODO
public void testListAndGetInstances() throws Exception {
Set<? extends Instance> response = client.listInstances();
assert null != response;
long instanceCount = response.size();
assertTrue(instanceCount >= 0);
for (Instance instance : response) {
Instance newDetails = client.getInstance(instance.getHref());
assertEquals(instance, newDetails);
}
}
@Test
public void testCreateInstanceWithOptions() throws Exception {
protected String prefix = System.getProperty("user.name") + ".test";
protected Instance instance;
public void testCreateInstance() throws Exception {
Logger.getAnonymousLogger().info("starting instance");
instance = client.createInstance(Iterables.get(client.listImages(), 0).getId(),
CreateInstanceOptions.Builder.named(prefix));
instance = client.getInstance(instance.getHref());
checkStartedInstance();
Instance newInfo = client.getInstance(instance.getHref());
checkInstanceMatchesGet(newInfo);
}
protected void checkInstanceMatchesGet(Instance newInfo) {
assertEquals(newInfo.getHref(), instance.getHref());
}
protected void checkStartedInstance() {
System.out.println(new Gson().toJson(instance));
assertEquals(instance.getName(), prefix);
assertEquals(instance.getState(), InstanceState.RUNNING);
}
@Test(dependsOnMethods = "testCreateInstance")
public void testConnectivity() throws Exception {
Logger.getAnonymousLogger().info("awaiting ssh");
// TODO
// assert socketTester.apply(new IPSocket(Iterables.get(instance.getPublicAddresses(), 0),
// 22)) : instance;
// doConnectViaSsh(instance, getSshCredentials(instance));
}
private Credentials getSshCredentials(Instance instance2) {
// TODO
return null;
}
public URI refreshInstanceAndGetAction(InstanceAction action) {
return client.getInstance(instance.getHref()).getActions().get(action);
}
@Test(dependsOnMethods = "testConnectivity")
public void testLifeCycle() throws Exception {
client.performAction(refreshInstanceAndGetAction(InstanceAction.STOP));
assertEquals(client.getInstance(instance.getHref()).getState(), InstanceState.STOPPED);
client.performAction(refreshInstanceAndGetAction(InstanceAction.START));
assertEquals(client.getInstance(instance.getHref()).getState(), InstanceState.RUNNING);
client.performAction(refreshInstanceAndGetAction(InstanceAction.REBOOT));
assertEquals(client.getInstance(instance.getHref()).getState(), InstanceState.RUNNING);
}
@Test(dependsOnMethods = "testLifeCycle")
public void testDestroyInstance() throws Exception {
try {
client.deleteResource(instance.getHref());
} catch (IllegalArgumentException e) {
client.performAction(refreshInstanceAndGetAction(InstanceAction.STOP));
client.deleteResource(instance.getHref());
assertEquals(client.getInstance(instance.getHref()), null);
}
}
protected void doConnectViaSsh(Instance instance, Credentials creds) throws IOException {
SshClient ssh = Guice.createInjector(new JschSshClientModule()).getInstance(SshClient.Factory.class)
.create(new IPSocket(Iterables.get(instance.getPublicAddresses(), 0), 22), creds);
try {
ssh.connect();
ExecResponse hello = ssh.exec("echo hello");
assertEquals(hello.getOutput().trim(), "hello");
System.err.println(ssh.exec("df -k").getOutput());
System.err.println(ssh.exec("mount").getOutput());
System.err.println(ssh.exec("uname -a").getOutput());
} finally {
if (ssh != null)
ssh.disconnect();
}
}
@AfterGroups(groups = "live")
protected void tearDown() {
try {
client.deleteResource(instance.getHref());
} catch (Exception e) {
// no need to check null or anything as we swallow all
}
if (context != null)
context.close();
}
}

View File

@ -0,0 +1,74 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed 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.deltacloud.xml;
import static org.testng.Assert.assertEquals;
import java.io.InputStream;
import java.net.URI;
import org.jclouds.deltacloud.domain.Instance;
import org.jclouds.deltacloud.domain.InstanceAction;
import org.jclouds.deltacloud.domain.InstanceState;
import org.jclouds.http.functions.ParseSax;
import org.jclouds.http.functions.config.SaxParserModule;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Guice;
import com.google.inject.Injector;
/**
* Tests behavior of {@code InstanceHandler}
*
* @author Adrian Cole
*/
@Test(groups = "unit")
public class InstanceHandlerTest {
static ParseSax<Instance> createParser() {
Injector injector = Guice.createInjector(new SaxParserModule());
ParseSax<Instance> parser = injector.getInstance(ParseSax.Factory.class).create(
injector.getInstance(InstanceHandler.class));
return parser;
}
public static Instance parseInstance() {
return parseInstance("/test_get_instance.xml");
}
public static Instance parseInstance(String resource) {
InputStream is = InstanceHandlerTest.class.getResourceAsStream(resource);
return createParser().parse(is);
}
public void test() {
Instance expects = new Instance(URI.create("http://fancycloudprovider.com/api/instances/inst1"), "inst1", "larry",
"Production JBoss Instance", URI.create("http://fancycloudprovider.com/api/images/img3"),
URI.create("http://fancycloudprovider.com/api/hardware_profiles/m1-small"),
URI.create("http://fancycloudprovider.com/api/realms/us"), InstanceState.RUNNING, ImmutableMap.of(
InstanceAction.REBOOT, URI.create("http://fancycloudprovider.com/api/instances/inst1/reboot"),
InstanceAction.STOP, URI.create("http://fancycloudprovider.com/api/instances/inst1/stop")),
ImmutableSet.of("inst1.larry.fancycloudprovider.com"), ImmutableSet.of("inst1.larry.internal"));
assertEquals(parseInstance(), expects);
}
}

View File

@ -0,0 +1,63 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed 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.deltacloud.xml;
import static org.testng.Assert.assertEquals;
import java.io.InputStream;
import java.net.URI;
import java.util.Set;
import org.jclouds.deltacloud.domain.Instance;
import org.jclouds.deltacloud.domain.InstanceAction;
import org.jclouds.deltacloud.domain.InstanceState;
import org.jclouds.http.functions.BaseHandlerTest;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
/**
* Tests behavior of {@code InstancesHandler}
*
* @author Adrian Cole
*/
@Test(groups = "unit")
public class InstancesHandlerTest extends BaseHandlerTest {
@Test
public void test() {
InputStream is = getClass().getResourceAsStream("/test_list_instances.xml");
Set<? extends Instance> expects = ImmutableSet.of(new Instance(URI
.create("http://fancycloudprovider.com/api/instances/inst1"), "inst1", "larry",
"Production JBoss Instance", URI.create("http://fancycloudprovider.com/api/images/img3"), URI
.create("http://fancycloudprovider.com/api/hardware_profiles/m1-small"), URI
.create("http://fancycloudprovider.com/api/realms/us"), InstanceState.RUNNING, ImmutableMap.of(
InstanceAction.REBOOT, URI.create("http://fancycloudprovider.com/api/instances/inst1/reboot"),
InstanceAction.STOP, URI.create("http://fancycloudprovider.com/api/instances/inst1/stop")),
ImmutableSet.of("inst1.larry.fancycloudprovider.com"), ImmutableSet.of("inst1.larry.internal")));
System.out.println(factory);
System.out.println(injector);
// not sure why this isn't always automatically called from surefire.
setUpInjector();
assertEquals(factory.create(injector.getInstance(InstancesHandler.class)).parse(is), expects);
}
}

View File

@ -17,7 +17,7 @@
* ====================================================================
*/
package org.jclouds.deltacloud.handlers;
package org.jclouds.deltacloud.xml;
import static org.testng.Assert.assertEquals;
@ -26,7 +26,6 @@ import java.net.URI;
import java.util.Map;
import org.jclouds.deltacloud.collections.DeltacloudCollection;
import org.jclouds.deltacloud.xml.LinksHandler;
import org.jclouds.http.functions.BaseHandlerTest;
import org.testng.annotations.Test;
@ -42,16 +41,17 @@ public class LinksHandlerTest extends BaseHandlerTest {
public void test() {
InputStream is = getClass().getResourceAsStream("/links.xml");
Map<DeltacloudCollection, URI> result = factory.create(injector.getInstance(LinksHandler.class)).parse(is);
assertEquals(result, ImmutableMap.of(//
Map<DeltacloudCollection, URI> expects = ImmutableMap.of(//
DeltacloudCollection.HARDWARE_PROFILES, URI.create("http://fancycloudprovider.com/api/hardware_profiles"),//
DeltacloudCollection.INSTANCE_STATES, URI.create("http://fancycloudprovider.com/api/instance_states"),//
DeltacloudCollection.REALMS, URI.create("http://fancycloudprovider.com/api/realms"),//
DeltacloudCollection.IMAGES, URI.create("http://fancycloudprovider.com/api/images"),//
DeltacloudCollection.INSTANCES, URI.create("http://fancycloudprovider.com/api/instances")
));
);
// not sure why this isn't always automatically called from surefire.
setUpInjector();
assertEquals(factory.create(injector.getInstance(LinksHandler.class)).parse(is), expects);
}

View File

@ -0,0 +1,170 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (C) 2010 Cloud Conscious, LLC.
<info@cloudconscious.com>
====================================================================
Licensed 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.
====================================================================
-->
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<!--
For more configuration infromation and examples see the Apache
Log4j website: http://logging.apache.org/log4j/
-->
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"
debug="false">
<!-- A time/date based rolling appender -->
<appender name="WIREFILE" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="target/test-data/jclouds-wire.log" />
<param name="Append" value="true" />
<!-- Rollover at midnight each day -->
<param name="DatePattern" value="'.'yyyy-MM-dd" />
<param name="Threshold" value="TRACE" />
<layout class="org.apache.log4j.PatternLayout">
<!-- The default pattern: Date Priority [Category] Message\n -->
<param name="ConversionPattern" value="%d %-5p [%c] (%t) %m%n" />
<!--
The full pattern: Date MS Priority [Category]
(Thread:NDC) Message\n <param name="ConversionPattern"
value="%d %-5r %-5p [%c] (%t:%x) %m%n"/>
-->
</layout>
</appender>
<!-- A time/date based rolling appender -->
<appender name="FILE" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="target/test-data/jclouds.log" />
<param name="Append" value="true" />
<!-- Rollover at midnight each day -->
<param name="DatePattern" value="'.'yyyy-MM-dd" />
<param name="Threshold" value="TRACE" />
<layout class="org.apache.log4j.PatternLayout">
<!-- The default pattern: Date Priority [Category] Message\n -->
<param name="ConversionPattern" value="%d %-5p [%c] (%t) %m%n" />
<!--
The full pattern: Date MS Priority [Category]
(Thread:NDC) Message\n <param name="ConversionPattern"
value="%d %-5r %-5p [%c] (%t:%x) %m%n"/>
-->
</layout>
</appender>
<!-- A time/date based rolling appender -->
<appender name="COMPUTEFILE" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="target/test-data/jclouds-compute.log" />
<param name="Append" value="true" />
<!-- Rollover at midnight each day -->
<param name="DatePattern" value="'.'yyyy-MM-dd" />
<param name="Threshold" value="TRACE" />
<layout class="org.apache.log4j.PatternLayout">
<!-- The default pattern: Date Priority [Category] Message\n -->
<param name="ConversionPattern" value="%d %-5p [%c] (%t) %m%n" />
<!--
The full pattern: Date MS Priority [Category]
(Thread:NDC) Message\n <param name="ConversionPattern"
value="%d %-5r %-5p [%c] (%t:%x) %m%n"/>
-->
</layout>
</appender>
<!-- A time/date based rolling appender -->
<appender name="SSHFILE" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="target/test-data/jclouds-ssh.log" />
<param name="Append" value="true" />
<!-- Rollover at midnight each day -->
<param name="DatePattern" value="'.'yyyy-MM-dd" />
<param name="Threshold" value="TRACE" />
<layout class="org.apache.log4j.PatternLayout">
<!-- The default pattern: Date Priority [Category] Message\n -->
<param name="ConversionPattern" value="%d %-5p [%c] (%t) %m%n" />
<!--
The full pattern: Date MS Priority [Category]
(Thread:NDC) Message\n <param name="ConversionPattern"
value="%d %-5r %-5p [%c] (%t:%x) %m%n"/>
-->
</layout>
</appender>
<appender name="ASYNCCOMPUTE" class="org.apache.log4j.AsyncAppender">
<appender-ref ref="COMPUTEFILE" />
</appender>
<appender name="ASYNCSSH" class="org.apache.log4j.AsyncAppender">
<appender-ref ref="SSHFILE" />
</appender>
<appender name="ASYNC" class="org.apache.log4j.AsyncAppender">
<appender-ref ref="FILE" />
</appender>
<appender name="ASYNCWIRE" class="org.apache.log4j.AsyncAppender">
<appender-ref ref="WIREFILE" />
</appender>
<!-- ================ -->
<!-- Limit categories -->
<!-- ================ -->
<category name="org.jclouds">
<priority value="DEBUG" />
<appender-ref ref="ASYNC" />
</category>
<category name="jclouds.headers">
<priority value="DEBUG" />
<appender-ref ref="ASYNCWIRE" />
</category>
<category name="jclouds.ssh">
<priority value="DEBUG" />
<appender-ref ref="ASYNCSSH" />
</category>
<category name="jclouds.wire">
<priority value="DEBUG" />
<appender-ref ref="ASYNCWIRE" />
</category>
<category name="jclouds.compute">
<priority value="TRACE" />
<appender-ref ref="ASYNCCOMPUTE" />
</category>
<!-- ======================= -->
<!-- Setup the Root category -->
<!-- ======================= -->
<root>
<priority value="WARN" />
</root>
</log4j:configuration>

View File

@ -0,0 +1,20 @@
<instance href="http://fancycloudprovider.com/api/instances/inst1" id='inst1'>
<owner_id>larry</owner_id>
<name>Production JBoss Instance</name>
<image href="http://fancycloudprovider.com/api/images/img3"/>
<hardware_profile href="http://fancycloudprovider.com/api/hardware_profiles/m1-small"/>
<realm href="http://fancycloudprovider.com/api/realms/us"/>
<state>RUNNING</state>
<actions>
<link rel="reboot" href="http://fancycloudprovider.com/api/instances/inst1/reboot"/>
<link rel="stop" href="http://fancycloudprovider.com/api/instances/inst1/stop"/>
</actions>
<public_addresses>
<address>inst1.larry.fancycloudprovider.com</address>
</public_addresses>
<private_addresses>
<address>inst1.larry.internal</address>
</private_addresses>
</instance>

View File

@ -0,0 +1,22 @@
<instances>
<instance href="http://fancycloudprovider.com/api/instances/inst1" id='inst1'>
<owner_id>larry</owner_id>
<name>Production JBoss Instance</name>
<image href="http://fancycloudprovider.com/api/images/img3"/>
<hardware_profile href="http://fancycloudprovider.com/api/hardware_profiles/m1-small"/>
<realm href="http://fancycloudprovider.com/api/realms/us"/>
<state>RUNNING</state>
<actions>
<link rel="reboot" href="http://fancycloudprovider.com/api/instances/inst1/reboot"/>
<link rel="stop" href="http://fancycloudprovider.com/api/instances/inst1/stop"/>
</actions>
<public_addresses>
<address>inst1.larry.fancycloudprovider.com</address>
</public_addresses>
<private_addresses>
<address>inst1.larry.internal</address>
</private_addresses>
</instance>
</instances>