diff --git a/.gitignore b/.gitignore index 7c10a8150f..6da66153e1 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ test-output/ *.iml *.ipr *.iws +TAGS diff --git a/README.txt b/README.txt index a547eb6471..a5311e4791 100644 --- a/README.txt +++ b/README.txt @@ -30,7 +30,7 @@ our current version is 1.0-beta-9c our dev version is 1.0-SNAPSHOT our compute api supports: aws-ec2, gogrid, cloudservers-us, stub (in-memory), deltacloud, - cloudservers-uk, vcloud (generic), ec2 (generic), byon, + cloudservers-uk, vcloud (generic), ec2 (generic), byon, nova, trmk-ecloud, trmk-vcloudexpress, eucalyptus (generic), cloudsigma-zrh, elasticstack(generic), bluelock-vclouddirector, slicehost, eucalyptus-partnercloud-ec2, elastichosts-lon-p (Peer 1), @@ -49,6 +49,11 @@ our blobstore api supports: aws-s3, cloudfiles-us, cloudfiles-uk, filesystem, * note * the pom dependency org.jclouds/jclouds-allblobstore gives you access to to all of these providers +our loadbalancer api supports: cloudloadbalancers-us + + * note * the pom dependency org.jclouds/jclouds-allloadbalancer gives you access to + to all of these providers + we also have support for: ibmdev, mezeo, nirvanix, boxdotnet, rimuhosting, openstack nova, azurequeue, simpledb, cloudstack as well as a async-http-client driver in the sandbox diff --git a/all/pom.xml b/all/pom.xml index 51f0aab3ea..6b3864bffc 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -29,6 +29,11 @@ jclouds-all all + + ${project.groupId} + jclouds-allloadbalancer + ${project.version} + ${project.groupId} jclouds-allcompute diff --git a/allcompute/pom.xml b/allcompute/pom.xml index 47cdbc898f..8f41d5f0a8 100644 --- a/allcompute/pom.xml +++ b/allcompute/pom.xml @@ -44,6 +44,11 @@ aws-ec2 ${project.version} + + org.jclouds.api + nova + ${project.version} + org.jclouds.api byon diff --git a/allloadbalancer/pom.xml b/allloadbalancer/pom.xml new file mode 100644 index 0000000000..895cba5f32 --- /dev/null +++ b/allloadbalancer/pom.xml @@ -0,0 +1,38 @@ + + + 4.0.0 + + org.jclouds + jclouds-project + 1.0-SNAPSHOT + ../project/pom.xml + + jclouds-allloadbalancer + allloadbalancer + + + org.jclouds.provider + cloudloadbalancers-us + ${project.version} + + + diff --git a/apis/cloudservers/src/main/java/org/jclouds/cloudservers/binders/BindBackupScheduleToJsonPayload.java b/apis/cloudservers/src/main/java/org/jclouds/cloudservers/binders/BindBackupScheduleToJsonPayload.java index df4a6ade7f..5c4b819203 100644 --- a/apis/cloudservers/src/main/java/org/jclouds/cloudservers/binders/BindBackupScheduleToJsonPayload.java +++ b/apis/cloudservers/src/main/java/org/jclouds/cloudservers/binders/BindBackupScheduleToJsonPayload.java @@ -22,9 +22,11 @@ import static com.google.common.base.Preconditions.checkArgument; import java.util.Map; +import javax.inject.Inject; import javax.inject.Singleton; import org.jclouds.http.HttpRequest; +import org.jclouds.json.Json; import org.jclouds.cloudservers.domain.BackupSchedule; import org.jclouds.rest.binders.BindToJsonPayload; @@ -37,17 +39,19 @@ import com.google.common.collect.ImmutableMap; */ @Singleton public class BindBackupScheduleToJsonPayload extends BindToJsonPayload { + @Inject + public BindBackupScheduleToJsonPayload(Json jsonBinder) { + super(jsonBinder); + } @Override public R bindToRequest(R request, Map postParams) { - throw new IllegalStateException( - "Replace Backup Schedule needs an BackupSchedule object, not a Map"); + throw new IllegalStateException("Replace Backup Schedule needs an BackupSchedule object, not a Map"); } @Override public R bindToRequest(R request, Object toBind) { - checkArgument(toBind instanceof BackupSchedule, - "this binder is only valid for BackupSchedules!"); + checkArgument(toBind instanceof BackupSchedule, "this binder is only valid for BackupSchedules!"); return super.bindToRequest(request, ImmutableMap.of("backupSchedule", toBind)); } } diff --git a/apis/cloudservers/src/main/java/org/jclouds/cloudservers/options/CreateServerOptions.java b/apis/cloudservers/src/main/java/org/jclouds/cloudservers/options/CreateServerOptions.java index 4d99b3e9b1..2fb8814686 100644 --- a/apis/cloudservers/src/main/java/org/jclouds/cloudservers/options/CreateServerOptions.java +++ b/apis/cloudservers/src/main/java/org/jclouds/cloudservers/options/CreateServerOptions.java @@ -26,9 +26,12 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import javax.inject.Inject; + +import org.jclouds.cloudservers.domain.Addresses; import org.jclouds.encryption.internal.Base64; import org.jclouds.http.HttpRequest; -import org.jclouds.cloudservers.domain.Addresses; +import org.jclouds.rest.MapBinder; import org.jclouds.rest.binders.BindToJsonPayload; import com.google.common.collect.ImmutableMap; @@ -40,7 +43,9 @@ import com.google.common.collect.Maps; * @author Adrian Cole * */ -public class CreateServerOptions extends BindToJsonPayload { +public class CreateServerOptions implements MapBinder { + @Inject + private BindToJsonPayload jsonBinder; static class File { private final String path; @@ -50,11 +55,9 @@ public class CreateServerOptions extends BindToJsonPayload { this.path = checkNotNull(path, "path"); this.contents = Base64.encodeBytes(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)); + "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)); + "maximum size of the file is 10KB. Contents specified is %d bytes", contents.length)); } public String getContents() { @@ -92,10 +95,9 @@ public class CreateServerOptions extends BindToJsonPayload { @Override public R bindToRequest(R request, Map postParams) { - 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"))); + 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) @@ -162,19 +164,15 @@ public class CreateServerOptions extends BindToJsonPayload { */ public CreateServerOptions withMetadata(Map metadata) { checkNotNull(metadata, "metadata"); - checkArgument(metadata.size() <= 5, - "you cannot have more then 5 metadata values. You specified: " + metadata.size()); + checkArgument(metadata.size() <= 5, "you cannot have more then 5 metadata values. You specified: " + + metadata.size()); for (Entry 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)); + "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; @@ -196,8 +194,7 @@ public class CreateServerOptions extends BindToJsonPayload { * sharedIpGroupId is also supplied. */ public CreateServerOptions withSharedIp(String publicIp) { - checkState(sharedIpGroupId != null, - "sharedIp is invalid unless a shared ip group is specified."); + checkState(sharedIpGroupId != null, "sharedIp is invalid unless a shared ip group is specified."); this.publicIp = checkNotNull(publicIp, "ip"); return this; } @@ -237,4 +234,9 @@ public class CreateServerOptions extends BindToJsonPayload { } } + + @Override + public R bindToRequest(R request, Object input) { + return jsonBinder.bindToRequest(request, input); + } } diff --git a/apis/cloudservers/src/main/java/org/jclouds/cloudservers/options/CreateSharedIpGroupOptions.java b/apis/cloudservers/src/main/java/org/jclouds/cloudservers/options/CreateSharedIpGroupOptions.java index b2d1366eed..7421ab24b0 100644 --- a/apis/cloudservers/src/main/java/org/jclouds/cloudservers/options/CreateSharedIpGroupOptions.java +++ b/apis/cloudservers/src/main/java/org/jclouds/cloudservers/options/CreateSharedIpGroupOptions.java @@ -24,8 +24,10 @@ import static com.google.common.base.Preconditions.checkNotNull; import java.util.Map; import javax.annotation.Nullable; +import javax.inject.Inject; import org.jclouds.http.HttpRequest; +import org.jclouds.rest.MapBinder; import org.jclouds.rest.binders.BindToJsonPayload; import com.google.common.collect.ImmutableMap; @@ -36,7 +38,10 @@ import com.google.common.collect.ImmutableMap; * @author Adrian Cole * */ -public class CreateSharedIpGroupOptions extends BindToJsonPayload { +public class CreateSharedIpGroupOptions implements MapBinder { + @Inject + private BindToJsonPayload jsonBinder; + Integer serverId; @SuppressWarnings("unused") @@ -53,9 +58,8 @@ public class CreateSharedIpGroupOptions extends BindToJsonPayload { @Override public R bindToRequest(R request, Map postParams) { - SharedIpGroupRequest createRequest = new SharedIpGroupRequest(checkNotNull(postParams - .get("name")), serverId); - return super.bindToRequest(request, ImmutableMap.of("sharedIpGroup", createRequest)); + SharedIpGroupRequest createRequest = new SharedIpGroupRequest(checkNotNull(postParams.get("name")), serverId); + return jsonBinder.bindToRequest(request, ImmutableMap.of("sharedIpGroup", createRequest)); } @Override @@ -84,4 +88,5 @@ public class CreateSharedIpGroupOptions extends BindToJsonPayload { return options.withServer(id); } } + } diff --git a/apis/cloudservers/src/main/java/org/jclouds/cloudservers/options/RebuildServerOptions.java b/apis/cloudservers/src/main/java/org/jclouds/cloudservers/options/RebuildServerOptions.java index 2c252c7494..3d8f42a91b 100644 --- a/apis/cloudservers/src/main/java/org/jclouds/cloudservers/options/RebuildServerOptions.java +++ b/apis/cloudservers/src/main/java/org/jclouds/cloudservers/options/RebuildServerOptions.java @@ -22,7 +22,10 @@ import static com.google.common.base.Preconditions.checkArgument; import java.util.Map; +import javax.inject.Inject; + import org.jclouds.http.HttpRequest; +import org.jclouds.rest.MapBinder; import org.jclouds.rest.binders.BindToJsonPayload; import com.google.common.collect.ImmutableMap; @@ -34,7 +37,9 @@ import com.google.common.collect.Maps; * @author Adrian Cole * */ -public class RebuildServerOptions extends BindToJsonPayload { +public class RebuildServerOptions implements MapBinder { + @Inject + private BindToJsonPayload jsonBinder; Integer imageId; @Override @@ -42,7 +47,7 @@ public class RebuildServerOptions extends BindToJsonPayload { Map image = Maps.newHashMap(); if (imageId != null) image.put("imageId", imageId); - return super.bindToRequest(request, ImmutableMap.of("rebuild", image)); + return jsonBinder.bindToRequest(request, ImmutableMap.of("rebuild", image)); } @Override diff --git a/apis/deltacloud/src/test/java/org/jclouds/deltacloud/handlers/DeltacloudRedirectionRetryHandlerTest.java b/apis/deltacloud/src/test/java/org/jclouds/deltacloud/handlers/DeltacloudRedirectionRetryHandlerTest.java index 35bc8b855c..6ad5fb7088 100644 --- a/apis/deltacloud/src/test/java/org/jclouds/deltacloud/handlers/DeltacloudRedirectionRetryHandlerTest.java +++ b/apis/deltacloud/src/test/java/org/jclouds/deltacloud/handlers/DeltacloudRedirectionRetryHandlerTest.java @@ -25,6 +25,9 @@ import static org.easymock.classextension.EasyMock.verify; import java.net.URI; +import javax.inject.Named; +import javax.inject.Singleton; + import org.jclouds.http.HttpCommand; import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpResponse; @@ -32,7 +35,12 @@ import org.jclouds.rest.BaseRestClientTest.MockModule; import org.jclouds.rest.config.RestModule; import org.testng.annotations.Test; +import com.google.common.collect.LinkedHashMultimap; +import com.google.common.collect.Multimap; +import com.google.inject.AbstractModule; import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.Provides; /** * Tests behavior of {@code DeltacloudRedirectionRetry} @@ -41,6 +49,19 @@ import com.google.inject.Guice; */ @Test(groups = "unit") public class DeltacloudRedirectionRetryHandlerTest { + Injector injector = Guice.createInjector(new MockModule(), new RestModule(), new AbstractModule() { + @SuppressWarnings("unused") + @Provides + @Singleton + @Named("CONSTANTS") + protected Multimap constants() { + return LinkedHashMultimap.create(); + } + + @Override + protected void configure() { + } + }); @Test public void test302DoesNotRetryOnDelete() { @@ -53,7 +74,7 @@ public class DeltacloudRedirectionRetryHandlerTest { replay(command); - DeltacloudRedirectionRetryHandler retry = Guice.createInjector(new MockModule(), new RestModule()).getInstance( + DeltacloudRedirectionRetryHandler retry = injector.getInstance( DeltacloudRedirectionRetryHandler.class); assert !retry.shouldRetryRequest(command, response); @@ -74,7 +95,7 @@ public class DeltacloudRedirectionRetryHandlerTest { replay(command); - DeltacloudRedirectionRetryHandler retry = Guice.createInjector(new MockModule(), new RestModule()).getInstance( + DeltacloudRedirectionRetryHandler retry = injector.getInstance( DeltacloudRedirectionRetryHandler.class); assert !retry.shouldRetryRequest(command, response); diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ComputeService.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ComputeService.java index 85b2490c74..7cbbb0dd5a 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ComputeService.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ComputeService.java @@ -42,6 +42,7 @@ import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.internal.BaseComputeService; +import org.jclouds.compute.internal.PersistNodeCredentials; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts; import org.jclouds.compute.strategy.CreateNodesInGroupThenAddToSet; @@ -60,6 +61,7 @@ import org.jclouds.ec2.compute.domain.RegionNameAndIngressRules; import org.jclouds.ec2.compute.options.EC2TemplateOptions; import org.jclouds.ec2.domain.KeyPair; import org.jclouds.ec2.domain.RunningInstance; +import org.jclouds.scriptbuilder.functions.InitAdminAccess; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; @@ -89,13 +91,14 @@ public class EC2ComputeService extends BaseComputeService { @Named("NODE_RUNNING") Predicate nodeRunning, @Named("NODE_TERMINATED") Predicate nodeTerminated, @Named("NODE_SUSPENDED") Predicate nodeSuspended, - InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, Timeouts timeouts, + InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, InitAdminAccess initAdminAccess, + PersistNodeCredentials persistNodeCredentials, Timeouts timeouts, @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, EC2Client ec2Client, Map credentialsMap, @Named("SECURITY") Map securityGroupMap) { super(context, credentialStore, images, sizes, locations, listNodesStrategy, getNodeMetadataStrategy, runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy, startNodeStrategy, stopNodeStrategy, templateBuilderProvider, templateOptionsProvider, nodeRunning, nodeTerminated, nodeSuspended, - initScriptRunnerFactory, timeouts, executor); + initScriptRunnerFactory, initAdminAccess, persistNodeCredentials, timeouts, executor); this.ec2Client = ec2Client; this.credentialsMap = credentialsMap; this.securityGroupMap = securityGroupMap; @@ -125,7 +128,8 @@ public class EC2ComputeService extends BaseComputeService { // when the keypair is unique per group keyPair.getKeyName().equals("jclouds#" + group) || keyPair.getKeyName().matches(String.format("jclouds#%s#%s", group, "[0-9a-f]+")) - // old keypair pattern too verbose as it has an unnecessary region qualifier + // old keypair pattern too verbose as it has an unnecessary + // region qualifier || keyPair.getKeyName().matches(String.format("jclouds#%s#%s#%s", group, region, "[0-9a-f]+"))) { Set instancesUsingKeyPair = extractIdsFromInstances(filter(concat(ec2Client.getInstanceServices() .describeInstancesInRegion(region)), usingKeyPairAndNotDead(keyPair))); @@ -170,8 +174,8 @@ public class EC2ComputeService extends BaseComputeService { } /** - * like {@link BaseComputeService#destroyNodesMatching} except that this will clean implicit - * keypairs and security groups. + * like {@link BaseComputeService#destroyNodesMatching} except that this will + * clean implicit keypairs and security groups. */ @Override public Set destroyNodesMatching(Predicate filter) { diff --git a/sandbox-apis/nova/pom.xml b/apis/nova/pom.xml similarity index 71% rename from sandbox-apis/nova/pom.xml rename to apis/nova/pom.xml index 13e3dda25b..0104b4bd9c 100644 --- a/sandbox-apis/nova/pom.xml +++ b/apis/nova/pom.xml @@ -1,150 +1,179 @@ - - - - - 4.0.0 - - org.jclouds - jclouds-project - 1.0-SNAPSHOT - ../../project/pom.xml - - org.jclouds.api - nova - jcloud nova api - jclouds components to access an implementation of OpenStack Nova - - - https://auth.api.rackspacecloud.com - 1.1 - FIXME - FIXME - - - - - org.jclouds.common - openstack-common - ${project.version} - - - org.jclouds - jclouds-compute - ${project.version} - - - org.jclouds - jclouds-core - ${project.version} - test-jar - test - - - org.jclouds.common - openstack-common - ${project.version} - test-jar - test - - - org.jclouds - jclouds-compute - ${project.version} - test-jar - test - - - org.jclouds.driver - jclouds-jsch - ${project.version} - test - - - com.jcraft - jsch - test - - - org.jclouds.driver - jclouds-log4j - ${project.version} - test - - - log4j - log4j - 1.2.16 - test - - - - - - live - - - - org.apache.maven.plugins - maven-surefire-plugin - - - integration - integration-test - - test - - - - - test.nova.endpoint - ${test.nova.endpoint} - - - test.nova.apiversion - ${test.nova.apiversion} - - - test.nova.identity - ${test.nova.identity} - - - test.nova.credential - ${test.nova.credential} - - - test.initializer - ${test.initializer} - - - - - - - - - - - - - - + + + + + 4.0.0 + + org.jclouds + jclouds-project + 1.0-SNAPSHOT + ../../project/pom.xml + + org.jclouds.api + nova + jcloud nova api + jclouds components to access an implementation of OpenStack Nova + + + + + + + + org.jclouds.common + openstack-common + ${project.version} + + + org.jclouds + jclouds-compute + ${project.version} + + + org.jclouds + jclouds-core + ${project.version} + test-jar + test + + + org.jclouds.common + openstack-common + ${project.version} + test-jar + test + + + org.jclouds + jclouds-compute + ${project.version} + test-jar + test + + + org.jclouds.driver + jclouds-jsch + ${project.version} + test + + + org.jclouds.driver + jclouds-slf4j + ${project.version} + test + + + slf4j-api + org.slf4j + 1.6.1 + test + + + ch.qos.logback + logback-classic + 0.9.28 + test + + + + + + + maven-remote-resources-plugin + 1.2 + + + process-remote-resources + + + + + + + + + + + live + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.8.1 + + + org.apache.maven.surefire + surefire-testng + 2.8.1 + + + + + integration + integration-test + + test + + + + + test.nova.endpoint + ${test.nova.endpoint} + + + test.nova.apiversion + ${test.nova.apiversion} + + + test.nova.identity + ${test.nova.identity} + + + test.nova.credential + ${test.nova.credential} + + + test.ssh.keyfile.public + ${test.ssh.keyfile.public} + + + test.ssh.keyfile.private + ${test.ssh.keyfile.private} + + + test.initializer + ${test.initializer} + + + + + + + + + + + + + diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/NovaAsyncClient.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/NovaAsyncClient.java similarity index 63% rename from sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/NovaAsyncClient.java rename to apis/nova/src/main/java/org/jclouds/openstack/nova/NovaAsyncClient.java index 73bbab0d57..663ade8c32 100644 --- a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/NovaAsyncClient.java +++ b/apis/nova/src/main/java/org/jclouds/openstack/nova/NovaAsyncClient.java @@ -18,50 +18,22 @@ */ package org.jclouds.openstack.nova; -import java.util.Set; -import java.util.concurrent.ExecutionException; - -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; - -import org.jclouds.openstack.nova.binders.BindBackupScheduleToJsonPayload; -import org.jclouds.openstack.nova.domain.Addresses; -import org.jclouds.openstack.nova.domain.BackupSchedule; -import org.jclouds.openstack.nova.domain.Flavor; -import org.jclouds.openstack.nova.domain.Image; -import org.jclouds.openstack.nova.domain.RebootType; -import org.jclouds.openstack.nova.domain.Server; -import org.jclouds.openstack.nova.domain.SharedIpGroup; -import org.jclouds.openstack.nova.options.CreateServerOptions; -import org.jclouds.openstack.nova.options.CreateSharedIpGroupOptions; -import org.jclouds.openstack.nova.options.ListOptions; -import org.jclouds.openstack.nova.options.RebuildServerOptions; -import org.jclouds.http.functions.ReturnFalseOn404; +import com.google.common.util.concurrent.ListenableFuture; import org.jclouds.openstack.filters.AddTimestampQuery; import org.jclouds.openstack.filters.AuthenticateRequest; -import org.jclouds.rest.annotations.BinderParam; -import org.jclouds.rest.annotations.Endpoint; -import org.jclouds.rest.annotations.ExceptionParser; -import org.jclouds.rest.annotations.MapBinder; -import org.jclouds.rest.annotations.Payload; -import org.jclouds.rest.annotations.PayloadParam; -import org.jclouds.rest.annotations.QueryParams; -import org.jclouds.rest.annotations.RequestFilters; -import org.jclouds.rest.annotations.SkipEncoding; -import org.jclouds.rest.annotations.Unwrap; +import org.jclouds.openstack.nova.domain.*; +import org.jclouds.openstack.nova.options.CreateServerOptions; +import org.jclouds.openstack.nova.options.ListOptions; +import org.jclouds.openstack.nova.options.RebuildServerOptions; +import org.jclouds.rest.annotations.*; import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404; import org.jclouds.rest.functions.ReturnFalseOnNotFoundOr404; import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; -import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404; -import com.google.common.util.concurrent.ListenableFuture; +import javax.ws.rs.*; +import javax.ws.rs.core.MediaType; +import java.util.Set; +import java.util.concurrent.ExecutionException; /** * Provides asynchronous access to OpenStack Nova via their REST API. @@ -69,13 +41,13 @@ import com.google.common.util.concurrent.ListenableFuture; * All commands return a ListenableFuture of the result from OpenStack Nova. Any exceptions incurred * during processing will be wrapped in an {@link ExecutionException} as documented in * {@link ListenableFuture#get()}. - * + * + * @author Adrian Cole * @see NovaClient * @see - * @author Adrian Cole */ -@SkipEncoding({ '/', '=' }) -@RequestFilters({ AuthenticateRequest.class, AddTimestampQuery.class }) +@SkipEncoding({'/', '='}) +@RequestFilters({AuthenticateRequest.class, AddTimestampQuery.class}) @Endpoint(ServerManagement.class) public interface NovaAsyncClient { @@ -158,8 +130,8 @@ public interface NovaAsyncClient { @QueryParams(keys = "format", values = "json") @Path("/servers") @MapBinder(CreateServerOptions.class) - ListenableFuture createServer(@PayloadParam("name") String name, @PayloadParam("imageId") int imageId, - @PayloadParam("flavorId") int flavorId, CreateServerOptions... options); + ListenableFuture createServer(@PayloadParam("name") String name, @PayloadParam("imageRef") String imageRef, + @PayloadParam("flavorRef") String flavorRef, CreateServerOptions... options); /** * @see NovaClient#rebuildServer @@ -170,33 +142,14 @@ public interface NovaAsyncClient { @MapBinder(RebuildServerOptions.class) ListenableFuture rebuildServer(@PathParam("id") int id, RebuildServerOptions... options); - /** - * @see NovaClient#shareIp - */ - @PUT - @Path("/servers/{id}/ips/public/{address}") - @Produces(MediaType.APPLICATION_JSON) - @Payload("%7B\"shareIp\":%7B\"sharedIpGroupId\":{sharedIpGroupId},\"configureServer\":{configureServer}%7D%7D") - ListenableFuture shareIp(@PathParam("address") String addressToShare, - @PathParam("id") int serverToTosignBindressTo, @PayloadParam("sharedIpGroupId") int sharedIpGroup, - @PayloadParam("configureServer") boolean configureServer); - - /** - * @see NovaClient#unshareIp - */ - @DELETE - @Path("/servers/{id}/ips/public/{address}") - @ExceptionParser(ReturnVoidOnNotFoundOr404.class) - ListenableFuture unshareIp(@PathParam("address") String addressToShare, - @PathParam("id") int serverToTosignBindressTo); /** * @see NovaClient#changeAdminPass */ - @PUT - @Path("/servers/{id}") + @POST + @Path("/servers/{id}/action") @Produces(MediaType.APPLICATION_JSON) - @Payload("%7B\"server\":%7B\"adminPass\":\"{adminPass}\"%7D%7D") + @Payload("%7B\"changePassword\":%7B\"adminPass\":\"{adminPass}\"%7D%7D") ListenableFuture changeAdminPass(@PathParam("id") int id, @PayloadParam("adminPass") String adminPass); /** @@ -271,76 +224,7 @@ public interface NovaAsyncClient { @Produces(MediaType.APPLICATION_JSON) @Payload("%7B\"image\":%7B\"serverId\":{serverId},\"name\":\"{name}\"%7D%7D") ListenableFuture createImageFromServer(@PayloadParam("name") String imageName, - @PayloadParam("serverId") int serverId); - - /** - * @see NovaClient#listSharedIpGroups - */ - @GET - @Unwrap - @Consumes(MediaType.APPLICATION_JSON) - @QueryParams(keys = "format", values = "json") - @Path("/shared_ip_groups") - @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class) - ListenableFuture> listSharedIpGroups(ListOptions... options); - - /** - * @see NovaClient#getSharedIpGroup - */ - @GET - @Unwrap - @Consumes(MediaType.APPLICATION_JSON) - @QueryParams(keys = "format", values = "json") - @Path("/shared_ip_groups/{id}") - @ExceptionParser(ReturnNullOnNotFoundOr404.class) - ListenableFuture getSharedIpGroup(@PathParam("id") int id); - - /** - * @see NovaClient#createSharedIpGroup - */ - @POST - @Unwrap - @Consumes(MediaType.APPLICATION_JSON) - @QueryParams(keys = "format", values = "json") - @Path("/shared_ip_groups") - @MapBinder(CreateSharedIpGroupOptions.class) - ListenableFuture createSharedIpGroup(@PayloadParam("name") String name, - CreateSharedIpGroupOptions... options); - - /** - * @see NovaClient#deleteSharedIpGroup - */ - @DELETE - @ExceptionParser(ReturnFalseOnNotFoundOr404.class) - @Path("/shared_ip_groups/{id}") - ListenableFuture deleteSharedIpGroup(@PathParam("id") int id); - - /** - * @see NovaClient#listBackupSchedule - */ - @GET - @Unwrap - @Consumes(MediaType.APPLICATION_JSON) - @QueryParams(keys = "format", values = "json") - @Path("/servers/{id}/backup_schedule") - ListenableFuture getBackupSchedule(@PathParam("id") int serverId); - - /** - * @see NovaClient#deleteBackupSchedule - */ - @DELETE - @ExceptionParser(ReturnFalseOnNotFoundOr404.class) - @Path("/servers/{id}/backup_schedule") - ListenableFuture deleteBackupSchedule(@PathParam("id") int serverId); - - /** - * @see NovaClient#replaceBackupSchedule - */ - @POST - @ExceptionParser(ReturnFalseOn404.class) - @Path("/servers/{id}/backup_schedule") - ListenableFuture replaceBackupSchedule(@PathParam("id") int id, - @BinderParam(BindBackupScheduleToJsonPayload.class) BackupSchedule backupSchedule); + @PayloadParam("serverId") int serverId); /** * @see NovaClient#listAddresses diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/NovaClient.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/NovaClient.java similarity index 72% rename from sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/NovaClient.java rename to apis/nova/src/main/java/org/jclouds/openstack/nova/NovaClient.java index 22acaf2ce6..3ed6f04e50 100644 --- a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/NovaClient.java +++ b/apis/nova/src/main/java/org/jclouds/openstack/nova/NovaClient.java @@ -26,14 +26,11 @@ import javax.ws.rs.PathParam; import org.jclouds.concurrent.Timeout; import org.jclouds.openstack.nova.domain.Addresses; -import org.jclouds.openstack.nova.domain.BackupSchedule; import org.jclouds.openstack.nova.domain.Flavor; import org.jclouds.openstack.nova.domain.Image; import org.jclouds.openstack.nova.domain.RebootType; import org.jclouds.openstack.nova.domain.Server; -import org.jclouds.openstack.nova.domain.SharedIpGroup; import org.jclouds.openstack.nova.options.CreateServerOptions; -import org.jclouds.openstack.nova.options.CreateSharedIpGroupOptions; import org.jclouds.openstack.nova.options.ListOptions; import org.jclouds.openstack.nova.options.RebuildServerOptions; import org.jclouds.rest.ResourceNotFoundException; @@ -154,7 +151,7 @@ public interface NovaClient { * @param options * - used to specify extra files, metadata, or ip parameters during server creation. */ - Server createServer(String name, int imageId, int flavorId, CreateServerOptions... options); + Server createServer(String name, String imageRef, String flavorRef, CreateServerOptions... options); /** * The rebuild function removes all data on the server and replaces it with the specified image. @@ -173,40 +170,6 @@ public interface NovaClient { */ void rebuildServer(int id, RebuildServerOptions... options); - /** - * /** This operation allows you share an IP address to the specified server - *

- * This operation shares an IP from an existing server in the specified shared IP group to - * another specified server in the same group. The operation modifies cloud network restrictions - * to allow IP traffic for the given IP to/from the server specified. - * - *

- * Status Transition: ACTIVE - SHARE_IP - ACTIVE (if configureServer is true) ACTIVE - - * SHARE_IP_NO_CONFIG - ACTIVE - * - * @param configureServer - *

- * if set to true, the server is configured with the new address, though the address is - * not enabled. Note that configuring the server does require a reboot. - *

- * If set to false, does not bind the IP to the server itself. A heartbeat facility - * (e.g. keepalived) can then be used within the servers to perform health checks and - * manage IP failover. - */ - void shareIp(String addressToShare, int serverToTosignBindressTo, int sharedIpGroup, - boolean configureServer); - - /** - * This operation removes a shared IP address from the specified server. - *

- * Status Transition: ACTIVE - DELETE_IP - ACTIVE - * - * @param addressToShare - * @param serverToTosignBindressTo - * @return - */ - void unshareIp(String addressToShare, int serverToTosignBindressTo); - /** * This operation allows you to change the administrative password. *

@@ -295,66 +258,6 @@ public interface NovaClient { */ Image createImageFromServer(String imageName, int serverId); - /** - * - * List shared IP groups (IDs and names only) - * - * in order to retrieve all details, pass the option {@link ListOptions#withDetails() - * withDetails()} - */ - Set listSharedIpGroups(ListOptions... options); - - /** - * - * This operation returns details of the specified shared IP group. - * - * @return null, if the shared ip group is not found - * - * @see SharedIpGroup - */ - SharedIpGroup getSharedIpGroup(int id); - - /** - * This operation creates a new shared IP group. Please note, all responses to requests for - * shared_ip_groups return an array of servers. However, on a create request, the shared IP group - * can be created empty or can be initially populated with a single server. Use - * {@link CreateSharedIpGroupOptions} to specify an server. - */ - SharedIpGroup createSharedIpGroup(String name, CreateSharedIpGroupOptions... options); - - /** - * This operation deletes the specified shared IP group. This operation will ONLY succeed if 1) - * there are no active servers in the group (i.e. they have all been terminated) or 2) no servers - * in the group are actively sharing IPs. - * - * @return false if the shared ip group is not found - * @see SharedIpGroup - */ - boolean deleteSharedIpGroup(int id); - - /** - * List the backup schedule for the specified server - * - * @throws ResourceNotFoundException - * , if the server doesn't exist - */ - BackupSchedule getBackupSchedule(int serverId); - - /** - * Delete backup schedule for the specified server. - *

- * Web Hosting #119571 currently disables the schedule, not deletes it. - * - * @return false if the schedule is not found - */ - boolean deleteBackupSchedule(int serverId); - - /** - * Enable/update the backup schedule for the specified server - * - */ - void replaceBackupSchedule(int id, BackupSchedule backupSchedule); - /** * List all server addresses * diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/NovaContextBuilder.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/NovaContextBuilder.java similarity index 100% rename from sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/NovaContextBuilder.java rename to apis/nova/src/main/java/org/jclouds/openstack/nova/NovaContextBuilder.java diff --git a/apis/nova/src/main/java/org/jclouds/openstack/nova/NovaPropertiesBuilder.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/NovaPropertiesBuilder.java new file mode 100644 index 0000000000..8e98a04fcd --- /dev/null +++ b/apis/nova/src/main/java/org/jclouds/openstack/nova/NovaPropertiesBuilder.java @@ -0,0 +1,47 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.openstack.nova; + +import org.jclouds.PropertiesBuilder; + +import java.util.Properties; + +import static org.jclouds.Constants.PROPERTY_API_VERSION; + +/** + * Builds properties used in Openstack Nova Clients + * + * @author Dmitri Babaev + */ +public class NovaPropertiesBuilder extends PropertiesBuilder { + @Override + protected Properties defaultProperties() { + Properties properties = super.defaultProperties(); + properties.setProperty(PROPERTY_API_VERSION, "1.1"); + return properties; + } + + public NovaPropertiesBuilder(Properties properties) { + super(properties); + } + + public NovaPropertiesBuilder() { + super(); + } +} diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/ServerManagement.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/ServerManagement.java similarity index 100% rename from sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/ServerManagement.java rename to apis/nova/src/main/java/org/jclouds/openstack/nova/ServerManagement.java diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/config/NovaBindComputeStrategiesByClass.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/compute/config/NovaBindComputeStrategiesByClass.java similarity index 100% rename from sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/config/NovaBindComputeStrategiesByClass.java rename to apis/nova/src/main/java/org/jclouds/openstack/nova/compute/config/NovaBindComputeStrategiesByClass.java diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/config/NovaBindComputeSuppliersByClass.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/compute/config/NovaBindComputeSuppliersByClass.java similarity index 100% rename from sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/config/NovaBindComputeSuppliersByClass.java rename to apis/nova/src/main/java/org/jclouds/openstack/nova/compute/config/NovaBindComputeSuppliersByClass.java diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/config/NovaComputeServiceContextModule.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/compute/config/NovaComputeServiceContextModule.java similarity index 100% rename from sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/config/NovaComputeServiceContextModule.java rename to apis/nova/src/main/java/org/jclouds/openstack/nova/compute/config/NovaComputeServiceContextModule.java diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/config/NovaComputeServiceDependenciesModule.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/compute/config/NovaComputeServiceDependenciesModule.java similarity index 90% rename from sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/config/NovaComputeServiceDependenciesModule.java rename to apis/nova/src/main/java/org/jclouds/openstack/nova/compute/config/NovaComputeServiceDependenciesModule.java index acd932d565..234b2971bc 100644 --- a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/config/NovaComputeServiceDependenciesModule.java +++ b/apis/nova/src/main/java/org/jclouds/openstack/nova/compute/config/NovaComputeServiceDependenciesModule.java @@ -89,19 +89,11 @@ public class NovaComputeServiceDependenciesModule extends AbstractModule { .put(ServerStatus.PREP_RESIZE, NodeState.PENDING)// .put(ServerStatus.RESIZE, NodeState.PENDING)// .put(ServerStatus.VERIFY_RESIZE, NodeState.PENDING)// - .put(ServerStatus.QUEUE_MOVE, NodeState.PENDING)// - .put(ServerStatus.PREP_MOVE, NodeState.PENDING)// - .put(ServerStatus.MOVE, NodeState.PENDING)// - .put(ServerStatus.VERIFY_MOVE, NodeState.PENDING)// .put(ServerStatus.RESCUE, NodeState.PENDING)// - .put(ServerStatus.ERROR, NodeState.ERROR)// .put(ServerStatus.BUILD, NodeState.PENDING)// - .put(ServerStatus.RESTORING, NodeState.PENDING)// .put(ServerStatus.PASSWORD, NodeState.PENDING)// .put(ServerStatus.REBUILD, NodeState.PENDING)// .put(ServerStatus.DELETE_IP, NodeState.PENDING)// - .put(ServerStatus.SHARE_IP_NO_CONFIG, NodeState.PENDING)// - .put(ServerStatus.SHARE_IP, NodeState.PENDING)// .put(ServerStatus.REBOOT, NodeState.PENDING)// .put(ServerStatus.HARD_REBOOT, NodeState.PENDING)// .put(ServerStatus.UNKNOWN, NodeState.UNRECOGNIZED)// diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/functions/FlavorToHardware.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/compute/functions/FlavorToHardware.java similarity index 86% rename from sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/functions/FlavorToHardware.java rename to apis/nova/src/main/java/org/jclouds/openstack/nova/compute/functions/FlavorToHardware.java index 8f5064a61b..f166754303 100644 --- a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/functions/FlavorToHardware.java +++ b/apis/nova/src/main/java/org/jclouds/openstack/nova/compute/functions/FlavorToHardware.java @@ -36,8 +36,13 @@ import com.google.common.collect.ImmutableList; @Singleton public class FlavorToHardware implements Function { public Hardware apply(Flavor from) { - return new HardwareBuilder().ids(from.getId() + "").name(from.getName()) - .processors(ImmutableList.of(new Processor(from.getDisk() / 10.0, 1.0))).ram(from.getRam()) - .volumes(ImmutableList. of(new VolumeImpl((float) from.getDisk(), true, true))).build(); + return new HardwareBuilder() + .ids(from.getId() + "") + .name(from.getName()) + .processors(ImmutableList.of(new Processor(from.getDisk() / 10.0, 1.0))) + .ram(from.getRam()) + .volumes(ImmutableList. of(new VolumeImpl((float) from.getDisk(), true, true))) + .uri(from.getURI()) + .build(); } } diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/functions/NovaImageToImage.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/compute/functions/NovaImageToImage.java similarity index 86% rename from sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/functions/NovaImageToImage.java rename to apis/nova/src/main/java/org/jclouds/openstack/nova/compute/functions/NovaImageToImage.java index 23660c95d4..e606c5b13a 100644 --- a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/functions/NovaImageToImage.java +++ b/apis/nova/src/main/java/org/jclouds/openstack/nova/compute/functions/NovaImageToImage.java @@ -44,11 +44,12 @@ public class NovaImageToImage implements Function { @Override public boolean apply(Image input) { - return input.getProviderId().equals(instance.getImageId() + ""); + return input.getUri().toString().equals(instance.getImageRef() + ""); } } @@ -88,14 +82,14 @@ public class ServerToNodeMetadata implements Function { @Override public boolean apply(Hardware input) { - return input.getProviderId().equals(instance.getFlavorId() + ""); + return input.getUri().toString().equals(instance.getFlavorRef() + ""); } } @Inject ServerToNodeMetadata(Map serverStateToNodeState, Map credentialStore, - @Memoized Supplier> images, Supplier location, - @Memoized Supplier> hardwares) { + @Memoized Supplier> images, Supplier location, + @Memoized Supplier> hardwares) { this.serverToNodeState = checkNotNull(serverStateToNodeState, "serverStateToNodeState"); this.credentialStore = checkNotNull(credentialStore, "credentialStore"); this.images = checkNotNull(images, "images"); @@ -109,16 +103,20 @@ public class ServerToNodeMetadata implements Function { builder.ids(from.getId() + ""); builder.name(from.getName()); builder.location(new LocationBuilder().scope(LocationScope.HOST).id(from.getHostId()).description( - from.getHostId()).parent(location.get()).build()); + from.getHostId()).parent(location.get()).build()); builder.userMetadata(from.getMetadata()); builder.group(parseGroupFromName(from.getName())); - builder.imageId(from.getImageId() + ""); - builder.operatingSystem(parseOperatingSystem(from)); + Image image = parseImage(from); + if (image != null) { + builder.imageId(image.getId()); + builder.operatingSystem(image.getOperatingSystem()); + } builder.hardware(parseHardware(from)); builder.state(serverToNodeState.get(from.getStatus())); - builder.publicAddresses(from.getAddresses().getPublicAddresses()); - builder.privateAddresses(from.getAddresses().getPrivateAddresses()); + builder.publicAddresses(Iterables.transform(from.getAddresses().getPublicAddresses(), Address.newAddress2StringFunction())); + builder.privateAddresses(Iterables.transform(from.getAddresses().getPrivateAddresses(), Address.newAddress2StringFunction())); builder.credentials(credentialStore.get("node#" + from.getId())); + builder.uri(from.getURI()); return builder.build(); } @@ -130,10 +128,10 @@ public class ServerToNodeMetadata implements Function { } return null; } - - protected OperatingSystem parseOperatingSystem(Server from) { + + protected Image parseImage(Server from) { try { - return Iterables.find(images.get(), new FindImageForServer(from)).getOperatingSystem(); + return Iterables.find(images.get(), new FindImageForServer(from)); } catch (NoSuchElementException e) { logger.warn("could not find a matching image for server %s in location %s", from, location); } diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/strategy/NovaCreateNodeWithGroupEncodedIntoName.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/compute/strategy/NovaCreateNodeWithGroupEncodedIntoName.java similarity index 92% rename from sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/strategy/NovaCreateNodeWithGroupEncodedIntoName.java rename to apis/nova/src/main/java/org/jclouds/openstack/nova/compute/strategy/NovaCreateNodeWithGroupEncodedIntoName.java index 71e37bbb16..5cb3b1fa5a 100644 --- a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/strategy/NovaCreateNodeWithGroupEncodedIntoName.java +++ b/apis/nova/src/main/java/org/jclouds/openstack/nova/compute/strategy/NovaCreateNodeWithGroupEncodedIntoName.java @@ -53,8 +53,7 @@ public class NovaCreateNodeWithGroupEncodedIntoName implements CreateNodeWithGro @Override public NodeMetadata createNodeWithGroupEncodedIntoName(String group, String name, Template template) { - Server from = client.createServer(name, Integer.parseInt(template.getImage().getProviderId()), Integer - .parseInt(template.getHardware().getProviderId())); + Server from = client.createServer(name, template.getImage().getId(), template.getHardware().getId()); credentialStore.put("node#" + from.getId(), new Credentials("root", from.getAdminPass())); return serverToNodeMetadata.apply(from); } diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/strategy/NovaDestroyNodeStrategy.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/compute/strategy/NovaDestroyNodeStrategy.java similarity index 100% rename from sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/strategy/NovaDestroyNodeStrategy.java rename to apis/nova/src/main/java/org/jclouds/openstack/nova/compute/strategy/NovaDestroyNodeStrategy.java diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/strategy/NovaGetNodeMetadataStrategy.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/compute/strategy/NovaGetNodeMetadataStrategy.java similarity index 100% rename from sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/strategy/NovaGetNodeMetadataStrategy.java rename to apis/nova/src/main/java/org/jclouds/openstack/nova/compute/strategy/NovaGetNodeMetadataStrategy.java diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/strategy/NovaLifeCycleStrategy.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/compute/strategy/NovaLifeCycleStrategy.java similarity index 97% rename from sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/strategy/NovaLifeCycleStrategy.java rename to apis/nova/src/main/java/org/jclouds/openstack/nova/compute/strategy/NovaLifeCycleStrategy.java index 79a552cd27..41a0c3873b 100644 --- a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/strategy/NovaLifeCycleStrategy.java +++ b/apis/nova/src/main/java/org/jclouds/openstack/nova/compute/strategy/NovaLifeCycleStrategy.java @@ -47,7 +47,7 @@ public class NovaLifeCycleStrategy implements RebootNodeStrategy, SuspendNodeStr public NodeMetadata rebootNode(String id) { int serverId = Integer.parseInt(id); // if false server wasn't around in the first place - client.rebootServer(serverId, RebootType.HARD); + client.rebootServer(serverId, RebootType.SOFT); return getNode.getNode(id); } diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/strategy/NovaListNodesStrategy.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/compute/strategy/NovaListNodesStrategy.java similarity index 100% rename from sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/strategy/NovaListNodesStrategy.java rename to apis/nova/src/main/java/org/jclouds/openstack/nova/compute/strategy/NovaListNodesStrategy.java diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/suppliers/NovaHardwareSupplier.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/compute/suppliers/NovaHardwareSupplier.java similarity index 100% rename from sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/suppliers/NovaHardwareSupplier.java rename to apis/nova/src/main/java/org/jclouds/openstack/nova/compute/suppliers/NovaHardwareSupplier.java diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/suppliers/NovaImageSupplier.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/compute/suppliers/NovaImageSupplier.java similarity index 100% rename from sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/suppliers/NovaImageSupplier.java rename to apis/nova/src/main/java/org/jclouds/openstack/nova/compute/suppliers/NovaImageSupplier.java diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/config/NovaRestClientModule.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/config/NovaRestClientModule.java similarity index 100% rename from sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/config/NovaRestClientModule.java rename to apis/nova/src/main/java/org/jclouds/openstack/nova/config/NovaRestClientModule.java diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/AbsoluteLimit.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/domain/AbsoluteLimit.java similarity index 100% rename from sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/AbsoluteLimit.java rename to apis/nova/src/main/java/org/jclouds/openstack/nova/domain/AbsoluteLimit.java diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Action.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Action.java similarity index 100% rename from sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Action.java rename to apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Action.java diff --git a/apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Address.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Address.java new file mode 100644 index 0000000000..ce2908a29a --- /dev/null +++ b/apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Address.java @@ -0,0 +1,98 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.openstack.nova.domain; + +import com.google.common.base.Function; +import com.google.gson.annotations.SerializedName; + +import javax.annotation.Nullable; + +/** + * @author Dmitri Babaev + */ +public class Address { + @SerializedName("addr") + private String address; + private int version; + + //for de-serialization + @SuppressWarnings("unused") + private Address() { + } + + public Address(String address, int version) { + this.address = address; + this.version = version; + } + + public String getAddress() { + return address; + } + + public int getVersion() { + return version; + } + + @Override + public String toString() { + return address; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Address address1 = (Address) o; + + if (version != address1.version) return false; + if (address != null ? !address.equals(address1.address) : address1.address != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = address != null ? address.hashCode() : 0; + result = 31 * result + version; + return result; + } + + public static Function newAddress2StringFunction() { + return new Function() { + @Override + public String apply(@Nullable Address input) { + return input.getAddress(); + } + }; + } + + public static Address valueOf(String address) { + return new Address(address, address.startsWith("::") ? 6 : 4); + } + + public static Function newString2AddressFunction() { + return new Function() { + @Override + public Address apply(@Nullable String input) { + return valueOf(input); + } + }; + } +} diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Addresses.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Addresses.java similarity index 85% rename from sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Addresses.java rename to apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Addresses.java index 02cdc9f6e0..478808f156 100644 --- a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Addresses.java +++ b/apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Addresses.java @@ -30,31 +30,31 @@ import com.google.gson.annotations.SerializedName; public class Addresses { @SerializedName("public") - private Set publicAddresses = Sets.newLinkedHashSet(); + private Set

publicAddresses = Sets.newLinkedHashSet(); @SerializedName("private") - private Set privateAddresses = Sets.newLinkedHashSet(); + private Set
privateAddresses = Sets.newLinkedHashSet(); public Addresses() { } - public Addresses(Set publicAddresses, Set privateAddresses) { + public Addresses(Set
publicAddresses, Set
privateAddresses) { this.publicAddresses = publicAddresses; this.privateAddresses = privateAddresses; } - public void setPublicAddresses(Set publicAddresses) { + public void setPublicAddresses(Set
publicAddresses) { this.publicAddresses = publicAddresses; } - public Set getPublicAddresses() { + public Set
getPublicAddresses() { return publicAddresses; } - public void setPrivateAddresses(Set privateAddresses) { + public void setPrivateAddresses(Set
privateAddresses) { this.privateAddresses = privateAddresses; } - public Set getPrivateAddresses() { + public Set
getPrivateAddresses() { return privateAddresses; } diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Flavor.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Flavor.java similarity index 98% rename from sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Flavor.java rename to apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Flavor.java index b0f9cb2b1b..94ab5c61cf 100644 --- a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Flavor.java +++ b/apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Flavor.java @@ -25,7 +25,7 @@ package org.jclouds.openstack.nova.domain; * * @author Adrian Cole */ -public class Flavor { +public class Flavor extends Resource { public Flavor() { } diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Image.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Image.java similarity index 77% rename from sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Image.java rename to apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Image.java index a98eeeecf9..8a3934ee6d 100644 --- a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Image.java +++ b/apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Image.java @@ -18,26 +18,41 @@ */ package org.jclouds.openstack.nova.domain; +import com.google.common.collect.Maps; + +import java.util.Collections; import java.util.Date; +import java.util.Map; /** * An image is a collection of files used to create or rebuild a server. Rackspace provides a number * of pre-built OS images by default. You may also create custom images from cloud servers you have * launched. These custom images are useful for backup purposes or for producing gold server images * if you plan to deploy a particular server configuration frequently. - * + * * @author Adrian Cole */ -public class Image { +public class Image extends Resource { - private Date created; private int id; private String name; private Integer progress; - private Integer serverId; + private String serverRef; private ImageStatus status; + private Map metadata = Maps.newHashMap(); + + private Date created; private Date updated; + public Date getCreated() { + return created; + } + + public Date getUpdated() { + return updated; + } + + public Image() { } @@ -46,13 +61,6 @@ public class Image { this.name = name; } - public void setCreated(Date created) { - this.created = created; - } - - public Date getCreated() { - return created; - } public void setId(int id) { this.id = id; @@ -78,12 +86,12 @@ public class Image { return progress; } - public void setServerId(Integer serverId) { - this.serverId = serverId; + public void setServerRef(String serverRef) { + this.serverRef = serverRef; } - public Integer getServerId() { - return serverId; + public String getServerRef() { + return serverRef; } public void setStatus(ImageStatus status) { @@ -94,15 +102,17 @@ public class Image { return status; } - public void setUpdated(Date updated) { - this.updated = updated; + + public Map getMetadata() { + return Collections.unmodifiableMap(metadata); } - public Date getUpdated() { - return updated; + public void setMetadata(Map metadata) { + this.metadata = Maps.newHashMap(metadata); } + /** - * note that this ignores the create time + * note that this ignores some fields */ @Override public int hashCode() { @@ -110,12 +120,12 @@ public class Image { int result = 1; result = prime * result + id; result = prime * result + ((name == null) ? 0 : name.hashCode()); - result = prime * result + ((serverId == null) ? 0 : serverId.hashCode()); + result = prime * result + ((serverRef == null) ? 0 : serverRef.hashCode()); return result; } /** - * note that this ignores the serverid and create time. + * note that this ignores some fields */ @Override public boolean equals(Object obj) { @@ -138,8 +148,8 @@ public class Image { @Override public String toString() { - return "Image [created=" + created + ", id=" + id + ", name=" + name + ", serverId=" - + serverId + "]"; + return "Image [created=" + getCreated() + ", id=" + id + ", name=" + name + ", serverRef=" + + serverRef + "]"; } } diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/ImageStatus.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/domain/ImageStatus.java similarity index 100% rename from sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/ImageStatus.java rename to apis/nova/src/main/java/org/jclouds/openstack/nova/domain/ImageStatus.java diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Limits.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Limits.java similarity index 100% rename from sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Limits.java rename to apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Limits.java diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/RateLimit.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/domain/RateLimit.java similarity index 100% rename from sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/RateLimit.java rename to apis/nova/src/main/java/org/jclouds/openstack/nova/domain/RateLimit.java diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/RateLimitUnit.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/domain/RateLimitUnit.java similarity index 100% rename from sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/RateLimitUnit.java rename to apis/nova/src/main/java/org/jclouds/openstack/nova/domain/RateLimitUnit.java diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/RebootType.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/domain/RebootType.java similarity index 100% rename from sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/RebootType.java rename to apis/nova/src/main/java/org/jclouds/openstack/nova/domain/RebootType.java diff --git a/apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Resource.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Resource.java new file mode 100644 index 0000000000..ed415c03dd --- /dev/null +++ b/apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Resource.java @@ -0,0 +1,52 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.openstack.nova.domain; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; +import java.util.Map; + +import com.google.common.base.Functions; +import com.google.common.collect.Lists; + +/** + * @author Dmitri Babaev + */ +public class Resource { + + private List> links = Lists.newArrayList(); + + public URI getURI() { + for (Map linkProperties : links) { + try { + if (!Functions.forMap(linkProperties, "").apply("rel").equals("bookmark")) + continue; + if (!Functions.forMap(linkProperties, "").apply("type").contains("json")) + continue; + + return new URI(linkProperties.get("href")); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } + + throw new IllegalStateException("URI is not available"); + } +} diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Server.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Server.java similarity index 77% rename from sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Server.java rename to apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Server.java index 1ef8e28bd7..c496210097 100644 --- a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Server.java +++ b/apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Server.java @@ -18,17 +18,18 @@ */ package org.jclouds.openstack.nova.domain; -import java.util.Map; - import com.google.common.collect.Maps; +import java.util.Date; +import java.util.Map; + /** * A server is a virtual machine instance in the OpenStack Nova system. Flavor and image are * requisite elements when creating a server. - * + * * @author Adrian Cole */ -public class Server { +public class Server extends Resource { private int id; private String name; @@ -36,10 +37,22 @@ public class Server { private Addresses addresses; private String adminPass; - private Integer flavorId; + private String flavorRef; private String hostId; - private Integer imageId; - private Integer sharedIpGroupId; + private String imageRef; + private String affinityId; + + private Date created; + private Date updated; + + public Date getCreated() { + return created; + } + + public Date getUpdated() { + return updated; + } + private Integer progress; private ServerStatus status; @@ -52,6 +65,14 @@ public class Server { this.name = name; } + public String getAffinityId() { + return affinityId; + } + + public void setAffinityId(String affinityId) { + this.affinityId = affinityId; + } + public void setMetadata(Map metadata) { this.metadata = metadata; } @@ -76,12 +97,12 @@ public class Server { return adminPass; } - public void setFlavorId(Integer flavorId) { - this.flavorId = flavorId; + public void setFlavorRef(String flavorRef) { + this.flavorRef = flavorRef; } - public Integer getFlavorId() { - return flavorId; + public String getFlavorRef() { + return flavorRef; } public void setHostId(String hostId) { @@ -104,12 +125,12 @@ public class Server { return id; } - public void setImageId(Integer imageId) { - this.imageId = imageId; + public void setImageRef(String imageRef) { + this.imageRef = imageRef; } - public Integer getImageId() { - return imageId; + public String getImageRef() { + return imageRef; } public String getName() { @@ -124,14 +145,6 @@ public class Server { return progress; } - public void setSharedIpGroupId(Integer sharedIpGroupId) { - this.sharedIpGroupId = sharedIpGroupId; - } - - public Integer getSharedIpGroupId() { - return sharedIpGroupId; - } - public void setStatus(ServerStatus status) { this.status = status; } @@ -150,13 +163,12 @@ public class Server { int result = 1; result = prime * result + ((addresses == null) ? 0 : addresses.hashCode()); result = prime * result + ((adminPass == null) ? 0 : adminPass.hashCode()); - result = prime * result + ((flavorId == null) ? 0 : flavorId.hashCode()); + result = prime * result + ((flavorRef == null) ? 0 : flavorRef.hashCode()); result = prime * result + ((hostId == null) ? 0 : hostId.hashCode()); result = prime * result + id; - result = prime * result + ((imageId == null) ? 0 : imageId.hashCode()); + result = prime * result + ((imageRef == null) ? 0 : imageRef.hashCode()); result = prime * result + ((metadata == null) ? 0 : metadata.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); - result = prime * result + ((sharedIpGroupId == null) ? 0 : sharedIpGroupId.hashCode()); return result; } @@ -179,10 +191,10 @@ public class Server { return false; } else if (!adminPass.equals(other.adminPass)) return false; - if (flavorId == null) { - if (other.flavorId != null) + if (flavorRef == null) { + if (other.flavorRef != null) return false; - } else if (!flavorId.equals(other.flavorId)) + } else if (!flavorRef.equals(other.flavorRef)) return false; if (hostId == null) { if (other.hostId != null) @@ -191,10 +203,10 @@ public class Server { return false; if (id != other.id) return false; - if (imageId == null) { - if (other.imageId != null) + if (imageRef == null) { + if (other.imageRef != null) return false; - } else if (!imageId.equals(other.imageId)) + } else if (!imageRef.equals(other.imageRef)) return false; if (metadata == null) { if (other.metadata != null) @@ -206,11 +218,6 @@ public class Server { return false; } else if (!name.equals(other.name)) return false; - if (sharedIpGroupId == null) { - if (other.sharedIpGroupId != null) - return false; - } else if (!sharedIpGroupId.equals(other.sharedIpGroupId)) - return false; return true; } @@ -220,10 +227,9 @@ public class Server { @Override public String toString() { - return "Server [addresses=" + addresses + ", adminPass=" + adminPass + ", flavorId=" - + flavorId + ", hostId=" + hostId + ", id=" + id + ", imageId=" + imageId - + ", metadata=" + metadata + ", name=" + name + ", sharedIpGroupId=" - + sharedIpGroupId + "]"; + return "Server [addresses=" + addresses + ", adminPass=" + adminPass + ", flavorRef=" + + flavorRef + ", hostId=" + hostId + ", id=" + id + ", imageRef=" + imageRef + + ", metadata=" + metadata + ", name=" + name + "]"; } } diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/ServerStatus.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/domain/ServerStatus.java similarity index 86% rename from sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/ServerStatus.java rename to apis/nova/src/main/java/org/jclouds/openstack/nova/domain/ServerStatus.java index 8b433524e4..5fe64c794a 100644 --- a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/ServerStatus.java +++ b/apis/nova/src/main/java/org/jclouds/openstack/nova/domain/ServerStatus.java @@ -37,8 +37,8 @@ package org.jclouds.openstack.nova.domain; */ public enum ServerStatus { - ACTIVE, SUSPENDED, DELETED, QUEUE_RESIZE, PREP_RESIZE, RESIZE, VERIFY_RESIZE, QUEUE_MOVE, PREP_MOVE, MOVE, VERIFY_MOVE, RESCUE, ERROR, BUILD, RESTORING, PASSWORD, REBUILD, DELETE_IP, SHARE_IP_NO_CONFIG, SHARE_IP, REBOOT, HARD_REBOOT, UNKNOWN, UNRECOGNIZED; - + ACTIVE, SUSPENDED, QUEUE_RESIZE, PREP_RESIZE, RESIZE, VERIFY_RESIZE, RESCUE, BUILD, PASSWORD, REBUILD, REBOOT, HARD_REBOOT, UNKNOWN, DELETE_IP, UNRECOGNIZED, DELETED; + public String value() { return name(); } diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/handlers/ParseNovaErrorFromHttpResponse.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/handlers/ParseNovaErrorFromHttpResponse.java similarity index 100% rename from sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/handlers/ParseNovaErrorFromHttpResponse.java rename to apis/nova/src/main/java/org/jclouds/openstack/nova/handlers/ParseNovaErrorFromHttpResponse.java diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/options/CreateServerOptions.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/options/CreateServerOptions.java similarity index 54% rename from sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/options/CreateServerOptions.java rename to apis/nova/src/main/java/org/jclouds/openstack/nova/options/CreateServerOptions.java index 14f4fb578f..d5c9f984dc 100644 --- a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/options/CreateServerOptions.java +++ b/apis/nova/src/main/java/org/jclouds/openstack/nova/options/CreateServerOptions.java @@ -26,9 +26,11 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import javax.inject.Inject; + import org.jclouds.encryption.internal.Base64; import org.jclouds.http.HttpRequest; -import org.jclouds.openstack.nova.domain.Addresses; +import org.jclouds.rest.MapBinder; import org.jclouds.rest.binders.BindToJsonPayload; import com.google.common.collect.ImmutableMap; @@ -40,7 +42,9 @@ import com.google.common.collect.Maps; * @author Adrian Cole * */ -public class CreateServerOptions extends BindToJsonPayload { +public class CreateServerOptions implements MapBinder { + @Inject + private BindToJsonPayload jsonBinder; static class File { private final String path; @@ -50,11 +54,9 @@ public class CreateServerOptions extends BindToJsonPayload { this.path = checkNotNull(path, "path"); this.contents = Base64.encodeBytes(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)); + "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)); + "maximum size of the file is 10KB. Contents specified is %d bytes", contents.length)); } public String getContents() { @@ -70,43 +72,32 @@ public class CreateServerOptions extends BindToJsonPayload { @SuppressWarnings("unused") private class ServerRequest { final String name; - final int imageId; - final int flavorId; + final String imageRef; + final String flavorRef; Map metadata; List personality; - Integer sharedIpGroupId; - Addresses addresses; - private ServerRequest(String name, int imageId, int flavorId) { + private ServerRequest(String name, String imageRef, String flavorRef) { this.name = name; - this.imageId = imageId; - this.flavorId = flavorId; + this.imageRef = imageRef; + this.flavorRef = flavorRef; } } private Map metadata = Maps.newHashMap(); private List files = Lists.newArrayList(); - private Integer sharedIpGroupId; - private String publicIp; @Override public R bindToRequest(R request, Map postParams) { - 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"))); + ServerRequest server = new ServerRequest(checkNotNull(postParams.get("name"), "name parameter not present"), + checkNotNull(postParams.get("imageRef"), "imageRef parameter not present"), checkNotNull(postParams + .get("flavorRef"), "flavorRef 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.getPublicAddresses().add(publicIp); - server.addresses.setPrivateAddresses(null); - } + return bindToRequest(request, ImmutableMap.of("server", server)); } @@ -131,29 +122,6 @@ public class CreateServerOptions extends BindToJsonPayload { return this; } - /** - * A shared IP group is a collection of servers that can share IPs with other members of the - * group. Any server in a group can share one or more public IPs with any other server in the - * group. With the exception of the first server in a shared IP group, servers must be launched - * into shared IP groups. A server may only be a member of one shared IP group. - * - *

- * 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. - *

- * - * Note: sharedIpGroupId is an optional parameter and for optimal performance, should ONLY be - * specified when intending to share IPs between servers. - * - * @see #withSharedIp(String) - */ - 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 @@ -162,46 +130,20 @@ public class CreateServerOptions extends BindToJsonPayload { */ public CreateServerOptions withMetadata(Map metadata) { checkNotNull(metadata, "metadata"); - checkArgument(metadata.size() <= 5, - "you cannot have more then 5 metadata values. You specified: " + metadata.size()); + checkArgument(metadata.size() <= 5, "you cannot have more then 5 metadata values. You specified: " + + metadata.size()); for (Entry 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)); + "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; } - /** - * Public IP addresses can be shared across multiple servers for use in various high availability - * scenarios. When an IP address is shared to another server, the cloud network restrictions are - * modified to allow each server to listen to and respond on that IP address (you may optionally - * specify that the target server network configuration be modified). Shared IP addresses can be - * used with many standard heartbeat facilities (e.g. keepalived) that monitor for failure and - * manage IP failover. - * - *

- * 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(String 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 { /** @@ -212,14 +154,6 @@ public class CreateServerOptions extends BindToJsonPayload { 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) */ @@ -227,14 +161,10 @@ public class CreateServerOptions extends BindToJsonPayload { CreateServerOptions options = new CreateServerOptions(); return options.withMetadata(metadata); } + } - /** - * @see CreateServerOptions#withSharedIp(String) - */ - public static CreateServerOptions withSharedIp(String publicIp) { - CreateServerOptions options = new CreateServerOptions(); - return options.withSharedIp(publicIp); - } - + @Override + public R bindToRequest(R request, Object input) { + return jsonBinder.bindToRequest(request, input); } } diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/options/ListOptions.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/options/ListOptions.java similarity index 100% rename from sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/options/ListOptions.java rename to apis/nova/src/main/java/org/jclouds/openstack/nova/options/ListOptions.java diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/options/RebuildServerOptions.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/options/RebuildServerOptions.java similarity index 63% rename from sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/options/RebuildServerOptions.java rename to apis/nova/src/main/java/org/jclouds/openstack/nova/options/RebuildServerOptions.java index db9cd56bff..ab6b6ad17a 100644 --- a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/options/RebuildServerOptions.java +++ b/apis/nova/src/main/java/org/jclouds/openstack/nova/options/RebuildServerOptions.java @@ -19,10 +19,14 @@ package org.jclouds.openstack.nova.options; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; import java.util.Map; +import javax.inject.Inject; + import org.jclouds.http.HttpRequest; +import org.jclouds.rest.MapBinder; import org.jclouds.rest.binders.BindToJsonPayload; import com.google.common.collect.ImmutableMap; @@ -34,15 +38,17 @@ import com.google.common.collect.Maps; * @author Adrian Cole * */ -public class RebuildServerOptions extends BindToJsonPayload { - Integer imageId; +public class RebuildServerOptions implements MapBinder { + @Inject + private BindToJsonPayload jsonBinder; + String imageRef; @Override public R bindToRequest(R request, Map postParams) { - Map image = Maps.newHashMap(); - if (imageId != null) - image.put("imageId", imageId); - return super.bindToRequest(request, ImmutableMap.of("rebuild", image)); + Map image = Maps.newHashMap(); + if (imageRef != null) + image.put("imageRef", imageRef); + return jsonBinder.bindToRequest(request, ImmutableMap.of("rebuild", image)); } @Override @@ -51,24 +57,24 @@ public class RebuildServerOptions extends BindToJsonPayload { } /** - * - * @param id - * of the image to rebuild the server with. + * @param ref + * - reference of the image to rebuild the server with. */ - public RebuildServerOptions withImage(int id) { - checkArgument(id > 0, "server id must be a positive number"); - this.imageId = id; + public RebuildServerOptions withImage(String ref) { + checkNotNull(ref, "image reference should not be null"); + checkArgument(!ref.isEmpty(), "image reference should not be empty"); + this.imageRef = ref; return this; } public static class Builder { /** - * @see RebuildServerOptions#withImage(int) + * @see RebuildServerOptions#withImage(String) */ - public static RebuildServerOptions withImage(int id) { + public static RebuildServerOptions withImage(String ref) { RebuildServerOptions options = new RebuildServerOptions(); - return options.withImage(id); + return options.withImage(ref); } } } diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/predicates/ServerActive.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/predicates/ServerActive.java similarity index 100% rename from sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/predicates/ServerActive.java rename to apis/nova/src/main/java/org/jclouds/openstack/nova/predicates/ServerActive.java diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/predicates/ServerDeleted.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/predicates/ServerDeleted.java similarity index 100% rename from sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/predicates/ServerDeleted.java rename to apis/nova/src/main/java/org/jclouds/openstack/nova/predicates/ServerDeleted.java diff --git a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/NovaAsyncClientTest.java b/apis/nova/src/test/java/org/jclouds/openstack/nova/NovaAsyncClientTest.java similarity index 57% rename from sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/NovaAsyncClientTest.java rename to apis/nova/src/test/java/org/jclouds/openstack/nova/NovaAsyncClientTest.java index 26753f1571..6728c0acf1 100644 --- a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/NovaAsyncClientTest.java +++ b/apis/nova/src/test/java/org/jclouds/openstack/nova/NovaAsyncClientTest.java @@ -18,81 +18,69 @@ */ package org.jclouds.openstack.nova; -import static org.jclouds.Constants.PROPERTY_API_VERSION; -import static org.jclouds.openstack.nova.options.CreateServerOptions.Builder.withFile; -import static org.jclouds.openstack.nova.options.CreateServerOptions.Builder.withMetadata; -import static org.jclouds.openstack.nova.options.CreateServerOptions.Builder.withSharedIpGroup; -import static org.jclouds.openstack.nova.options.CreateSharedIpGroupOptions.Builder.withServer; -import static org.jclouds.openstack.nova.options.ListOptions.Builder.changesSince; -import static org.jclouds.openstack.nova.options.ListOptions.Builder.withDetails; -import static org.jclouds.openstack.nova.options.RebuildServerOptions.Builder.withImage; -import static org.jclouds.location.reference.LocationConstants.PROPERTY_REGIONS; -import static org.testng.Assert.assertEquals; - -import java.io.IOException; -import java.lang.reflect.Method; -import java.net.URI; -import java.net.UnknownHostException; -import java.util.Date; -import java.util.Properties; - -import javax.ws.rs.core.MediaType; - -import org.jclouds.openstack.nova.config.NovaRestClientModule; -import org.jclouds.openstack.nova.domain.BackupSchedule; -import org.jclouds.openstack.nova.domain.DailyBackup; -import org.jclouds.openstack.nova.domain.RebootType; -import org.jclouds.openstack.nova.domain.WeeklyBackup; -import org.jclouds.openstack.nova.options.CreateServerOptions; -import org.jclouds.openstack.nova.options.CreateSharedIpGroupOptions; -import org.jclouds.openstack.nova.options.ListOptions; -import org.jclouds.openstack.nova.options.RebuildServerOptions; +import com.google.common.collect.ImmutableMap; +import com.google.inject.Module; +import com.google.inject.TypeLiteral; import org.jclouds.http.HttpRequest; import org.jclouds.http.RequiresHttp; import org.jclouds.http.functions.ReleasePayloadAndReturn; -import org.jclouds.http.functions.ReturnFalseOn404; import org.jclouds.http.functions.ReturnTrueIf2xx; import org.jclouds.http.functions.UnwrapOnlyJsonValue; import org.jclouds.openstack.OpenStackAuthAsyncClient.AuthenticationResponse; import org.jclouds.openstack.TestOpenStackAuthenticationModule; import org.jclouds.openstack.filters.AddTimestampQuery; import org.jclouds.openstack.filters.AuthenticateRequest; +import org.jclouds.openstack.nova.config.NovaRestClientModule; +import org.jclouds.openstack.nova.domain.RebootType; +import org.jclouds.openstack.nova.options.CreateServerOptions; +import org.jclouds.openstack.nova.options.ListOptions; +import org.jclouds.openstack.nova.options.RebuildServerOptions; 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.ReturnFalseOnNotFoundOr404; import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; -import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404; import org.jclouds.rest.internal.RestAnnotationProcessor; import org.testng.annotations.Test; -import com.google.common.collect.ImmutableMap; -import com.google.inject.Module; -import com.google.inject.TypeLiteral; +import javax.ws.rs.core.MediaType; +import java.io.IOException; +import java.lang.reflect.Method; +import java.net.URI; +import java.util.Date; +import java.util.Properties; + +import static org.jclouds.Constants.PROPERTY_API_VERSION; +import static org.jclouds.openstack.nova.options.CreateServerOptions.Builder.withFile; +import static org.jclouds.openstack.nova.options.CreateServerOptions.Builder.withMetadata; +import static org.jclouds.openstack.nova.options.ListOptions.Builder.changesSince; +import static org.jclouds.openstack.nova.options.ListOptions.Builder.withDetails; +import static org.jclouds.openstack.nova.options.RebuildServerOptions.Builder.withImage; +import static org.testng.Assert.assertEquals; /** * Tests behavior of {@code NovaAsyncClient} - * + * * @author Adrian Cole */ // NOTE:without testName, this will not call @Before* and fail w/NPE during surefire @Test(groups = "unit", testName = "NovaAsyncClientTest") public class NovaAsyncClientTest extends RestClientTest { - private static final Class listOptionsVarargsClass = new ListOptions[] {}.getClass(); - private static final Class createServerOptionsVarargsClass = new CreateServerOptions[] {} + private static final Class listOptionsVarargsClass = new ListOptions[]{}.getClass(); + private static final Class createServerOptionsVarargsClass = new CreateServerOptions[]{} .getClass(); + @Test public void testCreateServer() throws IOException, SecurityException, NoSuchMethodException { - Method method = NovaAsyncClient.class.getMethod("createServer", String.class, int.class, int.class, + Method method = NovaAsyncClient.class.getMethod("createServer", String.class, String.class, String.class, createServerOptionsVarargsClass); HttpRequest request = processor.createRequest(method, "ralphie", 2, 1); - assertRequestLineEquals(request, "POST http://serverManagementUrl/servers?format=json HTTP/1.1"); + assertRequestLineEquals(request, "POST http://endpoint/vapiversion/servers?format=json HTTP/1.1"); assertNonPayloadHeadersEqual(request, "Accept: application/json\n"); - assertPayloadEquals(request, "{\"server\":{\"name\":\"ralphie\",\"imageId\":2,\"flavorId\":1}}", + assertPayloadEquals(request, "{\"server\":{\"name\":\"ralphie\",\"imageRef\":\"2\",\"flavorRef\":\"1\"}}", "application/json", false); assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class); @@ -103,35 +91,18 @@ public class NovaAsyncClientTest extends RestClientTest { } - public void testCreateServerWithIpGroup() throws IOException, SecurityException, NoSuchMethodException { - Method method = NovaAsyncClient.class.getMethod("createServer", String.class, int.class, int.class, - createServerOptionsVarargsClass); - HttpRequest request = processor.createRequest(method, "ralphie", 2, 1, withSharedIpGroup(2)); - - assertRequestLineEquals(request, "POST http://serverManagementUrl/servers?format=json HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Accept: application/json\n"); - assertPayloadEquals(request, - "{\"server\":{\"name\":\"ralphie\",\"imageId\":2,\"flavorId\":1,\"sharedIpGroupId\":2}}", - "application/json", false); - - assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class); - assertSaxResponseParserClassEquals(method, null); - assertExceptionParserClassEquals(method, null); - - checkFilters(request); - } - + @Test public void testCreateServerWithFile() throws IOException, SecurityException, NoSuchMethodException { - Method method = NovaAsyncClient.class.getMethod("createServer", String.class, int.class, int.class, + Method method = NovaAsyncClient.class.getMethod("createServer", String.class, String.class, String.class, createServerOptionsVarargsClass); HttpRequest request = processor .createRequest(method, "ralphie", 2, 1, withFile("/etc/jclouds", "foo".getBytes())); - assertRequestLineEquals(request, "POST http://serverManagementUrl/servers?format=json HTTP/1.1"); + assertRequestLineEquals(request, "POST http://endpoint/vapiversion/servers?format=json HTTP/1.1"); assertNonPayloadHeadersEqual(request, "Accept: application/json\n"); assertPayloadEquals( request, - "{\"server\":{\"name\":\"ralphie\",\"imageId\":2,\"flavorId\":1,\"personality\":[{\"path\":\"/etc/jclouds\",\"contents\":\"Zm9v\"}]}}", + "{\"server\":{\"name\":\"ralphie\",\"imageRef\":\"2\",\"flavorRef\":\"1\",\"personality\":[{\"path\":\"/etc/jclouds\",\"contents\":\"Zm9v\"}]}}", "application/json", false); assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class); @@ -142,16 +113,17 @@ public class NovaAsyncClientTest extends RestClientTest { } + @Test public void testCreateServerWithMetadata() throws IOException, SecurityException, NoSuchMethodException { - Method method = NovaAsyncClient.class.getMethod("createServer", String.class, int.class, int.class, + Method method = NovaAsyncClient.class.getMethod("createServer", String.class, String.class, String.class, createServerOptionsVarargsClass); HttpRequest request = processor.createRequest(method, "ralphie", 2, 1, withMetadata(ImmutableMap.of("foo", "bar"))); - assertRequestLineEquals(request, "POST http://serverManagementUrl/servers?format=json HTTP/1.1"); + assertRequestLineEquals(request, "POST http://endpoint/vapiversion/servers?format=json HTTP/1.1"); assertNonPayloadHeadersEqual(request, "Accept: application/json\n"); assertPayloadEquals(request, - "{\"server\":{\"name\":\"ralphie\",\"imageId\":2,\"flavorId\":1,\"metadata\":{\"foo\":\"bar\"}}}", + "{\"server\":{\"name\":\"ralphie\",\"imageRef\":\"2\",\"flavorRef\":\"1\",\"metadata\":{\"foo\":\"bar\"}}}", "application/json", false); assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class); @@ -162,32 +134,11 @@ public class NovaAsyncClientTest extends RestClientTest { } - public void testCreateServerWithIpGroupAndSharedIp() throws IOException, SecurityException, NoSuchMethodException, - UnknownHostException { - Method method = NovaAsyncClient.class.getMethod("createServer", String.class, int.class, int.class, - createServerOptionsVarargsClass); - HttpRequest request = processor.createRequest(method, "ralphie", 2, 1, - withSharedIpGroup(2).withSharedIp("127.0.0.1")); - - assertRequestLineEquals(request, "POST http://serverManagementUrl/servers?format=json HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Accept: application/json\n"); - assertPayloadEquals( - request, - "{\"server\":{\"name\":\"ralphie\",\"imageId\":2,\"flavorId\":1,\"sharedIpGroupId\":2,\"addresses\":{\"public\":[\"127.0.0.1\"]}}}", - "application/json", false); - - assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class); - assertSaxResponseParserClassEquals(method, null); - assertExceptionParserClassEquals(method, null); - - checkFilters(request); - } - public void testDeleteImage() throws IOException, SecurityException, NoSuchMethodException { Method method = NovaAsyncClient.class.getMethod("deleteImage", int.class); HttpRequest request = processor.createRequest(method, 2); - assertRequestLineEquals(request, "DELETE http://serverManagementUrl/images/2 HTTP/1.1"); + assertRequestLineEquals(request, "DELETE http://endpoint/vapiversion/images/2 HTTP/1.1"); assertNonPayloadHeadersEqual(request, ""); assertPayloadEquals(request, null, null, false); @@ -202,7 +153,7 @@ public class NovaAsyncClientTest extends RestClientTest { Method method = NovaAsyncClient.class.getMethod("listServers", listOptionsVarargsClass); HttpRequest request = processor.createRequest(method); - assertRequestLineEquals(request, "GET http://serverManagementUrl/servers?format=json HTTP/1.1"); + assertRequestLineEquals(request, "GET http://endpoint/vapiversion/servers?format=json HTTP/1.1"); assertNonPayloadHeadersEqual(request, "Accept: application/json\n"); assertPayloadEquals(request, null, null, false); @@ -220,7 +171,7 @@ public class NovaAsyncClientTest extends RestClientTest { HttpRequest request = processor.createRequest(method, changesSince(now).maxResults(1).startAt(2)); assertRequestLineEquals(request, - "GET http://serverManagementUrl/servers?format=json&changes-since=10000&limit=1&offset=2 HTTP/1.1"); + "GET http://endpoint/vapiversion/servers?format=json&changes-since=10000&limit=1&offset=2 HTTP/1.1"); assertNonPayloadHeadersEqual(request, "Accept: application/json\n"); assertPayloadEquals(request, null, null, false); @@ -235,7 +186,7 @@ public class NovaAsyncClientTest extends RestClientTest { Method method = NovaAsyncClient.class.getMethod("listServers", listOptionsVarargsClass); HttpRequest request = processor.createRequest(method, withDetails()); - assertRequestLineEquals(request, "GET http://serverManagementUrl/servers/detail?format=json HTTP/1.1"); + assertRequestLineEquals(request, "GET http://endpoint/vapiversion/servers/detail?format=json HTTP/1.1"); assertNonPayloadHeadersEqual(request, "Accept: application/json\n"); assertPayloadEquals(request, null, null, false); @@ -250,7 +201,7 @@ public class NovaAsyncClientTest extends RestClientTest { Method method = NovaAsyncClient.class.getMethod("getServer", int.class); HttpRequest request = processor.createRequest(method, 2); - assertRequestLineEquals(request, "GET http://serverManagementUrl/servers/2?format=json HTTP/1.1"); + assertRequestLineEquals(request, "GET http://endpoint/vapiversion/servers/2?format=json HTTP/1.1"); assertNonPayloadHeadersEqual(request, "Accept: application/json\n"); assertPayloadEquals(request, null, null, false); @@ -265,7 +216,7 @@ public class NovaAsyncClientTest extends RestClientTest { Method method = NovaAsyncClient.class.getMethod("listFlavors", listOptionsVarargsClass); HttpRequest request = processor.createRequest(method); - assertRequestLineEquals(request, "GET http://serverManagementUrl/flavors?format=json HTTP/1.1"); + assertRequestLineEquals(request, "GET http://endpoint/vapiversion/flavors?format=json HTTP/1.1"); assertNonPayloadHeadersEqual(request, "Accept: application/json\n"); assertPayloadEquals(request, null, null, false); @@ -281,7 +232,7 @@ public class NovaAsyncClientTest extends RestClientTest { HttpRequest request = processor.createRequest(method, changesSince(now).maxResults(1).startAt(2)); assertRequestLineEquals(request, - "GET http://serverManagementUrl/flavors?format=json&changes-since=10000&limit=1&offset=2 HTTP/1.1"); + "GET http://endpoint/vapiversion/flavors?format=json&changes-since=10000&limit=1&offset=2 HTTP/1.1"); assertNonPayloadHeadersEqual(request, "Accept: application/json\n"); assertPayloadEquals(request, null, null, false); @@ -296,7 +247,7 @@ public class NovaAsyncClientTest extends RestClientTest { Method method = NovaAsyncClient.class.getMethod("listFlavors", listOptionsVarargsClass); HttpRequest request = processor.createRequest(method, withDetails()); - assertRequestLineEquals(request, "GET http://serverManagementUrl/flavors/detail?format=json HTTP/1.1"); + assertRequestLineEquals(request, "GET http://endpoint/vapiversion/flavors/detail?format=json HTTP/1.1"); assertNonPayloadHeadersEqual(request, "Accept: application/json\n"); assertPayloadEquals(request, null, null, false); @@ -312,7 +263,7 @@ public class NovaAsyncClientTest extends RestClientTest { HttpRequest request = processor.createRequest(method, withDetails().changesSince(now).maxResults(1).startAt(2)); assertRequestLineEquals(request, - "GET http://serverManagementUrl/flavors/detail?format=json&changes-since=10000&limit=1&offset=2 HTTP/1.1"); + "GET http://endpoint/vapiversion/flavors/detail?format=json&changes-since=10000&limit=1&offset=2 HTTP/1.1"); assertNonPayloadHeadersEqual(request, "Accept: application/json\n"); assertPayloadEquals(request, null, null, false); @@ -327,7 +278,7 @@ public class NovaAsyncClientTest extends RestClientTest { Method method = NovaAsyncClient.class.getMethod("getFlavor", int.class); HttpRequest request = processor.createRequest(method, 2); - assertRequestLineEquals(request, "GET http://serverManagementUrl/flavors/2?format=json HTTP/1.1"); + assertRequestLineEquals(request, "GET http://endpoint/vapiversion/flavors/2?format=json HTTP/1.1"); assertNonPayloadHeadersEqual(request, "Accept: application/json\n"); assertPayloadEquals(request, null, null, false); @@ -342,7 +293,7 @@ public class NovaAsyncClientTest extends RestClientTest { Method method = NovaAsyncClient.class.getMethod("listImages", listOptionsVarargsClass); HttpRequest request = processor.createRequest(method); - assertRequestLineEquals(request, "GET http://serverManagementUrl/images?format=json HTTP/1.1"); + assertRequestLineEquals(request, "GET http://endpoint/vapiversion/images?format=json HTTP/1.1"); assertNonPayloadHeadersEqual(request, "Accept: application/json\n"); assertPayloadEquals(request, null, null, false); @@ -357,7 +308,7 @@ public class NovaAsyncClientTest extends RestClientTest { Method method = NovaAsyncClient.class.getMethod("listImages", listOptionsVarargsClass); HttpRequest request = processor.createRequest(method, withDetails()); - assertRequestLineEquals(request, "GET http://serverManagementUrl/images/detail?format=json HTTP/1.1"); + assertRequestLineEquals(request, "GET http://endpoint/vapiversion/images/detail?format=json HTTP/1.1"); assertNonPayloadHeadersEqual(request, "Accept: application/json\n"); assertPayloadEquals(request, null, null, false); @@ -373,7 +324,7 @@ public class NovaAsyncClientTest extends RestClientTest { HttpRequest request = processor.createRequest(method, changesSince(now).maxResults(1).startAt(2)); assertRequestLineEquals(request, - "GET http://serverManagementUrl/images?format=json&changes-since=10000&limit=1&offset=2 HTTP/1.1"); + "GET http://endpoint/vapiversion/images?format=json&changes-since=10000&limit=1&offset=2 HTTP/1.1"); assertNonPayloadHeadersEqual(request, "Accept: application/json\n"); assertPayloadEquals(request, null, null, false); @@ -389,7 +340,7 @@ public class NovaAsyncClientTest extends RestClientTest { HttpRequest request = processor.createRequest(method, withDetails().changesSince(now).maxResults(1).startAt(2)); assertRequestLineEquals(request, - "GET http://serverManagementUrl/images/detail?format=json&changes-since=10000&limit=1&offset=2 HTTP/1.1"); + "GET http://endpoint/vapiversion/images/detail?format=json&changes-since=10000&limit=1&offset=2 HTTP/1.1"); assertNonPayloadHeadersEqual(request, "Accept: application/json\n"); assertPayloadEquals(request, null, null, false); @@ -404,7 +355,7 @@ public class NovaAsyncClientTest extends RestClientTest { Method method = NovaAsyncClient.class.getMethod("getImage", int.class); HttpRequest request = processor.createRequest(method, 2); - assertRequestLineEquals(request, "GET http://serverManagementUrl/images/2?format=json HTTP/1.1"); + assertRequestLineEquals(request, "GET http://endpoint/vapiversion/images/2?format=json HTTP/1.1"); assertNonPayloadHeadersEqual(request, "Accept: application/json\n"); assertPayloadEquals(request, null, null, false); @@ -419,7 +370,7 @@ public class NovaAsyncClientTest extends RestClientTest { Method method = NovaAsyncClient.class.getMethod("deleteServer", int.class); HttpRequest request = processor.createRequest(method, 2); - assertRequestLineEquals(request, "DELETE http://serverManagementUrl/servers/2 HTTP/1.1"); + assertRequestLineEquals(request, "DELETE http://endpoint/vapiversion/servers/2 HTTP/1.1"); assertNonPayloadHeadersEqual(request, ""); assertPayloadEquals(request, null, null, false); @@ -430,101 +381,13 @@ public class NovaAsyncClientTest extends RestClientTest { checkFilters(request); } - public void testShareIpNoConfig() throws IOException, SecurityException, NoSuchMethodException, UnknownHostException { - Method method = NovaAsyncClient.class.getMethod("shareIp", String.class, int.class, int.class, - boolean.class); - HttpRequest request = processor.createRequest(method, "127.0.0.1", 2, 3, false); - - assertRequestLineEquals(request, "PUT http://serverManagementUrl/servers/2/ips/public/127.0.0.1 HTTP/1.1"); - assertNonPayloadHeadersEqual(request, ""); - assertPayloadEquals(request, "{\"shareIp\":{\"sharedIpGroupId\":3,\"configureServer\":false}}", - MediaType.APPLICATION_JSON, false); - - assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class); - assertSaxResponseParserClassEquals(method, null); - assertExceptionParserClassEquals(method, null); - - checkFilters(request); - - } - - public void testShareIpConfig() throws IOException, SecurityException, NoSuchMethodException, UnknownHostException { - Method method = NovaAsyncClient.class.getMethod("shareIp", String.class, int.class, int.class, - boolean.class); - HttpRequest request = processor.createRequest(method, "127.0.0.1", 2, 3, true); - - assertRequestLineEquals(request, "PUT http://serverManagementUrl/servers/2/ips/public/127.0.0.1 HTTP/1.1"); - assertNonPayloadHeadersEqual(request, ""); - assertPayloadEquals(request, "{\"shareIp\":{\"sharedIpGroupId\":3,\"configureServer\":true}}", - MediaType.APPLICATION_JSON, false); - - assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class); - assertSaxResponseParserClassEquals(method, null); - assertExceptionParserClassEquals(method, null); - - checkFilters(request); - - } - - public void testUnshareIpNoConfig() throws IOException, SecurityException, NoSuchMethodException, - UnknownHostException { - Method method = NovaAsyncClient.class.getMethod("unshareIp", String.class, int.class); - HttpRequest request = processor.createRequest(method, "127.0.0.1", 2, 3, false); - - assertRequestLineEquals(request, "DELETE http://serverManagementUrl/servers/2/ips/public/127.0.0.1 HTTP/1.1"); - assertNonPayloadHeadersEqual(request, ""); - assertPayloadEquals(request, null, null, false); - - assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class); - assertSaxResponseParserClassEquals(method, null); - assertExceptionParserClassEquals(method, ReturnVoidOnNotFoundOr404.class); - - checkFilters(request); - - } - - public void testReplaceBackupSchedule() throws IOException, SecurityException, NoSuchMethodException { - Method method = NovaAsyncClient.class.getMethod("replaceBackupSchedule", int.class, BackupSchedule.class); - HttpRequest request = processor.createRequest(method, 2, new BackupSchedule(WeeklyBackup.MONDAY, - DailyBackup.H_0800_1000, true)); - - assertRequestLineEquals(request, "POST http://serverManagementUrl/servers/2/backup_schedule HTTP/1.1"); - assertNonPayloadHeadersEqual(request, ""); - assertPayloadEquals(request, - "{\"backupSchedule\":{\"daily\":\"H_0800_1000\",\"enabled\":true,\"weekly\":\"MONDAY\"}}", - MediaType.APPLICATION_JSON, false); - - assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class); - assertSaxResponseParserClassEquals(method, null); - assertExceptionParserClassEquals(method, ReturnFalseOn404.class); - - checkFilters(request); - - } - - public void testDeleteBackupSchedule() throws IOException, SecurityException, NoSuchMethodException { - Method method = NovaAsyncClient.class.getMethod("deleteBackupSchedule", int.class); - HttpRequest request = processor.createRequest(method, 2); - - assertRequestLineEquals(request, "DELETE http://serverManagementUrl/servers/2/backup_schedule HTTP/1.1"); - assertNonPayloadHeadersEqual(request, ""); - assertPayloadEquals(request, null, MediaType.APPLICATION_JSON, false); - - assertResponseParserClassEquals(method, request, ReturnTrueIf2xx.class); - assertSaxResponseParserClassEquals(method, null); - assertExceptionParserClassEquals(method, ReturnFalseOnNotFoundOr404.class); - - checkFilters(request); - - } - public void testChangeAdminPass() throws IOException, SecurityException, NoSuchMethodException { Method method = NovaAsyncClient.class.getMethod("changeAdminPass", int.class, String.class); HttpRequest request = processor.createRequest(method, 2, "foo"); - assertRequestLineEquals(request, "PUT http://serverManagementUrl/servers/2 HTTP/1.1"); + assertRequestLineEquals(request, "POST http://endpoint/vapiversion/servers/2/action HTTP/1.1"); assertNonPayloadHeadersEqual(request, ""); - assertPayloadEquals(request, "{\"server\":{\"adminPass\":\"foo\"}}", MediaType.APPLICATION_JSON, false); + assertPayloadEquals(request, "{\"changePassword\":{\"adminPass\":\"foo\"}}", MediaType.APPLICATION_JSON, false); assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class); assertSaxResponseParserClassEquals(method, null); @@ -538,7 +401,7 @@ public class NovaAsyncClientTest extends RestClientTest { Method method = NovaAsyncClient.class.getMethod("renameServer", int.class, String.class); HttpRequest request = processor.createRequest(method, 2, "foo"); - assertRequestLineEquals(request, "PUT http://serverManagementUrl/servers/2 HTTP/1.1"); + assertRequestLineEquals(request, "PUT http://endpoint/vapiversion/servers/2 HTTP/1.1"); assertNonPayloadHeadersEqual(request, ""); assertPayloadEquals(request, "{\"server\":{\"name\":\"foo\"}}", MediaType.APPLICATION_JSON, false); @@ -550,140 +413,11 @@ public class NovaAsyncClientTest extends RestClientTest { } - public void testListSharedIpGroups() throws IOException, SecurityException, NoSuchMethodException { - Method method = NovaAsyncClient.class.getMethod("listSharedIpGroups", listOptionsVarargsClass); - HttpRequest request = processor.createRequest(method); - - assertRequestLineEquals(request, "GET http://serverManagementUrl/shared_ip_groups?format=json HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Accept: application/json\n"); - assertPayloadEquals(request, null, null, false); - - assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class); - assertSaxResponseParserClassEquals(method, null); - assertExceptionParserClassEquals(method, ReturnEmptySetOnNotFoundOr404.class); - - checkFilters(request); - } - - public void testListSharedIpGroupsOptions() throws IOException, SecurityException, NoSuchMethodException { - Method method = NovaAsyncClient.class.getMethod("listSharedIpGroups", listOptionsVarargsClass); - HttpRequest request = processor.createRequest(method, changesSince(now).maxResults(1).startAt(2)); - - assertRequestLineEquals(request, - "GET http://serverManagementUrl/shared_ip_groups?format=json&changes-since=10000&limit=1&offset=2 HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Accept: application/json\n"); - assertPayloadEquals(request, null, null, false); - - assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class); - assertSaxResponseParserClassEquals(method, null); - assertExceptionParserClassEquals(method, ReturnEmptySetOnNotFoundOr404.class); - - checkFilters(request); - } - - public void testListSharedIpGroupsDetail() throws IOException, SecurityException, NoSuchMethodException { - Method method = NovaAsyncClient.class.getMethod("listSharedIpGroups", listOptionsVarargsClass); - HttpRequest request = processor.createRequest(method, withDetails()); - - assertRequestLineEquals(request, "GET http://serverManagementUrl/shared_ip_groups/detail?format=json HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Accept: application/json\n"); - assertPayloadEquals(request, null, null, false); - - assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class); - assertSaxResponseParserClassEquals(method, null); - assertExceptionParserClassEquals(method, ReturnEmptySetOnNotFoundOr404.class); - - checkFilters(request); - } - - public void testListSharedIpGroupsDetailOptions() throws IOException, SecurityException, NoSuchMethodException { - Method method = NovaAsyncClient.class.getMethod("listSharedIpGroups", listOptionsVarargsClass); - HttpRequest request = processor.createRequest(method, withDetails().changesSince(now).maxResults(1).startAt(2)); - - assertRequestLineEquals(request, - "GET http://serverManagementUrl/shared_ip_groups/detail?format=json&changes-since=10000&limit=1&offset=2 HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Accept: application/json\n"); - assertPayloadEquals(request, null, null, false); - - assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class); - assertSaxResponseParserClassEquals(method, null); - assertExceptionParserClassEquals(method, ReturnEmptySetOnNotFoundOr404.class); - - checkFilters(request); - } - - public void testGetSharedIpGroup() throws IOException, SecurityException, NoSuchMethodException { - Method method = NovaAsyncClient.class.getMethod("getSharedIpGroup", int.class); - HttpRequest request = processor.createRequest(method, 2); - - assertRequestLineEquals(request, "GET http://serverManagementUrl/shared_ip_groups/2?format=json HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Accept: application/json\n"); - assertPayloadEquals(request, null, null, false); - - assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class); - assertSaxResponseParserClassEquals(method, null); - assertExceptionParserClassEquals(method, ReturnNullOnNotFoundOr404.class); - - checkFilters(request); - } - - private static final Class createSharedIpGroupOptionsVarargsClass = new CreateSharedIpGroupOptions[] {} - .getClass(); - - public void testCreateSharedIpGroup() throws IOException, SecurityException, NoSuchMethodException { - Method method = NovaAsyncClient.class.getMethod("createSharedIpGroup", String.class, - createSharedIpGroupOptionsVarargsClass); - HttpRequest request = processor.createRequest(method, "ralphie"); - - assertRequestLineEquals(request, "POST http://serverManagementUrl/shared_ip_groups?format=json HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Accept: application/json\n"); - assertPayloadEquals(request, "{\"sharedIpGroup\":{\"name\":\"ralphie\"}}", MediaType.APPLICATION_JSON, false); - - assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class); - assertSaxResponseParserClassEquals(method, null); - assertExceptionParserClassEquals(method, null); - - checkFilters(request); - - } - - public void testCreateSharedIpGroupWithIpGroup() throws IOException, SecurityException, NoSuchMethodException { - Method method = NovaAsyncClient.class.getMethod("createSharedIpGroup", String.class, - createSharedIpGroupOptionsVarargsClass); - HttpRequest request = processor.createRequest(method, "ralphie", withServer(2)); - - assertRequestLineEquals(request, "POST http://serverManagementUrl/shared_ip_groups?format=json HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Accept: application/json\n"); - assertPayloadEquals(request, "{\"sharedIpGroup\":{\"name\":\"ralphie\",\"server\":2}}", - MediaType.APPLICATION_JSON, false); - - assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class); - assertSaxResponseParserClassEquals(method, null); - assertExceptionParserClassEquals(method, null); - - checkFilters(request); - } - - public void testDeleteSharedIpGroup() throws IOException, SecurityException, NoSuchMethodException { - Method method = NovaAsyncClient.class.getMethod("deleteSharedIpGroup", int.class); - HttpRequest request = processor.createRequest(method, 2); - - assertRequestLineEquals(request, "DELETE http://serverManagementUrl/shared_ip_groups/2 HTTP/1.1"); - assertNonPayloadHeadersEqual(request, ""); - assertPayloadEquals(request, null, null, false); - - assertResponseParserClassEquals(method, request, ReturnTrueIf2xx.class); - assertSaxResponseParserClassEquals(method, null); - assertExceptionParserClassEquals(method, ReturnFalseOnNotFoundOr404.class); - - checkFilters(request); - } - public void testListAddresses() throws IOException, SecurityException, NoSuchMethodException { Method method = NovaAsyncClient.class.getMethod("getAddresses", int.class); HttpRequest request = processor.createRequest(method, 2); - assertRequestLineEquals(request, "GET http://serverManagementUrl/servers/2/ips?format=json HTTP/1.1"); + assertRequestLineEquals(request, "GET http://endpoint/vapiversion/servers/2/ips?format=json HTTP/1.1"); assertNonPayloadHeadersEqual(request, "Accept: application/json\n"); assertPayloadEquals(request, null, null, false); @@ -698,7 +432,7 @@ public class NovaAsyncClientTest extends RestClientTest { Method method = NovaAsyncClient.class.getMethod("listPublicAddresses", int.class); HttpRequest request = processor.createRequest(method, 2); - assertRequestLineEquals(request, "GET http://serverManagementUrl/servers/2/ips/public?format=json HTTP/1.1"); + assertRequestLineEquals(request, "GET http://endpoint/vapiversion/servers/2/ips/public?format=json HTTP/1.1"); assertNonPayloadHeadersEqual(request, "Accept: application/json\n"); assertPayloadEquals(request, null, null, false); @@ -709,11 +443,12 @@ public class NovaAsyncClientTest extends RestClientTest { checkFilters(request); } + @Test public void testListPrivateAddresses() throws IOException, SecurityException, NoSuchMethodException { Method method = NovaAsyncClient.class.getMethod("listPrivateAddresses", int.class); HttpRequest request = processor.createRequest(method, 2); - assertRequestLineEquals(request, "GET http://serverManagementUrl/servers/2/ips/private?format=json HTTP/1.1"); + assertRequestLineEquals(request, "GET http://endpoint/vapiversion/servers/2/ips/private?format=json HTTP/1.1"); assertNonPayloadHeadersEqual(request, "Accept: application/json\n"); assertPayloadEquals(request, null, null, false); @@ -724,26 +459,12 @@ public class NovaAsyncClientTest extends RestClientTest { checkFilters(request); } - public void testListBackupSchedule() throws IOException, SecurityException, NoSuchMethodException { - Method method = NovaAsyncClient.class.getMethod("getBackupSchedule", int.class); - HttpRequest request = processor.createRequest(method, 2); - - assertRequestLineEquals(request, "GET http://serverManagementUrl/servers/2/backup_schedule?format=json HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Accept: application/json\n"); - assertPayloadEquals(request, null, null, false); - - assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class); - assertSaxResponseParserClassEquals(method, null); - assertExceptionParserClassEquals(method, MapHttp4xxCodesToExceptions.class); - - checkFilters(request); - } - + @Test public void testCreateImageWithIpGroup() throws IOException, SecurityException, NoSuchMethodException { Method method = NovaAsyncClient.class.getMethod("createImageFromServer", String.class, int.class); HttpRequest request = processor.createRequest(method, "ralphie", 2); - assertRequestLineEquals(request, "POST http://serverManagementUrl/images?format=json HTTP/1.1"); + assertRequestLineEquals(request, "POST http://endpoint/vapiversion/images?format=json HTTP/1.1"); assertNonPayloadHeadersEqual(request, "Accept: application/json\n"); assertPayloadEquals(request, "{\"image\":{\"serverId\":2,\"name\":\"ralphie\"}}", MediaType.APPLICATION_JSON, false); @@ -756,15 +477,16 @@ public class NovaAsyncClientTest extends RestClientTest { } - private static final Class rebuildServerOptionsVarargsClass = new RebuildServerOptions[] {} + private static final Class rebuildServerOptionsVarargsClass = new RebuildServerOptions[]{} .getClass(); + @Test public void testRebuildServer() throws IOException, SecurityException, NoSuchMethodException { Method method = NovaAsyncClient.class.getMethod("rebuildServer", int.class, rebuildServerOptionsVarargsClass); HttpRequest request = processor.createRequest(method, 3); - assertRequestLineEquals(request, "POST http://serverManagementUrl/servers/3/action?format=json HTTP/1.1"); + assertRequestLineEquals(request, "POST http://endpoint/vapiversion/servers/3/action?format=json HTTP/1.1"); assertNonPayloadHeadersEqual(request, ""); assertPayloadEquals(request, "{\"rebuild\":{}}", MediaType.APPLICATION_JSON, false); @@ -775,14 +497,15 @@ public class NovaAsyncClientTest extends RestClientTest { checkFilters(request); } + @Test public void testRebuildServerWithImage() throws IOException, SecurityException, NoSuchMethodException { Method method = NovaAsyncClient.class.getMethod("rebuildServer", int.class, rebuildServerOptionsVarargsClass); - HttpRequest request = processor.createRequest(method, 3, withImage(2)); + HttpRequest request = processor.createRequest(method, 3, withImage("2")); - assertRequestLineEquals(request, "POST http://serverManagementUrl/servers/3/action?format=json HTTP/1.1"); + assertRequestLineEquals(request, "POST http://endpoint/vapiversion/servers/3/action?format=json HTTP/1.1"); assertNonPayloadHeadersEqual(request, ""); - assertPayloadEquals(request, "{\"rebuild\":{\"imageId\":2}}", MediaType.APPLICATION_JSON, false); + assertPayloadEquals(request, "{\"rebuild\":{\"imageRef\":\"2\"}}", MediaType.APPLICATION_JSON, false); assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class); assertSaxResponseParserClassEquals(method, null); @@ -791,11 +514,12 @@ public class NovaAsyncClientTest extends RestClientTest { checkFilters(request); } + @Test public void testReboot() throws IOException, SecurityException, NoSuchMethodException { Method method = NovaAsyncClient.class.getMethod("rebootServer", int.class, RebootType.class); HttpRequest request = processor.createRequest(method, 2, RebootType.HARD); - assertRequestLineEquals(request, "POST http://serverManagementUrl/servers/2/action?format=json HTTP/1.1"); + assertRequestLineEquals(request, "POST http://endpoint/vapiversion/servers/2/action?format=json HTTP/1.1"); assertNonPayloadHeadersEqual(request, ""); assertPayloadEquals(request, "{\"reboot\":{\"type\":\"HARD\"}}", MediaType.APPLICATION_JSON, false); @@ -806,11 +530,12 @@ public class NovaAsyncClientTest extends RestClientTest { checkFilters(request); } + @Test public void testResize() throws IOException, SecurityException, NoSuchMethodException { Method method = NovaAsyncClient.class.getMethod("resizeServer", int.class, int.class); HttpRequest request = processor.createRequest(method, 2, 3); - assertRequestLineEquals(request, "POST http://serverManagementUrl/servers/2/action?format=json HTTP/1.1"); + assertRequestLineEquals(request, "POST http://endpoint/vapiversion/servers/2/action?format=json HTTP/1.1"); assertNonPayloadHeadersEqual(request, ""); assertPayloadEquals(request, "{\"resize\":{\"flavorId\":3}}", MediaType.APPLICATION_JSON, false); @@ -826,7 +551,7 @@ public class NovaAsyncClientTest extends RestClientTest { Method method = NovaAsyncClient.class.getMethod("confirmResizeServer", int.class); HttpRequest request = processor.createRequest(method, 2); - assertRequestLineEquals(request, "POST http://serverManagementUrl/servers/2/action?format=json HTTP/1.1"); + assertRequestLineEquals(request, "POST http://endpoint/vapiversion/servers/2/action?format=json HTTP/1.1"); assertNonPayloadHeadersEqual(request, ""); assertPayloadEquals(request, "{\"confirmResize\":null}", MediaType.APPLICATION_JSON, false); @@ -841,7 +566,7 @@ public class NovaAsyncClientTest extends RestClientTest { Method method = NovaAsyncClient.class.getMethod("revertResizeServer", int.class); HttpRequest request = processor.createRequest(method, 2); - assertRequestLineEquals(request, "POST http://serverManagementUrl/servers/2/action?format=json HTTP/1.1"); + assertRequestLineEquals(request, "POST http://endpoint/vapiversion/servers/2/action?format=json HTTP/1.1"); assertNonPayloadHeadersEqual(request, ""); assertPayloadEquals(request, "{\"revertResize\":null}", MediaType.APPLICATION_JSON, false); @@ -880,7 +605,7 @@ public class NovaAsyncClientTest extends RestClientTest { @Override protected URI provideServerUrl(AuthenticationResponse response) { - return URI.create("http://serverManagementUrl"); + return URI.create("http://endpoint/vapiversion"); } } @@ -895,9 +620,8 @@ public class NovaAsyncClientTest extends RestClientTest { @Override protected Properties getProperties() { Properties overrides = new Properties(); - overrides.setProperty(PROPERTY_REGIONS, "US"); - overrides.setProperty(PROPERTY_API_VERSION, "1"); - overrides.setProperty(provider + ".endpoint", "https://auth"); + overrides.setProperty(PROPERTY_API_VERSION, "apiversion"); + overrides.setProperty(provider + ".endpoint", "http://endpoint"); overrides.setProperty(provider + ".contextbuilder", NovaContextBuilder.class.getName()); return overrides; } diff --git a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/compute/config/NovaComputeServiceContextModuleTest.java b/apis/nova/src/test/java/org/jclouds/openstack/nova/compute/config/NovaComputeServiceContextModuleTest.java similarity index 100% rename from sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/compute/config/NovaComputeServiceContextModuleTest.java rename to apis/nova/src/test/java/org/jclouds/openstack/nova/compute/config/NovaComputeServiceContextModuleTest.java diff --git a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/compute/functions/FlavorToHardwareTest.java b/apis/nova/src/test/java/org/jclouds/openstack/nova/compute/functions/FlavorToHardwareTest.java similarity index 71% rename from sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/compute/functions/FlavorToHardwareTest.java rename to apis/nova/src/test/java/org/jclouds/openstack/nova/compute/functions/FlavorToHardwareTest.java index a8f8607b92..414f5ba06e 100644 --- a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/compute/functions/FlavorToHardwareTest.java +++ b/apis/nova/src/test/java/org/jclouds/openstack/nova/compute/functions/FlavorToHardwareTest.java @@ -16,25 +16,23 @@ * limitations under the License. * ==================================================================== */ + package org.jclouds.openstack.nova.compute.functions; -import static org.testng.Assert.assertEquals; - -import java.net.UnknownHostException; - -import org.jclouds.openstack.nova.domain.Flavor; -import org.jclouds.openstack.nova.functions.ParseFlavorFromJsonResponseTest; -import org.jclouds.compute.domain.Hardware; -import org.jclouds.compute.domain.HardwareBuilder; -import org.jclouds.compute.domain.Processor; -import org.jclouds.compute.domain.Volume; -import org.jclouds.compute.domain.VolumeBuilder; +import com.google.common.collect.ImmutableList; +import org.jclouds.compute.domain.*; import org.jclouds.domain.Location; import org.jclouds.domain.LocationBuilder; import org.jclouds.domain.LocationScope; +import org.jclouds.openstack.nova.domain.Flavor; +import org.jclouds.openstack.nova.functions.ParseFlavorFromJsonResponseTest; import org.testng.annotations.Test; -import com.google.common.collect.ImmutableList; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.UnknownHostException; + +import static org.testng.Assert.assertEquals; /** * @author Adrian Cole @@ -44,11 +42,16 @@ public class FlavorToHardwareTest { Location provider = new LocationBuilder().scope(LocationScope.ZONE).id("dallas").description("description").build(); @Test - public void test() throws UnknownHostException { - assertEquals(convertFlavor(), new HardwareBuilder().ids("1").name("256 MB Server").processors( - ImmutableList.of(new Processor(1.0, 1.0))).ram(256).volumes( - ImmutableList.of(new VolumeBuilder().type(Volume.Type.LOCAL).size(10.0f).durable(true).bootDevice(true) - .build())).build()); + public void test() throws UnknownHostException, URISyntaxException { + Hardware flavor = convertFlavor(); + Hardware tempFlavor = new HardwareBuilder().ids("1").name("256 MB Server") + .processors(ImmutableList.of(new Processor(1.0, 1.0))) + .ram(256) + .volumes(ImmutableList.of( + new VolumeBuilder().type(Volume.Type.LOCAL).size(10.0f).durable(true).bootDevice(true).build())) + .uri(new URI("http://servers.api.openstack.org/1234/flavors/1")) + .build(); + assertEquals(flavor, tempFlavor); } public static Hardware convertFlavor() { diff --git a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/compute/functions/NovaImageToImageTest.java b/apis/nova/src/test/java/org/jclouds/openstack/nova/compute/functions/NovaImageToImageTest.java similarity index 75% rename from sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/compute/functions/NovaImageToImageTest.java rename to apis/nova/src/test/java/org/jclouds/openstack/nova/compute/functions/NovaImageToImageTest.java index 7e7045f474..9d1ae36b4b 100644 --- a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/compute/functions/NovaImageToImageTest.java +++ b/apis/nova/src/test/java/org/jclouds/openstack/nova/compute/functions/NovaImageToImageTest.java @@ -18,14 +18,11 @@ */ package org.jclouds.openstack.nova.compute.functions; -import static org.testng.Assert.assertEquals; - -import java.net.UnknownHostException; - +import com.google.inject.Guice; import org.jclouds.compute.config.BaseComputeServiceContextModule; import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.ImageBuilder; -import org.jclouds.compute.domain.OperatingSystemBuilder; +import org.jclouds.compute.domain.OperatingSystem; import org.jclouds.compute.domain.OsFamily; import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.domain.Credentials; @@ -34,7 +31,11 @@ import org.jclouds.json.config.GsonModule; import org.jclouds.openstack.nova.functions.ParseImageFromJsonResponseTest; import org.testng.annotations.Test; -import com.google.inject.Guice; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.UnknownHostException; + +import static org.testng.Assert.assertEquals; /** * @author Adrian Cole @@ -43,15 +44,16 @@ import com.google.inject.Guice; public class NovaImageToImageTest { @Test - public void testApplyWhereImageNotFound() throws UnknownHostException { - assertEquals( - convertImage(), - new ImageBuilder() - .name("CentOS 5.2") - .operatingSystem( - new OperatingSystemBuilder().family(OsFamily.CENTOS).version("5.2").description("CentOS 5.2").is64Bit(true) - .build()).description("CentOS 5.2").defaultCredentials(new Credentials("root", null)) - .ids("2").version("1286712000000").build()); + public void testApplyWhereImageNotFound() throws UnknownHostException, URISyntaxException { + Image image = new ImageBuilder() + .name("CentOS 5.2") + .operatingSystem( + new OperatingSystem.Builder().family(OsFamily.CENTOS).version("5.2").description("CentOS 5.2").is64Bit(true) + .build()).description("CentOS 5.2").defaultCredentials(new Credentials("root", null)) + .ids("2").version("1286712000000").uri(new URI("https://servers.api.rackspacecloud.com/v1.1/1234/images/1")).build(); + Image parsedImage = convertImage(); + + assertEquals(parsedImage, image); } public static Image convertImage() { diff --git a/apis/nova/src/test/java/org/jclouds/openstack/nova/compute/functions/ServerToNodeMetadataTest.java b/apis/nova/src/test/java/org/jclouds/openstack/nova/compute/functions/ServerToNodeMetadataTest.java new file mode 100644 index 0000000000..33e55af97a --- /dev/null +++ b/apis/nova/src/test/java/org/jclouds/openstack/nova/compute/functions/ServerToNodeMetadataTest.java @@ -0,0 +1,170 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.openstack.nova.compute.functions; + +import com.google.common.base.Suppliers; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import org.jclouds.compute.domain.*; +import org.jclouds.domain.Credentials; +import org.jclouds.domain.Location; +import org.jclouds.domain.LocationBuilder; +import org.jclouds.domain.LocationScope; +import org.jclouds.openstack.nova.compute.config.NovaComputeServiceDependenciesModule; +import org.jclouds.openstack.nova.domain.Server; +import org.jclouds.openstack.nova.domain.ServerStatus; +import org.jclouds.openstack.nova.functions.ParseServerFromJsonResponseTest; +import org.testng.annotations.Test; + +import java.net.URI; +import java.net.URISyntaxException; +import java.net.UnknownHostException; +import java.util.Map; +import java.util.Set; + +import static org.testng.Assert.assertEquals; + +/** + * @author Adrian Cole + */ +@Test(groups = "unit") +public class ServerToNodeMetadataTest { + Location provider = new LocationBuilder().scope(LocationScope.ZONE).id("dallas").description("description").build(); + + @Test + public void testApplyWhereImageAndHardwareNotFoundButCredentialsFound() throws UnknownHostException, NoSuchMethodException, ClassNotFoundException, URISyntaxException { + Credentials creds = new Credentials("root", "abdce"); + + Map serverStateToNodeState = NovaComputeServiceDependenciesModule.serverToNodeState; + Set images = ImmutableSet.of(); + Set hardwares = ImmutableSet.of(); + Server server = ParseServerFromJsonResponseTest.parseServer(); + + ServerToNodeMetadata parser = new ServerToNodeMetadata(serverStateToNodeState, ImmutableMap + .of("node#1234", creds), Suppliers.>ofInstance(images), + Suppliers.ofInstance(provider), Suppliers.>ofInstance(hardwares)); + + NodeMetadata metadata = parser.apply(server); + + NodeMetadata constructedMetadata = newNodeMetadataBuilder() + .credentials(creds).build(); + assertEquals(metadata, constructedMetadata); + } + + @Test + public void testApplyWhereImageAndHardwareNotFound() throws UnknownHostException, NoSuchMethodException, ClassNotFoundException, URISyntaxException { + Map serverStateToNodeState = NovaComputeServiceDependenciesModule.serverToNodeState; + Set images = ImmutableSet.of(); + Set hardwares = ImmutableSet.of(); + Server server = ParseServerFromJsonResponseTest.parseServer(); + + ServerToNodeMetadata parser = new ServerToNodeMetadata(serverStateToNodeState, ImmutableMap + .of(), Suppliers.>ofInstance(images), Suppliers + .ofInstance(provider), Suppliers.>ofInstance(hardwares)); + + NodeMetadata metadata = parser.apply(server); + + NodeMetadata constructedMetadata = newNodeMetadataBuilder().build(); + + assertEquals(metadata, constructedMetadata); + + } + + private NodeMetadataBuilder newNodeMetadataBuilder() throws URISyntaxException { + return new NodeMetadataBuilder() + .state(NodeState.PENDING) + .publicAddresses(ImmutableSet.of("67.23.10.132", "::babe:67.23.10.132", "67.23.10.131", "::babe:4317:0A83")) + .privateAddresses(ImmutableSet.of("10.176.42.16", "::babe:10.176.42.16")) + .id("1234") + .providerId("1234") + .name("sample-server") + .location(new LocationBuilder() + .scope(LocationScope.HOST) + .id("e4d909c290d0fb1ca068ffaddf22cbd0") + .description("e4d909c290d0fb1ca068ffaddf22cbd0") + .parent(provider).build()) + .userMetadata(ImmutableMap.of("Server Label", "Web Head 1", "Image Version", "2.1")) + .uri(new URI("http://servers.api.openstack.org/1234/servers/1234")); + } + + @Test + public void testApplyWhereImageFoundAndHardwareNotFound() throws UnknownHostException, NoSuchMethodException, ClassNotFoundException, URISyntaxException { + Map serverStateToNodeState = NovaComputeServiceDependenciesModule.serverToNodeState; + org.jclouds.compute.domain.Image jcImage = NovaImageToImageTest.convertImage(); + Set images = ImmutableSet.of(jcImage); + Set hardwares = ImmutableSet.of(); + Server server = ParseServerFromJsonResponseTest.parseServer(); + + ServerToNodeMetadata parser = new ServerToNodeMetadata(serverStateToNodeState, ImmutableMap + .of(), Suppliers.>ofInstance(images), Suppliers + .ofInstance(provider), Suppliers.>ofInstance(hardwares)); + + NodeMetadata metadata = parser.apply(server); + + NodeMetadata constructedMetadata = newNodeMetadataBuilder() + .imageId("2") + .operatingSystem(new OperatingSystem.Builder() + .family(OsFamily.CENTOS) + .description("CentOS 5.2") + .version("5.2") + .is64Bit(true).build()) + .build(); + + assertEquals(metadata, constructedMetadata); + + } + + @Test + public void testApplyWhereImageAndHardwareFound() throws UnknownHostException, NoSuchMethodException, ClassNotFoundException, URISyntaxException { + Map serverStateToNodeState = NovaComputeServiceDependenciesModule.serverToNodeState; + Set images = ImmutableSet.of(NovaImageToImageTest.convertImage()); + Set hardwares = ImmutableSet.of(FlavorToHardwareTest.convertFlavor()); + Server server = ParseServerFromJsonResponseTest.parseServer(); + + ServerToNodeMetadata parser = new ServerToNodeMetadata(serverStateToNodeState, ImmutableMap + .of(), Suppliers.>ofInstance(images), Suppliers + .ofInstance(provider), Suppliers.>ofInstance(hardwares)); + + NodeMetadata metadata = parser.apply(server); + + NodeMetadata constructedMetadata = newNodeMetadataBuilder() + .imageId("2") + .operatingSystem(new OperatingSystem.Builder() + .family(OsFamily.CENTOS) + .description("CentOS 5.2") + .version("5.2") + .is64Bit(true).build()) + .hardware(new HardwareBuilder() + .ids("1") + .name("256 MB Server") + .processors(ImmutableList.of(new Processor(1.0, 1.0))) + .ram(256) + .volumes(ImmutableList.of(new VolumeBuilder() + .type(Volume.Type.LOCAL) + .size(10.0f) + .durable(true) + .bootDevice(true).build())) + .uri(new URI("http://servers.api.openstack.org/1234/flavors/1")) + .build()) + .build(); + + assertEquals(metadata, constructedMetadata); + } +} diff --git a/apis/nova/src/test/java/org/jclouds/openstack/nova/domain/ServerStatusTest.java b/apis/nova/src/test/java/org/jclouds/openstack/nova/domain/ServerStatusTest.java new file mode 100644 index 0000000000..c22d10b579 --- /dev/null +++ b/apis/nova/src/test/java/org/jclouds/openstack/nova/domain/ServerStatusTest.java @@ -0,0 +1,65 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.openstack.nova.domain; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import org.testng.annotations.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +import static junit.framework.Assert.assertTrue; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; + +/** + * Tests behavior of {@code CreateImageBinder} + * + * @author Adrian Cole + */ +@Test(groups = "unit") +public class ServerStatusTest { + + @Test + public void testAllKnownStatusesIsRecognized() { + List knownStatuses = Arrays.asList( + "ACTIVE", "BUILD", "REBUILD", "SUSPENDED", "QUEUE_RESIZE", + "PREP_RESIZE", "RESIZE", "VERIFY_RESIZE", + "PASSWORD", "RESCUE", "REBOOT", + "HARD_REBOOT", "DELETE_IP", "UNKNOWN", "DELETED"); + for (String status : knownStatuses) { + assertFalse(ServerStatus.fromValue(status).equals(ServerStatus.UNRECOGNIZED)); + } + + List allStatuses = Lists.newArrayList(knownStatuses); + allStatuses.add("UNRECOGNIZED"); + + Set enumValues = Sets.newHashSet(ServerStatus.values()); + + assertEquals(enumValues.size(), allStatuses.size()); + + for (String status : allStatuses) { + assertTrue(enumValues.contains(ServerStatus.valueOf(status))); + } + } + +} + diff --git a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/domain/ServerTest.java b/apis/nova/src/test/java/org/jclouds/openstack/nova/domain/ServerTest.java similarity index 99% rename from sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/domain/ServerTest.java rename to apis/nova/src/test/java/org/jclouds/openstack/nova/domain/ServerTest.java index bbeefa7dfe..e9e81f2800 100644 --- a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/domain/ServerTest.java +++ b/apis/nova/src/test/java/org/jclouds/openstack/nova/domain/ServerTest.java @@ -18,13 +18,13 @@ */ package org.jclouds.openstack.nova.domain; -import static org.testng.Assert.assertEquals; - import org.testng.annotations.Test; +import static org.testng.Assert.assertEquals; + /** * Tests behavior of {@code CreateImageBinder} - * + * * @author Adrian Cole */ @Test(groups = "unit") diff --git a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseAddressesFromJsonResponseTest.java b/apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseAddressesFromJsonResponseTest.java similarity index 70% rename from sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseAddressesFromJsonResponseTest.java rename to apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseAddressesFromJsonResponseTest.java index 19724a6bd3..a945d1522a 100644 --- a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseAddressesFromJsonResponseTest.java +++ b/apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseAddressesFromJsonResponseTest.java @@ -18,45 +18,53 @@ */ package org.jclouds.openstack.nova.functions; -import static org.testng.Assert.assertEquals; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.common.collect.Sets; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.Key; +import com.google.inject.TypeLiteral; +import org.jclouds.http.HttpResponse; +import org.jclouds.http.functions.UnwrapOnlyJsonValue; +import org.jclouds.io.Payloads; +import org.jclouds.json.config.GsonModule; +import org.jclouds.openstack.nova.domain.Address; +import org.jclouds.openstack.nova.domain.Addresses; +import org.testng.annotations.Test; import java.io.InputStream; import java.net.UnknownHostException; import java.util.List; -import org.jclouds.openstack.nova.domain.Addresses; -import org.jclouds.http.HttpResponse; -import org.jclouds.http.functions.UnwrapOnlyJsonValue; -import org.jclouds.io.Payloads; -import org.jclouds.json.config.GsonModule; -import org.testng.annotations.Test; - -import com.google.common.collect.ImmutableList; -import com.google.inject.Guice; -import com.google.inject.Injector; -import com.google.inject.Key; -import com.google.inject.TypeLiteral; +import static org.testng.Assert.assertTrue; /** * Tests behavior of {@code ParseAddressesFromJsonResponse} - * + * * @author Adrian Cole */ @Test(groups = "unit") public class ParseAddressesFromJsonResponseTest { Injector i = Guice.createInjector(new GsonModule()); + @Test public void testApplyInputStreamDetails() throws UnknownHostException { InputStream is = getClass().getResourceAsStream("/test_list_addresses.json"); UnwrapOnlyJsonValue parser = i.getInstance(Key.get(new TypeLiteral>() { })); Addresses response = parser.apply(new HttpResponse(200, "ok", Payloads.newInputStreamPayload(is))); - List publicAddresses = ImmutableList.of("67.23.10.132", "67.23.10.131"); - List privateAddresses = ImmutableList.of("10.176.42.16"); + List

publicAddresses = ImmutableList.copyOf( + Iterables.transform(ImmutableList.of("67.23.10.132", "::babe:67.23.10.132", "67.23.10.131", "::babe:4317:0A83"), + Address.newString2AddressFunction())); - assertEquals(response.getPublicAddresses(), publicAddresses); - assertEquals(response.getPrivateAddresses(), privateAddresses); + List
privateAddresses = ImmutableList.copyOf( + Iterables.transform(ImmutableList.of("10.176.42.16", "::babe:10.176.42.16"), + Address.newString2AddressFunction())); + + assertTrue(response.getPublicAddresses().equals(Sets.newHashSet(publicAddresses))); + assertTrue(response.getPrivateAddresses().equals(Sets.newHashSet(privateAddresses))); } } diff --git a/apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseFaultFromJsonResponseTest.java b/apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseFaultFromJsonResponseTest.java new file mode 100644 index 0000000000..5a62140cd2 --- /dev/null +++ b/apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseFaultFromJsonResponseTest.java @@ -0,0 +1,149 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.openstack.nova.functions; + +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.Key; +import com.google.inject.TypeLiteral; +import org.jclouds.http.HttpCommand; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpResponse; +import org.jclouds.http.functions.UnwrapOnlyJsonValue; +import org.jclouds.io.Payloads; +import org.jclouds.json.config.GsonModule; +import org.jclouds.openstack.nova.domain.Server; +import org.jclouds.openstack.nova.handlers.ParseNovaErrorFromHttpResponse; +import org.testng.annotations.Test; + +import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; + +/** + * Tests behavior of {@code ParseServerListFromJsonResponse} + * + * @author Adrian Cole + */ +@Test(groups = "unit") +public class ParseFaultFromJsonResponseTest { + + Injector i = Guice.createInjector(new GsonModule()); + + @Test + public void testApplyInputStream() { + InputStream is = getClass().getResourceAsStream("/test_list_servers.json"); + + @SuppressWarnings("unused") + UnwrapOnlyJsonValue> parser = i.getInstance(Key + .get(new TypeLiteral>>() { + })); + //List response = parser.apply(new HttpResponse(413, "Over limit", Payloads.newInputStreamPayload(is))); + new ParseNovaErrorFromHttpResponse().handleError(createHttpCommand(), new HttpResponse(413, "Over limit", Payloads.newInputStreamPayload(is))); + + //assertEquals(response, expects); + } + + @Test + public void testHandler() { + //InputStream is = getClass().getResourceAsStream("/test_error_handler.json"); + +// +// +// NovaErrorHandler handler = Guice.createInjector(new GsonModule()).getInstance(GoGridErrorHandler.class); +// +// HttpCommand command = createHttpCommand(); +// handler.handleError(command, new HttpResponse(200, "ok", Payloads.newInputStreamPayload(is))); +// +// Exception createdException = command.getException(); +// +// assertNotNull(createdException, "There should've been an exception generated"); +// String message = createdException.getMessage(); +// assertTrue(message.contains("No object found that matches your input criteria."), +// "Didn't find the expected error cause in the exception message"); +// assertTrue(message.contains("IllegalArgumentException"), +// "Didn't find the expected error code in the exception message"); +// +// // make sure the InputStream is closed +// try { +// is.available(); +// throw new TestException("Stream wasn't closed by the GoGridErrorHandler when it should've"); +// } catch (IOException e) { +// // this is the excepted output +// } + } + + HttpCommand createHttpCommand() { + return new HttpCommand() { + private Exception exception; + + @Override + public int incrementRedirectCount() { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public int getRedirectCount() { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public boolean isReplayable() { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public int incrementFailureCount() { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public int getFailureCount() { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public HttpRequest getCurrentRequest() { + try { + return new HttpRequest("method", new URI("http://endpoint")); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } + + @Override + public void setCurrentRequest(HttpRequest request) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void setException(Exception exception) { + this.exception = exception; + } + + @Override + public Exception getException() { + return exception; + } + }; + } + + +} diff --git a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseFlavorFromJsonResponseTest.java b/apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseFlavorFromJsonResponseTest.java similarity index 79% rename from sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseFlavorFromJsonResponseTest.java rename to apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseFlavorFromJsonResponseTest.java index ada42756c8..7b9b01d8d9 100644 --- a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseFlavorFromJsonResponseTest.java +++ b/apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseFlavorFromJsonResponseTest.java @@ -18,36 +18,43 @@ */ package org.jclouds.openstack.nova.functions; -import static org.testng.Assert.assertEquals; - -import java.io.InputStream; - -import org.jclouds.openstack.nova.domain.Flavor; -import org.jclouds.http.HttpResponse; -import org.jclouds.http.functions.UnwrapOnlyJsonValue; -import org.jclouds.io.Payloads; -import org.jclouds.json.config.GsonModule; -import org.testng.annotations.Test; - import com.google.gson.Gson; import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.TypeLiteral; +import org.jclouds.http.HttpResponse; +import org.jclouds.http.functions.UnwrapOnlyJsonValue; +import org.jclouds.io.Payloads; +import org.jclouds.json.config.GsonModule; +import org.jclouds.openstack.nova.domain.Flavor; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.io.InputStream; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; /** * Tests behavior of {@code ParseFlavorFromJsonResponse} - * + * * @author Adrian Cole */ @Test(groups = "unit") public class ParseFlavorFromJsonResponseTest { - public void test() { + + @Test + public void testParseFlavorFromJsonResponseTest() throws IOException { Flavor response = parseFlavor(); String json = new Gson().toJson(response); + assertNotNull(json); - assertEquals(json, "{\"id\":1,\"name\":\"256 MB Server\",\"disk\":10,\"ram\":256}"); + assertEquals(response.getId(), 1); + assertEquals(response.getName(), "256 MB Server"); + assertEquals(response.getDisk().intValue(), 10); + assertEquals(response.getRam().intValue(), 256); } public static Flavor parseFlavor() { @@ -57,8 +64,7 @@ public class ParseFlavorFromJsonResponseTest { UnwrapOnlyJsonValue parser = i.getInstance(Key.get(new TypeLiteral>() { })); - Flavor response = parser.apply(new HttpResponse(200, "ok", Payloads.newInputStreamPayload(is))); - return response; + return parser.apply(new HttpResponse(200, "ok", Payloads.newInputStreamPayload(is))); } } diff --git a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseFlavorListFromJsonResponseTest.java b/apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseFlavorListFromJsonResponseTest.java similarity index 99% rename from sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseFlavorListFromJsonResponseTest.java rename to apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseFlavorListFromJsonResponseTest.java index f4d6b63bda..1603185edd 100644 --- a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseFlavorListFromJsonResponseTest.java +++ b/apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseFlavorListFromJsonResponseTest.java @@ -18,28 +18,27 @@ */ package org.jclouds.openstack.nova.functions; -import static org.testng.Assert.assertEquals; - -import java.io.InputStream; -import java.net.UnknownHostException; -import java.util.List; - -import org.jclouds.openstack.nova.domain.Flavor; -import org.jclouds.http.HttpResponse; -import org.jclouds.http.functions.UnwrapOnlyJsonValue; -import org.jclouds.io.Payloads; -import org.jclouds.json.config.GsonModule; -import org.testng.annotations.Test; - import com.google.common.collect.ImmutableList; import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.TypeLiteral; +import org.jclouds.http.HttpResponse; +import org.jclouds.http.functions.UnwrapOnlyJsonValue; +import org.jclouds.io.Payloads; +import org.jclouds.json.config.GsonModule; +import org.jclouds.openstack.nova.domain.Flavor; +import org.testng.annotations.Test; + +import java.io.InputStream; +import java.net.UnknownHostException; +import java.util.List; + +import static org.testng.Assert.assertEquals; /** * Tests behavior of {@code ParseFlavorListFromJsonResponse} - * + * * @author Adrian Cole */ @Test(groups = "unit") @@ -47,6 +46,7 @@ public class ParseFlavorListFromJsonResponseTest { Injector i = Guice.createInjector(new GsonModule()); + @Test public void testApplyInputStream() { InputStream is = getClass().getResourceAsStream("/test_list_flavors.json"); @@ -59,6 +59,7 @@ public class ParseFlavorListFromJsonResponseTest { assertEquals(response, expects); } + @Test public void testApplyInputStreamDetails() throws UnknownHostException { InputStream is = getClass().getResourceAsStream("/test_list_flavors_detail.json"); diff --git a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseImageFromJsonResponseTest.java b/apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseImageFromJsonResponseTest.java similarity index 89% rename from sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseImageFromJsonResponseTest.java rename to apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseImageFromJsonResponseTest.java index 9fe8962c80..550406e300 100644 --- a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseImageFromJsonResponseTest.java +++ b/apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseImageFromJsonResponseTest.java @@ -18,13 +18,7 @@ */ package org.jclouds.openstack.nova.functions; -import static org.testng.Assert.assertEquals; - -import java.io.InputStream; -import java.net.UnknownHostException; - -import org.jclouds.openstack.nova.domain.Image; -import org.jclouds.openstack.nova.domain.ImageStatus; +import com.google.inject.*; import org.jclouds.date.DateService; import org.jclouds.http.HttpResponse; import org.jclouds.http.functions.UnwrapOnlyJsonValue; @@ -32,17 +26,18 @@ import org.jclouds.io.Payloads; import org.jclouds.json.config.GsonModule; import org.jclouds.json.config.GsonModule.DateAdapter; import org.jclouds.json.config.GsonModule.Iso8601DateAdapter; +import org.jclouds.openstack.nova.domain.Image; +import org.jclouds.openstack.nova.domain.ImageStatus; import org.testng.annotations.Test; -import com.google.inject.AbstractModule; -import com.google.inject.Guice; -import com.google.inject.Injector; -import com.google.inject.Key; -import com.google.inject.TypeLiteral; +import java.io.InputStream; +import java.net.UnknownHostException; + +import static org.testng.Assert.assertEquals; /** * Tests behavior of {@code ParseImageFromJsonResponse} - * + * * @author Adrian Cole */ @Test(groups = "unit") @@ -58,6 +53,7 @@ public class ParseImageFromJsonResponseTest { DateService dateService = i.getInstance(DateService.class); + @Test public void testApplyInputStreamDetails() throws UnknownHostException { Image response = parseImage(); @@ -65,10 +61,12 @@ public class ParseImageFromJsonResponseTest { assertEquals(response.getName(), "CentOS 5.2"); assertEquals(response.getCreated(), dateService.iso8601SecondsDateParse("2010-08-10T12:00:00Z")); assertEquals(response.getProgress(), new Integer(80)); - assertEquals(response.getServerId(), new Integer(12)); assertEquals(response.getStatus(), ImageStatus.SAVING); assertEquals(response.getUpdated(), dateService.iso8601SecondsDateParse(("2010-10-10T12:00:00Z"))); - + assertEquals(response.getServerRef(), "http://servers.api.openstack.org/v1.1/1234/servers/12"); + assertEquals(response.getMetadata().get("ImageVersion"), "1.5"); + assertEquals(response.getMetadata().get("ImageType"), "Gold"); + assertEquals(response.getMetadata().size(), 2); } public static Image parseImage() { diff --git a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseImageListFromJsonResponseTest.java b/apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseImageListFromJsonResponseTest.java similarity index 83% rename from sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseImageListFromJsonResponseTest.java rename to apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseImageListFromJsonResponseTest.java index 48c56cfcef..9fe0df1e36 100644 --- a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseImageListFromJsonResponseTest.java +++ b/apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseImageListFromJsonResponseTest.java @@ -18,14 +18,8 @@ */ package org.jclouds.openstack.nova.functions; -import static org.testng.Assert.assertEquals; - -import java.io.InputStream; -import java.net.UnknownHostException; -import java.util.List; - -import org.jclouds.openstack.nova.domain.Image; -import org.jclouds.openstack.nova.domain.ImageStatus; +import com.google.common.collect.ImmutableList; +import com.google.inject.*; import org.jclouds.date.DateService; import org.jclouds.http.HttpResponse; import org.jclouds.http.functions.UnwrapOnlyJsonValue; @@ -33,18 +27,19 @@ import org.jclouds.io.Payloads; import org.jclouds.json.config.GsonModule; import org.jclouds.json.config.GsonModule.DateAdapter; import org.jclouds.json.config.GsonModule.Iso8601DateAdapter; +import org.jclouds.openstack.nova.domain.Image; +import org.jclouds.openstack.nova.domain.ImageStatus; import org.testng.annotations.Test; -import com.google.common.collect.ImmutableList; -import com.google.inject.AbstractModule; -import com.google.inject.Guice; -import com.google.inject.Injector; -import com.google.inject.Key; -import com.google.inject.TypeLiteral; +import java.io.InputStream; +import java.net.UnknownHostException; +import java.util.List; + +import static org.testng.Assert.assertEquals; /** * Tests behavior of {@code ParseImageListFromJsonResponse} - * + * * @author Adrian Cole */ @Test(groups = "unit") @@ -56,13 +51,14 @@ public class ParseImageListFromJsonResponseTest { bind(DateAdapter.class).to(Iso8601DateAdapter.class); } - },new GsonModule()); + }, new GsonModule()); DateService dateService = i.getInstance(DateService.class); + @Test 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")); + List expects = ImmutableList.of(new Image(1, "CentOS 5.2"), new Image(743, "My Server Backup")); UnwrapOnlyJsonValue> parser = i.getInstance(Key .get(new TypeLiteral>>() { @@ -72,6 +68,7 @@ public class ParseImageListFromJsonResponseTest { assertEquals(response, expects); } + @Test public void testApplyInputStreamDetails() throws UnknownHostException { InputStream is = getClass().getResourceAsStream("/test_list_images_detail.json"); @@ -80,22 +77,29 @@ public class ParseImageListFromJsonResponseTest { })); List response = parser.apply(new HttpResponse(200, "ok", Payloads.newInputStreamPayload(is))); - assertEquals(response.get(0).getId(), 2); + assertEquals(response.get(0).getId(), 1); assertEquals(response.get(0).getName(), "CentOS 5.2"); assertEquals(response.get(0).getCreated(), dateService.iso8601SecondsDateParse("2010-08-10T12:00:00Z")); assertEquals(response.get(0).getProgress(), null); - assertEquals(response.get(0).getServerId(), null); + assertEquals(response.get(0).getServerRef(), null); assertEquals(response.get(0).getStatus(), ImageStatus.ACTIVE); assertEquals(response.get(0).getUpdated(), dateService.iso8601SecondsDateParse("2010-10-10T12:00:00Z")); + assertEquals(response.get(0).getMetadata().get("ImageType"), "Gold"); + assertEquals(response.get(0).getMetadata().get("ImageVersion"), "1.5"); + assertEquals(response.get(0).getMetadata().size(), 2); + assertEquals(response.get(1).getId(), 743); assertEquals(response.get(1).getName(), "My Server Backup"); - assertEquals(response.get(1).getCreated(), dateService.iso8601SecondsDateParse("2009-07-07T09:56:16-05:00")); - ; + assertEquals(response.get(1).getCreated(), dateService.iso8601SecondsDateParse("2009-07-07T09:56:16Z")); + 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.iso8601SecondsDateParse("2010-10-10T12:00:00Z")); + assertEquals(response.get(1).getServerRef(), "http://servers.api.openstack.org/v1.1/1234/servers/12"); + + //short form of reference + assertEquals(response.get(2).getServerRef(), "12"); } } diff --git a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseInetAddressListFromJsonResponseTest.java b/apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseInetAddressListFromJsonResponseTest.java similarity index 65% rename from sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseInetAddressListFromJsonResponseTest.java rename to apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseInetAddressListFromJsonResponseTest.java index 362f61e27a..dbdc14c41f 100644 --- a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseInetAddressListFromJsonResponseTest.java +++ b/apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseInetAddressListFromJsonResponseTest.java @@ -18,27 +18,27 @@ */ package org.jclouds.openstack.nova.functions; -import static org.testng.Assert.assertEquals; - -import java.io.InputStream; -import java.net.UnknownHostException; -import java.util.List; - -import org.jclouds.http.HttpResponse; -import org.jclouds.http.functions.UnwrapOnlyJsonValue; -import org.jclouds.io.Payloads; -import org.jclouds.json.config.GsonModule; -import org.testng.annotations.Test; - import com.google.common.collect.ImmutableList; import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.TypeLiteral; +import org.jclouds.http.HttpResponse; +import org.jclouds.http.functions.UnwrapOnlyJsonValue; +import org.jclouds.io.Payloads; +import org.jclouds.json.config.GsonModule; +import org.jclouds.openstack.nova.domain.Address; +import org.testng.annotations.Test; + +import java.io.InputStream; +import java.net.UnknownHostException; +import java.util.List; + +import static org.testng.Assert.assertEquals; /** * Tests behavior of {@code ParseInetAddressListFromJsonResponse} - * + * * @author Adrian Cole */ @Test(groups = "unit") @@ -46,25 +46,33 @@ public class ParseInetAddressListFromJsonResponseTest { Injector i = Guice.createInjector(new GsonModule()); + @Test public void testPublic() throws UnknownHostException { InputStream is = getClass().getResourceAsStream("/test_list_addresses_public.json"); - UnwrapOnlyJsonValue> parser = i.getInstance(Key - .get(new TypeLiteral>>() { + UnwrapOnlyJsonValue> parser = i.getInstance(Key + .get(new TypeLiteral>>() { })); - List response = parser.apply(new HttpResponse(200, "ok", Payloads.newInputStreamPayload(is))); + List
response = parser.apply(new HttpResponse(200, "ok", Payloads.newInputStreamPayload(is))); - assertEquals(response, ImmutableList.of("67.23.10.132", "67.23.10.131")); + assertEquals(response, ImmutableList.of(Address.valueOf("67.23.10.132"), + Address.valueOf("::babe:67.23.10.132"), + Address.valueOf("67.23.10.131"), Address.valueOf("::babe:4317:0A83"))); } + @Test public void testPrivate() throws UnknownHostException { InputStream is = getClass().getResourceAsStream("/test_list_addresses_private.json"); - UnwrapOnlyJsonValue> parser = i.getInstance(Key - .get(new TypeLiteral>>() { + UnwrapOnlyJsonValue> parser = i.getInstance(Key + .get(new TypeLiteral>>() { })); - List response = parser.apply(new HttpResponse(200, "ok", Payloads.newInputStreamPayload(is))); + List
response = parser.apply(new HttpResponse(200, "ok", Payloads.newInputStreamPayload(is))); + + assertEquals(response, ImmutableList.of(Address.valueOf("67.23.10.132"), + Address.valueOf("::babe:67.23.10.132"), + Address.valueOf("67.23.10.131"), Address.valueOf("::babe:4317:0A83"))); + - assertEquals(response, ImmutableList.of("10.176.42.16")); } } diff --git a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseServerFromJsonResponseTest.java b/apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseServerFromJsonResponseTest.java similarity index 55% rename from sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseServerFromJsonResponseTest.java rename to apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseServerFromJsonResponseTest.java index 86f376ee79..25c7a1aac8 100644 --- a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseServerFromJsonResponseTest.java +++ b/apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseServerFromJsonResponseTest.java @@ -18,65 +18,90 @@ */ package org.jclouds.openstack.nova.functions; -import static org.testng.Assert.assertEquals; - -import java.io.InputStream; -import java.net.UnknownHostException; -import java.util.List; - -import org.jclouds.openstack.nova.domain.Addresses; -import org.jclouds.openstack.nova.domain.Server; -import org.jclouds.openstack.nova.domain.ServerStatus; -import org.jclouds.http.HttpResponse; -import org.jclouds.http.functions.UnwrapOnlyJsonValue; -import org.jclouds.io.Payloads; -import org.jclouds.json.config.GsonModule; -import org.testng.annotations.Test; - -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Lists; -import com.google.inject.Guice; -import com.google.inject.Injector; -import com.google.inject.Key; -import com.google.inject.TypeLiteral; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.Key; +import com.google.inject.TypeLiteral; +import org.jclouds.http.HttpResponse; +import org.jclouds.http.functions.UnwrapOnlyJsonValue; +import org.jclouds.io.Payloads; +import org.jclouds.json.config.GsonModule; +import org.jclouds.openstack.nova.domain.Address; +import org.jclouds.openstack.nova.domain.Addresses; +import org.jclouds.openstack.nova.domain.Server; +import org.jclouds.openstack.nova.domain.ServerStatus; +import org.testng.annotations.Test; + +import java.io.InputStream; +import java.net.UnknownHostException; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.SimpleTimeZone; + +import static org.testng.Assert.assertEquals; /** * Tests behavior of {@code ParseServerFromJsonResponse} - * + * * @author Adrian Cole */ @Test(groups = "unit") public class ParseServerFromJsonResponseTest { - public void testApplyInputStreamDetails() throws UnknownHostException { + @Test + public void testApplyInputStreamDetails() throws UnknownHostException, NoSuchMethodException, ClassNotFoundException, ParseException { Server response = parseServer(); assertEquals(response.getId(), 1234); assertEquals(response.getName(), "sample-server"); - assertEquals(response.getImageId(), new Integer(2)); - assertEquals(response.getFlavorId(), new Integer(1)); + assertEquals(response.getImageRef(), "https://servers.api.rackspacecloud.com/v1.1/1234/images/1"); + assertEquals(response.getFlavorRef(), "http://servers.api.openstack.org/1234/flavors/1"); assertEquals(response.getHostId(), "e4d909c290d0fb1ca068ffaddf22cbd0"); assertEquals(response.getStatus(), ServerStatus.BUILD); assertEquals(response.getProgress(), new Integer(60)); - List publicAddresses = Lists.newArrayList("67.23.10.132", "67.23.10.131"); - List privateAddresses = Lists.newArrayList("10.176.42.16"); - Addresses addresses1 = new Addresses(); - addresses1.getPrivateAddresses().addAll(privateAddresses); - addresses1.getPublicAddresses().addAll(publicAddresses); + SimpleDateFormat dateFormat = new SimpleDateFormat( + "yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US); + dateFormat.setTimeZone(new SimpleTimeZone(0, "GMT")); + assertEquals(response.getCreated(), + dateFormat.parse("2010-08-10T12:00:00Z")); + assertEquals(response.getUpdated(), + dateFormat.parse("2010-10-10T12:00:00Z")); + + List
publicAddresses = ImmutableList.copyOf(Iterables.transform( + ImmutableList.of("67.23.10.132", "::babe:67.23.10.132", "67.23.10.131", "::babe:4317:0A83"), + Address.newString2AddressFunction())); + List
privateAddresses = ImmutableList.copyOf(Iterables.transform( + ImmutableList.of("10.176.42.16", "::babe:10.176.42.16"), + Address.newString2AddressFunction())); + Addresses addresses1 = new Addresses(new HashSet
(publicAddresses), new HashSet
(privateAddresses)); assertEquals(response.getAddresses(), addresses1); assertEquals(response.getMetadata(), ImmutableMap.of("Server Label", "Web Head 1", "Image Version", "2.1")); - + assertEquals(response.getAddresses(), addresses1); } - public static Server parseServer() { - Injector i = Guice.createInjector(new GsonModule()); + + public static Server parseServer() throws NoSuchMethodException, ClassNotFoundException { + + Injector i = Guice.createInjector(new GsonModule() { + @Override + protected void configure() { + super.configure(); + bind(DateAdapter.class).to(Iso8601DateAdapter.class); + } + }); InputStream is = ParseServerFromJsonResponseTest.class.getResourceAsStream("/test_get_server_detail.json"); UnwrapOnlyJsonValue parser = i.getInstance(Key.get(new TypeLiteral>() { })); - Server response = parser.apply(new HttpResponse(200, "ok", Payloads.newInputStreamPayload(is))); - return response; + + return (Server) parser.apply(new HttpResponse(200, "ok", Payloads.newInputStreamPayload(is))); } } diff --git a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseServerListFromJsonResponseTest.java b/apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseServerListFromJsonResponseTest.java similarity index 64% rename from sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseServerListFromJsonResponseTest.java rename to apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseServerListFromJsonResponseTest.java index 8a61582da5..1c4004d6f8 100644 --- a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseServerListFromJsonResponseTest.java +++ b/apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseServerListFromJsonResponseTest.java @@ -22,20 +22,22 @@ import static org.testng.Assert.assertEquals; import java.io.InputStream; import java.net.UnknownHostException; +import java.util.HashSet; import java.util.List; -import org.jclouds.openstack.nova.domain.Addresses; -import org.jclouds.openstack.nova.domain.Server; -import org.jclouds.openstack.nova.domain.ServerStatus; import org.jclouds.http.HttpResponse; import org.jclouds.http.functions.UnwrapOnlyJsonValue; import org.jclouds.io.Payloads; import org.jclouds.json.config.GsonModule; +import org.jclouds.openstack.nova.domain.Address; +import org.jclouds.openstack.nova.domain.Addresses; +import org.jclouds.openstack.nova.domain.Server; +import org.jclouds.openstack.nova.domain.ServerStatus; import org.testng.annotations.Test; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Lists; +import com.google.common.collect.Iterables; import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.Key; @@ -43,14 +45,21 @@ import com.google.inject.TypeLiteral; /** * Tests behavior of {@code ParseServerListFromJsonResponse} - * + * * @author Adrian Cole */ @Test(groups = "unit") public class ParseServerListFromJsonResponseTest { - Injector i = Guice.createInjector(new GsonModule()); + Injector i = Guice.createInjector(new GsonModule() { + @Override + protected void configure() { + super.configure(); + bind(DateAdapter.class).to(Iso8601DateAdapter.class); + } + }); + @Test public void testApplyInputStream() { InputStream is = getClass().getResourceAsStream("/test_list_servers.json"); @@ -64,6 +73,7 @@ public class ParseServerListFromJsonResponseTest { assertEquals(response, expects); } + @Test public void testApplyInputStreamDetails() throws UnknownHostException { InputStream is = getClass().getResourceAsStream("/test_list_servers_detail.json"); @@ -74,32 +84,42 @@ public class ParseServerListFromJsonResponseTest { assertEquals(response.get(0).getId(), 1234); assertEquals(response.get(0).getName(), "sample-server"); - assertEquals(response.get(0).getImageId(), new Integer(2)); - assertEquals(response.get(0).getFlavorId(), new Integer(1)); + assertEquals(response.get(0).getImageRef(), "https://servers.api.rackspacecloud.com/v1.1/32278/images/1234"); + assertEquals(response.get(0).getFlavorRef(), "https://servers.api.rackspacecloud.com/v1.1/32278/flavors/1"); assertEquals(response.get(0).getHostId(), "e4d909c290d0fb1ca068ffaddf22cbd0"); assertEquals(response.get(0).getStatus(), ServerStatus.BUILD); assertEquals(response.get(0).getProgress(), new Integer(60)); - List publicAddresses = Lists.newArrayList("67.23.10.132", "67.23.10.131"); - List privateAddresses = Lists.newArrayList("10.176.42.16"); - Addresses addresses1 = new Addresses(); - addresses1.getPrivateAddresses().addAll(privateAddresses); - addresses1.getPublicAddresses().addAll(publicAddresses); + + List
publicAddresses = ImmutableList.copyOf(Iterables.transform( + ImmutableList.of("67.23.10.132", "::babe:67.23.10.132", "67.23.10.131", "::babe:4317:0A83"), + Address.newString2AddressFunction())); + List
privateAddresses = ImmutableList.copyOf(Iterables.transform( + ImmutableList.of("10.176.42.16", "::babe:10.176.42.16"), + Address.newString2AddressFunction())); + Addresses addresses1 = new Addresses(new HashSet
(publicAddresses), new HashSet
(privateAddresses)); + assertEquals(response.get(0).getAddresses(), addresses1); assertEquals(response.get(0).getMetadata(), ImmutableMap.of("Server Label", "Web Head 1", "Image Version", "2.1")); assertEquals(response.get(1).getId(), 5678); assertEquals(response.get(1).getName(), "sample-server2"); - assertEquals(response.get(1).getImageId(), new Integer(2)); - assertEquals(response.get(1).getFlavorId(), new Integer(1)); + assertEquals(response.get(1).getImageRef(), "https://servers.api.rackspacecloud.com/v1.1/32278/images/1"); + assertEquals(response.get(1).getFlavorRef(), "1"); + assertEquals(response.get(1).getAffinityId(), "b414fa41cb37b97dcb58d6c76112af1258e9eae2"); assertEquals(response.get(1).getHostId(), "9e107d9d372bb6826bd81d3542a419d6"); assertEquals(response.get(1).getStatus(), ServerStatus.ACTIVE); assertEquals(response.get(1).getProgress(), null); - List publicAddresses2 = Lists.newArrayList("67.23.10.133"); - List privateAddresses2 = Lists.newArrayList("10.176.42.17"); - Addresses addresses2 = new Addresses(); - addresses2.getPrivateAddresses().addAll(privateAddresses2); - addresses2.getPublicAddresses().addAll(publicAddresses2); + + List
publicAddresses2 = ImmutableList.copyOf(Iterables.transform( + ImmutableList.of("67.23.10.133", "::babe:67.23.10.133"), + Address.newString2AddressFunction())); + List
privateAddresses2 = ImmutableList.copyOf(Iterables.transform( + ImmutableList.of("10.176.42.17", "::babe:10.176.42.17"), + Address.newString2AddressFunction())); + Addresses addresses2 = new Addresses(new HashSet
(publicAddresses2), new HashSet
(privateAddresses2)); + assertEquals(response.get(1).getAddresses(), addresses2); assertEquals(response.get(1).getMetadata(), ImmutableMap.of("Server Label", "DB 1")); + assertEquals(response.get(1).getURI().toString(), "http://servers.api.openstack.org/1234/servers/56789"); } diff --git a/apis/nova/src/test/java/org/jclouds/openstack/nova/live/PropertyHelper.java b/apis/nova/src/test/java/org/jclouds/openstack/nova/live/PropertyHelper.java new file mode 100644 index 0000000000..73def076ee --- /dev/null +++ b/apis/nova/src/test/java/org/jclouds/openstack/nova/live/PropertyHelper.java @@ -0,0 +1,78 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.openstack.nova.live; + +import com.google.common.base.Charsets; +import com.google.common.collect.ImmutableMap; +import com.google.common.io.Files; +import org.jclouds.Constants; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; +import java.util.Properties; + +/** + * @author Victor Galkin + */ +public class PropertyHelper { + + private static String provider = "nova"; + + public static void overridePropertyFromSystemProperty(final Properties properties, String propertyName) { + if ((System.getProperty(propertyName) != null) && !System.getProperty(propertyName).equals("${" + propertyName + "}")) + properties.setProperty(propertyName, System.getProperty(propertyName)); + } + + public static Map setupKeyPair(Properties properties) throws FileNotFoundException, IOException { + return ImmutableMap.of( + "private", Files.toString(new File(properties.getProperty("test.ssh.keyfile.private")), Charsets.UTF_8), + "public", Files.toString(new File(properties.getProperty("test.ssh.keyfile.public")), Charsets.UTF_8)); + } + + public static Properties setupProperties(Class clazz) throws IOException { + Properties properties = new Properties(); + + InputStream propertiesStream = clazz.getResourceAsStream("/test.properties"); + if (propertiesStream != null) + properties.load(propertiesStream); + overridePropertyFromSystemProperty(properties, "test." + provider + ".endpoint"); + overridePropertyFromSystemProperty(properties, "test." + provider + ".apiversion"); + overridePropertyFromSystemProperty(properties, "test." + provider + ".identity"); + overridePropertyFromSystemProperty(properties, "test." + provider + ".credential"); + overridePropertyFromSystemProperty(properties, "test.ssh.keyfile.private"); + overridePropertyFromSystemProperty(properties, "test.ssh.keyfile.public"); + overridePropertyFromSystemProperty(properties, "test.initializer"); + return properties; + } + + public static Properties setupOverrides(final Properties properties) { + properties.setProperty(provider + ".identity", properties.getProperty("test." + provider + ".identity")); + properties.setProperty(provider + ".credential", properties.getProperty("test." + provider + ".credential")); + properties.setProperty(provider + ".endpoint", properties.getProperty("test." + provider + ".endpoint")); + properties.setProperty(provider + ".apiversion", properties.getProperty("test." + provider + ".apiversion")); + properties.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true"); + properties.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true"); + return properties; + } + + +} diff --git a/apis/nova/src/test/java/org/jclouds/openstack/nova/live/compute/ComputeBase.java b/apis/nova/src/test/java/org/jclouds/openstack/nova/live/compute/ComputeBase.java new file mode 100644 index 0000000000..c11c3eab81 --- /dev/null +++ b/apis/nova/src/test/java/org/jclouds/openstack/nova/live/compute/ComputeBase.java @@ -0,0 +1,173 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.openstack.nova.live.compute; + +import static com.google.common.base.Predicates.and; +import static com.google.common.base.Predicates.not; +import static com.google.common.collect.Sets.filter; +import static org.jclouds.compute.predicates.NodePredicates.TERMINATED; +import static org.jclouds.compute.predicates.NodePredicates.all; +import static org.jclouds.compute.predicates.NodePredicates.inGroup; +import static org.jclouds.openstack.nova.live.PropertyHelper.setupKeyPair; +import static org.jclouds.openstack.nova.live.PropertyHelper.setupOverrides; +import static org.jclouds.openstack.nova.live.PropertyHelper.setupProperties; +import static org.testng.Assert.assertEquals; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.jclouds.compute.ComputeService; +import org.jclouds.compute.ComputeServiceContext; +import org.jclouds.compute.ComputeServiceContextFactory; +import org.jclouds.compute.RunNodesException; +import org.jclouds.compute.domain.ComputeMetadata; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.domain.NodeState; +import org.jclouds.compute.domain.TemplateBuilder; +import org.jclouds.compute.options.TemplateOptions; +import org.jclouds.domain.Credentials; +import org.jclouds.domain.Location; +import org.jclouds.http.handlers.BackoffLimitedRetryHandler; +import org.jclouds.logging.slf4j.config.SLF4JLoggingModule; +import org.jclouds.net.IPSocket; +import org.jclouds.predicates.RetryablePredicate; +import org.jclouds.predicates.SocketOpen; +import org.jclouds.ssh.SshException; +import org.jclouds.ssh.jsch.JschSshClient; +import org.jclouds.ssh.jsch.config.JschSshClientModule; +import org.testng.annotations.BeforeTest; + +import com.google.common.collect.ImmutableSet; +import com.google.inject.Guice; + +/** + * @author Victor Galkin + */ +public class ComputeBase { + protected ComputeServiceContext context; + protected ComputeService computeService; + + protected String provider = "nova"; + + + protected Map keyPair; + protected Properties overrides; + + @BeforeTest + public void before() throws InterruptedException, ExecutionException, TimeoutException, IOException { + Properties properties = setupProperties(this.getClass()); + setupOverrides(properties); + overrides = properties; + keyPair = setupKeyPair(properties); + initializeContextAndComputeService(properties); + + } + + @SuppressWarnings("unused") + private RetryablePredicate buildSocket() { + SocketOpen socketOpen = Guice.createInjector(getSshModule()).getInstance(SocketOpen.class); + return new RetryablePredicate(socketOpen, 60, 1, TimeUnit.SECONDS); + } + + private JschSshClientModule getSshModule() { + return new JschSshClientModule(); + } + + protected TemplateBuilder getDefaultTemplateBuilder() { + return computeService.templateBuilder().imageId("95").options(getDefaultTemplateOptions()); + } + + private TemplateOptions getDefaultTemplateOptions() { + return TemplateOptions.Builder.blockUntilRunning(false); + //.installPrivateKey(Payloads.newStringPayload(keyPair.get("private"))); + } + + protected NodeMetadata getDefaultNodeImmediately(String group) throws RunNodesException { + for (ComputeMetadata node : computeService.listNodes()) { + if (((NodeMetadata) node).getGroup() != null) + if (((NodeMetadata) node).getGroup().equals(group)) + if (((NodeMetadata) node).getState().equals(NodeState.PENDING) + || ((NodeMetadata) node).getState().equals(NodeState.RUNNING)) return (NodeMetadata) node; + } + return createDefaultNode(group); + } + + protected NodeMetadata createDefaultNode(TemplateOptions options, String group) throws RunNodesException { + return computeService.createNodesInGroup(group, 1, getDefaultTemplateBuilder().options(options).build()) + .iterator().next(); + } + + protected NodeMetadata createDefaultNode(String group) throws RunNodesException { + return createDefaultNode(getDefaultTemplateOptions(), group); + } + + + protected void initializeContextAndComputeService(Properties properties) throws IOException { + if (context != null) + context.close(); + context = new ComputeServiceContextFactory().createContext(provider, ImmutableSet.of( + new SLF4JLoggingModule(), getSshModule()), properties); + computeService = context.getComputeService(); + } + + protected String awaitForPublicAddressAssigned(String nodeId) throws InterruptedException { + while (true) { + Set addresses = computeService.getNodeMetadata(nodeId).getPublicAddresses(); + System.out.println(addresses); + System.out.println(computeService.getNodeMetadata(nodeId).getState()); + if (addresses != null) + if (!addresses.isEmpty()) return addresses.iterator().next(); + Thread.sleep(1000); + } + } + + protected Set getFreshNodes(String group) { + return filter(computeService.listNodesDetailsMatching(all()), and(inGroup(group), not(TERMINATED))); + } + + protected void awaitForSshPort(String address, Credentials credentials) throws URISyntaxException { + IPSocket socket = new IPSocket(address, 22); + + JschSshClient ssh = new JschSshClient( + new BackoffLimitedRetryHandler(), socket, 10000, credentials.identity, null, credentials.credential.getBytes()); + while (true) { + try { + System.out.println("ping: " + socket); + ssh.connect(); + return; + } catch (SshException ignore) { + } + } + } + + protected void assertLocationSameOrChild(Location test, Location expected) { + if (!test.equals(expected)) { + assertEquals(test.getParent().getId(), expected.getId()); + } else { + assertEquals(test, expected); + } + } + +} diff --git a/apis/nova/src/test/java/org/jclouds/openstack/nova/live/compute/ComputeServiceCheck.java b/apis/nova/src/test/java/org/jclouds/openstack/nova/live/compute/ComputeServiceCheck.java new file mode 100644 index 0000000000..3778b9be9f --- /dev/null +++ b/apis/nova/src/test/java/org/jclouds/openstack/nova/live/compute/ComputeServiceCheck.java @@ -0,0 +1,95 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.openstack.nova.live.compute; + +import com.google.common.collect.ImmutableSet; +import org.jclouds.compute.ComputeService; +import org.jclouds.compute.ComputeServiceContext; +import org.jclouds.compute.ComputeServiceContextFactory; +import org.jclouds.compute.RunNodesException; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.domain.Template; +import org.jclouds.compute.options.TemplateOptions; +import org.jclouds.logging.slf4j.config.SLF4JLoggingModule; +import org.jclouds.ssh.jsch.config.JschSshClientModule; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.util.NoSuchElementException; +import java.util.Properties; +import java.util.Set; + +import static org.jclouds.openstack.nova.live.PropertyHelper.setupOverrides; +import static org.jclouds.openstack.nova.live.PropertyHelper.setupProperties; + +/** + * Not intended to be run with maven and does not performs a cleanup after tests + * + * @author Dmitri Babaev + */ +public class ComputeServiceCheck { + private ComputeServiceContextFactory contextFactory; + private ComputeServiceContext context; + + @BeforeTest + public void setupClient() throws IOException { + contextFactory = new ComputeServiceContextFactory(); + Properties properties = setupOverrides(setupProperties(this.getClass())); + context = contextFactory.createContext("nova", + ImmutableSet.of(new JschSshClientModule(), new SLF4JLoggingModule()), properties); + } + + @Test + public void testLists() { + ComputeService cs = context.getComputeService(); + + System.out.println(cs.listImages()); + System.out.println(cs.listHardwareProfiles()); + System.out.println(cs.listAssignableLocations()); + System.out.println(cs.listNodes()); + } + + @Test + public void testCreateServer() throws RunNodesException { + ComputeService cs = context.getComputeService(); + + TemplateOptions options = new TemplateOptions().blockUntilRunning(false); + Template template = cs.templateBuilder().imageId("95").hardwareId("2").options(options).build(); + Set metedata = cs.createNodesInGroup("test", 1, template); + System.out.println(metedata); + } + + @Test(expectedExceptions = NoSuchElementException.class) + public void testDefaultTempateDoesNotSpecifyTheOS() { + ComputeService cs = context.getComputeService(); + Template template = cs.templateBuilder().build(); + System.out.println(template); + } + + @AfterTest + public void after() { + context.close(); + } + + //curl -v -H "X-Auth-User:admin" -H "X-Auth-Key: d744752f-20d3-4d75-979f-f62f16033b07" http://dragon004.hw.griddynamics.net:8774/v1.0/ + //curl -v -H "X-Auth-Token: c97b10659008d5a9ce91462f8c6a5c2c80439762" http://dragon004.hw.griddynamics.net:8774/v1.0/images/detail?format=json + +} diff --git a/apis/nova/src/test/java/org/jclouds/openstack/nova/live/compute/NovaComputeServiceLiveTest.java b/apis/nova/src/test/java/org/jclouds/openstack/nova/live/compute/NovaComputeServiceLiveTest.java new file mode 100644 index 0000000000..b3d6690b27 --- /dev/null +++ b/apis/nova/src/test/java/org/jclouds/openstack/nova/live/compute/NovaComputeServiceLiveTest.java @@ -0,0 +1,454 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.openstack.nova.live.compute; + +import com.google.common.base.Function; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; +import com.google.inject.Module; +import com.jcraft.jsch.JSchException; +import org.jclouds.compute.ComputeServiceContext; +import org.jclouds.compute.ComputeServiceContextFactory; +import org.jclouds.compute.RunNodesException; +import org.jclouds.compute.RunScriptOnNodesException; +import org.jclouds.compute.domain.*; +import org.jclouds.compute.options.TemplateOptions; +import org.jclouds.domain.Credentials; +import org.jclouds.domain.Location; +import org.jclouds.domain.LocationScope; +import org.jclouds.logging.slf4j.config.SLF4JLoggingModule; +import org.jclouds.rest.AuthorizationException; +import org.jclouds.scriptbuilder.domain.Statements; +import org.jclouds.ssh.SshClient; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.*; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +import static com.google.common.base.Predicates.and; +import static com.google.common.base.Predicates.not; +import static com.google.common.collect.Iterables.getOnlyElement; +import static com.google.common.collect.Maps.newLinkedHashMap; +import static com.google.common.collect.Maps.uniqueIndex; +import static com.google.common.collect.Sets.filter; +import static com.google.common.collect.Sets.newTreeSet; +import static org.jclouds.compute.ComputeTestUtils.buildScript; +import static org.jclouds.compute.options.TemplateOptions.Builder.overrideCredentialsWith; +import static org.jclouds.compute.predicates.NodePredicates.*; +import static org.jclouds.compute.util.ComputeServiceUtils.getCores; +import static org.jclouds.compute.util.ComputeServiceUtils.parseGroupFromName; +import static org.testng.Assert.*; + +/** + * Generally disabled, as it incurs higher fees. + * + * @author Adrian Cole + */ +@Test(groups = "novalive", enabled = true, sequential = true) +public class NovaComputeServiceLiveTest extends ComputeBase { + + private static String group = "compute service test group"; + + + protected void checkNodes(Iterable nodes, String tag) throws IOException { + for (NodeMetadata node : nodes) { + assertNotNull(node.getProviderId()); + assertNotNull(node.getGroup()); + assertEquals(node.getGroup(), group); + //assertEquals(node.getState(), NodeState.RUNNING); + Credentials fromStore = context.getCredentialStore().get("node#" + node.getId()); + assertEquals(fromStore, node.getCredentials()); + assert node.getPublicAddresses().size() >= 1 || node.getPrivateAddresses().size() >= 1 : "no ips in" + node; +// assertNotNull(node.getCredentials()); +// if (node.getCredentials().identity != null) { +// assertNotNull(node.getCredentials().identity); +// assertNotNull(node.getCredentials().credential); +// doCheckJavaIsInstalledViaSsh(node); +// } + assertEquals(node.getLocation().getScope(), LocationScope.HOST); + } + } + + + @BeforeTest + @Override + public void before() throws IOException, ExecutionException, TimeoutException, InterruptedException { + super.before(); + computeService.destroyNodesMatching(inGroup(group)); + } + + @Test(enabled = true, expectedExceptions = AuthorizationException.class, timeOut = 60000) + public void testCorrectAuthException() throws Exception { + Properties properties = new Properties(); + properties.putAll(overrides); + properties.remove(provider + ".identity"); + ComputeServiceContext context = null; + try { + context = new ComputeServiceContextFactory().createContext(provider, "MOMMA", "MIA", ImmutableSet + .of(new SLF4JLoggingModule()), properties); + context.getComputeService().listNodes(); + } finally { + if (context != null) + context.close(); + } + } + + @Test(timeOut = 60000) + public void testImagesCache() throws Exception { + computeService.listImages(); + long time = System.currentTimeMillis(); + computeService.listImages(); + long duration = System.currentTimeMillis() - time; + assert duration < 1000 : String.format("%dms to get images", duration); + } + + @Test(enabled = true, expectedExceptions = NoSuchElementException.class, timeOut = 60000) + public void testCorrectExceptionRunningNodesNotFound() throws Exception { + computeService.runScriptOnNodesMatching(runningInGroup("zebras-are-awesome"), buildScript(new OperatingSystem.Builder() + .family(OsFamily.UBUNTU).description("ffoo").build())); + } + + @Test(expectedExceptions = JSchException.class, expectedExceptionsMessageRegExp = "Auth fail", timeOut = 60000) + void testScriptExecutionWithWrongCredentials() throws Throwable, RunScriptOnNodesException, URISyntaxException, InterruptedException { + NodeMetadata node = getDefaultNodeImmediately(group); + String address = awaitForPublicAddressAssigned(node.getId()); + awaitForSshPort(address, new Credentials("root", keyPair.get("private"))); + OperatingSystem os = node.getOperatingSystem(); + try { + @SuppressWarnings("unused") + Map responses = runJavaInstallationScriptWithCreds(group, os, new Credentials( + "root", "romeo")); + } catch (RunScriptOnNodesException e) { + throw e.getNodeErrors().values().iterator().next().getCause(); + } + } + + @Test(timeOut = 60000) + public void testScriptExecutionAfterBootWithBasicTemplate() throws InterruptedException, RunNodesException, RunScriptOnNodesException, URISyntaxException, IOException { + + NodeMetadata node = getDefaultNodeImmediately(group); + String address = awaitForPublicAddressAssigned(node.getId()); + awaitForSshPort(address, new Credentials("root", keyPair.get("private"))); + for (Map.Entry response : computeService.runScriptOnNodesMatching( + runningInGroup(group), Statements.exec("echo hello"), + overrideCredentialsWith(new Credentials("root", keyPair.get("private"))).wrapInInitScript(false).runAsRoot(false)).entrySet()) + assert response.getValue().getOutput().trim().equals("hello") : response.getKey() + ": " + + response.getValue(); + + //TODO runJavaInstallationScriptWithCreds(group, os, new Credentials("root", keyPair.get("private"))); + //TODO no response? if os is null (ZYPPER) + + checkNodes(Sets.newHashSet(node), group); + + @SuppressWarnings("unused") + Credentials good = node.getCredentials(); + //TODO check good is being private key .overrideCredentialsWith + //TODO test for .blockOnPort + } + + @Test(timeOut = 60000) + public void testTemplateMatch() throws Exception { + Template template = buildTemplate(getDefaultTemplateBuilder()); + Template toMatch = computeService.templateBuilder().imageId(template.getImage().getId()).build(); + assertEquals(toMatch.getImage(), template.getImage()); + } + +// protected void checkHttpGet(NodeMetadata node) { +// ComputeTestUtils.checkHttpGet(context.utils().http(), node, 8080); +// } + + @Test(timeOut = 60000) + public void testCreateTwoNodesWithRunScript() throws Exception { + computeService.destroyNodesMatching(inGroup(group)); + Template template = getDefaultTemplateBuilder().options(TemplateOptions.Builder.blockUntilRunning(true)).build(); + SortedSet nodes = newTreeSet(computeService.createNodesInGroup(group, 2, template)); + + assertEquals(nodes.size(), 2); + checkNodes(nodes, group); + NodeMetadata node1 = nodes.first(); + NodeMetadata node2 = nodes.last(); + // credentials aren't always the same + // assertEquals(node1.getCredentials(), node2.getCredentials()); + + assertLocationSameOrChild(node1.getLocation(), template.getLocation()); + assertLocationSameOrChild(node2.getLocation(), template.getLocation()); + assertEquals(node1.getImageId(), template.getImage().getId()); + assertEquals(node2.getImageId(), template.getImage().getId()); +// checkOsMatchesTemplate(node1); +// checkOsMatchesTemplate(node2); + //TODO add with script; + } + +// protected void checkOsMatchesTemplate(NodeMetadata node) { +// if (node.getOperatingSystem() != null) +// assert node.getOperatingSystem().getFamily().equals(getDefaultTemplateBuilder().build().getImage().getOperatingSystem().getFamily()) : String +// .format("expecting family %s but got %s", getDefaultTemplateBuilder().build().getImage().getOperatingSystem().getFamily(), node +// .getOperatingSystem()); +// } + + + @Test(timeOut = 60000) + public void testCreateAnotherNodeWithNewContextToEnsureSharedMemIsntRequired() throws Exception { + getDefaultNodeImmediately(group); + initializeContextAndComputeService(overrides); + + NodeMetadata node = createDefaultNode(TemplateOptions.Builder.blockUntilRunning(true), group); + checkNodes(Sets.newHashSet(node), group); + assertLocationSameOrChild(node.getLocation(), getDefaultTemplateBuilder().build().getLocation()); +// checkOsMatchesTemplate(node); + } + + @Test(timeOut = 60000) + public void testCredentialsCache() throws Exception { + LinkedList nodes = new LinkedList(); + nodes.add(getDefaultNodeImmediately(group)); + initializeContextAndComputeService(overrides); + nodes.add(createDefaultNode(group)); + initializeContextAndComputeService(overrides); + for (NodeMetadata node : nodes) + assert (context.getCredentialStore().get("node#" + node.getId()) != null) : "credentials for " + node.getId(); + } + + protected Map runJavaInstallationScriptWithCreds(final String group, OperatingSystem os, + Credentials creds) throws RunScriptOnNodesException { + return computeService.runScriptOnNodesMatching(runningInGroup(group), buildScript(os), overrideCredentialsWith(creds) + .nameTask("runJavaInstallationScriptWithCreds")); + + } + + + protected Template buildTemplate(TemplateBuilder templateBuilder) { + return templateBuilder.build(); + } + + @Test(timeOut = 60000) + public void testGetNodeMetadata() throws Exception { + Set nodes = Sets.newHashSet(getDefaultNodeImmediately(group)); + awaitForPublicAddressAssigned(nodes.iterator().next().getId()); + Map metadataMap = newLinkedHashMap(uniqueIndex(filter(computeService + .listNodesDetailsMatching(all()), and(inGroup(group), not(TERMINATED))), + new Function() { + + @Override + public String apply(NodeMetadata from) { + return from.getId(); + } + + })); + for (NodeMetadata node : nodes) { + metadataMap.remove(node.getId()); + NodeMetadata nodeMetadata = computeService.getNodeMetadata(node.getId()); + assertEquals(parseGroupFromName(nodeMetadata.getName()), group); + assertEquals(nodeMetadata.getProviderId(), node.getProviderId()); + assertEquals(nodeMetadata.getGroup(), node.getGroup()); + assertLocationSameOrChild(nodeMetadata.getLocation(), getDefaultTemplateBuilder().build().getLocation()); + assertEquals(nodeMetadata.getImageId(), getDefaultTemplateBuilder().build().getImage().getId()); +// checkOsMatchesTemplate(metadata); + assertEquals(nodeMetadata.getState(), NodeState.RUNNING); + // due to DHCP the addresses can actually change in-between runs. + assertTrue(nodeMetadata.getPrivateAddresses().size() > 0); + assertTrue(nodeMetadata.getPublicAddresses().size() > 0); + } + assertNodeZero(metadataMap.values(), nodes); + } + + + protected void assertNodeZero(Collection metadataSet, Set nodes) { + assert metadataSet.size() == 0 : String.format("nodes left in set: [%s] which didn't match set: [%s]", + metadataSet, nodes); + } + + + @Test(timeOut = 60000) + public void testListNodes() throws Exception { + for (ComputeMetadata node : computeService.listNodes()) { + assert node.getProviderId() != null; + assert node.getLocation() != null; + assertEquals(node.getType(), ComputeType.NODE); + } + } + + @Test(timeOut = 60000) + public void testGetNodesWithDetails() throws Exception { + for (NodeMetadata node : computeService.listNodesDetailsMatching(all())) { + assert node.getProviderId() != null : node; + assert node.getLocation() != null : node; + assertEquals(node.getType(), ComputeType.NODE); + assert node.getProviderId() != null : node; + // nullable + // assert nodeMetadata.getImage() != null : node; + // user specified name is not always supported + // assert nodeMetadata.getName().parseGroupFromName() != null : nodeMetadata; + + if (node.getState() == NodeState.RUNNING) { + assert node.getPublicAddresses() != null : node; + assert node.getPublicAddresses().size() > 0 || node.getPrivateAddresses().size() > 0 : node; + assertNotNull(node.getPrivateAddresses()); + } + } + } + + @Test(timeOut = 60000) + public void testDestroyNodes() { + int toDestroy = getFreshNodes(group).size(); + Set destroyed = computeService.destroyNodesMatching(inGroup(group)); + assertEquals(toDestroy, destroyed.size()); + for (NodeMetadata node : filter(computeService.listNodesDetailsMatching(all()), inGroup(group))) { + assert node.getState() == NodeState.TERMINATED : node; + assertEquals(context.getCredentialStore().get("node#" + node.getId()), null); + } + } + + + @Test(timeOut = 60000) + public void testCreateAndRunService() throws Exception { + @SuppressWarnings("unused") + NodeMetadata node = getDefaultNodeImmediately(group); + //TODO .inboundPorts + //checkHttpGet(node); + } + + @Test(timeOut = 60000) + public void testTemplateOptions() throws Exception { + TemplateOptions options = new TemplateOptions().withMetadata(); + Template t = getDefaultTemplateBuilder().smallest().options(options).build(); + assert t.getOptions().isIncludeMetadata() : "The metadata option should be 'true' " + "for the created template"; + } + + public void testListImages() throws Exception { + for (Image image : computeService.listImages()) { + assert image.getProviderId() != null : image; + // image.getLocationId() can be null, if it is a location-free image + assertEquals(image.getType(), ComputeType.IMAGE); + } + } + + @Test(timeOut = 60000) + public void testGetAssignableLocations() throws Exception { + for (Location location : computeService.listAssignableLocations()) { + System.err.printf("location %s%n", location); + assert location.getId() != null : location; + assert location != location.getParent() : location; + assert location.getScope() != null : location; + switch (location.getScope()) { + case PROVIDER: + assertProvider(location); + break; + case REGION: + assertProvider(location.getParent()); + break; + case ZONE: + Location provider = location.getParent().getParent(); + // zone can be a direct descendant of provider + if (provider == null) + provider = location.getParent(); + assertProvider(provider); + break; + case HOST: + Location provider2 = location.getParent().getParent().getParent(); + // zone can be a direct descendant of provider + if (provider2 == null) + provider2 = location.getParent().getParent(); + assertProvider(provider2); + break; + } + } + } + + public void testOptionToNotBlock() throws Exception { + //TODO no inbound ports + //TemplateOptions options = computeService.templateOptions().blockUntilRunning(false).inboundPorts(); + long time = System.currentTimeMillis(); + NodeMetadata node = getOnlyElement(computeService.createNodesInGroup(group, 1, getDefaultTemplateBuilder().build())); + assert node.getState() != NodeState.RUNNING; + long duration = System.currentTimeMillis() - time; + assert duration < 30 * 1000 : "duration longer than 30 seconds!: " + duration / 1000; + } + + private void assertProvider(Location provider) { + assertEquals(provider.getScope(), LocationScope.PROVIDER); + assertEquals(provider.getParent(), null); + } + + @Test(timeOut = 60000) + public void testListHardwareProfiles() throws Exception { + for (Hardware hardware : computeService.listHardwareProfiles()) { + assert hardware.getProviderId() != null; + assert getCores(hardware) > 0; + assert hardware.getVolumes().size() >= 0; + assert hardware.getRam() > 0; + assertEquals(hardware.getType(), ComputeType.HARDWARE); + } + } + + + @Test(timeOut = 60000) + public void testCompareSizes() throws Exception { + TemplateBuilder templateBuilder = getDefaultTemplateBuilder(); + + Hardware defaultSize = templateBuilder.build().getHardware(); + + Hardware smallest = templateBuilder.smallest().build().getHardware(); + Hardware fastest = templateBuilder.fastest().build().getHardware(); + Hardware biggest = templateBuilder.biggest().build().getHardware(); + + System.out.printf("smallest %s%n", smallest); + System.out.printf("fastest %s%n", fastest); + System.out.printf("biggest %s%n", biggest); + + assertEquals(defaultSize, smallest); + + assert getCores(smallest) <= getCores(fastest); + assert getCores(biggest) <= getCores(fastest); + + assert biggest.getRam() >= fastest.getRam(); + assert biggest.getRam() >= smallest.getRam(); + + assert getCores(fastest) >= getCores(biggest); + assert getCores(fastest) >= getCores(smallest); + } + + + protected void doCheckJavaIsInstalledViaSsh(NodeMetadata node) throws IOException { + + SshClient ssh = context.utils().sshForNode().apply(node); + try { + ssh.connect(); + ExecResponse hello = ssh.exec("echo hello"); + assertEquals(hello.getOutput().trim(), "hello"); + ExecResponse exec = ssh.exec("java -version"); + assert exec.getError().indexOf("1.6") != -1 || exec.getOutput().indexOf("1.6") != -1 : exec + "\n" + + ssh.exec("cat /tmp/bootstrap/stdout.log /tmp/bootstrap/stderr.log"); + } finally { + if (ssh != null) + ssh.disconnect(); + } + } + + @AfterTest + protected void cleanup() throws InterruptedException, ExecutionException, TimeoutException { + computeService.destroyNodesMatching(inGroup(group)); + context.close(); + } +} diff --git a/apis/nova/src/test/java/org/jclouds/openstack/nova/live/compute/ServiceActionsLiveTest.java b/apis/nova/src/test/java/org/jclouds/openstack/nova/live/compute/ServiceActionsLiveTest.java new file mode 100644 index 0000000000..7e9f7f8417 --- /dev/null +++ b/apis/nova/src/test/java/org/jclouds/openstack/nova/live/compute/ServiceActionsLiveTest.java @@ -0,0 +1,88 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.openstack.nova.live.compute; + +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.domain.NodeState; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +import static org.jclouds.compute.predicates.NodePredicates.inGroup; + +/** + * @author Victor Galkin + */ +public class ServiceActionsLiveTest extends ComputeBase { + + static private String group = "ServiceActionsLiveTest"; + + @BeforeTest + @Override + public void before() throws IOException, ExecutionException, TimeoutException, InterruptedException { + super.before(); + computeService.destroyNodesMatching(inGroup(group)); + } + + @Test + public void testReboot() throws Exception { + getDefaultNodeImmediately(group); + computeService.rebootNodesMatching(inGroup(group));// TODO test + Thread.sleep(5000); + // // validation + //testGetNodeMetadata(); + } + + @Test + public void testSuspendResume() throws Exception { + getDefaultNodeImmediately(group); + computeService.suspendNodesMatching(inGroup(group)); + + Set stoppedNodes = getFreshNodes(group); + + assert Iterables.all(stoppedNodes, new Predicate() { + + @Override + public boolean apply(NodeMetadata input) { + boolean returnVal = input.getState() == NodeState.SUSPENDED; + if (!returnVal) + System.err.printf("warning: node %s in state %s%n", input.getId(), input.getState()); + return returnVal; + } + + }) : stoppedNodes; + + computeService.resumeNodesMatching(inGroup(group)); + //testGetNodeMetadata(); + } + + @AfterTest + protected void cleanup() throws InterruptedException, ExecutionException, TimeoutException { + computeService.destroyNodesMatching(inGroup(group)); + context.close(); + } + +} diff --git a/apis/nova/src/test/java/org/jclouds/openstack/nova/live/novaclient/ClientBase.java b/apis/nova/src/test/java/org/jclouds/openstack/nova/live/novaclient/ClientBase.java new file mode 100644 index 0000000000..d97177b3bf --- /dev/null +++ b/apis/nova/src/test/java/org/jclouds/openstack/nova/live/novaclient/ClientBase.java @@ -0,0 +1,115 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.openstack.nova.live.novaclient; + +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.inject.Injector; +import com.google.inject.Module; +import org.jclouds.logging.slf4j.config.SLF4JLoggingModule; +import org.jclouds.net.IPSocket; +import org.jclouds.openstack.nova.NovaClient; +import org.jclouds.openstack.nova.domain.Image; +import org.jclouds.openstack.nova.domain.Server; +import org.jclouds.predicates.RetryablePredicate; +import org.jclouds.predicates.SocketOpen; +import org.jclouds.rest.RestContextFactory; +import org.jclouds.ssh.SshClient; +import org.jclouds.ssh.jsch.config.JschSshClientModule; +import org.testng.annotations.BeforeTest; + +import java.io.IOException; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.TimeUnit; + +import static org.jclouds.openstack.nova.live.PropertyHelper.*; +import static org.jclouds.openstack.nova.options.CreateServerOptions.Builder.withFile; + +/** + * @author Victor Galkin + */ +public class ClientBase { + protected int testImageId = 95; + protected NovaClient client; + protected SshClient.Factory sshFactory; + @SuppressWarnings("unused") + private Predicate socketTester; + protected String provider = "nova"; + protected String serverPrefix = System.getProperty("user.name") + ".cs"; + protected Map keyPair; + Map metadata = ImmutableMap.of("jclouds", "rackspace"); + + @BeforeTest + public void before() throws IOException { + Properties properties = setupOverrides(setupProperties(this.getClass())); + + Injector injector = new RestContextFactory().createContextBuilder(provider, + ImmutableSet.of(new SLF4JLoggingModule(), new JschSshClientModule()), properties) + .buildInjector(); + + client = injector.getInstance(NovaClient.class); + + sshFactory = injector.getInstance(SshClient.Factory.class); + SocketOpen socketOpen = injector.getInstance(SocketOpen.class); + socketTester = new RetryablePredicate(socketOpen, 120, 1, TimeUnit.SECONDS); + injector.injectMembers(socketOpen); // add logger + + keyPair = setupKeyPair(properties); + } + + protected Server getDefaultServerImmediately() { + String defaultName = serverPrefix + "default"; + for (Server server : client.listServers()) { + if (server.getName().equals(defaultName)) + return server; + } + return createDefaultServer(defaultName); + } + + private Server createDefaultServer(String serverName) { + String imageRef = client.getImage(testImageId).getURI().toASCIIString(); + String flavorRef = client.getFlavor(1).getURI().toASCIIString(); + + return client.createServer(serverName, imageRef, flavorRef, withFile("/etc/jclouds.txt", + "rackspace".getBytes()).withMetadata(metadata)); + } + + protected Image getDefaultImageImmediately(Server server) { + String defaultName = "hoofie"; + for (Image image : client.listImages()) { + if (image.getName() != null) + if (image.getName().equals(defaultName)) + return image; + } + return createDefaultImage("hoofie", server); + } + + private Image createDefaultImage(String name, Server server) { + return client.createImageFromServer("hoofie", server.getId()); + } + + protected void waitServerDeleted(int serverId) throws InterruptedException { + while (null != client.getServer(serverId)) { + System.out.println("Await deleted server" + serverId); + Thread.sleep(1000); + } + } +} diff --git a/apis/nova/src/test/java/org/jclouds/openstack/nova/live/novaclient/DeleteServersInVariousStatesLiveTest.java b/apis/nova/src/test/java/org/jclouds/openstack/nova/live/novaclient/DeleteServersInVariousStatesLiveTest.java new file mode 100644 index 0000000000..e6b5c67e29 --- /dev/null +++ b/apis/nova/src/test/java/org/jclouds/openstack/nova/live/novaclient/DeleteServersInVariousStatesLiveTest.java @@ -0,0 +1,67 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.openstack.nova.live.novaclient; + +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableMap; +import org.jclouds.net.IPSocket; +import org.jclouds.openstack.nova.NovaClient; +import org.jclouds.openstack.nova.domain.Server; +import org.jclouds.openstack.nova.domain.ServerStatus; +import org.jclouds.ssh.SshClient; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.Test; + +import java.util.Map; + +/** + * @author Victor Galkin + */ +@Test(groups = "live", sequential = true) +public class DeleteServersInVariousStatesLiveTest { + + protected NovaClient client; + protected SshClient.Factory sshFactory; + @SuppressWarnings("unused") + private Predicate socketTester; + protected String provider = "nova"; + + Map metadata = ImmutableMap.of("jclouds", "rackspace"); + Server server = null; + + @AfterMethod + public void after() { + if (server != null) client.deleteServer(server.getId()); + } + + @Test(enabled = true) + public void testDeleteAfterCreate() throws Exception { + + } + + @SuppressWarnings("unused") + private void blockUntilServerActive(int serverId) throws InterruptedException { + Server currentDetails; + for (currentDetails = client.getServer(serverId); currentDetails.getStatus() != ServerStatus.ACTIVE; currentDetails = client + .getServer(serverId)) { + System.out.printf("blocking on status active%n%s%n", currentDetails); + Thread.sleep(5 * 1000); + } + } +} diff --git a/apis/nova/src/test/java/org/jclouds/openstack/nova/live/novaclient/NovaClientLiveTest.java b/apis/nova/src/test/java/org/jclouds/openstack/nova/live/novaclient/NovaClientLiveTest.java new file mode 100644 index 0000000000..be0ba6af9f --- /dev/null +++ b/apis/nova/src/test/java/org/jclouds/openstack/nova/live/novaclient/NovaClientLiveTest.java @@ -0,0 +1,382 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.openstack.nova.live.novaclient; + +import com.google.common.collect.Iterables; +import org.jclouds.domain.Credentials; +import org.jclouds.http.HttpResponseException; +import org.jclouds.io.Payload; +import org.jclouds.net.IPSocket; +import org.jclouds.openstack.nova.domain.*; +import org.jclouds.openstack.nova.options.RebuildServerOptions; +import org.jclouds.ssh.SshClient; +import org.jclouds.util.Strings2; +import org.testng.annotations.AfterTest; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.util.Set; + +import static org.jclouds.openstack.nova.options.ListOptions.Builder.withDetails; +import static org.testng.Assert.*; + +/** + * Tests behavior of {@code NovaClient} + * + * @author Adrian Cole + */ +// disabled [Web Hosting #129069 +@Test(groups = "live", sequential = true) +public class NovaClientLiveTest extends ClientBase { + + + @Test + public void testListServers() throws Exception { + Set response = client.listServers(); + assert null != response; + long initialContainerCount = response.size(); + assertTrue(initialContainerCount >= 0); + } + + @Test + public void testListServersDetail() throws Exception { + Set response = client.listServers(withDetails()); + assert null != response; + long initialContainerCount = response.size(); + assertTrue(initialContainerCount >= 0); + } + + @Test + public void testListImages() throws Exception { + Set response = client.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 { + Set response = client.listImages(withDetails()); + assert null != response; + long imageCount = response.size(); + assertTrue(imageCount >= 0); + for (Image image : response) { + assertTrue(image.getId() >= 1); + assert null != image.getName() : image; + assert null != image.getStatus() : image; + } + } + + @Test + public void testGetImagesDetail() throws Exception { + Set response = client.listImages(withDetails()); + assert null != response; + long imageCount = response.size(); + assertTrue(imageCount >= 0); + for (Image image : response) { + try { + Image newDetails = client.getImage(image.getId()); + assertEquals(image, newDetails); + } catch (HttpResponseException e) {// Ticket #9867 + if (e.getResponse().getStatusCode() != 400) + throw e; + } + } + } + + @Test + public void testGetImageDetailsNotFound() throws Exception { + assert client.getImage(12312987) == null; + } + + @Test + public void testGetServerDetailsNotFound() throws Exception { + assert client.getServer(12312987) == null; + } + + @Test + public void testGetServersDetail() throws Exception { + Set response = client.listServers(withDetails()); + assert null != response; + assertTrue(response.size() >= 0); + for (Server server : response) { + Server newDetails = client.getServer(server.getId()); + System.out.println("===="); + + System.out.println(server); + System.out.println(newDetails); + System.out.println("===="); + } + for (Server server : response) { + Server newDetails = client.getServer(server.getId()); + assertEquals(server, newDetails); + } + } + + @Test + public void testListFlavors() throws Exception { + Set response = client.listFlavors(); + assert null != response; + long flavorCount = response.size(); + assertTrue(flavorCount >= 1); + for (Flavor flavor : response) { + assertTrue(flavor.getId() >= 0); + assert null != flavor.getName() : flavor; + } + + } + + @Test + public void testListFlavorsDetail() throws Exception { + Set response = client.listFlavors(withDetails()); + assert null != response; + long flavorCount = response.size(); + assertTrue(flavorCount >= 0); + for (Flavor flavor : response) { + assertTrue(flavor.getId() >= 1); + assert null != flavor.getName() : flavor; + assert null != flavor.getDisk() : flavor; + assert null != flavor.getRam() : flavor; + } + } + + @Test + public void testGetFlavorsDetail() throws Exception { + Set response = client.listFlavors(withDetails()); + assert null != response; + long flavorCount = response.size(); + assertTrue(flavorCount >= 0); + for (Flavor flavor : response) { + Flavor newDetails = client.getFlavor(flavor.getId()); + assertEquals(flavor, newDetails); + } + } + + @Test + public void testGetFlavorDetailsNotFound() throws Exception { + assert client.getFlavor(12312987) == null; + } + + + @Test(enabled = true) + public void testCreateServer() throws Exception { + Server server = getDefaultServerImmediately(); + assertNotNull(server.getAdminPass()); + assertEquals(server.getStatus(), ServerStatus.BUILD); + int serverId = server.getId(); + @SuppressWarnings("unused") + String adminPass = server.getAdminPass(); + blockUntilServerActive(serverId); + blockUntilPublicAddress(serverId); + client.getServer(serverId).getAddresses().getPublicAddresses().iterator().next().getAddress(); + } + + private void blockUntilPublicAddress(int serverId) throws InterruptedException { + while (client.getServer(serverId).getAddresses().getPublicAddresses().isEmpty()) { + System.out.println("Awaiting public address"); + Thread.sleep(1000); + } + } + + private void blockUntilServerActive(int serverId) throws InterruptedException { + Server currentDetails; + for (currentDetails = client.getServer(serverId); currentDetails.getStatus() != ServerStatus.ACTIVE; currentDetails = client + .getServer(serverId)) { + System.out.printf("blocking on status active%n%s%n", currentDetails); + Thread.sleep(5 * 1000); + } + } + + private void blockUntilServerVerifyResize(int serverId) throws InterruptedException { + Server currentDetails; + for (currentDetails = client.getServer(serverId); currentDetails.getStatus() != ServerStatus.VERIFY_RESIZE; currentDetails = client + .getServer(serverId)) { + System.out.printf("blocking on status verify resize%n%s%n", currentDetails); + Thread.sleep(5 * 1000); + } + } + + private void blockUntilImageActive(int createdImageId) throws InterruptedException { + Image currentDetails; + for (currentDetails = client.getImage(createdImageId); currentDetails.getStatus() != ImageStatus.ACTIVE; currentDetails = client + .getImage(createdImageId)) { + System.out.printf("blocking on status active%n%s%n", currentDetails); + Thread.sleep(5 * 1000); + } + } + + @Test(enabled = true, timeOut = 300000) + public void testServerDetails() throws Exception { + Server server = getDefaultServerImmediately(); + assertNotNull(server.getHostId(), "Host id: "); + assertEquals(server.getStatus(), ServerStatus.ACTIVE); + assertNotNull(server.getAddresses()); + // check metadata + assertEquals(server.getMetadata(), metadata); + assertTrue(server.getImageRef().endsWith(String.valueOf(testImageId))); + // listAddresses tests.. + assertEquals(client.getAddresses(server.getId()), server.getAddresses()); + assertEquals(server.getAddresses().getPublicAddresses().size(), 1); + assertEquals(client.listPublicAddresses(server.getId()), server.getAddresses().getPublicAddresses()); + assertEquals(server.getAddresses().getPrivateAddresses().size(), 1); + assertEquals(client.listPrivateAddresses(server.getId()), server.getAddresses().getPrivateAddresses()); + assertPassword(server, server.getAdminPass()); + assertTrue(server.getFlavorRef().endsWith("1")); + assert server.getProgress() >= 0 : "newDetails.getProgress()" + server.getProgress(); + } + + + private void assertPassword(Server server, String pass) throws IOException { + IPSocket socket = new IPSocket(Iterables.get(server.getAddresses().getPublicAddresses(), 0).getAddress(), 22); + //socketTester.apply(socket); + + SshClient client = sshFactory.create(socket, new Credentials("root", keyPair.get("private"))); + try { + client.connect(); + Payload etcPasswd = client.get("/etc/jclouds.txt"); + String etcPasswdContents = Strings2.toStringAndClose(etcPasswd.getInput()); + assertEquals("rackspace", etcPasswdContents.trim()); + } finally { + if (client != null) + client.disconnect(); + } + } + + @Test(enabled = true, timeOut = 5 * 60 * 1000) + public void testRenameServer() throws Exception { + Server server = getDefaultServerImmediately(); + int serverId = server.getId(); + String oldName = server.getName(); + client.renameServer(serverId, oldName + "new"); + blockUntilServerActive(serverId); + assertEquals(oldName + "new", client.getServer(serverId).getName()); + } + + @Test(enabled = true, timeOut = 5 * 60 * 1000) + public void testChangePassword() throws Exception { + int serverId = getDefaultServerImmediately().getId(); + blockUntilServerActive(serverId); + client.changeAdminPass(serverId, "elmo"); + assertPassword(client.getServer(serverId), "elmo"); + + } + + @Test(enabled = true, timeOut = 10 * 600 * 1000) + public void testCreateImage() throws Exception { + Server server = getDefaultServerImmediately(); + Image image = getDefaultImageImmediately(server); + blockUntilImageActive(image.getId()); + assertEquals("hoofie", image.getName()); + assertEquals(image.getServerRef(), ""); + } + + + @Test(enabled = true, timeOut = 10 * 60 * 1000) + public void testRebuildServer() throws Exception { + Server server = getDefaultServerImmediately(); + Image image = getDefaultImageImmediately(server); + client.rebuildServer(server.getId(), new RebuildServerOptions().withImage(String.valueOf(image.getId()))); + blockUntilServerActive(server.getId()); + // issue Web Hosting #119580 createdImageId comes back incorrect after rebuild + assertEquals(image.getURI(), client.getServer(server.getId()).getImageRef()); + } + + @Test(enabled = true, timeOut = 10 * 60 * 1000) + public void testRebootHard() throws Exception { + Server server = getDefaultServerImmediately(); + client.rebootServer(server.getId(), RebootType.HARD); + blockUntilServerActive(server.getId()); + //TODO check + } + + @Test(enabled = true, timeOut = 10 * 60 * 1000) + public void testRebootSoft() throws Exception { + Server server = getDefaultServerImmediately(); + client.rebootServer(server.getId(), RebootType.SOFT); + blockUntilServerActive(server.getId()); + //TODO check + } + + @Test(enabled = false, timeOut = 60000, dependsOnMethods = "testRebootSoft") + public void testRevertResize() throws Exception { + Server server = getDefaultServerImmediately(); + int serverId = server.getId(); + client.resizeServer(serverId, 2); + blockUntilServerVerifyResize(serverId); + client.revertResizeServer(serverId); + blockUntilServerActive(serverId); + assertEquals(1, client.getServer(serverId).getFlavorRef()); + } + + @Test(enabled = false, timeOut = 10 * 60 * 1000) + public void testConfirmResize() throws Exception { + Server server = getDefaultServerImmediately(); + int serverId = server.getId(); + client.resizeServer(serverId, 2); + blockUntilServerVerifyResize(serverId); + client.confirmResizeServer(serverId); + blockUntilServerActive(serverId); + assertEquals(2, client.getServer(serverId).getFlavorRef()); + } + + @Test(enabled = true, timeOut = 60000) + void deleteServer2() throws Exception { + Server server = getDefaultServerImmediately(); + int serverId = server.getId(); + client.deleteServer(serverId); + waitServerDeleted(serverId); + } + + @Test(enabled = true, timeOut = 60000) + void testDeleteImage() throws Exception { + Image image = getDefaultImageImmediately(getDefaultServerImmediately()); + client.deleteImage(image.getId()); + assert client.getImage(image.getId()) == null; + } + + @Test(enabled = true, timeOut = 60000) + void deleteServer1() throws Exception { + Server server = getDefaultServerImmediately(); + int serverId = server.getId(); + client.deleteServer(serverId); + waitServerDeleted(serverId); + } + + @Test + public void testDeleteAllCreatedServers() { + for (Server server : client.listServers()) { + if (server.getName().startsWith(serverPrefix)) { + client.deleteServer(server.getId()); + System.out.println("Deleted server: " + server); + } + } + } + + @AfterTest + void deleteServersOnEnd() { + testDeleteAllCreatedServers(); + } + +} diff --git a/apis/nova/src/test/java/org/jclouds/openstack/nova/live/novaclient/ServerCreateLiveTest.java b/apis/nova/src/test/java/org/jclouds/openstack/nova/live/novaclient/ServerCreateLiveTest.java new file mode 100644 index 0000000000..9f3fb72947 --- /dev/null +++ b/apis/nova/src/test/java/org/jclouds/openstack/nova/live/novaclient/ServerCreateLiveTest.java @@ -0,0 +1,133 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.openstack.nova.live.novaclient; + +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.inject.Injector; +import com.google.inject.Module; +import org.jclouds.http.HttpResponseException; +import org.jclouds.logging.slf4j.config.SLF4JLoggingModule; +import org.jclouds.net.IPSocket; +import org.jclouds.openstack.nova.NovaClient; +import org.jclouds.openstack.nova.domain.Server; +import org.jclouds.openstack.nova.domain.ServerStatus; +import org.jclouds.predicates.RetryablePredicate; +import org.jclouds.predicates.SocketOpen; +import org.jclouds.rest.RestContextFactory; +import org.jclouds.ssh.SshClient; +import org.jclouds.ssh.jsch.config.JschSshClientModule; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.TimeUnit; + +import static org.jclouds.openstack.nova.live.PropertyHelper.*; +import static org.jclouds.openstack.nova.options.CreateServerOptions.Builder.withFile; + +/** + * @author Victor Galkin + */ +@Test(groups = "live", sequential = true) +public class ServerCreateLiveTest { + + protected NovaClient client; + protected SshClient.Factory sshFactory; + @SuppressWarnings("unused") + private Predicate socketTester; + protected String provider = "nova"; + + Map metadata = ImmutableMap.of("jclouds", "rackspace"); + Server server = null; + Map keyPair; + + @BeforeTest + public void setupClient() throws IOException { + Properties properties = setupOverrides(setupProperties(this.getClass())); + + Injector injector = new RestContextFactory().createContextBuilder(provider, + ImmutableSet.of(new SLF4JLoggingModule(), new JschSshClientModule()), properties) + .buildInjector(); + + client = injector.getInstance(NovaClient.class); + + sshFactory = injector.getInstance(SshClient.Factory.class); + SocketOpen socketOpen = injector.getInstance(SocketOpen.class); + socketTester = new RetryablePredicate(socketOpen, 120, 1, TimeUnit.SECONDS); + injector.injectMembers(socketOpen); // add logger + + keyPair = setupKeyPair(properties); + } + + @Test(expectedExceptions = HttpResponseException.class, expectedExceptionsMessageRegExp = ".*Internal Server Error.*") + public void testCreateServerWithUnknownImage() throws Exception { + try { + server = client.createServer("serverName", String.valueOf(88888888), "1", withFile("/etc/jclouds.txt", + "rackspace".getBytes()).withMetadata(metadata)); + } catch (HttpResponseException e) { + throw e; + } + } + + @Test(expectedExceptions = HttpResponseException.class, expectedExceptionsMessageRegExp = ".*Internal Server Error.*") + public void testCreateServerWithUnknownFlavor() throws Exception { + try { + server = client.createServer("serverName", String.valueOf(13), "88888888", withFile("/etc/jclouds.txt", + "rackspace".getBytes()).withMetadata(metadata)); + } catch (HttpResponseException e) { + throw e; + } + } + + @AfterMethod + public void after() { + if (server != null) client.deleteServer(server.getId()); + } + + @Test(enabled = true) + public void testCreateServer() throws Exception { +// String imageRef = client.getImage(13).getURI().toASCIIString(); +// String flavorRef = client.getFlavor(1).getURI().toASCIIString(); +// String serverName = serverPrefix + "createserver" + new SecureRandom().nextInt(); +// Server server = client.createServer(serverName, imageRef, flavorRef, withFile("/etc/jclouds.txt", +// "rackspace".getBytes()).withMetadata(metadata)); +// +// assertNotNull(server.getAdminPass()); +// assertEquals(server.getStatus(), ServerStatus.BUILD); +// serverId = server.getId(); +// adminPass = server.getAdminPass(); +// blockUntilServerActive(serverId); +// client.getServer(serverId).getAddresses().getPublicAddresses().iterator().next().getAddress(); + } + + @SuppressWarnings("unused") + private void blockUntilServerActive(int serverId) throws InterruptedException { + Server currentDetails; + for (currentDetails = client.getServer(serverId); currentDetails.getStatus() != ServerStatus.ACTIVE; currentDetails = client + .getServer(serverId)) { + System.out.printf("blocking on status active%n%s%n", currentDetails); + Thread.sleep(5 * 1000); + } + } +} diff --git a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/options/CreateSharedIpGroupOptionsTest.java b/apis/nova/src/test/java/org/jclouds/openstack/nova/options/CreateServerOptionsTest.java similarity index 63% rename from sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/options/CreateSharedIpGroupOptionsTest.java rename to apis/nova/src/test/java/org/jclouds/openstack/nova/options/CreateServerOptionsTest.java index bb1df92fd8..0facf5b16e 100644 --- a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/options/CreateSharedIpGroupOptionsTest.java +++ b/apis/nova/src/test/java/org/jclouds/openstack/nova/options/CreateServerOptionsTest.java @@ -18,62 +18,64 @@ */ package org.jclouds.openstack.nova.options; -import static org.jclouds.openstack.nova.options.CreateSharedIpGroupOptions.Builder.withServer; -import static org.testng.Assert.assertEquals; - -import java.net.URI; - -import javax.ws.rs.HttpMethod; - +import com.google.common.collect.ImmutableMap; +import com.google.inject.Guice; +import com.google.inject.Injector; import org.jclouds.http.HttpRequest; import org.jclouds.json.config.GsonModule; import org.testng.annotations.Test; -import com.google.common.collect.ImmutableMap; -import com.google.inject.Guice; -import com.google.inject.Injector; +import javax.ws.rs.HttpMethod; +import java.net.URI; + +import static org.jclouds.openstack.nova.options.CreateServerOptions.Builder.withFile; +import static org.testng.Assert.assertEquals; /** * Tests behavior of {@code ParseFlavorFromJsonResponse} - * + * * @author Adrian Cole */ @Test(groups = "unit") -public class CreateSharedIpGroupOptionsTest { +public class CreateServerOptionsTest { Injector injector = Guice.createInjector(new GsonModule()); @Test public void testAddPayloadToRequestMapOfStringStringHttpRequest() { - CreateSharedIpGroupOptions options = new CreateSharedIpGroupOptions(); + CreateServerOptions options = new CreateServerOptions(); HttpRequest request = buildRequest(options); - assertEquals("{\"sharedIpGroup\":{\"name\":\"foo\"}}", request.getPayload().getRawContent()); + assertEquals("{\"server\":{\"name\":\"foo\",\"imageRef\":\"1\",\"flavorRef\":\"2\"}}", request.getPayload().getRawContent()); } - private HttpRequest buildRequest(CreateSharedIpGroupOptions options) { + private HttpRequest buildRequest(CreateServerOptions options) { injector.injectMembers(options); HttpRequest request = new HttpRequest(HttpMethod.POST, URI.create("http://localhost")); - options.bindToRequest(request, ImmutableMap.of("name", "foo")); + options.bindToRequest(request, ImmutableMap.of("name", "foo", "imageRef", "1", "flavorRef", "2")); return request; } @Test - public void testWithServer() { - CreateSharedIpGroupOptions options = new CreateSharedIpGroupOptions(); - options.withServer(3); + public void testWithFile() { + CreateServerOptions options = new CreateServerOptions(); + options.withFile("/tmp/rhubarb", "foo".getBytes()); HttpRequest request = buildRequest(options); - assertSharedIpGroup(request); + assertFile(request); } @Test - public void testWithServerStatic() { - CreateSharedIpGroupOptions options = withServer(3); + public void testWithFileStatic() { + CreateServerOptions options = withFile("/tmp/rhubarb", "foo".getBytes()); HttpRequest request = buildRequest(options); - assertSharedIpGroup(request); + assertFile(request); } - private void assertSharedIpGroup(HttpRequest request) { - assertEquals("{\"sharedIpGroup\":{\"name\":\"foo\",\"server\":3}}", request.getPayload().getRawContent()); + private void assertFile(HttpRequest request) { + assertEquals(request.getPayload().getRawContent(), + "{\"server\":{\"name\":\"foo\",\"imageRef\":\"1\",\"flavorRef\":\"2\",\"personality\":[{\"path\":\"/tmp/rhubarb\",\"contents\":\"Zm9v\"}]}}"); } + @Test + public void testWithMetadata() { + } } diff --git a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/options/ListOptionsTest.java b/apis/nova/src/test/java/org/jclouds/openstack/nova/options/ListOptionsTest.java similarity index 87% rename from sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/options/ListOptionsTest.java rename to apis/nova/src/test/java/org/jclouds/openstack/nova/options/ListOptionsTest.java index f87773cf17..27d92cbd87 100644 --- a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/options/ListOptionsTest.java +++ b/apis/nova/src/test/java/org/jclouds/openstack/nova/options/ListOptionsTest.java @@ -18,21 +18,17 @@ */ package org.jclouds.openstack.nova.options; -import static org.jclouds.openstack.nova.options.ListOptions.Builder.changesSince; -import static org.jclouds.openstack.nova.options.ListOptions.Builder.maxResults; -import static org.jclouds.openstack.nova.options.ListOptions.Builder.startAt; -import static org.jclouds.openstack.nova.options.ListOptions.Builder.withDetails; -import static org.testng.Assert.assertEquals; +import com.google.common.collect.ImmutableList; +import org.testng.annotations.Test; import java.util.Date; -import org.testng.annotations.Test; - -import com.google.common.collect.ImmutableList; +import static org.jclouds.openstack.nova.options.ListOptions.Builder.*; +import static org.testng.Assert.assertEquals; /** * Tests behavior of {@code ListOptions} - * + * * @author Adrian Cole */ @Test(groups = "unit") @@ -52,7 +48,7 @@ public class ListOptionsTest { Date ifModifiedSince = new Date(); ListOptions options = new ListOptions().changesSince(ifModifiedSince); assertEquals(ImmutableList.of(ifModifiedSince.getTime() / 1000 + ""), options - .buildQueryParameters().get("changes-since")); + .buildQueryParameters().get("changes-since")); } public void testStartAt() { @@ -71,7 +67,7 @@ public class ListOptionsTest { Date ifModifiedSince = new Date(); ListOptions options = changesSince(ifModifiedSince); assertEquals(ImmutableList.of(ifModifiedSince.getTime() / 1000 + ""), options - .buildQueryParameters().get("changes-since")); + .buildQueryParameters().get("changes-since")); } public void testStartAtStatic() { diff --git a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/options/RebuildServerOptionsTest.java b/apis/nova/src/test/java/org/jclouds/openstack/nova/options/RebuildServerOptionsTest.java similarity index 92% rename from sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/options/RebuildServerOptionsTest.java rename to apis/nova/src/test/java/org/jclouds/openstack/nova/options/RebuildServerOptionsTest.java index 7d6ded590f..b70b6f06e5 100644 --- a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/options/RebuildServerOptionsTest.java +++ b/apis/nova/src/test/java/org/jclouds/openstack/nova/options/RebuildServerOptionsTest.java @@ -18,24 +18,22 @@ */ package org.jclouds.openstack.nova.options; -import static org.jclouds.openstack.nova.options.RebuildServerOptions.Builder.withImage; -import static org.testng.Assert.assertEquals; - -import java.net.URI; -import java.util.HashMap; - -import javax.ws.rs.HttpMethod; - +import com.google.inject.Guice; +import com.google.inject.Injector; import org.jclouds.http.HttpRequest; import org.jclouds.json.config.GsonModule; import org.testng.annotations.Test; -import com.google.inject.Guice; -import com.google.inject.Injector; +import javax.ws.rs.HttpMethod; +import java.net.URI; +import java.util.HashMap; + +import static org.jclouds.openstack.nova.options.RebuildServerOptions.Builder.withImage; +import static org.testng.Assert.assertEquals; /** * Tests behavior of {@code ParseFlavorFromJsonResponse} - * + * * @author Adrian Cole */ @Test(groups = "unit") @@ -60,20 +58,20 @@ public class RebuildServerOptionsTest { @Test public void testWithServer() { RebuildServerOptions options = new RebuildServerOptions(); - options.withImage(3); + options.withImage("3"); HttpRequest request = buildRequest(options); assertRebuild(request); } @Test public void testWithServerStatic() { - RebuildServerOptions options = withImage(3); + RebuildServerOptions options = withImage("3"); HttpRequest request = buildRequest(options); assertRebuild(request); } private void assertRebuild(HttpRequest request) { - assertEquals("{\"rebuild\":{\"imageId\":3}}", request.getPayload().getRawContent()); + assertEquals("{\"rebuild\":{\"imageRef\":\"3\"}}", request.getPayload().getRawContent()); } } diff --git a/apis/nova/src/test/resources/.gitignore b/apis/nova/src/test/resources/.gitignore new file mode 100644 index 0000000000..c6e479480a --- /dev/null +++ b/apis/nova/src/test/resources/.gitignore @@ -0,0 +1 @@ +/test.properties diff --git a/apis/nova/src/test/resources/keys/rhelimg.pem b/apis/nova/src/test/resources/keys/rhelimg.pem new file mode 100644 index 0000000000..f6bc72b83c --- /dev/null +++ b/apis/nova/src/test/resources/keys/rhelimg.pem @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICWgIBAAKBgQDItJzHxyFVPZwjE2Wl+17OLJotFruaMaoKLOH8yOr0WnKFYDCs +AiN88AvwH2YKaF6SQR3gmMTE8SbuKSXR/PVUZfWfhvDinVbAi8gn8A6P5xkym5A6 +uBnJn1Mb0rZAKCJT/nN62vomGGO/3VYkpQcKAWgfSpaB7F1BIebaWR/CgQIBIwKB +gQCa1Hjx4sHxTMjn28NxaiSQa4zg9EeUNPDjRzH9hQ0FpNwAfWdgHugtLjUYUrxu +bcVMQNyIsF1HeDP5jXTycq6e01EYDODnuPC3a06u6Drep0xm/8XuODwekApN811r +kfx9AsgRL9ZwkLqMY8E+OXUmsGVXzEM9jUO3iwA4CCBb/wJBAO0klCumLDm9rYXX +m3YHuwqcgFPnwSodTWX4tIzHKM0tn3PwajJ1x9kCVxsj9uFzz4LXNrxVrUKcYnVx +e/d7sksCQQDYqkr+vt9XaoQSio1M/OOkzc3ynmqtYrNrq9xM1M5IYY2/y5IVLAU3 +6tiyqXTObSTJT+iFd+OwQymi8FgeN77jAkBzLwYj1F6fs+aRd1ojYtcxCi+zubWY +HNxzW4rlSsqszQROQZKqy35TdipPAtb/yolczu6zP46rU0XEA+vBWVaZAkB1nkX4 +ATdp9/6NuO2e0mzzEKMADNrTJvOveo2Iy0tpHwPOhHPfm43N5eNZrHKb8htItlmn +ijnGFdTGKrN+HkMNAkAqh8ulNxVBAK5E0VzzzC2zAh2UFRe3J5K0SgzFkR8W9aM1 +Vb+iZvj4tVlbFtFLGv0ssty8Q0jS7+Hy75XMatry +-----END RSA PRIVATE KEY----- diff --git a/apis/nova/src/test/resources/keys/rhelimpg.pub b/apis/nova/src/test/resources/keys/rhelimpg.pub new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apis/nova/src/test/resources/logback.xml b/apis/nova/src/test/resources/logback.xml new file mode 100644 index 0000000000..5c9c501b0c --- /dev/null +++ b/apis/nova/src/test/resources/logback.xml @@ -0,0 +1,15 @@ + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + \ No newline at end of file diff --git a/apis/nova/src/test/resources/test.properties.template b/apis/nova/src/test/resources/test.properties.template new file mode 100644 index 0000000000..dd98c75dc9 --- /dev/null +++ b/apis/nova/src/test/resources/test.properties.template @@ -0,0 +1,7 @@ +test.nova.endpoint=http://dragon004.hw.griddynamics.net:8774 +test.nova.apiversion=1.1 +test.nova.identity=admin +test.nova.credential=d744752f-20d3-4d75-979f-f62f16033b07 +test.initializer= +test.ssh.keyfile.private=f:/gigaspace/distr/gigaspaces.pem +test.ssh.keyfile.public=f:/gigaspace/distr/gigaspaces.pem \ No newline at end of file diff --git a/apis/nova/src/test/resources/test_fault_response413.json b/apis/nova/src/test/resources/test_fault_response413.json new file mode 100644 index 0000000000..093cb11ded --- /dev/null +++ b/apis/nova/src/test/resources/test_fault_response413.json @@ -0,0 +1,9 @@ +{ + "overLimit" : { + "code" : 413, + "message" : "OverLimit Retry...", + "details" : "Error Details...", + "retryAt" : "2010-08-01T00:00:00Z" + } +} + diff --git a/apis/nova/src/test/resources/test_get_flavor_details.json b/apis/nova/src/test/resources/test_get_flavor_details.json new file mode 100644 index 0000000000..bd3266c04e --- /dev/null +++ b/apis/nova/src/test/resources/test_get_flavor_details.json @@ -0,0 +1,24 @@ +{ + "flavor" : { + "id" : 1, + "name" : "256 MB Server", + "ram" : 256, + "disk" : 10, + "links": [ + { + "rel" : "self", + "href" : "http://servers.api.openstack.org/v1.1/1234/flavors/1" + }, + { + "rel" : "bookmark", + "type" : "application/vnd.openstack.computev1.1+xml", + "href" : "http://servers.api.openstack.org/1234/flavors/1" + }, + { + "rel" : "bookmark", + "type" : "application/vnd.openstack.computev1.1+json", + "href" : "http://servers.api.openstack.org/1234/flavors/1" + } + ] + } +} \ No newline at end of file diff --git a/apis/nova/src/test/resources/test_get_image_details.json b/apis/nova/src/test/resources/test_get_image_details.json new file mode 100644 index 0000000000..f9aa2f5719 --- /dev/null +++ b/apis/nova/src/test/resources/test_get_image_details.json @@ -0,0 +1,31 @@ +{ + "image" : { + "id" : 2, + "name" : "CentOS 5.2", + "serverRef" : "http://servers.api.openstack.org/v1.1/1234/servers/12", + "updated" : "2010-10-10T12:00:00Z", + "created" : "2010-08-10T12:00:00Z", + "status" : "SAVING", + "progress" : 80, + "metadata" : { + "ImageVersion" : "1.5", + "ImageType" : "Gold" + }, + "links": [ + { + "rel" : "self", + "href" : "http://servers.api.openstack.org/v1.1/1234/images/1" + }, + { + "rel" : "bookmark", + "type" : "application/vnd.openstack.computev1.1+xml", + "href" : "http://servers.api.openstack.org/1234/images/1" + }, + { + "rel" : "bookmark", + "type" : "application/vnd.openstack.computev1.1+json", + "href" : "https://servers.api.rackspacecloud.com/v1.1/1234/images/1" + } + ] + } +} \ No newline at end of file diff --git a/apis/nova/src/test/resources/test_get_server_detail.json b/apis/nova/src/test/resources/test_get_server_detail.json new file mode 100644 index 0000000000..6a7bd39af3 --- /dev/null +++ b/apis/nova/src/test/resources/test_get_server_detail.json @@ -0,0 +1,46 @@ +{ + "server" : { + "id" : 1234, + "name" : "sample-server", + "imageRef" : "https://servers.api.rackspacecloud.com/v1.1/1234/images/1", + "flavorRef" : "http://servers.api.openstack.org/1234/flavors/1", + "updated" : "2010-10-10T12:00:00Z", + "created" : "2010-08-10T12:00:00Z", + "hostId" : "e4d909c290d0fb1ca068ffaddf22cbd0", + "affinityId" : "fc88bcf8394db9c8d0564e08ca6a9724188a84d1", + "status" : "BUILD", + "progress" : 60, + "addresses" : { + "public": [ + {"version" : 4, "addr" : "67.23.10.132"}, + {"version" : 6, "addr" : "::babe:67.23.10.132"}, + {"version" : 4, "addr" : "67.23.10.131"}, + {"version" : 6, "addr" : "::babe:4317:0A83"} + ], + "private" : [ + {"version" : 4, "addr" : "10.176.42.16"}, + {"version" : 6, "addr" : "::babe:10.176.42.16"} + ] + }, + "metadata" : { + "Server Label" : "Web Head 1", + "Image Version" : "2.1" + }, + "links": [ + { + "rel" : "self", + "href" : "http://servers.api.openstack.org/v1.1/1234/servers/1234" + }, + { + "rel" : "bookmark", + "type" : "application/vnd.openstack.compute-v1.1+xml", + "href" : "http://servers.api.openstack.org/1234/servers/1234" + }, + { + "rel" : "bookmark", + "type" : "application/vnd.openstack.compute-v1.1+json", + "href" : "http://servers.api.openstack.org/1234/servers/1234" + } + ] + } +} diff --git a/apis/nova/src/test/resources/test_list_addresses.json b/apis/nova/src/test/resources/test_list_addresses.json new file mode 100644 index 0000000000..8f3b3aab93 --- /dev/null +++ b/apis/nova/src/test/resources/test_list_addresses.json @@ -0,0 +1,15 @@ +{ + "addresses" : { + "public" : [ + {"version" : 4, "addr" : "67.23.10.132"}, + {"version" : 6, "addr" : "::babe:67.23.10.132"}, + {"version" : 4, "addr" : "67.23.10.131"}, + {"version" : 6, "addr" : "::babe:4317:0A83"} + ], + "private" : [ + {"version" : 4, "addr" : "10.176.42.16"}, + {"version" : 6, "addr" : "::babe:10.176.42.16"} + ] + } +} + diff --git a/apis/nova/src/test/resources/test_list_addresses_private.json b/apis/nova/src/test/resources/test_list_addresses_private.json new file mode 100644 index 0000000000..eb49fbdf1e --- /dev/null +++ b/apis/nova/src/test/resources/test_list_addresses_private.json @@ -0,0 +1,8 @@ +{ + "private":[ + {"version" : 4, "addr" : "67.23.10.132"}, + {"version" : 6, "addr" : "::babe:67.23.10.132"}, + {"version" : 4, "addr" : "67.23.10.131"}, + {"version" : 6, "addr" : "::babe:4317:0A83"} + ] +} diff --git a/apis/nova/src/test/resources/test_list_addresses_public.json b/apis/nova/src/test/resources/test_list_addresses_public.json new file mode 100644 index 0000000000..60bf20f718 --- /dev/null +++ b/apis/nova/src/test/resources/test_list_addresses_public.json @@ -0,0 +1,8 @@ +{ + "public" : [ + {"version" : 4, "addr" : "67.23.10.132"}, + {"version" : 6, "addr" : "::babe:67.23.10.132"}, + {"version" : 4, "addr" : "67.23.10.131"}, + {"version" : 6, "addr" : "::babe:4317:0A83"} + ] +} diff --git a/apis/nova/src/test/resources/test_list_flavors.json b/apis/nova/src/test/resources/test_list_flavors.json new file mode 100644 index 0000000000..5953f252a2 --- /dev/null +++ b/apis/nova/src/test/resources/test_list_flavors.json @@ -0,0 +1,46 @@ +{ + "flavors" : [ + { + "id" : 1, + "name" : "256 MB Server", + "links": [ + { + "rel" : "self", + "href" : "http://servers.api.openstack.org/v1.1/1234/flavors/1" + }, + { + "rel" : "bookmark", + "type" : "application/vnd.openstack.compute-v1.1+xml", + "href" : "http://servers.api.openstack.org/1234/flavors/1" + }, + { + "rel" : "bookmark", + "type" : "application/vnd.openstack.compute-v1.1+json", + "href" : "http://servers.api.openstack.org/1234/flavors/1" + } + ] + }, + + { + "id" : 2, + "name" : "512 MB Server", + "links": [ + { + "rel" : "self", + "href" : "http://servers.api.openstack.org/v1.1/1234/flavors/2" + }, + { + "rel" : "bookmark", + "type" : "application/vnd.openstack.compute-v1.1+xml", + "href" : "http://servers.api.openstack.org/1234/flavors/2" + }, + { + "rel" : "bookmark", + "type" : "application/vnd.openstack.compute-v1.1+json", + "href" : "http://servers.api.openstack.org/1234/flavors/2" + } + ] + } + ] +} + diff --git a/apis/nova/src/test/resources/test_list_flavors_detail.json b/apis/nova/src/test/resources/test_list_flavors_detail.json new file mode 100644 index 0000000000..c8939ee520 --- /dev/null +++ b/apis/nova/src/test/resources/test_list_flavors_detail.json @@ -0,0 +1,48 @@ +{ + "flavors" : [ + { + "id" : 1, + "name" : "256 MB Server", + "ram" : 256, + "disk" : 10, + "links": [ + { + "rel" : "self", + "href" : "http://servers.api.openstack.org/v1.1/1234/flavors/1" + }, + { + "rel" : "bookmark", + "type" : "application/vnd.openstack.compute-v1.1+xml", + "href" : "http://servers.api.openstack.org/1234/flavors/1" + }, + { + "rel" : "bookmark", + "type" : "application/vnd.openstack.compute-v1.1+json", + "href" : "http://servers.api.openstack.org/1234/flavors/1" + } + ] + }, + { + "id" : 2, + "name" : "512 MB Server", + "ram" : 512, + "disk" : 20, + "links": [ + { + "rel" : "self", + "href" : "http://servers.api.openstack.org/v1.1/1234/flavors/2" + }, + { + "rel" : "bookmark", + "type" : "application/vnd.openstack.compute-v1.1+xml", + "href" : "http://servers.api.openstack.org/1234/flavors/2" + }, + { + "rel" : "bookmark", + "type" : "application/vnd.openstack.compute-v1.1+json", + "href" : "http://servers.api.openstack.org/1234/flavors/2" + } + ] + } + ] +} diff --git a/apis/nova/src/test/resources/test_list_images.json b/apis/nova/src/test/resources/test_list_images.json new file mode 100644 index 0000000000..4772219ece --- /dev/null +++ b/apis/nova/src/test/resources/test_list_images.json @@ -0,0 +1,44 @@ +{ + "images" : [ + { + "id" : 1, + "name" : "CentOS 5.2", + "links": [ + { + "rel" : "self", + "href" : "http://servers.api.openstack.org/v1.1/1234/images/1" + }, + { + "rel" : "bookmark", + "type" : "application/vnd.openstack.compute-v1.1+xml", + "href" : "http://servers.api.openstack.org/1234/images/1" + }, + { + "rel" : "bookmark", + "type" : "application/vnd.openstack.compute-v1.1+json", + "href" : "http://servers.api.openstack.org/1234/images/1" + } + ] + }, + { + "id" : 743, + "name" : "My Server Backup", + "links": [ + { + "rel" : "self", + "href" : "http://servers.api.openstack.org/v1.1/1234/images/743" + }, + { + "rel" : "bookmark", + "type" : "application/vnd.openstack.compute-v1.1+xml", + "href" : "http://servers.api.openstack.org/1234/images/743" + }, + { + "rel" : "bookmark", + "type" : "application/vnd.openstack.compute-v1.1+json", + "href" : "http://servers.api.openstack.org/1234/images/743" + } + ] + } + ] +} diff --git a/apis/nova/src/test/resources/test_list_images_detail.json b/apis/nova/src/test/resources/test_list_images_detail.json new file mode 100644 index 0000000000..2ad7bd9688 --- /dev/null +++ b/apis/nova/src/test/resources/test_list_images_detail.json @@ -0,0 +1,84 @@ +{ + "images" : [ + { + "id" : 1, + "name" : "CentOS 5.2", + "updated" : "2010-10-10T12:00:00Z", + "created" : "2010-08-10T12:00:00Z", + "status" : "ACTIVE", + "metadata" : { + "ImageType" : "Gold", + "ImageVersion" : "1.5" + }, + "links": [ + { + "rel" : "self", + "href" : "http://servers.api.openstack.org/v1.1/1234/images/1" + }, + { + "rel" : "bookmark", + "type" : "application/vnd.openstack.compute-v1.1+xml", + "href" : "http://servers.api.openstack.org/1234/images/1" + }, + { + "rel" : "bookmark", + "type" : "application/vnd.openstack.compute-v1.1+json", + "href" : "http://servers.api.openstack.org/1234/images/1" + } + ] + }, + { + "id" : 743, + "name" : "My Server Backup", + "serverRef" : "http://servers.api.openstack.org/v1.1/1234/servers/12", + "updated" : "2010-10-10T12:00:00Z", + "created" : "2009-07-07T09:56:16Z", + "status" : "SAVING", + "progress" : 80, + "links": [ + { + "rel" : "self", + "href" : "http://servers.api.openstack.org/v1.1/1234/images/743" + }, + { + "rel" : "bookmark", + "type" : "application/vnd.openstack.compute-v1.1+xml", + "href" : "http://servers.api.openstack.org/1234/images/743" + }, + { + "rel" : "bookmark", + "type" : "application/vnd.openstack.compute-v1.1+json", + "href" : "http://servers.api.openstack.org/1234/images/743" + } + ] + }, + { + "id" : 5, + "name" : "CentOS 5.2", + "serverRef" : 12, + "updated" : "2010-10-10T12:00:00Z", + "created" : "2010-08-10T12:00:00Z", + "status" : "ACTIVE", + "metadata" : { + "ImageType" : "Gold", + "ImageVersion" : "1.5" + }, + "links": [ + { + "rel" : "self", + "href" : "http://servers.api.openstack.org/v1.1/1234/images/1" + }, + { + "rel" : "bookmark", + "type" : "application/vnd.openstack.compute-v1.1+xml", + "href" : "http://servers.api.openstack.org/1234/images/1" + }, + { + "rel" : "bookmark", + "type" : "application/vnd.openstack.compute-v1.1+json", + "href" : "http://servers.api.openstack.org/1234/images/1" + } + ] + } + ] +} diff --git a/apis/nova/src/test/resources/test_list_servers.json b/apis/nova/src/test/resources/test_list_servers.json new file mode 100644 index 0000000000..8cf580c3fc --- /dev/null +++ b/apis/nova/src/test/resources/test_list_servers.json @@ -0,0 +1,45 @@ +{ + "servers" : [ + { + "id" : 1234, + "name" : "sample-server", + "links": [ + { + "rel" : "self", + "href" : "http://servers.api.openstack.org/v1.1/1234/servers/1234" + }, + { + "rel" : "bookmark", + "type" : "application/vnd.openstack.compute-v1.1+xml", + "href" : "http://servers.api.openstack.org/1234/servers/1234" + }, + { + "rel" : "bookmark", + "type" : "application/vnd.openstack.compute-v1.1+json", + "href" : "http://servers.api.openstack.org/1234/servers/1234" + } + ] + }, + { + "id" : 5678, + "name" : "sample-server2", + "links": [ + { + "rel" : "self", + "href" : "http://servers.api.openstack.org/v1.1/1234/servers/5678" + }, + { + "rel" : "bookmark", + "type" : "application/vnd.openstack.compute-v1.1+xml", + "href" : "http://servers.api.openstack.org/1234/servers/5678" + }, + { + "rel" : "bookmark", + "type" : "application/vnd.openstack.compute-v1.1+json", + "href" : "http://servers.api.openstack.org/1234/servers/5678" + } + ] + } + ] +} + diff --git a/apis/nova/src/test/resources/test_list_servers_detail.json b/apis/nova/src/test/resources/test_list_servers_detail.json new file mode 100644 index 0000000000..6891e92091 --- /dev/null +++ b/apis/nova/src/test/resources/test_list_servers_detail.json @@ -0,0 +1,89 @@ +{ + "servers" : [ + { + "id" : 1234, + "name" : "sample-server", + "imageRef" : "https://servers.api.rackspacecloud.com/v1.1/32278/images/1234", + "flavorRef" : "https://servers.api.rackspacecloud.com/v1.1/32278/flavors/1", + "updated" : "2010-10-10T12:00:00Z", + "created" : "2010-08-10T12:00:00Z", + "hostId" : "e4d909c290d0fb1ca068ffaddf22cbd0", + "affinityId" : "fc88bcf8394db9c8d0564e08ca6a9724188a84d1", + "status" : "BUILD", + "progress" : 60, + "addresses" : { + "public" : [ + {"version" : 4, "addr" : "67.23.10.132"}, + {"version" : 6, "addr" : "::babe:67.23.10.132"}, + {"version" : 4, "addr" : "67.23.10.131"}, + {"version" : 6, "addr" : "::babe:4317:0A83"} + ], + "private" : [ + {"version" : 4, "addr" : "10.176.42.16"}, + {"version" : 6, "addr" : "::babe:10.176.42.16"} + ] + }, + "metadata" : { + "Server Label" : "Web Head 1", + "Image Version" : "2.1" + }, + "links": [ + { + "rel" : "self", + "href" : "http://servers.api.openstack.org/v1.1/1234/servers/1234" + }, + { + "rel" : "bookmark", + "type" : "application/vnd.openstack.compute-v1.1+xml", + "href" : "http://servers.api.openstack.org/1234/servers/1234" + }, + { + "rel" : "bookmark", + "type" : "application/vnd.openstack.compute-v1.1+json", + "href" : "http://servers.api.openstack.org/1234/servers/1234" + } + ] + }, + { + "id" : 5678, + "name" : "sample-server2", + "imageRef" : "https://servers.api.rackspacecloud.com/v1.1/32278/images/1", + "flavorRef" : 1, + "updated" : "2010-10-10T12:00:00Z", + "created" : "2010-08-10T12:00:00Z", + "hostId" : "9e107d9d372bb6826bd81d3542a419d6", + "affinityId" : "b414fa41cb37b97dcb58d6c76112af1258e9eae2", + "status" : "ACTIVE", + "addresses" : { + "public" : [ + {"version" : 4, "addr" : "67.23.10.133"}, + {"version" : 6, "addr" : "::babe:67.23.10.133"} + ], + "private" : [ + {"version" : 4, "addr" : "10.176.42.17"}, + {"version" : 6, "addr" : "::babe:10.176.42.17"} + ] + }, + "metadata" : { + "Server Label" : "DB 1" + }, + "links": [ + { + "rel" : "self", + "href" : "http://servers.api.openstack.org/v1.1/1234/servers/5678" + }, + { + "rel" : "bookmark", + "type" : "application/vnd.openstack.compute-v1.1+xml", + "href" : "http://servers.api.openstack.org/1234/servers/5678" + }, + { + "rel" : "bookmark", + "type" : "application/vnd.openstack.compute-v1.1+json", + "href" : "http://servers.api.openstack.org/1234/servers/56789" + } + ] + } + ] +} + diff --git a/apis/pom.xml b/apis/pom.xml index 1c913b32bb..d69cc78bac 100644 --- a/apis/pom.xml +++ b/apis/pom.xml @@ -48,5 +48,6 @@ vcloudexpress elasticstack atmos + nova diff --git a/apis/s3/src/main/java/org/jclouds/s3/Bucket.java b/apis/s3/src/main/java/org/jclouds/s3/Bucket.java index bc7a76c26d..819912163e 100644 --- a/apis/s3/src/main/java/org/jclouds/s3/Bucket.java +++ b/apis/s3/src/main/java/org/jclouds/s3/Bucket.java @@ -18,16 +18,20 @@ */ package org.jclouds.s3; +import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Retention; import java.lang.annotation.Target; +import javax.inject.Qualifier; + /** * Annotates the parameter that this is a bucket. */ -@Target(PARAMETER) +@Target({ PARAMETER, METHOD }) @Retention(RUNTIME) +@Qualifier public @interface Bucket { } diff --git a/apis/s3/src/main/java/org/jclouds/s3/S3AsyncClient.java b/apis/s3/src/main/java/org/jclouds/s3/S3AsyncClient.java index faafe7b954..1f53e212cc 100644 --- a/apis/s3/src/main/java/org/jclouds/s3/S3AsyncClient.java +++ b/apis/s3/src/main/java/org/jclouds/s3/S3AsyncClient.java @@ -33,6 +33,28 @@ import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; +import org.jclouds.blobstore.attr.BlobScope; +import org.jclouds.blobstore.functions.ReturnFalseOnContainerNotFound; +import org.jclouds.blobstore.functions.ReturnFalseOnKeyNotFound; +import org.jclouds.blobstore.functions.ReturnNullOnKeyNotFound; +import org.jclouds.blobstore.functions.ThrowContainerNotFoundOn404; +import org.jclouds.blobstore.functions.ThrowKeyNotFoundOn404; +import org.jclouds.http.functions.ParseETagHeader; +import org.jclouds.http.options.GetOptions; +import org.jclouds.location.functions.RegionToEndpointOrProviderIfNull; +import org.jclouds.rest.annotations.BinderParam; +import org.jclouds.rest.annotations.EndpointParam; +import org.jclouds.rest.annotations.ExceptionParser; +import org.jclouds.rest.annotations.Headers; +import org.jclouds.rest.annotations.ParamParser; +import org.jclouds.rest.annotations.ParamValidators; +import org.jclouds.rest.annotations.QueryParams; +import org.jclouds.rest.annotations.RequestFilters; +import org.jclouds.rest.annotations.ResponseParser; +import org.jclouds.rest.annotations.SkipEncoding; +import org.jclouds.rest.annotations.VirtualHost; +import org.jclouds.rest.annotations.XMLResponseParser; +import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404; import org.jclouds.s3.binders.BindACLToXMLPayload; import org.jclouds.s3.binders.BindAsHostPrefixIfConfigured; import org.jclouds.s3.binders.BindBucketLoggingToXmlPayload; @@ -65,26 +87,6 @@ import org.jclouds.s3.xml.ListAllMyBucketsHandler; import org.jclouds.s3.xml.ListBucketHandler; import org.jclouds.s3.xml.LocationConstraintHandler; import org.jclouds.s3.xml.PayerHandler; -import org.jclouds.blobstore.attr.BlobScope; -import org.jclouds.blobstore.functions.ReturnFalseOnContainerNotFound; -import org.jclouds.blobstore.functions.ReturnFalseOnKeyNotFound; -import org.jclouds.blobstore.functions.ReturnNullOnKeyNotFound; -import org.jclouds.blobstore.functions.ThrowContainerNotFoundOn404; -import org.jclouds.blobstore.functions.ThrowKeyNotFoundOn404; -import org.jclouds.http.functions.ParseETagHeader; -import org.jclouds.http.options.GetOptions; -import org.jclouds.rest.annotations.BinderParam; -import org.jclouds.rest.annotations.ExceptionParser; -import org.jclouds.rest.annotations.Headers; -import org.jclouds.rest.annotations.ParamParser; -import org.jclouds.rest.annotations.ParamValidators; -import org.jclouds.rest.annotations.QueryParams; -import org.jclouds.rest.annotations.RequestFilters; -import org.jclouds.rest.annotations.ResponseParser; -import org.jclouds.rest.annotations.SkipEncoding; -import org.jclouds.rest.annotations.VirtualHost; -import org.jclouds.rest.annotations.XMLResponseParser; -import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404; import com.google.common.util.concurrent.ListenableFuture; import com.google.inject.Provides; @@ -121,8 +123,8 @@ public interface S3AsyncClient { @ExceptionParser(ReturnNullOnKeyNotFound.class) @ResponseParser(ParseObjectFromHeadersAndHttpContent.class) ListenableFuture getObject( - @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName, - @PathParam("key") String key, GetOptions... options); + @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName, + @PathParam("key") String key, GetOptions... options); /** * @see S3Client#headObject @@ -132,8 +134,8 @@ public interface S3AsyncClient { @ExceptionParser(ReturnNullOnKeyNotFound.class) @ResponseParser(ParseObjectMetadataFromHeaders.class) ListenableFuture headObject( - @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName, - @PathParam("key") String key); + @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName, + @PathParam("key") String key); /** * @see S3Client#objectExists @@ -142,8 +144,8 @@ public interface S3AsyncClient { @Path("/{key}") @ExceptionParser(ReturnFalseOnKeyNotFound.class) ListenableFuture objectExists( - @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName, - @PathParam("key") String key); + @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName, + @PathParam("key") String key); /** * @see S3Client#deleteObject @@ -152,8 +154,8 @@ public interface S3AsyncClient { @Path("/{key}") @ExceptionParser(ReturnVoidOnNotFoundOr404.class) ListenableFuture deleteObject( - @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName, - @PathParam("key") String key); + @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName, + @PathParam("key") String key); /** * @see S3Client#putObject @@ -162,9 +164,9 @@ public interface S3AsyncClient { @Path("/{key}") @ResponseParser(ParseETagHeader.class) ListenableFuture putObject( - @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName, - @PathParam("key") @ParamParser(ObjectKey.class) @BinderParam(BindS3ObjectMetadataToRequest.class) S3Object object, - PutObjectOptions... options); + @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName, + @PathParam("key") @ParamParser(ObjectKey.class) @BinderParam(BindS3ObjectMetadataToRequest.class) S3Object object, + PutObjectOptions... options); /** * @see S3Client#putBucketInRegion @@ -173,10 +175,9 @@ public interface S3AsyncClient { @Path("/") @ExceptionParser(ReturnFalseIfBucketAlreadyOwnedByYouOrIllegalState.class) ListenableFuture putBucketInRegion( - // TODO endpoint based on region - @BinderParam(BindRegionToXmlPayload.class) @Nullable String region, - @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName, - PutBucketOptions... options); + @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @BinderParam(BindRegionToXmlPayload.class) @Nullable String region, + @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName, + PutBucketOptions... options); /** * @see S3Client#deleteBucketIfEmpty @@ -185,7 +186,7 @@ public interface S3AsyncClient { @Path("/") @ExceptionParser(ReturnTrueOn404OrNotFoundFalseOnIllegalState.class) ListenableFuture deleteBucketIfEmpty( - @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName); + @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName); /** * @see S3Client#bucketExists @@ -195,7 +196,7 @@ public interface S3AsyncClient { @QueryParams(keys = "max-keys", values = "0") @ExceptionParser(ReturnFalseOnContainerNotFound.class) ListenableFuture bucketExists( - @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName); + @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName); /** * @see S3Client#getBucketLocation @@ -205,7 +206,7 @@ public interface S3AsyncClient { @Path("/") @XMLResponseParser(LocationConstraintHandler.class) ListenableFuture getBucketLocation( - @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName); + @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName); /** * @see S3Client#getBucketPayer @@ -215,7 +216,7 @@ public interface S3AsyncClient { @Path("/") @XMLResponseParser(PayerHandler.class) ListenableFuture getBucketPayer( - @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName); + @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName); /** * @see S3Client#setBucketPayer @@ -224,8 +225,8 @@ public interface S3AsyncClient { @QueryParams(keys = "requestPayment") @Path("/") ListenableFuture setBucketPayer( - @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName, - @BinderParam(BindPayerToXmlPayload.class) Payer payer); + @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName, + @BinderParam(BindPayerToXmlPayload.class) Payer payer); /** * @see S3Client#listBucket @@ -234,8 +235,8 @@ public interface S3AsyncClient { @Path("/") @XMLResponseParser(ListBucketHandler.class) ListenableFuture listBucket( - @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName, - ListBucketOptions... options); + @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName, + ListBucketOptions... options); /** * @see S3Client#listOwnedBuckets @@ -254,10 +255,10 @@ public interface S3AsyncClient { @Headers(keys = "x-amz-copy-source", values = "/{sourceBucket}/{sourceObject}") @XMLResponseParser(CopyObjectHandler.class) ListenableFuture copyObject( - @PathParam("sourceBucket") String sourceBucket, - @PathParam("sourceObject") String sourceObject, - @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String destinationBucket, - @PathParam("destinationObject") String destinationObject, CopyObjectOptions... options); + @PathParam("sourceBucket") String sourceBucket, + @PathParam("sourceObject") String sourceObject, + @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String destinationBucket, + @PathParam("destinationObject") String destinationObject, CopyObjectOptions... options); /** * @see S3Client#getBucketACL @@ -268,7 +269,7 @@ public interface S3AsyncClient { @ExceptionParser(ThrowContainerNotFoundOn404.class) @Path("/") ListenableFuture getBucketACL( - @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName); + @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName); /** * @see S3Client#putBucketACL @@ -277,8 +278,8 @@ public interface S3AsyncClient { @Path("/") @QueryParams(keys = "acl") ListenableFuture putBucketACL( - @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName, - @BinderParam(BindACLToXMLPayload.class) AccessControlList acl); + @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName, + @BinderParam(BindACLToXMLPayload.class) AccessControlList acl); /** * @see S3Client#getObjectACL @@ -289,8 +290,8 @@ public interface S3AsyncClient { @XMLResponseParser(AccessControlListHandler.class) @ExceptionParser(ThrowKeyNotFoundOn404.class) ListenableFuture getObjectACL( - @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName, - @PathParam("key") String key); + @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName, + @PathParam("key") String key); /** * @see S3Client#putObjectACL @@ -299,9 +300,8 @@ public interface S3AsyncClient { @QueryParams(keys = "acl") @Path("/{key}") ListenableFuture putObjectACL( - @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName, - @PathParam("key") String key, - @BinderParam(BindACLToXMLPayload.class) AccessControlList acl); + @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName, + @PathParam("key") String key, @BinderParam(BindACLToXMLPayload.class) AccessControlList acl); /** * @see S3Client#getBucketLogging @@ -312,7 +312,7 @@ public interface S3AsyncClient { @ExceptionParser(ThrowContainerNotFoundOn404.class) @Path("/") ListenableFuture getBucketLogging( - @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName); + @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName); /** * @see S3Client#enableBucketLogging @@ -321,8 +321,8 @@ public interface S3AsyncClient { @Path("/") @QueryParams(keys = "logging") ListenableFuture enableBucketLogging( - @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName, - @BinderParam(BindBucketLoggingToXmlPayload.class) BucketLogging logging); + @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName, + @BinderParam(BindBucketLoggingToXmlPayload.class) BucketLogging logging); /** * @see S3Client#putBucketLogging @@ -332,6 +332,6 @@ public interface S3AsyncClient { @QueryParams(keys = "logging") @Produces(MediaType.TEXT_XML) ListenableFuture disableBucketLogging( - @Bucket @BinderParam(BindNoBucketLoggingToXmlPayload.class) @ParamValidators( { BucketNameValidator.class }) String bucketName); + @Bucket @BinderParam(BindNoBucketLoggingToXmlPayload.class) @ParamValidators({ BucketNameValidator.class }) String bucketName); } diff --git a/apis/s3/src/main/java/org/jclouds/s3/binders/BindAsHostPrefixIfConfigured.java b/apis/s3/src/main/java/org/jclouds/s3/binders/BindAsHostPrefixIfConfigured.java index 4699f5a626..f08c59e92a 100644 --- a/apis/s3/src/main/java/org/jclouds/s3/binders/BindAsHostPrefixIfConfigured.java +++ b/apis/s3/src/main/java/org/jclouds/s3/binders/BindAsHostPrefixIfConfigured.java @@ -45,10 +45,10 @@ import com.google.common.collect.Maps; @Singleton public class BindAsHostPrefixIfConfigured implements Binder { - private final Provider uriBuilderProvider; - private final BindAsHostPrefix bindAsHostPrefix; - private final boolean isVhostStyle; - private final String servicePath; + protected final Provider uriBuilderProvider; + protected final BindAsHostPrefix bindAsHostPrefix; + protected final boolean isVhostStyle; + protected final String servicePath; @Inject public BindAsHostPrefixIfConfigured(BindAsHostPrefix bindAsHostPrefix, diff --git a/apis/s3/src/main/java/org/jclouds/s3/config/S3RestClientModule.java b/apis/s3/src/main/java/org/jclouds/s3/config/S3RestClientModule.java index a70558c1a0..9517e6c12e 100644 --- a/apis/s3/src/main/java/org/jclouds/s3/config/S3RestClientModule.java +++ b/apis/s3/src/main/java/org/jclouds/s3/config/S3RestClientModule.java @@ -18,8 +18,10 @@ */ package org.jclouds.s3.config; +import java.util.Map; import java.util.concurrent.TimeUnit; +import javax.annotation.Nullable; import javax.inject.Named; import javax.inject.Singleton; @@ -32,8 +34,10 @@ import org.jclouds.http.RequiresHttp; import org.jclouds.http.annotation.ClientError; import org.jclouds.http.annotation.Redirection; import org.jclouds.http.annotation.ServerError; +import org.jclouds.location.Region; import org.jclouds.rest.ConfiguresRestClient; import org.jclouds.rest.RequestSigner; +import org.jclouds.s3.Bucket; import org.jclouds.s3.S3AsyncClient; import org.jclouds.s3.S3Client; import org.jclouds.s3.filters.RequestAuthorizeSignature; @@ -41,6 +45,7 @@ import org.jclouds.s3.handlers.ParseS3ErrorFromXmlContent; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; +import com.google.common.collect.Maps; import com.google.inject.Provides; import com.google.inject.Scopes; @@ -60,6 +65,21 @@ public class S3RestClientModule ext super(sync, async); } + @Provides + @Bucket + @Singleton + protected Map bucketToRegion() { + return Maps.newConcurrentMap(); + } + + @Provides + @Bucket + @Singleton + @Nullable + protected String defaultRegionForBucket(@Nullable @Region String defaultRegion) { + return defaultRegion; + } + @Override protected void configure() { install(new S3ObjectModule()); @@ -94,7 +114,7 @@ public class S3RestClientModule ext @TimeStamp @Singleton protected Supplier provideTimeStampCache(@Named(Constants.PROPERTY_SESSION_INTERVAL) long seconds, - final DateService dateService) { + final DateService dateService) { return Suppliers.memoizeWithExpiration(new Supplier() { public String get() { return dateService.rfc822DateFormat(); diff --git a/apis/s3/src/main/java/org/jclouds/s3/functions/BindRegionToXmlPayload.java b/apis/s3/src/main/java/org/jclouds/s3/functions/BindRegionToXmlPayload.java index 2a085bf311..c634bf5388 100644 --- a/apis/s3/src/main/java/org/jclouds/s3/functions/BindRegionToXmlPayload.java +++ b/apis/s3/src/main/java/org/jclouds/s3/functions/BindRegionToXmlPayload.java @@ -19,6 +19,7 @@ package org.jclouds.s3.functions; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; import java.util.Set; @@ -31,11 +32,12 @@ import javax.ws.rs.core.MediaType; import org.jclouds.http.HttpRequest; import org.jclouds.logging.Logger; import org.jclouds.rest.binders.BindToStringPayload; +import org.jclouds.s3.Bucket; /** * - * Depending on your latency and legal requirements, you can specify a location constraint that will - * affect where your data physically resides. + * Depending on your latency and legal requirements, you can specify a location + * constraint that will affect where your data physically resides. * * @author Adrian Cole * @@ -45,25 +47,28 @@ public class BindRegionToXmlPayload extends BindToStringPayload { @Resource protected Logger logger = Logger.NULL; - private final String defaultRegion; + private final String defaultRegionForEndpoint; + private final String defaultRegionForService; private final Set regions; @Inject - BindRegionToXmlPayload(@org.jclouds.location.Region @Nullable String defaultRegion, - @org.jclouds.location.Region Set regions) { - this.defaultRegion = defaultRegion; - this.regions = regions; + public BindRegionToXmlPayload(@org.jclouds.location.Region @Nullable String defaultRegionForEndpoint, + @Nullable @Bucket String defaultRegionForService, @org.jclouds.location.Region Set regions) { + this.defaultRegionForEndpoint = defaultRegionForEndpoint; + this.defaultRegionForService = defaultRegionForService; + this.regions = checkNotNull(regions, "regions"); } @Override public R bindToRequest(R request, Object input) { - if (defaultRegion == null) + if (defaultRegionForEndpoint == null) return request; - input = input == null ? defaultRegion : input; + input = input == null ? defaultRegionForEndpoint : input; checkArgument(input instanceof String, "this binder is only valid for Region!"); String constraint = (String) input; String value = null; - if (defaultRegion.equals(constraint)) { + if ((defaultRegionForService == null && constraint == null) + || (defaultRegionForService != null && defaultRegionForService.equals(constraint))) { // nothing to bind as this is default. return request; } else if (regions.contains(constraint)) { @@ -72,9 +77,10 @@ public class BindRegionToXmlPayload extends BindToStringPayload { logger.warn("region %s not in %s ", constraint, regions); value = constraint; } - String payload = String.format( - "%s", - value); + String payload = String + .format( + "%s", + value); request = super.bindToRequest(request, payload); request.getPayload().getContentMetadata().setContentType(MediaType.TEXT_XML); return request; diff --git a/apis/s3/src/main/java/org/jclouds/s3/xml/LocationConstraintHandler.java b/apis/s3/src/main/java/org/jclouds/s3/xml/LocationConstraintHandler.java index 7528d79a55..e3ffabc7ef 100644 --- a/apis/s3/src/main/java/org/jclouds/s3/xml/LocationConstraintHandler.java +++ b/apis/s3/src/main/java/org/jclouds/s3/xml/LocationConstraintHandler.java @@ -20,20 +20,36 @@ package org.jclouds.s3.xml; import static org.jclouds.util.SaxUtils.currentOrNull; +import java.util.Map; + +import javax.inject.Inject; + import org.jclouds.aws.domain.Region; +import org.jclouds.http.HttpRequest; import org.jclouds.http.functions.ParseSax; +import org.jclouds.rest.internal.GeneratedHttpRequest; +import org.jclouds.s3.Bucket; /** * Parses the response from Amazon S3 GET Bucket Location *

* Region is the document we expect to parse. * - * @see + * @see * @author Adrian Cole */ public class LocationConstraintHandler extends ParseSax.HandlerWithResult { + private final Map bucketToRegion; private StringBuilder currentText = new StringBuilder(); private String region; + private String bucket; + + @Inject + public LocationConstraintHandler(@Bucket Map bucketToRegion) { + this.bucketToRegion = bucketToRegion; + } public String getResult() { return region; @@ -41,6 +57,18 @@ public class LocationConstraintHandler extends ParseSax.HandlerWithResult getVApp(@EndpointParam URI vApp); /** - * @see CommonVCloudClient#deployVApp + * @see VCloudExpressClient#deployVApp */ @POST @Consumes(TASK_XML) @@ -173,7 +174,7 @@ public interface VCloudExpressAsyncClient extends CommonVCloudAsyncClient { ListenableFuture deployVApp(@EndpointParam URI vAppId); /** - * @see CommonVCloudClient#undeployVApp + * @see VCloudExpressClient#undeployVApp */ @POST @Consumes(TASK_XML) @@ -182,7 +183,7 @@ public interface VCloudExpressAsyncClient extends CommonVCloudAsyncClient { ListenableFuture undeployVApp(@EndpointParam URI vAppId); /** - * @see CommonVCloudClient#powerOnVApp + * @see VCloudExpressClient#powerOnVApp */ @POST @Consumes(TASK_XML) @@ -191,7 +192,7 @@ public interface VCloudExpressAsyncClient extends CommonVCloudAsyncClient { ListenableFuture powerOnVApp(@EndpointParam URI vAppId); /** - * @see CommonVCloudClient#powerOffVApp + * @see VCloudExpressClient#powerOffVApp */ @POST @Consumes(TASK_XML) @@ -200,14 +201,14 @@ public interface VCloudExpressAsyncClient extends CommonVCloudAsyncClient { ListenableFuture powerOffVApp(@EndpointParam URI vAppId); /** - * @see CommonVCloudClient#shutdownVApp + * @see VCloudExpressClient#shutdownVApp */ @POST @Path("/power/action/shutdown") ListenableFuture shutdownVApp(@EndpointParam URI vAppId); /** - * @see CommonVCloudClient#resetVApp + * @see VCloudExpressClient#resetVApp */ @POST @Consumes(TASK_XML) @@ -216,7 +217,7 @@ public interface VCloudExpressAsyncClient extends CommonVCloudAsyncClient { ListenableFuture resetVApp(@EndpointParam URI vAppId); /** - * @see CommonVCloudClient#suspendVApp + * @see VCloudExpressClient#suspendVApp */ @POST @Consumes(TASK_XML) @@ -225,10 +226,10 @@ public interface VCloudExpressAsyncClient extends CommonVCloudAsyncClient { ListenableFuture suspendVApp(@EndpointParam URI vAppId); /** - * @see CommonVCloudClient#deleteVApp + * @see VCloudExpressClient#deleteVApp */ @DELETE - @ExceptionParser(ReturnVoidOnNotFoundOr404.class) - ListenableFuture deleteVApp(@EndpointParam URI vAppId); - + @ResponseParser(ParseTaskFromLocationHeader.class) + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + ListenableFuture deleteVApp(@EndpointParam URI vAppId); } diff --git a/apis/vcloudexpress/src/main/java/org/jclouds/vcloud/VCloudExpressClient.java b/apis/vcloudexpress/src/main/java/org/jclouds/vcloud/VCloudExpressClient.java index 808c473405..8c4138f5c8 100644 --- a/apis/vcloudexpress/src/main/java/org/jclouds/vcloud/VCloudExpressClient.java +++ b/apis/vcloudexpress/src/main/java/org/jclouds/vcloud/VCloudExpressClient.java @@ -42,7 +42,7 @@ import org.jclouds.vcloud.options.InstantiateVAppTemplateOptions; public interface VCloudExpressClient extends CommonVCloudClient { VCloudExpressVApp instantiateVAppTemplateInVDC(URI vDC, URI template, String appName, - InstantiateVAppTemplateOptions... options); + InstantiateVAppTemplateOptions... options); Task cloneVAppInVDC(URI vDC, URI toClone, String newName, CloneVAppOptions... options); @@ -63,7 +63,7 @@ public interface VCloudExpressClient extends CommonVCloudClient { * if you specified an org, catalog, or catalog item name that isn't present */ VCloudExpressVAppTemplate findVAppTemplateInOrgCatalogNamed(@Nullable String orgName, @Nullable String catalogName, - String itemName); + String itemName); VCloudExpressVApp findVAppInOrgVDCNamed(@Nullable String orgName, @Nullable String catalogName, String vAppName); @@ -101,6 +101,6 @@ public interface VCloudExpressClient extends CommonVCloudClient { */ Task suspendVApp(URI vAppId); - void deleteVApp(URI vAppId); + Task deleteVApp(URI vAppId); } diff --git a/apis/vcloudexpress/src/main/java/org/jclouds/vcloud/compute/functions/ParseOsFromVAppTemplateName.java b/apis/vcloudexpress/src/main/java/org/jclouds/vcloud/compute/functions/ParseOsFromVAppTemplateName.java index a33c44a847..932d535739 100644 --- a/apis/vcloudexpress/src/main/java/org/jclouds/vcloud/compute/functions/ParseOsFromVAppTemplateName.java +++ b/apis/vcloudexpress/src/main/java/org/jclouds/vcloud/compute/functions/ParseOsFromVAppTemplateName.java @@ -29,7 +29,6 @@ import javax.inject.Inject; import javax.inject.Singleton; import org.jclouds.compute.domain.OperatingSystem; -import org.jclouds.compute.domain.OperatingSystemBuilder; import org.jclouds.compute.domain.OsFamily; import org.jclouds.compute.util.ComputeServiceUtils; @@ -51,7 +50,7 @@ public class ParseOsFromVAppTemplateName implements Function> deleting vApp(%s)", vApp.getName()); - VCloudExpressClient.class.cast(client).deleteVApp(vApp.getHref()); + Task task = VCloudExpressClient.class.cast(client).deleteVApp(vApp.getHref()); + if (task != null) + if (!taskTester.apply(task.getHref())) + throw new RuntimeException(String.format("failed to %s %s: %s", "delete", vApp.getName(), task)); } @Override diff --git a/common/trmk/src/main/java/org/jclouds/vcloud/terremark/functions/ParseTaskFromLocationHeader.java b/apis/vcloudexpress/src/main/java/org/jclouds/vcloud/functions/ParseTaskFromLocationHeader.java similarity index 97% rename from common/trmk/src/main/java/org/jclouds/vcloud/terremark/functions/ParseTaskFromLocationHeader.java rename to apis/vcloudexpress/src/main/java/org/jclouds/vcloud/functions/ParseTaskFromLocationHeader.java index 31e98d186a..388933f8ac 100644 --- a/common/trmk/src/main/java/org/jclouds/vcloud/terremark/functions/ParseTaskFromLocationHeader.java +++ b/apis/vcloudexpress/src/main/java/org/jclouds/vcloud/functions/ParseTaskFromLocationHeader.java @@ -16,7 +16,8 @@ * limitations under the License. * ==================================================================== */ -package org.jclouds.vcloud.terremark.functions; + +package org.jclouds.vcloud.functions; import java.net.URI; import java.util.Date; diff --git a/apis/vcloudexpress/src/test/java/org/jclouds/vcloud/VCloudExpressAsyncClientTest.java b/apis/vcloudexpress/src/test/java/org/jclouds/vcloud/VCloudExpressAsyncClientTest.java index baaf3a9591..3dae72731f 100644 --- a/apis/vcloudexpress/src/test/java/org/jclouds/vcloud/VCloudExpressAsyncClientTest.java +++ b/apis/vcloudexpress/src/test/java/org/jclouds/vcloud/VCloudExpressAsyncClientTest.java @@ -44,7 +44,6 @@ import org.jclouds.rest.RestClientTest; import org.jclouds.rest.RestContextFactory; import org.jclouds.rest.RestContextSpec; import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; -import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404; import org.jclouds.rest.internal.RestAnnotationProcessor; import org.jclouds.util.Strings2; import org.jclouds.vcloud.config.VCloudExpressRestClientModule; @@ -60,6 +59,7 @@ import org.jclouds.vcloud.domain.internal.VDCImpl; import org.jclouds.vcloud.domain.network.FenceMode; import org.jclouds.vcloud.domain.network.NetworkConfig; import org.jclouds.vcloud.filters.SetVCloudTokenCookie; +import org.jclouds.vcloud.functions.ParseTaskFromLocationHeader; import org.jclouds.vcloud.options.CloneVAppOptions; import org.jclouds.vcloud.options.InstantiateVAppTemplateOptions; import org.jclouds.vcloud.xml.CatalogHandler; @@ -500,9 +500,9 @@ public class VCloudExpressAsyncClientTest extends RestClientTest - 4.0.0 - - org.jclouds - jclouds-archetypes - 1.0-SNAPSHOT - - jclouds-compute-service-archetype - jclouds Compute service archetype - Maven archetype for a provider of a Compute service - maven-archetype - - - - - maven-archetype-plugin - 2.0-alpha-4 - true - - - maven-resources-plugin - - UTF-8 - - - - - - org.apache.maven.archetype - archetype-packaging - 2.0-alpha-4 - - - + 4.0.0 + + org.jclouds + jclouds-project + 1.0-SNAPSHOT + ../../project/pom.xml + + jclouds-compute-service-archetype + jclouds Compute service archetype + Maven archetype for a provider of a Compute service + maven-archetype + + + + + maven-archetype-plugin + 2.0-alpha-4 + true + + + maven-resources-plugin + + UTF-8 + + + + + + org.apache.maven.archetype + archetype-packaging + 2.0-alpha-4 + + + diff --git a/archetypes/rest-client-archetype/pom.xml b/archetypes/rest-client-archetype/pom.xml index 5def0a3240..f196efa967 100644 --- a/archetypes/rest-client-archetype/pom.xml +++ b/archetypes/rest-client-archetype/pom.xml @@ -22,37 +22,38 @@ - 4.0.0 - - org.jclouds - jclouds-archetypes - 1.0-SNAPSHOT - - jclouds-rest-client-archetype - jclouds rest client archetype - Maven archetype for a provider of a rest-speaking service - maven-archetype + 4.0.0 + + org.jclouds + jclouds-project + 1.0-SNAPSHOT + ../../project/pom.xml + + jclouds-rest-client-archetype + jclouds rest client archetype + Maven archetype for a provider of a rest-speaking service + maven-archetype - - - - maven-archetype-plugin - 2.0-alpha-4 - true - - - maven-resources-plugin - - UTF-8 - - - - - - org.apache.maven.archetype - archetype-packaging - 2.0-alpha-4 - - - + + + + maven-archetype-plugin + 2.0-alpha-4 + true + + + maven-resources-plugin + + UTF-8 + + + + + + org.apache.maven.archetype + archetype-packaging + 2.0-alpha-4 + + + diff --git a/blobstore/src/main/clojure/org/jclouds/blobstore2.clj b/blobstore/src/main/clojure/org/jclouds/blobstore2.clj index 7621f98334..d98dc74ad8 100644 --- a/blobstore/src/main/clojure/org/jclouds/blobstore2.clj +++ b/blobstore/src/main/clojure/org/jclouds/blobstore2.clj @@ -50,7 +50,7 @@ See http://code.google.com/p/jclouds for details." domain.Blob domain.internal.BlobBuilderImpl options.PutOptions options.PutOptions$Builder options.CreateContainerOptions options.ListContainerOptions] - org.jclouds.io.Payloads + [org.jclouds.io Payload Payloads payloads.StreamingPayload] java.util.Arrays [java.security DigestOutputStream MessageDigest] com.google.common.collect.ImmutableSet @@ -73,6 +73,39 @@ See http://code.google.com/p/jclouds for details." (catch Exception e (JCECrypto.)))) +;; +;; Payload support for creating Blobs. +;; + +(def ^{:doc "Type object for a Java primitive byte array, for use in the + PayloadSource protocol." + :private true} + byte-array-type (class (make-array Byte/TYPE 0))) + +(defprotocol PayloadSource + "Various types can have PayloadSource extended onto them so that they are + easily coerced into a Payload." + (^Payload payload [arg] "Coerce arg into a Payload.")) + +(extend-protocol PayloadSource + Payload + (payload [p] p) + java.io.InputStream + (payload [is] (Payloads/newInputStreamPayload is)) + byte-array-type + (payload [ba] (Payloads/newByteArrayPayload ba)) + String + (payload [s] (Payloads/newStringPayload s)) + java.io.File + (payload [f] (Payloads/newFilePayload f)) + clojure.lang.IFn + ;; This will let you pass a closure to payload that takes an OutputStream + ;; as argument and writes to it when called from a StreamingPayload. + (payload [func] + (StreamingPayload. (reify org.jclouds.io.WriteTo + (writeTo [this output-stream] + (func output-stream)))))) + (defn blobstore "Create a logged in context. Options for communication style @@ -278,7 +311,9 @@ Options can also be specified for extension modules (.countBlobs blobstore container-name)) (defn blob - "Create a new blob with the specified payload and options." + "Create a new blob with the specified payload and options. + + The payload argument can be anything accepted by the PayloadSource protocol." ([^String name & {:keys [payload content-type content-length content-md5 calculate-md5 content-disposition content-encoding content-language metadata]}] @@ -286,7 +321,8 @@ Options can also be specified for extension modules (not (and (nil? payload) calculate-md5))]} (let [blob-builder (.name (BlobBuilderImpl. crypto-impl) name) blob-builder (if payload - (.payload blob-builder payload) + (.payload blob-builder + (org.jclouds.blobstore2/payload payload)) (.forSigning blob-builder)) blob-builder (if content-length ;; Special case, arg is prim. (.contentLength blob-builder content-length) diff --git a/blobstore/src/test/clojure/org/jclouds/blobstore2_test.clj b/blobstore/src/test/clojure/org/jclouds/blobstore2_test.clj index 02673198e7..b520995709 100644 --- a/blobstore/src/test/clojure/org/jclouds/blobstore2_test.clj +++ b/blobstore/src/test/clojure/org/jclouds/blobstore2_test.clj @@ -22,7 +22,8 @@ (:use [clojure.test]) (:import [org.jclouds.blobstore BlobStoreContextFactory] [org.jclouds.crypto CryptoStreams] - [java.io ByteArrayOutputStream] + [java.io ByteArrayInputStream ByteArrayOutputStream + StringBufferInputStream] [org.jclouds.util Strings2])) (defn clean-stub-fixture @@ -157,4 +158,37 @@ (is (= (seq (.. a-blob (getPayload) (getContentMetadata) (getContentMD5))) (seq (CryptoStreams/md5 (.getBytes "test-payload"))))))) +(deftest payload-protocol-test + (is (instance? org.jclouds.io.Payload (payload "test"))) + (is (blob "blob1" :payload (payload "blob1"))) + (is (create-container *blobstore* "container")) + (is (= "blob1" + (do + (put-blob *blobstore* "container" + (blob "blob1" + :payload "blob1")) + (Strings2/toStringAndClose (get-blob-stream *blobstore* + "container" "blob1"))))) + (is (= "blob2" + (do + (put-blob *blobstore* "container" + (blob "blob2" + :payload (StringBufferInputStream. "blob2"))) + (Strings2/toStringAndClose (get-blob-stream *blobstore* + "container" "blob2"))))) + (is (= "blob3" + (do + (put-blob *blobstore* "container" + (blob "blob3" + :payload (.getBytes "blob3"))) + (Strings2/toStringAndClose (get-blob-stream *blobstore* + "container" "blob3"))))) + (is (= "blob4" + (do + (put-blob *blobstore* "container" + (blob "blob4" + :payload #(.write % (.getBytes "blob4")))) + (Strings2/toStringAndClose (get-blob-stream *blobstore* + "container" "blob4")))))) + ;; TODO: more tests involving blob-specific functions diff --git a/common/trmk/src/main/java/org/jclouds/vcloud/terremark/TerremarkVCloudAsyncClient.java b/common/trmk/src/main/java/org/jclouds/vcloud/terremark/TerremarkVCloudAsyncClient.java index 9d6873b795..b2be54b6bf 100644 --- a/common/trmk/src/main/java/org/jclouds/vcloud/terremark/TerremarkVCloudAsyncClient.java +++ b/common/trmk/src/main/java/org/jclouds/vcloud/terremark/TerremarkVCloudAsyncClient.java @@ -46,8 +46,8 @@ import org.jclouds.predicates.validators.DnsNameValidator; import org.jclouds.rest.annotations.EndpointParam; import org.jclouds.rest.annotations.ExceptionParser; import org.jclouds.rest.annotations.MapBinder; -import org.jclouds.rest.annotations.PayloadParam; import org.jclouds.rest.annotations.ParamValidators; +import org.jclouds.rest.annotations.PayloadParam; import org.jclouds.rest.annotations.RequestFilters; import org.jclouds.rest.annotations.ResponseParser; import org.jclouds.rest.annotations.XMLResponseParser; @@ -64,6 +64,7 @@ import org.jclouds.vcloud.filters.SetVCloudTokenCookie; import org.jclouds.vcloud.functions.OrgNameAndVDCNameToEndpoint; import org.jclouds.vcloud.functions.OrgNameCatalogNameItemNameToEndpoint; import org.jclouds.vcloud.functions.OrgNameToEndpoint; +import org.jclouds.vcloud.functions.ParseTaskFromLocationHeader; import org.jclouds.vcloud.options.InstantiateVAppTemplateOptions; import org.jclouds.vcloud.terremark.binders.BindNodeConfigurationToXmlPayload; import org.jclouds.vcloud.terremark.binders.BindVAppConfigurationToXmlPayload; @@ -77,7 +78,6 @@ import org.jclouds.vcloud.terremark.domain.TerremarkCatalogItem; import org.jclouds.vcloud.terremark.domain.TerremarkOrg; import org.jclouds.vcloud.terremark.domain.TerremarkVDC; import org.jclouds.vcloud.terremark.domain.VAppConfiguration; -import org.jclouds.vcloud.terremark.functions.ParseTaskFromLocationHeader; import org.jclouds.vcloud.terremark.functions.ReturnVoidOnDeleteDefaultIp; import org.jclouds.vcloud.terremark.functions.VDCURIToInternetServicesEndpoint; import org.jclouds.vcloud.terremark.functions.VDCURIToPublicIPsEndpoint; diff --git a/common/trmk/src/main/java/org/jclouds/vcloud/terremark/TerremarkVCloudClient.java b/common/trmk/src/main/java/org/jclouds/vcloud/terremark/TerremarkVCloudClient.java index 535feeb637..eed2fa45d2 100644 --- a/common/trmk/src/main/java/org/jclouds/vcloud/terremark/TerremarkVCloudClient.java +++ b/common/trmk/src/main/java/org/jclouds/vcloud/terremark/TerremarkVCloudClient.java @@ -95,7 +95,7 @@ public interface TerremarkVCloudClient extends VCloudExpressClient { * */ InternetService addInternetServiceToExistingIp(URI existingIpId, String serviceName, Protocol protocol, int port, - AddInternetServiceOptions... options); + AddInternetServiceOptions... options); void deleteInternetService(URI internetServiceId); @@ -177,4 +177,5 @@ public interface TerremarkVCloudClient extends VCloudExpressClient { // keyPairConfiguration); void deleteKeyPair(URI keyPair); + } \ No newline at end of file diff --git a/common/trmk/src/main/java/org/jclouds/vcloud/terremark/compute/TerremarkVCloudComputeClient.java b/common/trmk/src/main/java/org/jclouds/vcloud/terremark/compute/TerremarkVCloudComputeClient.java index 45c42f7a66..3fdded8cba 100644 --- a/common/trmk/src/main/java/org/jclouds/vcloud/terremark/compute/TerremarkVCloudComputeClient.java +++ b/common/trmk/src/main/java/org/jclouds/vcloud/terremark/compute/TerremarkVCloudComputeClient.java @@ -24,9 +24,9 @@ import static org.jclouds.vcloud.terremark.options.AddInternetServiceOptions.Bui import java.net.URI; import java.util.Map; +import java.util.Map.Entry; import java.util.NoSuchElementException; import java.util.Set; -import java.util.Map.Entry; import javax.annotation.Nullable; import javax.inject.Inject; @@ -71,10 +71,10 @@ public class TerremarkVCloudComputeClient extends VCloudExpressComputeClientImpl @Inject protected TerremarkVCloudComputeClient(TerremarkVCloudClient client, - PopulateDefaultLoginCredentialsForImageStrategy credentialsProvider, - @Named("PASSWORD") Provider passwordGenerator, Predicate successTester, - Map vAppStatusToNodeState, Map credentialStore, - InternetServiceAndPublicIpAddressSupplier internetServiceAndPublicIpAddressSupplier) { + PopulateDefaultLoginCredentialsForImageStrategy credentialsProvider, + @Named("PASSWORD") Provider passwordGenerator, Predicate successTester, + Map vAppStatusToNodeState, Map credentialStore, + InternetServiceAndPublicIpAddressSupplier internetServiceAndPublicIpAddressSupplier) { super(client, successTester, vAppStatusToNodeState); this.client = client; this.credentialsProvider = credentialsProvider; @@ -85,7 +85,7 @@ public class TerremarkVCloudComputeClient extends VCloudExpressComputeClientImpl @Override public VCloudExpressVApp start(@Nullable URI VDC, URI templateId, String name, - InstantiateVAppTemplateOptions options, int... portsToOpen) { + InstantiateVAppTemplateOptions options, int... portsToOpen) { if (options.getDiskSizeKilobytes() != null) { logger.warn("trmk does not support resizing the primary disk; unsetting disk size"); } @@ -95,7 +95,7 @@ public class TerremarkVCloudComputeClient extends VCloudExpressComputeClientImpl String password = null; VCloudExpressVAppTemplate template = client.getVAppTemplate(templateId); if (template.getDescription().indexOf("Windows") != -1 - && options instanceof TerremarkInstantiateVAppTemplateOptions) { + && options instanceof TerremarkInstantiateVAppTemplateOptions) { password = passwordGenerator.get(); TerremarkInstantiateVAppTemplateOptions.class.cast(options).getProperties().put("password", password); } @@ -104,7 +104,7 @@ public class TerremarkVCloudComputeClient extends VCloudExpressComputeClientImpl VCloudExpressVApp vAppResponse = super.start(VDC, templateId, name, options, portsToOpen); if (password != null) { credentialStore.put("node#" + vAppResponse.getHref().toASCIIString(), new Credentials( - defaultCredentials.identity, password)); + defaultCredentials.identity, password)); } if (portsToOpen.length > 0) createPublicAddressMappedToPorts(vAppResponse.getHref(), portsToOpen); @@ -119,37 +119,41 @@ public class TerremarkVCloudComputeClient extends VCloudExpressComputeClientImpl InternetService is = null; Protocol protocol; switch (port) { - case 22: - protocol = Protocol.TCP; - break; - case 80: - case 8080: - protocol = Protocol.HTTP; - break; - case 443: - protocol = Protocol.HTTPS; - break; - default: - protocol = Protocol.HTTP; - break; + case 22: + protocol = Protocol.TCP; + break; + case 80: + case 8080: + protocol = Protocol.HTTP; + break; + case 443: + protocol = Protocol.HTTPS; + break; + default: + protocol = Protocol.HTTP; + break; } if (ip == null) { Entry entry = internetServiceAndPublicIpAddressSupplier - .getNewInternetServiceAndIp(vApp, port, protocol); + .getNewInternetServiceAndIp(vApp, port, protocol); is = entry.getKey(); ip = entry.getValue(); } else { logger.debug(">> adding InternetService %s:%s:%d", ip.getAddress(), protocol, port); - is = client.addInternetServiceToExistingIp(ip.getId(), vApp.getName() + "-" + port, protocol, port, - withDescription(String.format("port %d access to serverId: %s name: %s", port, vApp.getName(), - vApp.getName()))); + is = client.addInternetServiceToExistingIp( + ip.getId(), + vApp.getName() + "-" + port, + protocol, + port, + withDescription(String.format("port %d access to serverId: %s name: %s", port, vApp.getName(), + vApp.getName()))); } - logger.debug("<< created InternetService(%s) %s:%s:%d", is.getName(), is.getPublicIpAddress().getAddress(), is - .getProtocol(), is.getPort()); + logger.debug("<< created InternetService(%s) %s:%s:%d", is.getName(), is.getPublicIpAddress().getAddress(), + is.getProtocol(), is.getPort()); logger.debug(">> adding Node %s:%d -> %s:%d", is.getPublicIpAddress().getAddress(), is.getPort(), - privateAddress, port); + privateAddress, port); Node node = client.addNode(is.getId(), privateAddress, vApp.getName() + "-" + port, port); logger.debug("<< added Node(%s)", node.getName()); } @@ -163,13 +167,13 @@ public class TerremarkVCloudComputeClient extends VCloudExpressComputeClientImpl if (vApp.getNetworkToAddresses().containsValue(node.getIpAddress())) { ipAddresses.add(service.getPublicIpAddress()); logger.debug(">> deleting Node(%s) %s:%d -> %s:%d", node.getName(), service.getPublicIpAddress() - .getAddress(), service.getPort(), node.getIpAddress(), node.getPort()); + .getAddress(), service.getPort(), node.getIpAddress(), node.getPort()); client.deleteNode(node.getId()); logger.debug("<< deleted Node(%s)", node.getName()); Set nodes = client.getNodes(service.getId()); if (nodes.size() == 0) { logger.debug(">> deleting InternetService(%s) %s:%d", service.getName(), service.getPublicIpAddress() - .getAddress(), service.getPort()); + .getAddress(), service.getPort()); client.deleteInternetService(service.getId()); logger.debug("<< deleted InternetService(%s)", service.getName()); continue SERVICE; diff --git a/common/trmk/src/main/java/org/jclouds/vcloud/terremark/compute/TerremarkVCloudComputeService.java b/common/trmk/src/main/java/org/jclouds/vcloud/terremark/compute/TerremarkVCloudComputeService.java index 15b9eac8d1..2b06e57639 100644 --- a/common/trmk/src/main/java/org/jclouds/vcloud/terremark/compute/TerremarkVCloudComputeService.java +++ b/common/trmk/src/main/java/org/jclouds/vcloud/terremark/compute/TerremarkVCloudComputeService.java @@ -36,18 +36,20 @@ import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.internal.BaseComputeService; +import org.jclouds.compute.internal.PersistNodeCredentials; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts; +import org.jclouds.compute.strategy.CreateNodesInGroupThenAddToSet; import org.jclouds.compute.strategy.DestroyNodeStrategy; import org.jclouds.compute.strategy.GetNodeMetadataStrategy; import org.jclouds.compute.strategy.InitializeRunScriptOnNodeOrPlaceInBadMap; import org.jclouds.compute.strategy.ListNodesStrategy; import org.jclouds.compute.strategy.RebootNodeStrategy; import org.jclouds.compute.strategy.ResumeNodeStrategy; -import org.jclouds.compute.strategy.CreateNodesInGroupThenAddToSet; import org.jclouds.compute.strategy.SuspendNodeStrategy; import org.jclouds.domain.Credentials; import org.jclouds.domain.Location; +import org.jclouds.scriptbuilder.functions.InitAdminAccess; import org.jclouds.vcloud.terremark.compute.domain.KeyPairCredentials; import org.jclouds.vcloud.terremark.compute.domain.OrgAndName; import org.jclouds.vcloud.terremark.compute.functions.NodeMetadataToOrgAndName; @@ -66,28 +68,29 @@ public class TerremarkVCloudComputeService extends BaseComputeService { @Inject protected TerremarkVCloudComputeService(ComputeServiceContext context, Map credentialStore, - @Memoized Supplier> images, @Memoized Supplier> sizes, - @Memoized Supplier> locations, ListNodesStrategy listNodesStrategy, - GetNodeMetadataStrategy getNodeMetadataStrategy, CreateNodesInGroupThenAddToSet runNodesAndAddToSetStrategy, - RebootNodeStrategy rebootNodeStrategy, DestroyNodeStrategy destroyNodeStrategy, - ResumeNodeStrategy resumeNodeStrategy, SuspendNodeStrategy suspendNodeStrategy, - Provider templateBuilderProvider, Provider templateOptionsProvider, - @Named("NODE_RUNNING") Predicate nodeRunning, - @Named("NODE_TERMINATED") Predicate nodeTerminated, - @Named("NODE_SUSPENDED") Predicate nodeSuspended, - InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, Timeouts timeouts, - @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, CleanupOrphanKeys cleanupOrphanKeys, - ConcurrentMap credentialsMap, NodeMetadataToOrgAndName nodeToOrgAndName) { + @Memoized Supplier> images, @Memoized Supplier> sizes, + @Memoized Supplier> locations, ListNodesStrategy listNodesStrategy, + GetNodeMetadataStrategy getNodeMetadataStrategy, CreateNodesInGroupThenAddToSet runNodesAndAddToSetStrategy, + RebootNodeStrategy rebootNodeStrategy, DestroyNodeStrategy destroyNodeStrategy, + ResumeNodeStrategy resumeNodeStrategy, SuspendNodeStrategy suspendNodeStrategy, + Provider templateBuilderProvider, Provider templateOptionsProvider, + @Named("NODE_RUNNING") Predicate nodeRunning, + @Named("NODE_TERMINATED") Predicate nodeTerminated, + @Named("NODE_SUSPENDED") Predicate nodeSuspended, + InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, InitAdminAccess initAdminAccess, + PersistNodeCredentials persistNodeCredentials, Timeouts timeouts, + @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, CleanupOrphanKeys cleanupOrphanKeys, + ConcurrentMap credentialsMap, NodeMetadataToOrgAndName nodeToOrgAndName) { super(context, credentialStore, images, sizes, locations, listNodesStrategy, getNodeMetadataStrategy, - runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy, resumeNodeStrategy, - suspendNodeStrategy, templateBuilderProvider, templateOptionsProvider, nodeRunning, nodeTerminated, - nodeSuspended, initScriptRunnerFactory, timeouts, executor); + runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy, resumeNodeStrategy, + suspendNodeStrategy, templateBuilderProvider, templateOptionsProvider, nodeRunning, nodeTerminated, + nodeSuspended, initScriptRunnerFactory, initAdminAccess, persistNodeCredentials, timeouts, executor); this.cleanupOrphanKeys = cleanupOrphanKeys; } /** - * like {@link BaseComputeService#destroyNodesMatching} except that this will clean implicit - * keypairs. + * like {@link BaseComputeService#destroyNodesMatching} except that this will + * clean implicit keypairs. */ @Override public Set destroyNodesMatching(Predicate filter) { @@ -97,7 +100,8 @@ public class TerremarkVCloudComputeService extends BaseComputeService { } /** - * returns template options, except of type {@link TerremarkVCloudTemplateOptions}. + * returns template options, except of type + * {@link TerremarkVCloudTemplateOptions}. */ @Override public TerremarkVCloudTemplateOptions templateOptions() { diff --git a/common/vcloud/src/main/java/org/jclouds/vcloud/functions/ParseLoginResponseFromHeaders.java b/common/vcloud/src/main/java/org/jclouds/vcloud/functions/ParseLoginResponseFromHeaders.java index 49514a8976..292ec7da7c 100755 --- a/common/vcloud/src/main/java/org/jclouds/vcloud/functions/ParseLoginResponseFromHeaders.java +++ b/common/vcloud/src/main/java/org/jclouds/vcloud/functions/ParseLoginResponseFromHeaders.java @@ -28,7 +28,6 @@ import java.util.regex.Pattern; import javax.inject.Inject; import javax.inject.Provider; import javax.inject.Singleton; -import javax.ws.rs.core.HttpHeaders; import org.jclouds.http.HttpResponse; import org.jclouds.http.HttpResponseException; @@ -49,36 +48,35 @@ import com.google.common.base.Function; */ @Singleton public class ParseLoginResponseFromHeaders implements Function { - static final Pattern pattern = Pattern.compile("vcloud-token=([^;]+);.*"); + static final Pattern pattern = Pattern.compile("(vcloud-token=)?([^;]+)(;.*)?"); private final ParseSax.Factory factory; private final Provider orgHandlerProvider; @Inject - private ParseLoginResponseFromHeaders(Factory factory, - Provider orgHandlerProvider) { + private ParseLoginResponseFromHeaders(Factory factory, Provider orgHandlerProvider) { this.factory = factory; this.orgHandlerProvider = orgHandlerProvider; } /** - * parses the http response headers to create a new {@link VCloudSession} object. + * parses the http response headers to create a new {@link VCloudSession} + * object. */ public VCloudSession apply(HttpResponse from) { - String cookieHeader = checkNotNull(from.getFirstHeaderOrNull(HttpHeaders.SET_COOKIE), - HttpHeaders.SET_COOKIE); + String cookieHeader = checkNotNull(from.getFirstHeaderOrNull("x-vcloud-authorization"), "x-vcloud-authorization"); final Matcher matcher = pattern.matcher(cookieHeader); boolean matchFound = matcher.find(); try { if (matchFound) { final Map org = factory.create(orgHandlerProvider.get()).parse( - from.getPayload().getInput()); + from.getPayload().getInput()); return new VCloudSession() { @VCloudToken public String getVCloudToken() { - return matcher.group(1); + return matcher.group(2); } @Org @@ -91,6 +89,6 @@ public class ParseLoginResponseFromHeaders implements Functionof(HttpHeaders.SET_COOKIE, "vcloud-token=9er4d061-4bff-48fa-84b1-5da7166764d2; path=/")); + .getResourceAsStream("/orglist.xml")), ImmutableMultimap.of("x-vcloud-authorization", "vcloud-token=9er4d061-4bff-48fa-84b1-5da7166764d2; path=/")); response.getPayload().getContentMetadata().setContentType("Content-Type: application/xml; charset=utf-8"); response.getPayload().getContentMetadata().setContentLength(307l); @@ -69,15 +67,28 @@ public class ParseLoginResponseFromHeadersTest extends BaseHandlerTest { @Test public void testApplyBlueLock() { HttpResponse response = new HttpResponse(200, "OK", Payloads.newInputStreamPayload(getClass() - .getResourceAsStream("/orglist.xml")), ImmutableMultimap.of(HttpHeaders.SET_COOKIE,"vcloud-token=c9f232506df9b65d7b7d97b7499eddd7; Domain=.bluelock.com; Path=/") ); + .getResourceAsStream("/orglist.xml")), ImmutableMultimap.of("x-vcloud-authorization","MUKOJ2HoAfoMmLnHRp4esNb2MtWscCLLhVysnsIsCG0=") ); response.getPayload().getContentMetadata().setContentType("Content-Type: application/xml; charset=utf-8"); response.getPayload().getContentMetadata().setContentLength(307l); VCloudSession reply = parser.apply(response); - assertEquals(reply.getVCloudToken(), "c9f232506df9b65d7b7d97b7499eddd7"); + assertEquals(reply.getVCloudToken(), "MUKOJ2HoAfoMmLnHRp4esNb2MtWscCLLhVysnsIsCG0="); assertEquals(reply.getOrgs(), ImmutableMap.of("adrian@jclouds.org", new ReferenceTypeImpl("adrian@jclouds.org", VCloudMediaType.ORG_XML, URI.create("https://services.vcloudexpress.terremark.com/api/v0.8/org/48")))); } + @Test + public void testApplyVirtacore() { + HttpResponse response = new HttpResponse(200, "OK", Payloads.newInputStreamPayload(getClass() + .getResourceAsStream("/orglist.xml")), ImmutableMultimap.of("x-vcloud-authorization","vcloud-token=IPy0w7UGD4lwtdWAK/ZVzfuLK+dztxGRqsOhWqV0i48=") ); + response.getPayload().getContentMetadata().setContentType("Content-Type: application/xml; charset=utf-8"); + response.getPayload().getContentMetadata().setContentLength(307l); + + VCloudSession reply = parser.apply(response); + assertEquals(reply.getVCloudToken(), "IPy0w7UGD4lwtdWAK/ZVzfuLK+dztxGRqsOhWqV0i48="); + assertEquals(reply.getOrgs(), ImmutableMap.of("adrian@jclouds.org", new ReferenceTypeImpl("adrian@jclouds.org", + VCloudMediaType.ORG_XML, URI.create("https://services.vcloudexpress.terremark.com/api/v0.8/org/48")))); + + } } diff --git a/compute/src/main/clojure/org/jclouds/compute.clj b/compute/src/main/clojure/org/jclouds/compute.clj index baa65f6bae..0ce39bf26a 100644 --- a/compute/src/main/clojure/org/jclouds/compute.clj +++ b/compute/src/main/clojure/org/jclouds/compute.clj @@ -21,7 +21,7 @@ "A clojure binding to the jclouds ComputeService. Current supported providers are: - [aws-ec2, eucualyptus-partnercloud-ec2, elastichosts-lon-b, + [aws-ec2, eucualyptus-partnercloud-ec2, elastichosts-lon-b, nova, cloudservers-uk, cloudservers-us, byon, cloudsigma-zrh, stub, trmk-ecloud, trmk-vcloudexpress, vcloud, bluelock, eucalyptus, slicehost, elastichosts-lon-p, elastichosts-sat-p, elastichosts, diff --git a/compute/src/main/java/org/jclouds/compute/config/BaseComputeServiceContextModule.java b/compute/src/main/java/org/jclouds/compute/config/BaseComputeServiceContextModule.java index 1cddb87180..78c319e67f 100644 --- a/compute/src/main/java/org/jclouds/compute/config/BaseComputeServiceContextModule.java +++ b/compute/src/main/java/org/jclouds/compute/config/BaseComputeServiceContextModule.java @@ -73,6 +73,7 @@ import com.google.inject.name.Names; * @author Adrian Cole */ public abstract class BaseComputeServiceContextModule extends AbstractModule { + @Override protected void configure() { install(new LocationModule(authException)); @@ -89,6 +90,8 @@ public abstract class BaseComputeServiceContextModule extends AbstractModule { .implement(RunScriptOnNode.class, Names.named("nonblocking"), RunScriptOnNodeAsInitScriptUsingSsh.class) .build(RunScriptOnNodeFactoryImpl.Factory.class)); + install(new PersistNodeCredentialsModule()); + bind(RunScriptOnNode.Factory.class).to(RunScriptOnNodeFactoryImpl.class); install(new FactoryModuleBuilder().implement(new TypeLiteral>() { @@ -170,8 +173,8 @@ public abstract class BaseComputeServiceContextModule extends AbstractModule { } /** - * supplies how the tag is encoded into the name. A string of hex characters is the last argument - * and tag is the first + * supplies how the tag is encoded into the name. A string of hex characters + * is the last argument and tag is the first */ @Provides @Named("NAMING_CONVENTION") @@ -207,8 +210,8 @@ public abstract class BaseComputeServiceContextModule extends AbstractModule { @Memoized protected Supplier> supplyImageCache(@Named(PROPERTY_SESSION_INTERVAL) long seconds, final Supplier> imageSupplier) { - return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier>(authException, seconds, - new Supplier>() { + return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier>(authException, + seconds, new Supplier>() { @Override public Set get() { return imageSupplier.get(); @@ -241,8 +244,8 @@ public abstract class BaseComputeServiceContextModule extends AbstractModule { @Memoized protected Supplier> supplySizeCache(@Named(PROPERTY_SESSION_INTERVAL) long seconds, final Supplier> hardwareSupplier) { - return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier>(authException, seconds, - new Supplier>() { + return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier>(authException, + seconds, new Supplier>() { @Override public Set get() { return hardwareSupplier.get(); diff --git a/compute/src/main/java/org/jclouds/compute/config/PersistNodeCredentialsModule.java b/compute/src/main/java/org/jclouds/compute/config/PersistNodeCredentialsModule.java new file mode 100644 index 0000000000..23396e81ee --- /dev/null +++ b/compute/src/main/java/org/jclouds/compute/config/PersistNodeCredentialsModule.java @@ -0,0 +1,97 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.compute.config; + +import java.util.Map; + +import javax.annotation.Nullable; + +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.domain.NodeMetadataBuilder; +import org.jclouds.compute.internal.PersistNodeCredentials; +import org.jclouds.domain.Credentials; +import org.jclouds.scriptbuilder.domain.Statement; +import org.jclouds.scriptbuilder.functions.CredentialsFromAdminAccess; + +import com.google.common.base.Function; +import com.google.inject.AbstractModule; +import com.google.inject.Inject; +import com.google.inject.TypeLiteral; +import com.google.inject.assistedinject.Assisted; +import com.google.inject.assistedinject.FactoryModuleBuilder; +import com.google.inject.name.Names; + +/** + * + * @author Adrian Cole + */ +public class PersistNodeCredentialsModule extends AbstractModule { + + static class RefreshCredentialsForNodeIfRanAdminAccess implements Function { + protected final Map credentialStore; + protected final Statement statement; + + @Inject + protected RefreshCredentialsForNodeIfRanAdminAccess(Map credentialStore, + @Nullable @Assisted Statement statement) { + this.credentialStore = credentialStore; + this.statement = statement; + } + + @Override + public NodeMetadata apply(NodeMetadata input) { + if (statement == null) + return input; + Credentials credentials = CredentialsFromAdminAccess.INSTANCE.apply(statement); + if (credentials != null) { + input = NodeMetadataBuilder.fromNodeMetadata(input).credentials(credentials).build(); + credentialStore.put("node#" + input.getId(), input.getCredentials()); + } + return input; + } + + } + + static class RefreshCredentialsForNode extends RefreshCredentialsForNodeIfRanAdminAccess { + + @Inject + public RefreshCredentialsForNode(Map credentialStore, @Assisted @Nullable Statement statement) { + super(credentialStore, statement); + } + + @Override + public NodeMetadata apply(NodeMetadata input) { + input = super.apply(input); + if (input.getCredentials() != null) + credentialStore.put("node#" + input.getId(), input.getCredentials()); + return input; + } + + } + + @Override + protected void configure() { + + install(new FactoryModuleBuilder().implement(new TypeLiteral>() { + }, Names.named("ifAdminAccess"), RefreshCredentialsForNodeIfRanAdminAccess.class) + .implement(new TypeLiteral>() { + }, Names.named("always"), RefreshCredentialsForNode.class).build(PersistNodeCredentials.class)); + } + +} \ No newline at end of file diff --git a/compute/src/main/java/org/jclouds/compute/internal/BaseComputeService.java b/compute/src/main/java/org/jclouds/compute/internal/BaseComputeService.java index c4ce30216f..a28a47d790 100755 --- a/compute/src/main/java/org/jclouds/compute/internal/BaseComputeService.java +++ b/compute/src/main/java/org/jclouds/compute/internal/BaseComputeService.java @@ -22,7 +22,6 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Predicates.and; import static com.google.common.base.Predicates.not; import static com.google.common.base.Predicates.notNull; -import static com.google.common.collect.Iterables.concat; import static com.google.common.collect.Iterables.filter; import static com.google.common.collect.Maps.newLinkedHashMap; import static com.google.common.collect.Sets.filter; @@ -86,6 +85,8 @@ import org.jclouds.logging.Logger; import org.jclouds.predicates.RetryablePredicate; import org.jclouds.scriptbuilder.domain.Statement; import org.jclouds.scriptbuilder.domain.Statements; +import org.jclouds.scriptbuilder.functions.InitAdminAccess; +import org.jclouds.util.Maps2; import org.jclouds.util.Strings2; import com.google.common.base.Function; @@ -93,6 +94,7 @@ import com.google.common.base.Predicate; import com.google.common.base.Supplier; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.Multimap; @@ -128,23 +130,24 @@ public class BaseComputeService implements ComputeService { private final Predicate nodeSuspended; private final InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory; private final Timeouts timeouts; + private final InitAdminAccess initAdminAccess; + private final PersistNodeCredentials persistNodeCredentials; private final ExecutorService executor; @Inject protected BaseComputeService(ComputeServiceContext context, Map credentialStore, - @Memoized Supplier> images, - @Memoized Supplier> hardwareProfiles, - @Memoized Supplier> locations, ListNodesStrategy listNodesStrategy, - GetNodeMetadataStrategy getNodeMetadataStrategy, - CreateNodesInGroupThenAddToSet runNodesAndAddToSetStrategy, RebootNodeStrategy rebootNodeStrategy, - DestroyNodeStrategy destroyNodeStrategy, ResumeNodeStrategy resumeNodeStrategy, - SuspendNodeStrategy suspendNodeStrategy, Provider templateBuilderProvider, - Provider templateOptionsProvider, - @Named("NODE_RUNNING") Predicate nodeRunning, - @Named("NODE_TERMINATED") Predicate nodeTerminated, - @Named("NODE_SUSPENDED") Predicate nodeSuspended, - InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, Timeouts timeouts, - @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor) { + @Memoized Supplier> images, @Memoized Supplier> hardwareProfiles, + @Memoized Supplier> locations, ListNodesStrategy listNodesStrategy, + GetNodeMetadataStrategy getNodeMetadataStrategy, CreateNodesInGroupThenAddToSet runNodesAndAddToSetStrategy, + RebootNodeStrategy rebootNodeStrategy, DestroyNodeStrategy destroyNodeStrategy, + ResumeNodeStrategy resumeNodeStrategy, SuspendNodeStrategy suspendNodeStrategy, + Provider templateBuilderProvider, Provider templateOptionsProvider, + @Named("NODE_RUNNING") Predicate nodeRunning, + @Named("NODE_TERMINATED") Predicate nodeTerminated, + @Named("NODE_SUSPENDED") Predicate nodeSuspended, + InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, InitAdminAccess initAdminAccess, + PersistNodeCredentials persistNodeCredentials, Timeouts timeouts, + @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor) { this.context = checkNotNull(context, "context"); this.credentialStore = checkNotNull(credentialStore, "credentialStore"); this.images = checkNotNull(images, "images"); @@ -164,6 +167,8 @@ public class BaseComputeService implements ComputeService { this.nodeSuspended = checkNotNull(nodeSuspended, "nodeSuspended"); this.initScriptRunnerFactory = checkNotNull(initScriptRunnerFactory, "initScriptRunnerFactory"); this.timeouts = checkNotNull(timeouts, "timeouts"); + this.initAdminAccess = checkNotNull(initAdminAccess, "initAdminAccess"); + this.persistNodeCredentials = checkNotNull(persistNodeCredentials, "persistNodeCredentials"); this.executor = checkNotNull(executor, "executor"); } @@ -180,7 +185,7 @@ public class BaseComputeService implements ComputeService { */ @Override public Set runNodesWithTag(String group, int count, Template template) - throws RunNodesException { + throws RunNodesException { return createNodesInGroup(group, count, template); } @@ -189,7 +194,7 @@ public class BaseComputeService implements ComputeService { */ @Override public Set runNodesWithTag(String group, int count, TemplateOptions templateOptions) - throws RunNodesException { + throws RunNodesException { return createNodesInGroup(group, count, templateBuilder().any().options(templateOptions).build()); } @@ -203,23 +208,26 @@ public class BaseComputeService implements ComputeService { @Override public Set createNodesInGroup(String group, int count, Template template) - throws RunNodesException { + throws RunNodesException { checkNotNull(group, "group cannot be null"); checkNotNull(template.getLocation(), "location"); logger.debug(">> running %d node%s group(%s) location(%s) image(%s) hardwareProfile(%s) options(%s)", count, - count > 1 ? "s" : "", group, template.getLocation().getId(), template.getImage().getId(), template - .getHardware().getId(), template.getOptions()); + count > 1 ? "s" : "", group, template.getLocation().getId(), template.getImage().getId(), template + .getHardware().getId(), template.getOptions()); Set goodNodes = newLinkedHashSet(); Map badNodes = newLinkedHashMap(); Multimap customizationResponses = LinkedHashMultimap.create(); + if (template.getOptions().getRunScript() != null) + template.getOptions().runScript(initAdminAccess.apply(template.getOptions().getRunScript())); + Map> responses = runNodesAndAddToSetStrategy.execute(group, count, template, goodNodes, badNodes, - customizationResponses); + customizationResponses); Map executionExceptions = awaitCompletion(responses, executor, null, logger, "runNodesWithTag(" - + group + ")"); - for (NodeMetadata node : concat(goodNodes, badNodes.keySet())) - if (node.getCredentials() != null) - credentialStore.put("node#" + node.getId(), node.getCredentials()); + + group + ")"); + Function fn = persistNodeCredentials.always(template.getOptions().getRunScript()); + badNodes = Maps2.transformKeys(badNodes, fn); + goodNodes = ImmutableSet.copyOf(Iterables.transform(goodNodes, fn)); if (executionExceptions.size() > 0 || badNodes.size() > 0) { throw new RunNodesException(group, count, template, goodNodes, executionExceptions, badNodes); } @@ -228,9 +236,8 @@ public class BaseComputeService implements ComputeService { @Override public Set createNodesInGroup(String group, int count, TemplateOptions templateOptions) - throws RunNodesException { + throws RunNodesException { return createNodesInGroup(group, count, templateBuilder().any().options(templateOptions).build()); - } @Override @@ -275,27 +282,27 @@ public class BaseComputeService implements ComputeService { public Set destroyNodesMatching(Predicate filter) { logger.debug(">> destroying nodes matching(%s)", filter); Set set = newLinkedHashSet(transformParallel(nodesMatchingFilterAndNotTerminated(filter), - new Function>() { + new Function>() { - // TODO make an async interface instead of re-wrapping - @Override - public Future apply(final NodeMetadata from) { - return executor.submit(new Callable() { + // TODO make an async interface instead of re-wrapping + @Override + public Future apply(final NodeMetadata from) { + return executor.submit(new Callable() { - @Override - public NodeMetadata call() throws Exception { - destroyNode(from.getId()); - return from; - } + @Override + public NodeMetadata call() throws Exception { + destroyNode(from.getId()); + return from; + } - @Override - public String toString() { - return "destroyNode(" + from.getId() + ")"; - } - }); - } + @Override + public String toString() { + return "destroyNode(" + from.getId() + ")"; + } + }); + } - }, executor, null, logger, "destroyNodesMatching(" + filter + ")")); + }, executor, null, logger, "destroyNodesMatching(" + filter + ")")); logger.debug("<< destroyed(%d)", set.size()); return set; } @@ -309,7 +316,7 @@ public class BaseComputeService implements ComputeService { * if none found */ Iterable nodesMatchingFilterAndNotTerminatedExceptionIfNotFound( - Predicate filter) { + Predicate filter) { Iterable nodes = nodesMatchingFilterAndNotTerminated(filter); if (Iterables.size(nodes) == 0) throw new NoSuchElementException("no nodes matched filter: " + filter); @@ -399,15 +406,15 @@ public class BaseComputeService implements ComputeService { public void rebootNodesMatching(Predicate filter) { logger.debug(">> rebooting nodes matching(%s)", filter); transformParallel(nodesMatchingFilterAndNotTerminatedExceptionIfNotFound(filter), - new Function>() { - // TODO use native async - @Override - public Future apply(NodeMetadata from) { - rebootNode(from.getId()); - return immediateFuture(null); - } + new Function>() { + // TODO use native async + @Override + public Future apply(NodeMetadata from) { + rebootNode(from.getId()); + return immediateFuture(null); + } - }, executor, null, logger, "rebootNodesMatching(" + filter + ")"); + }, executor, null, logger, "rebootNodesMatching(" + filter + ")"); logger.debug("<< rebooted"); } @@ -430,15 +437,15 @@ public class BaseComputeService implements ComputeService { public void resumeNodesMatching(Predicate filter) { logger.debug(">> resuming nodes matching(%s)", filter); transformParallel(nodesMatchingFilterAndNotTerminatedExceptionIfNotFound(filter), - new Function>() { - // TODO use native async - @Override - public Future apply(NodeMetadata from) { - resumeNode(from.getId()); - return immediateFuture(null); - } + new Function>() { + // TODO use native async + @Override + public Future apply(NodeMetadata from) { + resumeNode(from.getId()); + return immediateFuture(null); + } - }, executor, null, logger, "resumeNodesMatching(" + filter + ")"); + }, executor, null, logger, "resumeNodesMatching(" + filter + ")"); logger.debug("<< resumed"); } @@ -461,15 +468,15 @@ public class BaseComputeService implements ComputeService { public void suspendNodesMatching(Predicate filter) { logger.debug(">> suspending nodes matching(%s)", filter); transformParallel(nodesMatchingFilterAndNotTerminatedExceptionIfNotFound(filter), - new Function>() { - // TODO use native async - @Override - public Future apply(NodeMetadata from) { - suspendNode(from.getId()); - return immediateFuture(null); - } + new Function>() { + // TODO use native async + @Override + public Future apply(NodeMetadata from) { + suspendNode(from.getId()); + return immediateFuture(null); + } - }, executor, null, logger, "suspendNodesMatching(" + filter + ")"); + }, executor, null, logger, "suspendNodesMatching(" + filter + ")"); logger.debug("<< suspended"); } @@ -478,7 +485,7 @@ public class BaseComputeService implements ComputeService { */ @Override public Map runScriptOnNodesMatching(Predicate filter, Payload runScript) - throws RunScriptOnNodesException { + throws RunScriptOnNodesException { return runScriptOnNodesMatching(filter, runScript, RunScriptOptions.NONE); } @@ -487,10 +494,10 @@ public class BaseComputeService implements ComputeService { */ @Override public Map runScriptOnNodesMatching(Predicate filter, Payload runScript, - RunScriptOptions options) throws RunScriptOnNodesException { + RunScriptOptions options) throws RunScriptOnNodesException { try { - return runScriptOnNodesMatching(filter, Statements.exec(Strings2.toStringAndClose(checkNotNull(runScript, - "runScript").getInput())), options); + return runScriptOnNodesMatching(filter, + Statements.exec(Strings2.toStringAndClose(checkNotNull(runScript, "runScript").getInput())), options); } catch (IOException e) { Throwables.propagate(e); return null; @@ -502,7 +509,7 @@ public class BaseComputeService implements ComputeService { */ @Override public Map runScriptOnNodesMatching(Predicate filter, String runScript) - throws RunScriptOnNodesException { + throws RunScriptOnNodesException { return runScriptOnNodesMatching(filter, Statements.exec(checkNotNull(runScript, "runScript"))); } @@ -511,15 +518,15 @@ public class BaseComputeService implements ComputeService { */ @Override public Map runScriptOnNodesMatching(Predicate filter, Statement runScript) - throws RunScriptOnNodesException { + throws RunScriptOnNodesException { return runScriptOnNodesMatching(filter, runScript, RunScriptOptions.NONE); } @Override public Map runScriptOnNodesMatching(Predicate filter, - String runScript, RunScriptOptions options) throws RunScriptOnNodesException { + String runScript, RunScriptOptions options) throws RunScriptOnNodesException { return runScriptOnNodesMatching(filter, Statements.exec(checkNotNull(runScript, "runScript")), - RunScriptOptions.NONE); + RunScriptOptions.NONE); } /** @@ -527,7 +534,7 @@ public class BaseComputeService implements ComputeService { */ @Override public Map runScriptOnNodesMatching(Predicate filter, Statement runScript, - RunScriptOptions options) throws RunScriptOnNodesException { + RunScriptOptions options) throws RunScriptOnNodesException { checkNotNull(filter, "filter"); checkNotNull(runScript, "runScript"); @@ -538,16 +545,22 @@ public class BaseComputeService implements ComputeService { Map> responses = newLinkedHashMap(); Map exceptions = ImmutableMap. of(); + runScript = initAdminAccess.apply(runScript); + Iterable scriptRunners = transformNodesIntoInitializedScriptRunners( - nodesMatchingFilterAndNotTerminatedExceptionIfNotFound(filter), runScript, options, badNodes); + nodesMatchingFilterAndNotTerminatedExceptionIfNotFound(filter), runScript, options, badNodes); if (Iterables.size(scriptRunners) > 0) { for (RunScriptOnNode runner : scriptRunners) { responses.put(runner.getNode(), executor.submit(new RunScriptOnNodeAndAddToGoodMapOrPutExceptionIntoBadMap( - runner, goodNodes, badNodes))); + runner, goodNodes, badNodes))); } exceptions = awaitCompletion(responses, executor, null, logger, "runScriptOnNodesMatching(" + filter + ")"); } + Function fn = persistNodeCredentials.ifAdminAccess(runScript); + badNodes = Maps2.transformKeys(badNodes, fn); + goodNodes = Maps2.transformKeys(goodNodes, fn); + if (exceptions.size() > 0 || badNodes.size() > 0) { throw new RunScriptOnNodesException(runScript, options, goodNodes, exceptions, badNodes); } @@ -555,10 +568,11 @@ public class BaseComputeService implements ComputeService { } private Iterable transformNodesIntoInitializedScriptRunners( - Iterable nodes, Statement script, RunScriptOptions options, - Map badNodes) { - return filter(transformParallel(nodes, new TransformNodesIntoInitializedScriptRunners(script, options, badNodes), - executor, null, logger, "initialize script runners"), notNull()); + Iterable nodes, Statement script, RunScriptOptions options, + Map badNodes) { + return filter( + transformParallel(nodes, new TransformNodesIntoInitializedScriptRunners(script, options, badNodes), + executor, null, logger, "initialize script runners"), notNull()); } private Set detailsOnAllNodes() { @@ -571,13 +585,13 @@ public class BaseComputeService implements ComputeService { } private final class TransformNodesIntoInitializedScriptRunners implements - Function> { + Function> { private final Map badNodes; private final Statement script; private final RunScriptOptions options; private TransformNodesIntoInitializedScriptRunners(Statement script, RunScriptOptions options, - Map badNodes) { + Map badNodes) { this.badNodes = checkNotNull(badNodes, "badNodes"); this.script = checkNotNull(script, "script"); this.options = checkNotNull(options, "options"); @@ -588,7 +602,7 @@ public class BaseComputeService implements ComputeService { checkNotNull(node, "node"); if (options.getOverridingCredentials() != null) { Builder builder = node.getCredentials() != null ? node.getCredentials().toBuilder() - : new Credentials.Builder(); + : new Credentials.Builder(); if (options.getOverridingCredentials().identity != null) builder.identity(options.getOverridingCredentials().identity); if (options.getOverridingCredentials().credential != null) diff --git a/compute/src/main/java/org/jclouds/compute/internal/PersistNodeCredentials.java b/compute/src/main/java/org/jclouds/compute/internal/PersistNodeCredentials.java new file mode 100644 index 0000000000..b1d205ea8f --- /dev/null +++ b/compute/src/main/java/org/jclouds/compute/internal/PersistNodeCredentials.java @@ -0,0 +1,35 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.compute.internal; + +import javax.annotation.Nullable; +import javax.inject.Named; + +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.scriptbuilder.domain.Statement; + +import com.google.common.base.Function; + +public interface PersistNodeCredentials { + @Named("ifAdminAccess") + Function ifAdminAccess(@Nullable Statement statement); + + @Named("always") + Function always(@Nullable Statement statement); +} \ No newline at end of file diff --git a/compute/src/main/java/org/jclouds/compute/predicates/NodePredicates.java b/compute/src/main/java/org/jclouds/compute/predicates/NodePredicates.java index ea4342c1eb..ace7ee2185 100644 --- a/compute/src/main/java/org/jclouds/compute/predicates/NodePredicates.java +++ b/compute/src/main/java/org/jclouds/compute/predicates/NodePredicates.java @@ -222,7 +222,7 @@ public class NodePredicates { * @return predicate */ public static Predicate runningInGroup(final String group) { - Preconditions2.checkNotEmpty(group, "Tag must be defined"); + Preconditions2.checkNotEmpty(group, "group must be defined"); return new Predicate() { @Override public boolean apply(NodeMetadata nodeMetadata) { diff --git a/compute/src/test/clojure/org/jclouds/compute2_test.clj b/compute/src/test/clojure/org/jclouds/compute2_test.clj index d9ca0ea609..c4a8e3adef 100644 --- a/compute/src/test/clojure/org/jclouds/compute2_test.clj +++ b/compute/src/test/clojure/org/jclouds/compute2_test.clj @@ -61,24 +61,30 @@ list, Alan Dipert and MeikelBrandmeyer." (is (compute-service? *compute*)) (is (compute-service? (compute-service (compute-context *compute*))))) +(defn in-group [group] #(= (.getGroup %) group)) + (deftest nodes-test (is (create-node *compute* "fred" (build-template *compute* {} ))) (is (= 1 (count (nodes-in-group *compute* "fred")))) + ;; pass in a function that selects node metadata based on NodeMetadata field + (is (= 1 (count (nodes-with-details-matching *compute* (in-group "fred"))))) + ;; or make your query inline (is (= 1 (count (nodes-with-details-matching *compute* #(= (.getGroup %) "fred"))))) + ;; or get real fancy, and use the underlying Predicate object jclouds uses (is (= 1 (count (nodes-with-details-matching *compute* (reify com.google.common.base.Predicate (apply [this input] (= (.getGroup input) "fred"))))))) - (is (= 0 (count (nodes-with-details-matching *compute* #(= (.getGroup %) "othergroup"))))) - (suspend-nodes-matching *compute* #(= (.getGroup %) "fred")) - (is (suspended? (first (nodes-with-details-matching *compute* #(= (.getGroup %) "fred"))))) - (resume-nodes-matching *compute* #(= (.getGroup %) "fred")) + (is (= 0 (count (nodes-with-details-matching *compute* (in-group "othergroup"))))) + (suspend-nodes-matching *compute* (in-group "fred")) + (is (suspended? (first (nodes-with-details-matching *compute* (in-group "fred"))))) + (resume-nodes-matching *compute* (in-group "fred")) (is (running? (first (nodes-in-group *compute* "fred")))) - (reboot-nodes-matching *compute* #(= (.getGroup %) "fred")) + (reboot-nodes-matching *compute* (in-group "fred")) (is (running? (first (nodes-in-group *compute* "fred")))) (is (create-nodes *compute* "fred" 2 (build-template *compute* {} ))) (is (= 3 (count (nodes-in-group *compute* "fred")))) (is (= "fred" (group (first (nodes *compute*))))) - (destroy-nodes-matching *compute* #(= (.getGroup %) "fred")) + (destroy-nodes-matching *compute* (in-group "fred")) (is (terminated? (first (nodes-in-group *compute* "fred"))))) (defn localhost? [node] diff --git a/compute/src/test/java/org/jclouds/compute/BaseComputeServiceLiveTest.java b/compute/src/test/java/org/jclouds/compute/BaseComputeServiceLiveTest.java index 6561325afc..b2611b7fd8 100755 --- a/compute/src/test/java/org/jclouds/compute/BaseComputeServiceLiveTest.java +++ b/compute/src/test/java/org/jclouds/compute/BaseComputeServiceLiveTest.java @@ -44,12 +44,12 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.util.Collection; import java.util.Map; +import java.util.Map.Entry; import java.util.NoSuchElementException; import java.util.Properties; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; -import java.util.Map.Entry; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -77,6 +77,7 @@ import org.jclouds.predicates.SocketOpen; import org.jclouds.rest.AuthorizationException; import org.jclouds.rest.RestContextFactory; import org.jclouds.scriptbuilder.domain.Statements; +import org.jclouds.scriptbuilder.statements.login.AdminAccess; import org.jclouds.ssh.SshClient; import org.jclouds.ssh.SshException; import org.testng.annotations.AfterTest; @@ -159,8 +160,8 @@ public abstract class BaseComputeServiceLiveTest { if (context != null) context.close(); Properties props = setupProperties(); - context = new ComputeServiceContextFactory(setupRestProperties()).createContext(provider, ImmutableSet.of( - new Log4JLoggingModule(), getSshModule()), props); + context = new ComputeServiceContextFactory(setupRestProperties()).createContext(provider, + ImmutableSet.of(new Log4JLoggingModule(), getSshModule()), props); client = context.getComputeService(); } @@ -180,8 +181,8 @@ public abstract class BaseComputeServiceLiveTest { public void testCorrectAuthException() throws Exception { ComputeServiceContext context = null; try { - context = new ComputeServiceContextFactory(setupRestProperties()).createContext(provider, "MOMMA", "MIA", ImmutableSet - . of(new Log4JLoggingModule())); + context = new ComputeServiceContextFactory(setupRestProperties()).createContext(provider, "MOMMA", "MIA", + ImmutableSet. of(new Log4JLoggingModule())); context.getComputeService().listNodes(); } catch (AuthorizationException e) { throw e; @@ -206,7 +207,7 @@ public abstract class BaseComputeServiceLiveTest { @Test(enabled = true, expectedExceptions = NoSuchElementException.class) public void testCorrectExceptionRunningNodesNotFound() throws Exception { client.runScriptOnNodesMatching(runningInGroup("zebras-are-awesome"), buildScript(new OperatingSystem.Builder() - .family(OsFamily.UBUNTU).description("ffoo").build())); + .family(OsFamily.UBUNTU).description("ffoo").build())); } // since surefire and eclipse don't otherwise guarantee the order, we are @@ -230,17 +231,17 @@ public abstract class BaseComputeServiceLiveTest { OperatingSystem os = get(nodes, 0).getOperatingSystem(); try { Map responses = runScriptWithCreds(group, os, new Credentials( - good.identity, "romeo")); + good.identity, "romeo")); assert false : "shouldn't pass with a bad password\n" + responses; } catch (RunScriptOnNodesException e) { assert getRootCause(e).getMessage().contains("Auth fail") : e; } for (Entry response : client.runScriptOnNodesMatching( - runningInGroup(group), Statements.exec("echo hello"), - overrideCredentialsWith(good).wrapInInitScript(false).runAsRoot(false)).entrySet()) + runningInGroup(group), Statements.exec("echo hello"), + overrideCredentialsWith(good).wrapInInitScript(false).runAsRoot(false)).entrySet()) assert response.getValue().getOutput().trim().equals("hello") : response.getKey() + ": " - + response.getValue(); + + response.getValue(); runScriptWithCreds(group, os, good); @@ -294,8 +295,10 @@ public abstract class BaseComputeServiceLiveTest { private void refreshTemplate() { template = buildTemplate(client.templateBuilder()); - template.getOptions().installPrivateKey(keyPair.get("private")).authorizePublicKey(keyPair.get("public")) - .runScript(buildScript(template.getImage().getOperatingSystem())); + // template.getOptions().installPrivateKey(keyPair.get("private")).authorizePublicKey(keyPair.get("public")) + // .runScript(buildScript(template.getImage().getOperatingSystem())); + template.getOptions().runScript( + Statements.newStatementList(AdminAccess.standard(), buildScript(template.getImage().getOperatingSystem()))); } protected void checkImageIdMatchesTemplate(NodeMetadata node) { @@ -306,8 +309,8 @@ public abstract class BaseComputeServiceLiveTest { protected void checkOsMatchesTemplate(NodeMetadata node) { if (node.getOperatingSystem() != null) assert node.getOperatingSystem().getFamily().equals(template.getImage().getOperatingSystem().getFamily()) : String - .format("expecting family %s but got %s", template.getImage().getOperatingSystem().getFamily(), node - .getOperatingSystem()); + .format("expecting family %s but got %s", template.getImage().getOperatingSystem().getFamily(), + node.getOperatingSystem()); } void assertLocationSameOrChild(Location test, Location expected) { @@ -339,10 +342,10 @@ public abstract class BaseComputeServiceLiveTest { } protected Map runScriptWithCreds(final String group, OperatingSystem os, - Credentials creds) throws RunScriptOnNodesException { + Credentials creds) throws RunScriptOnNodesException { try { return client.runScriptOnNodesMatching(runningInGroup(group), buildScript(os), overrideCredentialsWith(creds) - .nameTask("runScriptWithCreds")); + .nameTask("runScriptWithCreds")); } catch (SshException e) { throw e; } @@ -372,16 +375,16 @@ public abstract class BaseComputeServiceLiveTest { @Test(enabled = true, dependsOnMethods = "testCreateAnotherNodeWithANewContextToEnsureSharedMemIsntRequired") public void testGet() throws Exception { - Map metadataMap = newLinkedHashMap(uniqueIndex(filter(client - .listNodesDetailsMatching(all()), and(inGroup(group), not(TERMINATED))), - new Function() { + Map metadataMap = newLinkedHashMap(uniqueIndex( + filter(client.listNodesDetailsMatching(all()), and(inGroup(group), not(TERMINATED))), + new Function() { - @Override - public String apply(NodeMetadata from) { - return from.getId(); - } + @Override + public String apply(NodeMetadata from) { + return from.getId(); + } - })); + })); for (NodeMetadata node : nodes) { metadataMap.remove(node.getId()); NodeMetadata metadata = client.getNodeMetadata(node.getId()); @@ -400,7 +403,7 @@ public abstract class BaseComputeServiceLiveTest { protected void assertNodeZero(Collection metadataSet) { assert metadataSet.size() == 0 : String.format("nodes left in set: [%s] which didn't match set: [%s]", - metadataSet, nodes); + metadataSet, nodes); } @Test(enabled = true, dependsOnMethods = "testGet") @@ -488,12 +491,12 @@ public abstract class BaseComputeServiceLiveTest { } template = client.templateBuilder().options(blockOnComplete(false).blockOnPort(8080, 600).inboundPorts(22, 8080)) - .build(); + .build(); // note this is a dependency on the template resolution template.getOptions().runScript( - RunScriptData.createScriptInstallAndStartJBoss(keyPair.get("public"), template.getImage() - .getOperatingSystem())); + RunScriptData.createScriptInstallAndStartJBoss(keyPair.get("public"), template.getImage() + .getOperatingSystem())); try { NodeMetadata node = getOnlyElement(client.createNodesInGroup(group, 1, template)); @@ -527,26 +530,26 @@ public abstract class BaseComputeServiceLiveTest { assert location != location.getParent() : location; assert location.getScope() != null : location; switch (location.getScope()) { - case PROVIDER: - assertProvider(location); - break; - case REGION: - assertProvider(location.getParent()); - break; - case ZONE: - Location provider = location.getParent().getParent(); - // zone can be a direct descendant of provider - if (provider == null) - provider = location.getParent(); - assertProvider(provider); - break; - case HOST: - Location provider2 = location.getParent().getParent().getParent(); - // zone can be a direct descendant of provider - if (provider2 == null) - provider2 = location.getParent().getParent(); - assertProvider(provider2); - break; + case PROVIDER: + assertProvider(location); + break; + case REGION: + assertProvider(location.getParent()); + break; + case ZONE: + Location provider = location.getParent().getParent(); + // zone can be a direct descendant of provider + if (provider == null) + provider = location.getParent(); + assertProvider(provider); + break; + case HOST: + Location provider2 = location.getParent().getParent().getParent(); + // zone can be a direct descendant of provider + if (provider2 == null) + provider2 = location.getParent().getParent(); + assertProvider(provider2); + break; } } } @@ -634,7 +637,7 @@ public abstract class BaseComputeServiceLiveTest { assertEquals(hello.getOutput().trim(), "hello"); ExecResponse exec = ssh.exec("java -version"); assert exec.getError().indexOf("1.6") != -1 || exec.getOutput().indexOf("1.6") != -1 : exec + "\n" - + ssh.exec("cat /tmp/bootstrap/stdout.log /tmp/bootstrap/stderr.log"); + + ssh.exec("cat /tmp/bootstrap/stdout.log /tmp/bootstrap/stderr.log"); } finally { if (ssh != null) ssh.disconnect(); diff --git a/compute/src/test/java/org/jclouds/compute/StubComputeServiceIntegrationTest.java b/compute/src/test/java/org/jclouds/compute/StubComputeServiceIntegrationTest.java index eb82dcf477..037e3b1bac 100644 --- a/compute/src/test/java/org/jclouds/compute/StubComputeServiceIntegrationTest.java +++ b/compute/src/test/java/org/jclouds/compute/StubComputeServiceIntegrationTest.java @@ -28,6 +28,7 @@ import static org.testng.Assert.assertEquals; import java.io.FileNotFoundException; import java.io.IOException; import java.io.Serializable; +import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; @@ -43,11 +44,16 @@ import org.jclouds.net.IPSocket; import org.jclouds.predicates.RetryablePredicate; import org.jclouds.predicates.SocketOpen; import org.jclouds.rest.RestContext; +import org.jclouds.scriptbuilder.statements.login.AdminAccess; +import org.jclouds.scriptbuilder.statements.login.AdminAccess.Configuration; import org.jclouds.ssh.SshClient; import org.jclouds.ssh.SshException; import org.jclouds.util.Strings2; import org.testng.annotations.Test; +import com.google.common.base.Function; +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableMap; import com.google.inject.AbstractModule; @@ -97,6 +103,35 @@ public class StubComputeServiceIntegrationTest extends BaseComputeServiceLiveTes @Override protected void configure() { + bind(AdminAccess.Configuration.class).toInstance(new Configuration() { + + @Override + public Supplier defaultAdminUsername() { + return Suppliers.ofInstance("defaultAdminUsername"); + } + + @Override + public Supplier> defaultAdminSshKeys() { + return Suppliers.> ofInstance(ImmutableMap.of("public", "publicKey", "private", + "privateKey")); + } + + @Override + public Function cryptFunction() { + return new Function() { + + @Override + public String apply(String input) { + return String.format("crypt(%s)", input); + } + + }; + } + + public Supplier passwordGenerator() { + return Suppliers.ofInstance("randompassword"); + } + }); SshClient.Factory factory = createMock(SshClient.Factory.class); SshClient client1 = createMock(SshClient.class); SshClient client2 = createMock(SshClient.class); @@ -105,14 +140,14 @@ public class StubComputeServiceIntegrationTest extends BaseComputeServiceLiveTes SshClient client5 = createMock(SshClient.class); expect(factory.create(new IPSocket("144.175.1.1", 22), new Credentials("root", "password1"))).andReturn( - client1); + client1); runScriptAndService(client1, 1); expect(factory.create(new IPSocket("144.175.1.2", 22), new Credentials("root", "password2"))).andReturn( - client2).times(3); + client2).times(3); expect(factory.create(new IPSocket("144.175.1.2", 22), new Credentials("root", "romeo"))).andThrow( - new SshException("Auth fail")); - + new SshException("Auth fail")); + // run script without backgrounding client2.connect(); expect(client2.exec("echo hello\n")).andReturn(new ExecResponse("hello\n", "", 0)); @@ -121,39 +156,39 @@ public class StubComputeServiceIntegrationTest extends BaseComputeServiceLiveTes client2.connect(); try { runScript(client2, "runScriptWithCreds", - Strings2.toStringAndClose(StubComputeServiceIntegrationTest.class - .getResourceAsStream("/runscript.sh")), 2); + Strings2.toStringAndClose(StubComputeServiceIntegrationTest.class + .getResourceAsStream("/runscript.sh")), 2); } catch (IOException e) { Throwables.propagate(e); } client2.disconnect(); expect(factory.create(new IPSocket("144.175.1.3", 22), new Credentials("root", "password3"))).andReturn( - client3).times(2); + client3).times(2); expect(factory.create(new IPSocket("144.175.1.4", 22), new Credentials("root", "password4"))).andReturn( - client4).times(2); + client4).times(2); expect(factory.create(new IPSocket("144.175.1.5", 22), new Credentials("root", "password5"))).andReturn( - client5).times(2); + client5).times(2); runScriptAndInstallSsh(client3, "bootstrap", 3); runScriptAndInstallSsh(client4, "bootstrap", 4); runScriptAndInstallSsh(client5, "bootstrap", 5); expect( - factory.create(eq(new IPSocket("144.175.1.1", 22)), eq(new Credentials("root", keyPair - .get("private"))))).andReturn(client1); + factory.create(eq(new IPSocket("144.175.1.1", 22)), + eq(new Credentials("defaultAdminUsername", "privateKey")))).andReturn(client1); expect( - factory.create(eq(new IPSocket("144.175.1.2", 22)), eq(new Credentials("root", keyPair - .get("private"))))).andReturn(client2); + factory.create(eq(new IPSocket("144.175.1.2", 22)), + eq(new Credentials("defaultAdminUsername", "privateKey")))).andReturn(client2); expect( - factory.create(eq(new IPSocket("144.175.1.3", 22)), eq(new Credentials("root", keyPair - .get("private"))))).andReturn(client3); + factory.create(eq(new IPSocket("144.175.1.3", 22)), + eq(new Credentials("defaultAdminUsername", "privateKey")))).andReturn(client3); expect( - factory.create(eq(new IPSocket("144.175.1.4", 22)), eq(new Credentials("root", keyPair - .get("private"))))).andReturn(client4); + factory.create(eq(new IPSocket("144.175.1.4", 22)), + eq(new Credentials("defaultAdminUsername", "privateKey")))).andReturn(client4); expect( - factory.create(eq(new IPSocket("144.175.1.5", 22)), eq(new Credentials("root", keyPair - .get("private"))))).andReturn(client5); + factory.create(eq(new IPSocket("144.175.1.5", 22)), + eq(new Credentials("defaultAdminUsername", "privateKey")))).andReturn(client5); helloAndJava(client2); helloAndJava(client3); @@ -175,7 +210,7 @@ public class StubComputeServiceIntegrationTest extends BaseComputeServiceLiveTes try { runScript(client, "jboss", Strings2.toStringAndClose(StubComputeServiceIntegrationTest.class - .getResourceAsStream("/initscript_with_jboss.sh")), nodeId); + .getResourceAsStream("/initscript_with_jboss.sh")), nodeId); } catch (IOException e) { Throwables.propagate(e); } @@ -189,7 +224,7 @@ public class StubComputeServiceIntegrationTest extends BaseComputeServiceLiveTes try { runScript(client, scriptName, Strings2.toStringAndClose(StubComputeServiceIntegrationTest.class - .getResourceAsStream("/initscript_with_java.sh")), nodeId); + .getResourceAsStream("/initscript_with_java.sh")), nodeId); } catch (IOException e) { Throwables.propagate(e); } @@ -242,7 +277,7 @@ public class StubComputeServiceIntegrationTest extends BaseComputeServiceLiveTes public void testAssignability() throws Exception { @SuppressWarnings("unused") RestContext, ConcurrentMap> stubContext = new ComputeServiceContextFactory() - .createContext(provider, identity, credential).getProviderSpecificContext(); + .createContext(provider, identity, credential).getProviderSpecificContext(); } private static class PayloadEquals implements IArgumentMatcher, Serializable { @@ -289,7 +324,7 @@ public class StubComputeServiceIntegrationTest extends BaseComputeServiceLiveTes return false; PayloadEquals other = (PayloadEquals) o; return this.expected == null && other.expected == null || this.expected != null - && this.expected.equals(other.expected); + && this.expected.equals(other.expected); } @Override diff --git a/compute/src/test/java/org/jclouds/compute/config/PersistNodeCredentialsTest.java b/compute/src/test/java/org/jclouds/compute/config/PersistNodeCredentialsTest.java new file mode 100644 index 0000000000..afb0c464ca --- /dev/null +++ b/compute/src/test/java/org/jclouds/compute/config/PersistNodeCredentialsTest.java @@ -0,0 +1,147 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.compute.config; + +import static org.easymock.EasyMock.expect; +import static org.easymock.classextension.EasyMock.createMock; +import static org.easymock.classextension.EasyMock.replay; +import static org.easymock.classextension.EasyMock.verify; +import static org.testng.Assert.assertEquals; + +import java.util.Map; + +import org.jclouds.compute.config.PersistNodeCredentialsModule.RefreshCredentialsForNode; +import org.jclouds.compute.config.PersistNodeCredentialsModule.RefreshCredentialsForNodeIfRanAdminAccess; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.domain.NodeMetadataBuilder; +import org.jclouds.compute.domain.NodeState; +import org.jclouds.compute.internal.PersistNodeCredentials; +import org.jclouds.domain.Credentials; +import org.jclouds.scriptbuilder.statements.login.AdminAccess; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMap; +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.TypeLiteral; + +/** + * + * @author Adrian Cole + */ +@Test(groups = "unit") +public class PersistNodeCredentialsTest { + + public void testReturnsCorrectFunction() { + PersistNodeCredentials persistNodeCredentials = Guice.createInjector(new PersistNodeCredentialsModule(), + new AbstractModule() { + + @Override + protected void configure() { + bind(new TypeLiteral>() { + }).toInstance(ImmutableMap. of()); + } + + }).getInstance(PersistNodeCredentials.class); + assertEquals(persistNodeCredentials.always(null).getClass(), + PersistNodeCredentialsModule.RefreshCredentialsForNode.class); + assertEquals(persistNodeCredentials.ifAdminAccess(null).getClass(), + PersistNodeCredentialsModule.RefreshCredentialsForNodeIfRanAdminAccess.class); + } + + public void testRefreshCredentialsForNodeIfRanAdminAccessWhenStatementIsNullSameCredentialsAndNoCaching() { + @SuppressWarnings("unchecked") + Map credstore = createMock(Map.class); + + replay(credstore); + + NodeMetadata node = new NodeMetadataBuilder().ids("id").state(NodeState.RUNNING).build(); + RefreshCredentialsForNodeIfRanAdminAccess fn = new PersistNodeCredentialsModule.RefreshCredentialsForNodeIfRanAdminAccess( + credstore, null); + assertEquals(node, fn.apply(node)); + + verify(credstore); + + } + + public void testRefreshCredentialsForNodeWhenStatementIsNullSameCredentialsAndDoesCache() { + @SuppressWarnings("unchecked") + Map credstore = createMock(Map.class); + Credentials credentials = createMock(Credentials.class); + + expect(credstore.put("node#id", credentials)).andReturn(null); + + replay(credstore); + + NodeMetadata node = new NodeMetadataBuilder().ids("id").state(NodeState.RUNNING).credentials(credentials).build(); + RefreshCredentialsForNode fn = new PersistNodeCredentialsModule.RefreshCredentialsForNode(credstore, null); + assertEquals(node, fn.apply(node)); + + verify(credstore); + + } + + public void testRefreshCredentialsForNodeIfRanAdminAccessWhenStatementIsAdminAccessNewCredentialsAndDoesCache() { + @SuppressWarnings("unchecked") + Map credstore = createMock(Map.class); + + AdminAccess statement = createMock(AdminAccess.class); + Credentials credentials = createMock(Credentials.class); + + expect(statement.getAdminCredentials()).andReturn(credentials).atLeastOnce(); + expect(credstore.put("node#id", credentials)).andReturn(null); + + replay(statement); + replay(credstore); + + NodeMetadata node = new NodeMetadataBuilder().ids("id").state(NodeState.RUNNING).build(); + RefreshCredentialsForNodeIfRanAdminAccess fn = new PersistNodeCredentialsModule.RefreshCredentialsForNodeIfRanAdminAccess( + credstore, statement); + assertEquals(fn.apply(node).getCredentials(), credentials); + + verify(statement); + verify(credstore); + + } + + public void testRefreshCredentialsForNodeWhenStatementIsAdminAccessNewCredentialsAndDoesCache() { + @SuppressWarnings("unchecked") + Map credstore = createMock(Map.class); + + AdminAccess statement = createMock(AdminAccess.class); + Credentials credentials = createMock(Credentials.class); + expect(statement.getAdminCredentials()).andReturn(credentials).atLeastOnce(); + expect(credstore.put("node#id", credentials)).andReturn(null); + expect(credstore.put("node#id", credentials)).andReturn(null); // TODO + // optimize + // this + + replay(statement); + replay(credstore); + + NodeMetadata node = new NodeMetadataBuilder().ids("id").state(NodeState.RUNNING).build(); + RefreshCredentialsForNode fn = new PersistNodeCredentialsModule.RefreshCredentialsForNode(credstore, statement); + assertEquals(fn.apply(node).getCredentials(), credentials); + + verify(statement); + verify(credstore); + + } + +} diff --git a/compute/src/test/resources/initscript_with_java.sh b/compute/src/test/resources/initscript_with_java.sh index a3a361c7dc..68db70f4ef 100644 --- a/compute/src/test/resources/initscript_with_java.sh +++ b/compute/src/test/resources/initscript_with_java.sh @@ -76,23 +76,33 @@ END_OF_SCRIPT # add desired commands from the user cat >> $INSTANCE_HOME/bootstrap.sh <<'END_OF_SCRIPT' cd $INSTANCE_HOME -mkdir -p ~/.ssh -cat >> ~/.ssh/authorized_keys <<'END_OF_FILE' -ssh-rsa +rm /etc/sudoers +cat >> /etc/sudoers <<'END_OF_FILE' +root ALL = (ALL) ALL +%wheel ALL = (ALL) NOPASSWD:ALL END_OF_FILE -chmod 600 ~/.ssh/authorized_keys +chmod 0440 /etc/sudoers +mkdir -p /home/users/defaultAdminUsername +groupadd -f wheel +useradd -s /bin/bash -g wheel -d /home/users/defaultAdminUsername -p 'crypt(randompassword)' defaultAdminUsername +mkdir -p /home/users/defaultAdminUsername/.ssh +cat >> /home/users/defaultAdminUsername/.ssh/authorized_keys <<'END_OF_FILE' +publicKey +END_OF_FILE +chmod 600 /home/users/defaultAdminUsername/.ssh/authorized_keys +chown -R defaultAdminUsername /home/users/defaultAdminUsername +exec 3<> /etc/ssh/sshd_config && awk -v TEXT="PasswordAuthentication no +PermitRootLogin no +" 'BEGIN {print TEXT}{print}' /etc/ssh/sshd_config >&3 +/etc/init.d/sshd reload||/etc/init.d/ssh reload +awk -v user=^${SUDO_USER:=${USER}}: -v password='crypt(randompassword)' 'BEGIN { FS=OFS=":" } $0 ~ user { $2 = password } 1' /etc/shadow >/etc/shadow.${SUDO_USER:=${USER}} +test -f /etc/shadow.${SUDO_USER:=${USER}} && mv /etc/shadow.${SUDO_USER:=${USER}} /etc/shadow which curl || (apt-get install -f -y -qq --force-yes curl || (apt-get update && apt-get install -f -y -qq --force-yes curl)) (which java && java -fullversion 2>&1|egrep -q 1.6 ) || curl -X GET -s --retry 20 http://whirr.s3.amazonaws.com/0.2.0-incubating-SNAPSHOT/sun/java/install |(bash) echo nameserver 208.67.222.222 >> /etc/resolv.conf rm -rf /var/cache/apt /usr/lib/vmware-tools echo "export PATH=\"\$JAVA_HOME/bin/:\$PATH\"" >> /root/.bashrc -mkdir -p ~/.ssh -rm ~/.ssh/id_rsa -cat >> ~/.ssh/id_rsa <<'END_OF_FILE' ------BEGIN RSA PRIVATE KEY----- -END_OF_FILE -chmod 600 ~/.ssh/id_rsa END_OF_SCRIPT diff --git a/core/src/main/java/org/jclouds/crypto/Crypto.java b/core/src/main/java/org/jclouds/crypto/Crypto.java index a218a98b28..fb54f1cee3 100644 --- a/core/src/main/java/org/jclouds/crypto/Crypto.java +++ b/core/src/main/java/org/jclouds/crypto/Crypto.java @@ -20,6 +20,7 @@ package org.jclouds.crypto; import java.security.InvalidKeyException; import java.security.KeyFactory; +import java.security.KeyPairGenerator; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateFactory; @@ -31,13 +32,14 @@ import org.jclouds.encryption.internal.JCECrypto; import com.google.inject.ImplementedBy; /** - * Allows you to access cryptographic objects and factories without adding a provider to the JCE - * runtime. + * Allows you to access cryptographic objects and factories without adding a + * provider to the JCE runtime. * * @author Adrian Cole */ @ImplementedBy(JCECrypto.class) public interface Crypto { + KeyPairGenerator rsaKeyPairGenerator(); KeyFactory rsaKeyFactory(); @@ -57,4 +59,6 @@ public interface Crypto { MessageDigest sha256(); + MessageDigest sha512(); + } \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/crypto/Pems.java b/core/src/main/java/org/jclouds/crypto/Pems.java index 02460e7657..e965d12030 100644 --- a/core/src/main/java/org/jclouds/crypto/Pems.java +++ b/core/src/main/java/org/jclouds/crypto/Pems.java @@ -93,8 +93,8 @@ public class Pems { if (parsers.containsKey(reader.getBeginMarker())) { return parsers.get(reader.getBeginMarker()).parseResult(bytes); } else { - throw new IOException(String.format("Invalid PEM file: no parsers for marker %s in %s", reader - .getBeginMarker(), parsers.keySet())); + throw new IOException(String.format("Invalid PEM file: no parsers for marker %s in %s", + reader.getBeginMarker(), parsers.keySet())); } } catch (IOException e) { throw new RuntimeException(e); @@ -103,7 +103,8 @@ public class Pems { } /** - * Returns the object of generic type {@code T} that is pem encoded in the supplier. + * Returns the object of generic type {@code T} that is pem encoded in the + * supplier. * * @param supplier * the input stream factory @@ -111,12 +112,13 @@ public class Pems { * header that begins the PEM block * @param processor * how to parser the object from a byte array - * @return the object of generic type {@code T} which was PEM encoded in the stream + * @return the object of generic type {@code T} which was PEM encoded in the + * stream * @throws IOException * if an I/O error occurs */ public static T fromPem(InputSupplier supplier, PemProcessor processor) - throws IOException { + throws IOException { try { return com.google.common.io.ByteStreams.readBytes(supplier, processor); } catch (RuntimeException e) { @@ -138,25 +140,27 @@ public class Pems { * if an I/O error occurs */ public static KeySpec privateKeySpec(InputSupplier supplier) throws IOException { - return fromPem(supplier, new PemProcessor(ImmutableMap.> of( - PRIVATE_PKCS1_MARKER, new ResultParser() { + return fromPem( + supplier, + new PemProcessor(ImmutableMap.> of(PRIVATE_PKCS1_MARKER, + new ResultParser() { - public KeySpec parseResult(byte[] bytes) throws IOException { - return (new PKCS1EncodedKeySpec(bytes)).getKeySpec(); - } + public KeySpec parseResult(byte[] bytes) throws IOException { + return (new PKCS1EncodedKeySpec(bytes)).getKeySpec(); + } - }, PRIVATE_PKCS8_MARKER, new ResultParser() { + }, PRIVATE_PKCS8_MARKER, new ResultParser() { - public KeySpec parseResult(byte[] bytes) throws IOException { - return new PKCS8EncodedKeySpec(bytes); - } + public KeySpec parseResult(byte[] bytes) throws IOException { + return new PKCS8EncodedKeySpec(bytes); + } - }))); + }))); } /** - * Executes {@link Pems#privateKeySpec(InputSupplier)} on the string which contains an encoded - * private key in PEM format. + * Executes {@link Pems#privateKeySpec(InputSupplier)} on the string which + * contains an encoded private key in PEM format. * * @param pem * private key in pem encoded format. @@ -177,25 +181,27 @@ public class Pems { * if an I/O error occurs */ public static KeySpec publicKeySpec(InputSupplier supplier) throws IOException { - return fromPem(supplier, new PemProcessor(ImmutableMap.> of( - PUBLIC_PKCS1_MARKER, new ResultParser() { + return fromPem( + supplier, + new PemProcessor(ImmutableMap.> of(PUBLIC_PKCS1_MARKER, + new ResultParser() { - public KeySpec parseResult(byte[] bytes) throws IOException { - return (new PKCS1EncodedPublicKeySpec(bytes)).getKeySpec(); - } + public KeySpec parseResult(byte[] bytes) throws IOException { + return (new PKCS1EncodedPublicKeySpec(bytes)).getKeySpec(); + } - }, PUBLIC_X509_MARKER, new ResultParser() { + }, PUBLIC_X509_MARKER, new ResultParser() { - public X509EncodedKeySpec parseResult(byte[] bytes) throws IOException { - return new X509EncodedKeySpec(bytes); - } + public X509EncodedKeySpec parseResult(byte[] bytes) throws IOException { + return new X509EncodedKeySpec(bytes); + } - }))); + }))); } /** - * Executes {@link Pems#publicKeySpec(InputSupplier)} on the string which contains an encoded - * public key in PEM format. + * Executes {@link Pems#publicKeySpec(InputSupplier)} on the string which + * contains an encoded public key in PEM format. * * @param pem * public key in pem encoded format. @@ -206,7 +212,8 @@ public class Pems { } /** - * Returns the {@link X509EncodedKeySpec} that is pem encoded in the supplier. + * Returns the {@link X509EncodedKeySpec} that is pem encoded in the + * supplier. * * @param supplier * the input stream factory @@ -219,24 +226,25 @@ public class Pems { * @throws CertificateException */ public static X509Certificate x509Certificate(InputSupplier supplier, - @Nullable CertificateFactory certFactory) throws IOException, CertificateException { + @Nullable CertificateFactory certFactory) throws IOException, CertificateException { final CertificateFactory finalCertFactory = certFactory != null ? certFactory : CertificateFactory - .getInstance("X.509"); + .getInstance("X.509"); try { - return fromPem(supplier, new PemProcessor(ImmutableMap - .> of(CERTIFICATE_X509_MARKER, - new ResultParser() { + return fromPem( + supplier, + new PemProcessor(ImmutableMap.> of( + CERTIFICATE_X509_MARKER, new ResultParser() { - public X509Certificate parseResult(byte[] bytes) throws IOException { - try { - return (X509Certificate) finalCertFactory - .generateCertificate(new ByteArrayInputStream(bytes)); - } catch (CertificateException e) { - throw new RuntimeException(e); - } - } + public X509Certificate parseResult(byte[] bytes) throws IOException { + try { + return (X509Certificate) finalCertFactory.generateCertificate(new ByteArrayInputStream( + bytes)); + } catch (CertificateException e) { + throw new RuntimeException(e); + } + } - }))); + }))); } catch (RuntimeException e) { if (e.getCause() != null && e.getCause() instanceof CertificateException) { throw (CertificateException) e.getCause(); @@ -246,8 +254,8 @@ public class Pems { } /** - * Executes {@link Pems#x509Certificate(InputSupplier, CertificateFactory)} on the string which - * contains an X.509 certificate in PEM format. + * Executes {@link Pems#x509Certificate(InputSupplier, CertificateFactory)} + * on the string which contains an X.509 certificate in PEM format. * * @param pem * certificate in pem encoded format. @@ -297,14 +305,14 @@ public class Pems { public static String pem(PrivateKey key) { String marker = key instanceof RSAPrivateCrtKey ? PRIVATE_PKCS1_MARKER : PRIVATE_PKCS8_MARKER; return pem(key instanceof RSAPrivateCrtKey ? getEncoded(RSAPrivateCrtKey.class.cast(key)) : key.getEncoded(), - marker); + marker); } // TODO find a way to do this without using bouncycastle static byte[] getEncoded(RSAPrivateCrtKey key) { - RSAPrivateKeyStructure keyStruct = new RSAPrivateKeyStructure(key.getModulus(), key.getPublicExponent(), key - .getPrivateExponent(), key.getPrimeP(), key.getPrimeQ(), key.getPrimeExponentP(), key - .getPrimeExponentQ(), key.getCrtCoefficient()); + RSAPrivateKeyStructure keyStruct = new RSAPrivateKeyStructure(key.getModulus(), key.getPublicExponent(), + key.getPrivateExponent(), key.getPrimeP(), key.getPrimeQ(), key.getPrimeExponentP(), + key.getPrimeExponentQ(), key.getCrtCoefficient()); ByteArrayOutputStream bOut = new ByteArrayOutputStream(); ASN1OutputStream aOut = new ASN1OutputStream(bOut); @@ -320,9 +328,13 @@ public class Pems { } private static String pem(byte[] key, String marker) { - return new StringBuilder(marker + "\n").append( - Joiner.on('\n').join(Splitter.fixedLength(64).split(CryptoStreams.base64(key)))).append( - "\n" + marker.replace("BEGIN", "END") + "\n").toString().trim(); + return pem(key, marker, 64); + } + + static String pem(byte[] key, String marker, int length) { + return new StringBuilder(marker + "\n") + .append(Joiner.on('\n').join(Splitter.fixedLength(length).split(CryptoStreams.base64(key)))) + .append("\n" + marker.replace("BEGIN", "END") + "\n").toString().trim(); } } diff --git a/core/src/main/java/org/jclouds/crypto/Sha512Crypt.java b/core/src/main/java/org/jclouds/crypto/Sha512Crypt.java new file mode 100644 index 0000000000..c352b72c3e --- /dev/null +++ b/core/src/main/java/org/jclouds/crypto/Sha512Crypt.java @@ -0,0 +1,329 @@ +/* + Sha512Crypt.java + + Created: 18 December 2007 + Last Changed By: $Author: broccol $ + Version: $Revision: 7692 $ + Last Mod Date: $Date: 2007-12-30 01:55:31 -0600 (Sun, 30 Dec 2007) $ + + Java Port By: James Ratcliff, falazar@arlut.utexas.edu + + This class implements the new generation, scalable, SHA512-based + Unix 'crypt' algorithm developed by a group of engineers from Red + Hat, Sun, IBM, and HP for common use in the Unix and Linux + /etc/shadow files. + + The Linux glibc library (starting at version 2.7) includes support + for validating passwords hashed using this algorithm. + + The algorithm itself was released into the Public Domain by Ulrich + Drepper . A discussion of the rationale and + development of this algorithm is at + + http://people.redhat.com/drepper/sha-crypt.html + + and the specification and a sample C language implementation is at + + http://people.redhat.com/drepper/SHA-crypt.txt + + This Java Port is + + Copyright (c) 2008 The University of Texas at Austin. + + All rights reserved. + + Redistribution and use in source and binary form are permitted + provided that distributions retain this entire copyright notice + and comment. Neither the name of the University nor the names of + its contributors may be used to endorse or promote products + derived from this software without specific prior written + permission. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE. + + */ + +package org.jclouds.crypto; + +import java.security.MessageDigest; + +import javax.annotation.Nullable; + +import org.jclouds.encryption.internal.JCECrypto; + +import com.google.common.base.Throwables; + +/** + * This class defines a method, + * {@link Sha512Crypt#Sha512_crypt(java.lang.String, java.lang.String, int) + * Sha512_crypt()}, which takes a password and a salt string and generates a + * Sha512 encrypted password entry. + * + * This class implements the new generation, scalable, SHA512-based Unix 'crypt' + * algorithm developed by a group of engineers from Red Hat, Sun, IBM, and HP + * for common use in the Unix and Linux /etc/shadow files. + * + * The Linux glibc library (starting at version 2.7) includes support for + * validating passwords hashed using this algorithm. + * + * The algorithm itself was released into the Public Domain by Ulrich Drepper + * <drepper@redhat.com>. A discussion of the rationale and development of + * this algorithm is at + * + * http://people.redhat.com/drepper/sha-crypt.html + * + * and the specification and a sample C language implementation is at + * + * http://people.redhat.com/drepper/SHA-crypt.txt + */ +public class Sha512Crypt { + public static com.google.common.base.Function function() { + return Function.INSTANCE; + } + + public static enum Function implements com.google.common.base.Function { + INSTANCE; + private Crypto crypto; + + Function() { + try { + this.crypto = new JCECrypto(); + } catch (Exception e) { + Throwables.propagate(e); + } + } + + @Override + public String apply(String input) { + return Sha512Crypt.makeShadowLine(input, null, crypto); + } + + @Override + public String toString() { + return "sha512Crypt()"; + } + + } + + static private final String sha512_salt_prefix = "$6$"; + static private final String sha512_rounds_prefix = "rounds="; + static private final int SALT_LEN_MAX = 16; + static private final int ROUNDS_DEFAULT = 5000; + static private final int ROUNDS_MIN = 1000; + static private final int ROUNDS_MAX = 999999999; + static private final String SALTCHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; + static private final String itoa64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + + /** + * This method actually generates an Sha512 crypted password hash from a + * plaintext password and a salt. + * + *

+ * The resulting string will be in the form + * '$6$<rounds=n>$<salt>$<hashed mess> + *

+ * + * @param password + * Plaintext password + * + * @param shadowPrefix + * An encoded salt/rounds which will be consulted to determine the + * salt and round count, if not null + * + * @return The Sha512 Unix Crypt hash text for the password + */ + public static String makeShadowLine(String password, @Nullable String shadowPrefix, Crypto crypto) { + MessageDigest ctx = crypto.sha512(); + MessageDigest alt_ctx = crypto.sha512(); + + byte[] alt_result; + byte[] temp_result; + byte[] p_bytes = null; + byte[] s_bytes = null; + int cnt, cnt2; + int rounds = ROUNDS_DEFAULT; // Default number of rounds. + StringBuffer buffer; + + /* -- */ + + if (shadowPrefix != null) { + if (shadowPrefix.startsWith(sha512_salt_prefix)) { + shadowPrefix = shadowPrefix.substring(sha512_salt_prefix.length()); + } + + if (shadowPrefix.startsWith(sha512_rounds_prefix)) { + String num = shadowPrefix.substring(sha512_rounds_prefix.length(), shadowPrefix.indexOf('$')); + int srounds = Integer.valueOf(num).intValue(); + shadowPrefix = shadowPrefix.substring(shadowPrefix.indexOf('$') + 1); + rounds = Math.max(ROUNDS_MIN, Math.min(srounds, ROUNDS_MAX)); + } + + if (shadowPrefix.length() > SALT_LEN_MAX) { + shadowPrefix = shadowPrefix.substring(0, SALT_LEN_MAX); + } + } else { + java.util.Random randgen = new java.util.Random(); + StringBuffer saltBuf = new StringBuffer(); + + while (saltBuf.length() < 16) { + int index = (int) (randgen.nextFloat() * SALTCHARS.length()); + saltBuf.append(SALTCHARS.substring(index, index + 1)); + } + + shadowPrefix = saltBuf.toString(); + } + + byte[] key = password.getBytes(); + byte[] salts = shadowPrefix.getBytes(); + + ctx.reset(); + ctx.update(key, 0, key.length); + ctx.update(salts, 0, salts.length); + + alt_ctx.reset(); + alt_ctx.update(key, 0, key.length); + alt_ctx.update(salts, 0, salts.length); + alt_ctx.update(key, 0, key.length); + + alt_result = alt_ctx.digest(); + + for (cnt = key.length; cnt > 64; cnt -= 64) { + ctx.update(alt_result, 0, 64); + } + + ctx.update(alt_result, 0, cnt); + + for (cnt = key.length; cnt > 0; cnt >>= 1) { + if ((cnt & 1) != 0) { + ctx.update(alt_result, 0, 64); + } else { + ctx.update(key, 0, key.length); + } + } + + alt_result = ctx.digest(); + + alt_ctx.reset(); + + for (cnt = 0; cnt < key.length; ++cnt) { + alt_ctx.update(key, 0, key.length); + } + + temp_result = alt_ctx.digest(); + + p_bytes = new byte[key.length]; + + for (cnt2 = 0, cnt = p_bytes.length; cnt >= 64; cnt -= 64) { + System.arraycopy(temp_result, 0, p_bytes, cnt2, 64); + cnt2 += 64; + } + + System.arraycopy(temp_result, 0, p_bytes, cnt2, cnt); + + alt_ctx.reset(); + + for (cnt = 0; cnt < 16 + (alt_result[0] & 0xFF); ++cnt) { + alt_ctx.update(salts, 0, salts.length); + } + + temp_result = alt_ctx.digest(); + + s_bytes = new byte[salts.length]; + + for (cnt2 = 0, cnt = s_bytes.length; cnt >= 64; cnt -= 64) { + System.arraycopy(temp_result, 0, s_bytes, cnt2, 64); + cnt2 += 64; + } + + System.arraycopy(temp_result, 0, s_bytes, cnt2, cnt); + + /* + * Repeatedly run the collected hash value through SHA512 to burn CPU + * cycles. + */ + + for (cnt = 0; cnt < rounds; ++cnt) { + ctx.reset(); + + if ((cnt & 1) != 0) { + ctx.update(p_bytes, 0, key.length); + } else { + ctx.update(alt_result, 0, 64); + } + + if (cnt % 3 != 0) { + ctx.update(s_bytes, 0, salts.length); + } + + if (cnt % 7 != 0) { + ctx.update(p_bytes, 0, key.length); + } + + if ((cnt & 1) != 0) { + ctx.update(alt_result, 0, 64); + } else { + ctx.update(p_bytes, 0, key.length); + } + + alt_result = ctx.digest(); + } + + buffer = new StringBuffer(sha512_salt_prefix); + + if (rounds != 5000) { + buffer.append(sha512_rounds_prefix); + buffer.append(rounds); + buffer.append("$"); + } + + buffer.append(shadowPrefix); + buffer.append("$"); + + buffer.append(b64_from_24bit(alt_result[0], alt_result[21], alt_result[42], 4)); + buffer.append(b64_from_24bit(alt_result[22], alt_result[43], alt_result[1], 4)); + buffer.append(b64_from_24bit(alt_result[44], alt_result[2], alt_result[23], 4)); + buffer.append(b64_from_24bit(alt_result[3], alt_result[24], alt_result[45], 4)); + buffer.append(b64_from_24bit(alt_result[25], alt_result[46], alt_result[4], 4)); + buffer.append(b64_from_24bit(alt_result[47], alt_result[5], alt_result[26], 4)); + buffer.append(b64_from_24bit(alt_result[6], alt_result[27], alt_result[48], 4)); + buffer.append(b64_from_24bit(alt_result[28], alt_result[49], alt_result[7], 4)); + buffer.append(b64_from_24bit(alt_result[50], alt_result[8], alt_result[29], 4)); + buffer.append(b64_from_24bit(alt_result[9], alt_result[30], alt_result[51], 4)); + buffer.append(b64_from_24bit(alt_result[31], alt_result[52], alt_result[10], 4)); + buffer.append(b64_from_24bit(alt_result[53], alt_result[11], alt_result[32], 4)); + buffer.append(b64_from_24bit(alt_result[12], alt_result[33], alt_result[54], 4)); + buffer.append(b64_from_24bit(alt_result[34], alt_result[55], alt_result[13], 4)); + buffer.append(b64_from_24bit(alt_result[56], alt_result[14], alt_result[35], 4)); + buffer.append(b64_from_24bit(alt_result[15], alt_result[36], alt_result[57], 4)); + buffer.append(b64_from_24bit(alt_result[37], alt_result[58], alt_result[16], 4)); + buffer.append(b64_from_24bit(alt_result[59], alt_result[17], alt_result[38], 4)); + buffer.append(b64_from_24bit(alt_result[18], alt_result[39], alt_result[60], 4)); + buffer.append(b64_from_24bit(alt_result[40], alt_result[61], alt_result[19], 4)); + buffer.append(b64_from_24bit(alt_result[62], alt_result[20], alt_result[41], 4)); + buffer.append(b64_from_24bit((byte) 0x00, (byte) 0x00, alt_result[63], 2)); + + /* + * Clear the buffer for the intermediate result so that people attaching + * to processes or reading core dumps cannot get any information. + */ + + ctx.reset(); + + return buffer.toString(); + } + + private static final String b64_from_24bit(byte B2, byte B1, byte B0, int size) { + int v = ((((int) B2) & 0xFF) << 16) | ((((int) B1) & 0xFF) << 8) | ((int) B0 & 0xff); + + StringBuffer result = new StringBuffer(); + + while (--size >= 0) { + result.append(itoa64.charAt((int) (v & 0x3f))); + v >>>= 6; + } + + return result.toString(); + } + +} \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/crypto/SshKeys.java b/core/src/main/java/org/jclouds/crypto/SshKeys.java new file mode 100644 index 0000000000..b9c1768eca --- /dev/null +++ b/core/src/main/java/org/jclouds/crypto/SshKeys.java @@ -0,0 +1,111 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.crypto; + +import java.math.BigInteger; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.util.Map; + +import com.google.common.annotations.Beta; +import com.google.common.base.Throwables; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMap.Builder; +import com.google.common.io.ByteArrayDataOutput; +import com.google.common.io.ByteStreams; + +/** + * Creates OpenSSH RSA keypairs + * + * @author Adrian Cole + * @see + */ +@Beta +public class SshKeys { + // All data type encoding is defined in the section #5 of RFC #4251. string + // and mpint (multiple precision integer) types are encoded this way : + private static final byte[] sshrsa = new byte[] { 0, 0, 0, 7, 's', 's', 'h', '-', 'r', 's', 'a' }; + + /** + * + * @param used + * to generate RSA key pairs + * @return new 2048 bit keyPair + * @see Crypto#rsaKeyPairGenerator() + */ + public static KeyPair generateRsaKeyPair(KeyPairGenerator generator) { + generator.initialize(2048); + return generator.genKeyPair(); + } + + /** + * return a "public" -> rsa public key, "private" -> its corresponding + * private key + */ + public static Map generate() { + try { + return generate(KeyPairGenerator.getInstance("RSA")); + } catch (NoSuchAlgorithmException e) { + Throwables.propagate(e); + return null; + } + } + + public static Map generate(KeyPairGenerator generator) { + KeyPair pair = generateRsaKeyPair(generator); + Builder builder = ImmutableMap. builder(); + builder.put("public", encodeAsOpenSSH(RSAPublicKey.class.cast(pair.getPublic()))); + builder.put("private", encodeAsPem(RSAPrivateKey.class.cast(pair.getPrivate()))); + return builder.build(); + } + + public static String encodeAsOpenSSH(RSAPublicKey key) { + ByteArrayDataOutput out = ByteStreams.newDataOutput(); + /* encode the "ssh-rsa" string */ + out.write(sshrsa); + /* Encode the public exponent */ + BigInteger e = key.getPublicExponent(); + byte[] data = e.toByteArray(); + encodeUint32(data.length, out); + out.write(data); + /* Encode the modulus */ + BigInteger m = key.getModulus(); + data = m.toByteArray(); + encodeUint32(data.length, out); + out.write(data); + return "ssh-rsa " + CryptoStreams.base64(out.toByteArray()); + } + + public static String encodeAsPem(RSAPrivateKey key) { + return Pems.pem(key.getEncoded(), Pems.PRIVATE_PKCS1_MARKER, 64); + } + + public static void encodeUint32(int value, ByteArrayDataOutput out) { + out.write((byte) ((value >>> 24) & 0xff)); + out.write((byte) ((value >>> 16) & 0xff)); + out.write((byte) ((value >>> 8) & 0xff)); + out.write((byte) (value & 0xff)); + } + +} diff --git a/core/src/main/java/org/jclouds/encryption/internal/JCECrypto.java b/core/src/main/java/org/jclouds/encryption/internal/JCECrypto.java index 914cd7974e..1363e65c5b 100755 --- a/core/src/main/java/org/jclouds/encryption/internal/JCECrypto.java +++ b/core/src/main/java/org/jclouds/encryption/internal/JCECrypto.java @@ -20,6 +20,7 @@ package org.jclouds.encryption.internal; import java.security.InvalidKeyException; import java.security.KeyFactory; +import java.security.KeyPairGenerator; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.Provider; @@ -41,6 +42,7 @@ import org.jclouds.crypto.Crypto; @Singleton public class JCECrypto implements Crypto { + private final KeyPairGenerator rsaKeyPairGenerator; private final KeyFactory rsaKeyFactory; private final CertificateFactory certFactory; private final Provider provider; @@ -51,9 +53,11 @@ public class JCECrypto implements Crypto { } public JCECrypto(@Nullable Provider provider) throws NoSuchAlgorithmException, CertificateException { + this.rsaKeyPairGenerator = provider == null ? KeyPairGenerator.getInstance("RSA") : KeyPairGenerator.getInstance( + "RSA", provider); this.rsaKeyFactory = provider == null ? KeyFactory.getInstance("RSA") : KeyFactory.getInstance("RSA", provider); this.certFactory = provider == null ? CertificateFactory.getInstance("X.509") : CertificateFactory.getInstance( - "X.509", provider); + "X.509", provider); this.provider = provider; } @@ -73,7 +77,8 @@ public class JCECrypto implements Crypto { public final static String MD5 = "MD5"; public final static String SHA1 = "SHA1"; - public final static String SHA256 = "SHA256"; + public final static String SHA256 = "SHA-256"; + public final static String SHA512 = "SHA-512"; @Override public MessageDigest md5() { @@ -98,7 +103,16 @@ public class JCECrypto implements Crypto { try { return digest(SHA256); } catch (NoSuchAlgorithmException e) { - throw new IllegalStateException("SHA256 must be supported", e); + throw new IllegalStateException(SHA256 + " must be supported", e); + } + } + + @Override + public MessageDigest sha512() { + try { + return digest(SHA512); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException(SHA512 + " must be supported", e); } } @@ -132,4 +146,9 @@ public class JCECrypto implements Crypto { public KeyFactory rsaKeyFactory() { return rsaKeyFactory; } + + @Override + public KeyPairGenerator rsaKeyPairGenerator() { + return rsaKeyPairGenerator; + } } diff --git a/core/src/main/java/org/jclouds/json/config/GsonModule.java b/core/src/main/java/org/jclouds/json/config/GsonModule.java index 1760798702..e0353efc7c 100644 --- a/core/src/main/java/org/jclouds/json/config/GsonModule.java +++ b/core/src/main/java/org/jclouds/json/config/GsonModule.java @@ -20,8 +20,10 @@ package org.jclouds.json.config; import java.lang.reflect.Type; import java.util.Date; +import java.util.Enumeration; import java.util.List; import java.util.Map; +import java.util.Properties; import javax.inject.Inject; import javax.inject.Singleton; @@ -33,6 +35,8 @@ import org.jclouds.json.Json; import org.jclouds.json.internal.EnumTypeAdapterThatReturnsFromValue; import org.jclouds.json.internal.GsonWrapper; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMap.Builder; import com.google.common.collect.Maps; import com.google.common.primitives.Bytes; import com.google.gson.Gson; @@ -51,6 +55,7 @@ import com.google.gson.reflect.TypeToken; import com.google.inject.AbstractModule; import com.google.inject.ImplementedBy; import com.google.inject.Provides; +import com.google.inject.TypeLiteral; /** * Contains logic for parsing objects from Strings. @@ -63,13 +68,15 @@ public class GsonModule extends AbstractModule { @Provides @Singleton Gson provideGson(JsonBallAdapter jsonAdapter, DateAdapter adapter, ByteListAdapter byteListAdapter, - ByteArrayAdapter byteArrayAdapter, JsonAdapterBindings bindings) throws ClassNotFoundException, Exception { + ByteArrayAdapter byteArrayAdapter, SerializePropertiesDefaults propertiesAdapter, JsonAdapterBindings bindings) + throws ClassNotFoundException, Exception { GsonBuilder builder = new GsonBuilder(); JcloudsGsonPackageAccessor.registerTypeHierarchyAdapter(builder, Enum.class, new EnumTypeAdapterThatReturnsFromValue()); JcloudsGsonPackageAccessor.registerTypeHierarchyAdapter(builder, Map.class, new MapTypeAdapter()); builder.registerTypeAdapter(JsonBall.class, jsonAdapter); builder.registerTypeAdapter(Date.class, adapter); + builder.registerTypeAdapter(Properties.class, propertiesAdapter); builder.registerTypeAdapter(new TypeToken>() { }.getType(), byteListAdapter); builder.registerTypeAdapter(byte[].class, byteArrayAdapter); @@ -169,6 +176,28 @@ public class GsonModule extends AbstractModule { } + @Singleton + public static class SerializePropertiesDefaults implements JsonSerializer { + private final Json json; + private final Type mapType = new TypeLiteral>() { + }.getRawType(); + + @Inject + public SerializePropertiesDefaults(Json json) { + this.json = json; + } + + public JsonElement serialize(Properties src, Type typeOfSrc, JsonSerializationContext context) { + Builder srcMap = ImmutableMap. builder(); + for (Enumeration propNames = src.propertyNames(); propNames.hasMoreElements();) { + String propName = (String) propNames.nextElement(); + srcMap.put(propName, src.getProperty(propName)); + } + return new JsonLiteral(json.toJson(srcMap.build(), mapType)); + } + + } + @Singleton public static class CDateAdapter implements DateAdapter { private final DateService dateService; diff --git a/core/src/main/java/org/jclouds/location/config/ProvideRegionToURIViaProperties.java b/core/src/main/java/org/jclouds/location/config/ProvideRegionToURIViaProperties.java index 00da7634fe..442ba62b66 100644 --- a/core/src/main/java/org/jclouds/location/config/ProvideRegionToURIViaProperties.java +++ b/core/src/main/java/org/jclouds/location/config/ProvideRegionToURIViaProperties.java @@ -24,14 +24,17 @@ import static org.jclouds.location.reference.LocationConstants.PROPERTY_REGIONS; import java.net.URI; import java.util.Map; +import java.util.Map.Entry; import javax.inject.Inject; +import javax.inject.Named; import javax.inject.Singleton; import org.jclouds.location.Region; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Multimap; import com.google.common.collect.ImmutableMap.Builder; import com.google.inject.ConfigurationException; import com.google.inject.Injector; @@ -48,10 +51,12 @@ import com.google.inject.name.Names; public class ProvideRegionToURIViaProperties implements javax.inject.Provider> { private final Injector injector; + private final Multimap constants; @Inject - ProvideRegionToURIViaProperties(Injector injector) { + protected ProvideRegionToURIViaProperties(Injector injector, @Named("CONSTANTS") Multimap constants) { this.injector = injector; + this.constants = constants; } @Singleton @@ -62,8 +67,13 @@ public class ProvideRegionToURIViaProperties implements javax.inject.Provider regions = ImmutableMap. builder(); for (String region : Splitter.on(',').split(regionString)) { - regions.put(region, URI.create(injector.getInstance(Key.get(String.class, Names.named(PROPERTY_REGION + "." - + region + "." + ENDPOINT))))); + String regionUri = injector.getInstance(Key.get(String.class, Names.named(PROPERTY_REGION + "." + region + + "." + ENDPOINT))); + for (Entry entry : constants.entries()) { + regionUri = regionUri.replace(new StringBuilder().append('{').append(entry.getKey()).append('}').toString(), entry + .getValue()); + } + regions.put(region, URI.create(regionUri)); } return regions.build(); } catch (ConfigurationException e) { diff --git a/core/src/main/java/org/jclouds/location/functions/RegionToEndpointOrProviderIfNull.java b/core/src/main/java/org/jclouds/location/functions/RegionToEndpointOrProviderIfNull.java index cb8d73be51..64bef28b9b 100644 --- a/core/src/main/java/org/jclouds/location/functions/RegionToEndpointOrProviderIfNull.java +++ b/core/src/main/java/org/jclouds/location/functions/RegionToEndpointOrProviderIfNull.java @@ -62,7 +62,6 @@ public class RegionToEndpointOrProviderIfNull implements Function { + ", but only the default location " + defaultProvider + " is configured"); checkArgument(from.equals(defaultProvider) || (regionToEndpoint != null && regionToEndpoint.containsKey(from)), "requested location %s, which is not in the configured locations: %s", from, regionToEndpoint); - return regionToEndpoint.get(from); } } \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/providers/ProviderMetadata.java b/core/src/main/java/org/jclouds/providers/ProviderMetadata.java new file mode 100644 index 0000000000..2866aebdcc --- /dev/null +++ b/core/src/main/java/org/jclouds/providers/ProviderMetadata.java @@ -0,0 +1,69 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.providers; + +import java.net.URI; + +/** + * The ProviderMetadata interface allows jclouds to provide a plugin framework + * for gathering cloud provider metadata. + * + * @author Jeremy Whitlock + */ +public interface ProviderMetadata { + + public static final String BLOBSTORE_TYPE = "blobstore"; + public static final String COMPUTE_TYPE = "compute"; + + /** + * Returns an identifier unique to the provider. + * + * @return the provider's unique identifier + */ + public String getId(); + + /** + * Returns the provider type. + * + * @return the provider's type + */ + public String getType(); + + /** + * Returns the name of the provider. + * + * @return the name (display name) of the provider + */ + public String getName(); + + /** + * Returns the URI to the provider's homepage. + * + * @return the url for the provider's homepage + */ + public URI getHomepage(); + + /** + * Returns the URI to the provider's console. + * + * @return the url for the provider's console + */ + public URI getConsole(); + +} \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/providers/ProviderPredicates.java b/core/src/main/java/org/jclouds/providers/ProviderPredicates.java new file mode 100644 index 0000000000..e4d1067c5b --- /dev/null +++ b/core/src/main/java/org/jclouds/providers/ProviderPredicates.java @@ -0,0 +1,100 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.providers; + +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; + +import org.jclouds.util.Preconditions2; + +/** + * Container for provider filters (predicates). + * + * @author Jeremy Whitlock + */ +public class ProviderPredicates { + + /** + * Returns all providers available to jclouds regardless of type. + * + * @return all available providers + */ + public static Predicate all() { + return Predicates. alwaysTrue(); + } + + /** + * Returns all providers with the given id. + * + * @param id + * the id of the provider to return + * + * @return the providers with the given id + */ + public static Predicate id(final String id) { + Preconditions2.checkNotEmpty(id, "id must be defined"); + return new Predicate() { + /** + * {@see com.google.common.base.Predicate#apply(T) + */ + @Override + public boolean apply(ProviderMetadata providerMetadata) { + return providerMetadata.getId().equals(id); + } + + /** + * {@see java.lang.Object#toString() + */ + @Override + public String toString() { + return "id(" + id + ")"; + } + }; + } + + /** + * Returns all providers with the given type. + * + * @param type + * the type of the provider to return + * + * @return the providers with the given type + */ + public static Predicate type(final String type) { + Preconditions2.checkNotEmpty(type, "type must be defined"); + return new Predicate() { + /** + * {@see com.google.common.base.Predicate#apply(T) + */ + @Override + public boolean apply(ProviderMetadata providerMetadata) { + return providerMetadata.getType().equals(type); + } + + /** + * {@see java.lang.Object#toString() + */ + @Override + public String toString() { + return "type(" + type + ")"; + } + }; + } + +} \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/providers/Providers.java b/core/src/main/java/org/jclouds/providers/Providers.java new file mode 100644 index 0000000000..7688fe99fe --- /dev/null +++ b/core/src/main/java/org/jclouds/providers/Providers.java @@ -0,0 +1,101 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.providers; + +import static com.google.common.collect.Iterables.filter; +import static com.google.common.collect.Iterables.find; + +import java.util.NoSuchElementException; +import java.util.ServiceLoader; + +/** + * The Providers class provides static methods for accessing providers. + * + * @author Jeremy Whitlock + */ +public class Providers { + + /** + * Returns the providers located on the classpath via + * {@link java.util.ServiceLoader}. + * + * @return all available providers loaded from classpath via ServiceLoader + */ + private static Iterable fromServiceLoader() { + return ServiceLoader.load(ProviderMetadata.class); + } + + /** + * Returns all available providers. + * + * @return all available providers + */ + public static Iterable all() { + return fromServiceLoader(); + } + + /** + * Returns the first provider with the provided id + * + * @param id + * the id of the provider to return + * + * @return the provider with the given id + * + * @throws NoSuchElementException + * whenever there are no providers with the provided id + */ + public static ProviderMetadata withId(String id) + throws NoSuchElementException { + return find(all(), ProviderPredicates.id(id)); + } + + /** + * Returns the providers that are of type + * {@link org.jclouds.providers.ProviderMetadata#BLOBSTORE_TYPE}. + * + * @return the blobstore providers + */ + public static Iterable allBlobStore() { + return filter(all(), ProviderPredicates.type(ProviderMetadata.BLOBSTORE_TYPE)); + } + + /** + * Returns the providers that are of type + * {@link org.jclouds.providers.ProviderMetadata#COMPUTE_TYPE}. + * + * @return the compute service providers + */ + public static Iterable allCompute() { + return filter(all(), ProviderPredicates.type(ProviderMetadata.COMPUTE_TYPE)); + } + + /** + * Returns the providers that are of the provided type. + * + * @param type + * the type to providers to return + * + * @return the providers of the provided type + */ + public static Iterable ofType(String type) { + return filter(all(), ProviderPredicates.type(type)); + } + +} diff --git a/core/src/main/java/org/jclouds/rest/RestContextBuilder.java b/core/src/main/java/org/jclouds/rest/RestContextBuilder.java index 4364d6e28a..80888aafb0 100755 --- a/core/src/main/java/org/jclouds/rest/RestContextBuilder.java +++ b/core/src/main/java/org/jclouds/rest/RestContextBuilder.java @@ -43,8 +43,11 @@ import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; +import java.util.Map.Entry; import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; import org.jclouds.concurrent.MoreExecutors; import org.jclouds.concurrent.SingleThreaded; @@ -69,12 +72,16 @@ import org.jclouds.rest.internal.RestContextImpl; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.LinkedHashMultimap; +import com.google.common.collect.Multimap; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.Module; +import com.google.inject.Provides; import com.google.inject.TypeLiteral; /** @@ -99,6 +106,17 @@ public class RestContextBuilder { this.properties = checkNotNull(properties, "properties"); } + @Provides + @Singleton + @Named("CONSTANTS") + protected Multimap constants() { + ImmutableMultimap.Builder builder = ImmutableMultimap. builder(); + for (Entry entry : properties.entrySet()) + if (entry.getValue() != null) + builder.put(entry.getKey().toString(), entry.getValue().toString()); + return LinkedHashMultimap.create(builder.build()); + } + @Override protected void configure() { Properties toBind = new Properties(); diff --git a/core/src/main/java/org/jclouds/rest/annotations/WrapWith.java b/core/src/main/java/org/jclouds/rest/annotations/WrapWith.java new file mode 100644 index 0000000000..51a13fc926 --- /dev/null +++ b/core/src/main/java/org/jclouds/rest/annotations/WrapWith.java @@ -0,0 +1,42 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.rest.annotations; + +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Wraps the payload in json nested one level deep, relating to the value parameter. + * + * ex. "bar" becomes { "foo" :"bar" } + * + * @author Adrian Cole + */ +@Target(PARAMETER) +@Retention(RUNTIME) +public @interface WrapWith { + + /** + * what to wrap the value in + */ + String value(); +} diff --git a/core/src/main/java/org/jclouds/rest/binders/BindToJsonPayload.java b/core/src/main/java/org/jclouds/rest/binders/BindToJsonPayload.java index 1a91725b0b..df0664e986 100644 --- a/core/src/main/java/org/jclouds/rest/binders/BindToJsonPayload.java +++ b/core/src/main/java/org/jclouds/rest/binders/BindToJsonPayload.java @@ -18,7 +18,7 @@ */ package org.jclouds.rest.binders; -import static com.google.common.base.Preconditions.checkState; +import static com.google.common.base.Preconditions.checkNotNull; import java.util.Map; @@ -36,8 +36,12 @@ import org.jclouds.rest.MapBinder; */ public class BindToJsonPayload implements MapBinder { + protected final Json jsonBinder; + @Inject - protected Json jsonBinder; + public BindToJsonPayload(Json jsonBinder) { + this.jsonBinder = checkNotNull(jsonBinder, "jsonBinder"); + } @Override public R bindToRequest(R request, Map postParams) { @@ -46,8 +50,7 @@ public class BindToJsonPayload implements MapBinder { @Override public R bindToRequest(R request, Object payload) { - checkState(jsonBinder != null, "Program error: json should have been injected at this point"); - String json = jsonBinder.toJson(payload); + String json = jsonBinder.toJson(checkNotNull(payload, "payload")); request.setPayload(json); request.getPayload().getContentMetadata().setContentType(MediaType.APPLICATION_JSON); return request; diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/binders/BindBackupScheduleToJsonPayload.java b/core/src/main/java/org/jclouds/rest/binders/BindToJsonPayloadWrappedWith.java similarity index 52% rename from sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/binders/BindBackupScheduleToJsonPayload.java rename to core/src/main/java/org/jclouds/rest/binders/BindToJsonPayloadWrappedWith.java index 30b2916f6e..33a694ae41 100644 --- a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/binders/BindBackupScheduleToJsonPayload.java +++ b/core/src/main/java/org/jclouds/rest/binders/BindToJsonPayloadWrappedWith.java @@ -16,38 +16,41 @@ * limitations under the License. * ==================================================================== */ -package org.jclouds.openstack.nova.binders; +package org.jclouds.rest.binders; -import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; -import java.util.Map; - -import javax.inject.Singleton; +import javax.inject.Inject; import org.jclouds.http.HttpRequest; -import org.jclouds.openstack.nova.domain.BackupSchedule; -import org.jclouds.rest.binders.BindToJsonPayload; +import org.jclouds.rest.Binder; import com.google.common.collect.ImmutableMap; +import com.google.inject.assistedinject.Assisted; /** + * Sometimes, cloud apis wrap requests inside an envelope. This addresses this. * * @author Adrian Cole - * */ -@Singleton -public class BindBackupScheduleToJsonPayload extends BindToJsonPayload { +public class BindToJsonPayloadWrappedWith implements Binder { - @Override - public R bindToRequest(R request, Map postParams) { - throw new IllegalStateException( - "Replace Backup Schedule needs an BackupSchedule object, not a Map"); + public static interface Factory { + BindToJsonPayloadWrappedWith create(String envelope); + } + + private final BindToJsonPayload jsonBinder; + private final String envelope; + + @Inject + BindToJsonPayloadWrappedWith(BindToJsonPayload jsonBinder, @Assisted String envelope) { + this.jsonBinder = checkNotNull(jsonBinder, "jsonBinder"); + this.envelope = checkNotNull(envelope, "envelope"); } @Override - public R bindToRequest(R request, Object toBind) { - checkArgument(toBind instanceof BackupSchedule, - "this binder is only valid for BackupSchedules!"); - return super.bindToRequest(request, ImmutableMap.of("backupSchedule", toBind)); + public R bindToRequest(R request, Object payload) { + return jsonBinder.bindToRequest(request, (Object) ImmutableMap.of(envelope, checkNotNull(payload, "payload"))); } -} + +} \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/rest/config/RestModule.java b/core/src/main/java/org/jclouds/rest/config/RestModule.java index b89aa578a1..64c82c70eb 100755 --- a/core/src/main/java/org/jclouds/rest/config/RestModule.java +++ b/core/src/main/java/org/jclouds/rest/config/RestModule.java @@ -36,8 +36,10 @@ import org.jclouds.json.config.GsonModule; import org.jclouds.rest.AsyncClientFactory; import org.jclouds.rest.HttpAsyncClient; import org.jclouds.rest.HttpClient; +import org.jclouds.rest.binders.BindToJsonPayloadWrappedWith; import org.jclouds.rest.internal.AsyncRestClientProxy; import org.jclouds.rest.internal.RestAnnotationProcessor; +import org.jclouds.rest.internal.SeedAnnotationCache; import com.google.common.base.Function; import com.google.common.collect.ImmutableMap; @@ -49,6 +51,7 @@ import com.google.inject.Key; import com.google.inject.Provides; import com.google.inject.Scopes; import com.google.inject.TypeLiteral; +import com.google.inject.assistedinject.FactoryModuleBuilder; import com.google.inject.name.Names; import com.google.inject.util.Types; import com.sun.jersey.api.uri.UriBuilderImpl; @@ -63,12 +66,19 @@ public class RestModule extends AbstractModule { protected void configure() { install(new SaxParserModule()); install(new GsonModule()); + install(new FactoryModuleBuilder().build(BindToJsonPayloadWrappedWith.Factory.class)); bind(IdentityFunction.class).toInstance(IdentityFunction.INSTANCE); bind(UriBuilder.class).to(UriBuilderImpl.class); bind(AsyncRestClientProxy.Factory.class).to(Factory.class).in(Scopes.SINGLETON); BinderUtils.bindAsyncClient(binder(), HttpAsyncClient.class); - BinderUtils.bindClient(binder(), HttpClient.class, HttpAsyncClient.class, - ImmutableMap., Class> of(HttpClient.class, HttpAsyncClient.class)); + BinderUtils.bindClient(binder(), HttpClient.class, HttpAsyncClient.class, ImmutableMap., Class> of( + HttpClient.class, HttpAsyncClient.class)); + } + + @Provides + @Singleton + protected ConcurrentMap, Boolean> seedAnnotationCache(SeedAnnotationCache seedAnnotationCache) { + return new MapMaker().makeComputingMap(seedAnnotationCache); } @Provides @@ -88,19 +98,19 @@ public class RestModule extends AbstractModule { this.factory = factory; } - @SuppressWarnings({ "unchecked", "rawtypes" }) + @SuppressWarnings( { "unchecked", "rawtypes" }) @Override public Object apply(final ClassMethodArgs from) { Class clazz = from.getAsyncClass(); TypeLiteral typeLiteral = TypeLiteral.get(clazz); RestAnnotationProcessor util = (RestAnnotationProcessor) injector.getInstance(Key.get(TypeLiteral.get(Types - .newParameterizedType(RestAnnotationProcessor.class, clazz)))); + .newParameterizedType(RestAnnotationProcessor.class, clazz)))); // cannot use child injectors due to the super coarse guice lock on // Singleton util.setCaller(from); ConcurrentMap delegateMap = injector.getInstance(Key.get( - new TypeLiteral>() { - }, Names.named("async"))); + new TypeLiteral>() { + }, Names.named("async"))); AsyncRestClientProxy proxy = new AsyncRestClientProxy(injector, factory, util, typeLiteral, delegateMap); injector.injectMembers(proxy); return AsyncClientFactory.create(clazz, proxy); @@ -111,10 +121,9 @@ public class RestModule extends AbstractModule { @Inject private TransformingHttpCommandExecutorService executorService; - @SuppressWarnings({ "unchecked", "rawtypes" }) + @SuppressWarnings( { "unchecked", "rawtypes" }) @Override - public TransformingHttpCommand create(HttpRequest request, - Function transformer) { + public TransformingHttpCommand create(HttpRequest request, Function transformer) { return new TransformingHttpCommandImpl(executorService, request, transformer); } diff --git a/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java b/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java index 29b0f2e34e..d1d3527fb9 100755 --- a/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java +++ b/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java @@ -23,13 +23,9 @@ import static com.google.common.collect.Collections2.filter; import static com.google.common.collect.Iterables.concat; import static com.google.common.collect.Iterables.get; import static com.google.common.collect.Iterables.transform; -import static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.Lists.newLinkedList; import static com.google.common.collect.Maps.filterValues; import static com.google.common.collect.Maps.newHashMap; -import static com.google.common.collect.Maps.newLinkedHashMap; -import static com.google.common.collect.Sets.difference; -import static com.google.common.collect.Sets.newHashSet; import static com.google.common.collect.Sets.newTreeSet; import static java.util.Arrays.asList; import static javax.ws.rs.core.HttpHeaders.ACCEPT; @@ -48,12 +44,12 @@ import java.net.URI; import java.util.Collection; import java.util.Collections; import java.util.Comparator; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.Set; import java.util.SortedSet; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentMap; import javax.annotation.Nullable; import javax.annotation.Resource; @@ -80,7 +76,6 @@ import org.jclouds.http.HttpResponse; import org.jclouds.http.HttpUtils; import org.jclouds.http.functions.ParseJson; import org.jclouds.http.functions.ParseSax; -import org.jclouds.http.functions.ParseSax.HandlerWithResult; import org.jclouds.http.functions.ParseURIFromListOrLocationHeaderIf20x; import org.jclouds.http.functions.ReleasePayloadAndReturn; import org.jclouds.http.functions.ReturnInputStream; @@ -89,6 +84,7 @@ import org.jclouds.http.functions.ReturnTrueIf2xx; import org.jclouds.http.functions.UnwrapOnlyJsonValue; import org.jclouds.http.functions.UnwrapOnlyNestedJsonValue; import org.jclouds.http.functions.UnwrapOnlyNestedJsonValueInSet; +import org.jclouds.http.functions.ParseSax.HandlerWithResult; import org.jclouds.http.options.HttpRequestOptions; import org.jclouds.http.utils.ModifyRequest; import org.jclouds.internal.ClassMethodArgs; @@ -104,7 +100,6 @@ import org.jclouds.rest.Binder; import org.jclouds.rest.InputParamValidator; import org.jclouds.rest.InvocationContext; import org.jclouds.rest.annotations.BinderParam; -import org.jclouds.rest.annotations.Delegate; import org.jclouds.rest.annotations.Endpoint; import org.jclouds.rest.annotations.EndpointParam; import org.jclouds.rest.annotations.ExceptionParser; @@ -123,30 +118,33 @@ import org.jclouds.rest.annotations.ResponseParser; import org.jclouds.rest.annotations.SkipEncoding; import org.jclouds.rest.annotations.Unwrap; import org.jclouds.rest.annotations.VirtualHost; +import org.jclouds.rest.annotations.WrapWith; import org.jclouds.rest.annotations.XMLResponseParser; import org.jclouds.rest.binders.BindMapToStringPayload; +import org.jclouds.rest.binders.BindToJsonPayloadWrappedWith; import org.jclouds.rest.functions.MapHttp4xxCodesToExceptions; +import org.jclouds.util.Maps2; import org.jclouds.util.Strings2; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.base.Predicates; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.LinkedListMultimap; import com.google.common.collect.Lists; import com.google.common.collect.MapMaker; import com.google.common.collect.Multimap; -import com.google.common.collect.Sets; +import com.google.common.collect.ImmutableSet.Builder; import com.google.common.util.concurrent.ListenableFuture; import com.google.inject.Inject; import com.google.inject.Injector; import com.google.inject.Key; -import com.google.inject.Provides; import com.google.inject.TypeLiteral; -import com.google.inject.name.Names; import com.google.inject.util.Types; /** @@ -161,7 +159,9 @@ public class RestAnnotationProcessor { private final Class declaring; - static final Map>> methodToIndexOfParamToDecoratorParamAnnotation = createMethodToIndexOfParamToAnnotation(BinderParam.class); + // TODO replace with Table object + static final Map>> methodToIndexOfParamToBinderParamAnnotation = createMethodToIndexOfParamToAnnotation(BinderParam.class); + static final Map>> methodToIndexOfParamToWrapWithAnnotation = createMethodToIndexOfParamToAnnotation(WrapWith.class); static final Map>> methodToIndexOfParamToHeaderParamAnnotations = createMethodToIndexOfParamToAnnotation(HeaderParam.class); static final Map>> methodToIndexOfParamToEndpointAnnotations = createMethodToIndexOfParamToAnnotation(Endpoint.class); static final Map>> methodToIndexOfParamToEndpointParamAnnotations = createMethodToIndexOfParamToAnnotation(EndpointParam.class); @@ -175,7 +175,7 @@ public class RestAnnotationProcessor { static final Map delegationMap = newHashMap(); static Map>> createMethodToIndexOfParamToAnnotation( - final Class annotation) { + final Class annotation) { return new MapMaker().makeComputingMap(new Function>>() { public Map> apply(final Method method) { return new MapMaker().makeComputingMap(new GetAnnotationsForMethodParameterIndex(method, annotation)); @@ -193,23 +193,18 @@ public class RestAnnotationProcessor { } public Set apply(final Integer index) { - Set keys = new HashSet(); - List parameterAnnotations = newArrayList(method.getParameterAnnotations()[index]); - Collection filtered = filter(parameterAnnotations, new Predicate() { - public boolean apply(Annotation input) { - return input.annotationType().equals(clazz); - } - }); - for (Annotation annotation : filtered) { - keys.add(annotation); - } - return keys; + return ImmutableSet. copyOf(filter(ImmutableList.copyOf(method.getParameterAnnotations()[index]), + new Predicate() { + public boolean apply(Annotation input) { + return input.annotationType().equals(clazz); + } + })); } } private static final Class optionsVarArgsClass = new HttpRequestOptions[] {} - .getClass(); + .getClass(); private static final Function, ? extends Part> ENTRY_TO_PART = new Function, Part>() { @@ -220,24 +215,24 @@ public class RestAnnotationProcessor { }; - private final Map> methodToIndexesOfOptions = new MapMaker() - .makeComputingMap(new Function>() { - public Set apply(final Method method) { - Set toReturn = newHashSet(); - for (int index = 0; index < method.getParameterTypes().length; index++) { - Class type = method.getParameterTypes()[index]; - if (HttpRequestOptions.class.isAssignableFrom(type) || optionsVarArgsClass.isAssignableFrom(type)) - toReturn.add(index); + static final Map> methodToIndexesOfOptions = new MapMaker() + .makeComputingMap(new Function>() { + public Set apply(final Method method) { + Builder toReturn = ImmutableSet. builder(); + for (int index = 0; index < method.getParameterTypes().length; index++) { + Class type = method.getParameterTypes()[index]; + if (HttpRequestOptions.class.isAssignableFrom(type) || optionsVarArgsClass.isAssignableFrom(type)) + toReturn.add(index); + } + return toReturn.build(); } - return toReturn; - } - }); + }); private final ParseSax.Factory parserFactory; private final HttpUtils utils; private final Provider uriBuilderProvider; + private final ConcurrentMap, Boolean> seedAnnotationCache; private final String apiVersion; - private char[] skips; @Inject @@ -250,7 +245,7 @@ public class RestAnnotationProcessor { @VisibleForTesting public static Function createResponseParser(ParseSax.Factory parserFactory, Injector injector, - Method method, HttpRequest request) { + Method method, HttpRequest request) { Function transformer; Class> handler = getSaxResponseParserClassOrNull(method); if (handler != null) { @@ -271,7 +266,7 @@ public class RestAnnotationProcessor { @VisibleForTesting public static Function createExceptionParserOrThrowResourceNotFoundOn404IfNoAnnotation( - Injector injector, Method method) { + Injector injector, Method method) { ExceptionParser annotation = method.getAnnotation(ExceptionParser.class); if (annotation != null) { return injector.getInstance(annotation.value()); @@ -281,60 +276,28 @@ public class RestAnnotationProcessor { @SuppressWarnings("unchecked") @Inject - public RestAnnotationProcessor(Injector injector, ParseSax.Factory parserFactory, HttpUtils utils, - TypeLiteral typeLiteral) { + public RestAnnotationProcessor(Injector injector, ConcurrentMap, Boolean> seedAnnotationCache, + @Named(Constants.PROPERTY_API_VERSION) String apiVersion, ParseSax.Factory parserFactory, HttpUtils utils, + TypeLiteral typeLiteral) { this.declaring = (Class) typeLiteral.getRawType(); this.injector = injector; this.parserFactory = parserFactory; this.utils = utils; this.uriBuilderProvider = injector.getProvider(UriBuilder.class); - seedCache(declaring); + this.seedAnnotationCache = seedAnnotationCache; + seedAnnotationCache.get(declaring); if (declaring.isAnnotationPresent(SkipEncoding.class)) { skips = declaring.getAnnotation(SkipEncoding.class).value(); } else { skips = new char[] {}; } - this.apiVersion = injector.getInstance(Key.get(String.class, Names.named(Constants.PROPERTY_API_VERSION))); + this.apiVersion = apiVersion; } public Method getDelegateOrNull(Method in) { return delegationMap.get(new MethodKey(in)); } - private void seedCache(Class declaring) { - Set methods = newHashSet(declaring.getMethods()); - methods = difference(methods, newHashSet(Object.class.getMethods())); - for (Method method : methods) { - if (isHttpMethod(method)) { - for (int index = 0; index < method.getParameterTypes().length; index++) { - methodToIndexOfParamToDecoratorParamAnnotation.get(method).get(index); - methodToIndexOfParamToHeaderParamAnnotations.get(method).get(index); - methodToIndexOfParamToMatrixParamAnnotations.get(method).get(index); - methodToIndexOfParamToFormParamAnnotations.get(method).get(index); - methodToIndexOfParamToQueryParamAnnotations.get(method).get(index); - methodToIndexOfParamToEndpointAnnotations.get(method).get(index); - methodToIndexOfParamToEndpointParamAnnotations.get(method).get(index); - methodToIndexOfParamToPathParamAnnotations.get(method).get(index); - methodToIndexOfParamToPostParamAnnotations.get(method).get(index); - methodToIndexOfParamToParamParserAnnotations.get(method).get(index); - methodToIndexOfParamToPartParamAnnotations.get(method).get(index); - methodToIndexesOfOptions.get(method); - } - delegationMap.put(new MethodKey(method), method); - } else if (isConstantDeclaration(method)) { - bindConstant(method); - } else if (!method.getDeclaringClass().equals(declaring)) { - logger.trace("skipping potentially overridden method %s", method); - } else if (method.isAnnotationPresent(Delegate.class)) { - logger.trace("skipping delegate method %s", method); - } else if (method.isAnnotationPresent(Provides.class)) { - logger.trace("skipping provider method %s", method); - } else { - logger.trace("Method is not annotated as either http or constant: %s", method); - } - } - } - public static class MethodKey { @Override @@ -389,7 +352,7 @@ public class RestAnnotationProcessor { private URI callerEndpoint; public void setCaller(ClassMethodArgs caller) { - seedCache(caller.getMethod().getDeclaringClass()); + seedAnnotationCache.get(caller.getMethod().getDeclaringClass()); this.caller = caller; try { callerEndpoint = getEndpointFor(caller.getMethod(), caller.getArgs(), injector); @@ -400,7 +363,7 @@ public class RestAnnotationProcessor { public GeneratedHttpRequest createRequest(Method method, Object... args) { inputParamValidator.validateMethodParametersOrThrow(method, args); ClassMethodArgs cma = logger.isTraceEnabled() ? new ClassMethodArgs(method.getDeclaringClass(), method, args) - : null; + : null; URI endpoint = callerEndpoint; try { @@ -488,7 +451,7 @@ public class RestAnnotationProcessor { requestBuilder.headers(filterOutContentHeaders(headers)); try { - requestBuilder.endpoint(builder.buildFromEncodedMap(convertUnsafe(tokenValues))); + requestBuilder.endpoint(builder.buildFromEncodedMap(Maps2.convertUnsafe(tokenValues))); } catch (IllegalArgumentException e) { throw new IllegalStateException(e); } catch (UriBuilderException e) { @@ -560,14 +523,14 @@ public class RestAnnotationProcessor { } public static URI replaceQuery(Provider uriBuilderProvider, URI in, String newQuery, - @Nullable Comparator> sorter, char... skips) { + @Nullable Comparator> sorter, char... skips) { UriBuilder builder = uriBuilderProvider.get().uri(in); builder.replaceQuery(ModifyRequest.makeQueryLine(ModifyRequest.parseQueryToMap(newQuery), sorter, skips)); return builder.build(); } private Multimap addMatrixParams(Collection> tokenValues, Method method, - Object... args) { + Object... args) { Multimap matrixMap = LinkedListMultimap.create(); if (declaring.isAnnotationPresent(MatrixParams.class)) { MatrixParams matrix = declaring.getAnnotation(MatrixParams.class); @@ -586,7 +549,7 @@ public class RestAnnotationProcessor { } private Multimap addFormParams(Collection> tokenValues, Method method, - Object... args) { + Object... args) { Multimap formMap = LinkedListMultimap.create(); if (declaring.isAnnotationPresent(FormParams.class)) { FormParams form = declaring.getAnnotation(FormParams.class); @@ -605,7 +568,7 @@ public class RestAnnotationProcessor { } private Multimap addQueryParams(Collection> tokenValues, Method method, - Object... args) { + Object... args) { Multimap queryMap = LinkedListMultimap.create(); if (declaring.isAnnotationPresent(QueryParams.class)) { QueryParams query = declaring.getAnnotation(QueryParams.class); @@ -624,7 +587,7 @@ public class RestAnnotationProcessor { } private void addForm(Multimap formParams, FormParams form, - Collection> tokenValues) { + Collection> tokenValues) { for (int i = 0; i < form.keys().length; i++) { if (form.values()[i].equals(FormParams.NULL)) { formParams.removeAll(form.keys()[i]); @@ -636,7 +599,7 @@ public class RestAnnotationProcessor { } private void addQuery(Multimap queryParams, QueryParams query, - Collection> tokenValues) { + Collection> tokenValues) { for (int i = 0; i < query.keys().length; i++) { if (query.values()[i].equals(QueryParams.NULL)) { queryParams.removeAll(query.keys()[i]); @@ -648,7 +611,7 @@ public class RestAnnotationProcessor { } private void addMatrix(Multimap matrixParams, MatrixParams matrix, - Collection> tokenValues) { + Collection> tokenValues) { for (int i = 0; i < matrix.keys().length; i++) { if (matrix.values()[i].equals(MatrixParams.NULL)) { matrixParams.removeAll(matrix.keys()[i]); @@ -660,7 +623,7 @@ public class RestAnnotationProcessor { } private void addMapPayload(Map postParams, PayloadParams mapDefaults, - Collection> tokenValues) { + Collection> tokenValues) { for (int i = 0; i < mapDefaults.keys().length; i++) { if (mapDefaults.values()[i].equals(PayloadParams.NULL)) { postParams.put(mapDefaults.keys()[i], null); @@ -695,7 +658,7 @@ public class RestAnnotationProcessor { @VisibleForTesting public static URI getEndpointInParametersOrNull(Method method, final Object[] args, Injector injector) { Map> map = indexWithAtLeastOneAnnotation(method, - methodToIndexOfParamToEndpointParamAnnotations); + methodToIndexOfParamToEndpointParamAnnotations); if (map.size() >= 1 && args.length > 0) { EndpointParam firstAnnotation = (EndpointParam) get(get(map.values(), 0), 0); Function parser = injector.getInstance(firstAnnotation.parser()); @@ -704,8 +667,8 @@ public class RestAnnotationProcessor { int index = map.keySet().iterator().next(); try { URI returnVal = parser.apply(args[index]); - checkArgument(returnVal != null, - String.format("endpoint for [%s] not configured for %s", args[index], method)); + checkArgument(returnVal != null, String.format("endpoint for [%s] not configured for %s", args[index], + method)); return returnVal; } catch (NullPointerException e) { throw new IllegalArgumentException(String.format("argument at index %d on method %s", index, method), e); @@ -722,12 +685,12 @@ public class RestAnnotationProcessor { }); try { URI returnVal = parser.apply(argsToParse); - checkArgument(returnVal != null, - String.format("endpoint for [%s] not configured for %s", argsToParse, method)); + checkArgument(returnVal != null, String.format("endpoint for [%s] not configured for %s", argsToParse, + method)); return returnVal; } catch (NullPointerException e) { throw new IllegalArgumentException(String.format("argument at indexes %s on method %s", map.keySet(), - method), e); + method), e); } } } @@ -763,21 +726,21 @@ public class RestAnnotationProcessor { public static final TypeLiteral> futureHttpResponseLiteral = new TypeLiteral>() { }; - @SuppressWarnings({ "unchecked", "rawtypes" }) + @SuppressWarnings( { "unchecked", "rawtypes" }) public static Key> getParserOrThrowException(Method method) { ResponseParser annotation = method.getAnnotation(ResponseParser.class); if (annotation == null) { if (method.getReturnType().equals(void.class) - || TypeLiteral.get(method.getGenericReturnType()).equals(futureVoidLiteral)) { + || TypeLiteral.get(method.getGenericReturnType()).equals(futureVoidLiteral)) { return Key.get(ReleasePayloadAndReturn.class); } else if (method.getReturnType().equals(boolean.class) || method.getReturnType().equals(Boolean.class) - || TypeLiteral.get(method.getGenericReturnType()).equals(futureBooleanLiteral)) { + || TypeLiteral.get(method.getGenericReturnType()).equals(futureBooleanLiteral)) { return Key.get(ReturnTrueIf2xx.class); } else if (method.getReturnType().equals(InputStream.class) - || TypeLiteral.get(method.getGenericReturnType()).equals(futureInputStreamLiteral)) { + || TypeLiteral.get(method.getGenericReturnType()).equals(futureInputStreamLiteral)) { return Key.get(ReturnInputStream.class); } else if (method.getReturnType().equals(HttpResponse.class) - || TypeLiteral.get(method.getGenericReturnType()).equals(futureHttpResponseLiteral)) { + || TypeLiteral.get(method.getGenericReturnType()).equals(futureHttpResponseLiteral)) { return Key.get((Class) IdentityFunction.class); } else if (getAcceptHeadersOrNull(method).contains(MediaType.APPLICATION_JSON)) { Type returnVal; @@ -803,16 +766,16 @@ public class RestAnnotationProcessor { parserType = Types.newParameterizedType(UnwrapOnlyNestedJsonValueInSet.class, returnVal); else throw new IllegalStateException(String.format( - "depth(%d) edgeCollection(%s) not yet supported for @Unwrap", depth, edgeCollection)); + "depth(%d) edgeCollection(%s) not yet supported for @Unwrap", depth, edgeCollection)); } else { parserType = Types.newParameterizedType(ParseJson.class, returnVal); } return (Key>) Key.get(parserType); } else if (method.getReturnType().equals(String.class) - || TypeLiteral.get(method.getGenericReturnType()).equals(futureStringLiteral)) { + || TypeLiteral.get(method.getGenericReturnType()).equals(futureStringLiteral)) { return Key.get(ReturnStringIf2xx.class); } else if (method.getReturnType().equals(URI.class) - || TypeLiteral.get(method.getGenericReturnType()).equals(futureURILiteral)) { + || TypeLiteral.get(method.getGenericReturnType()).equals(futureURILiteral)) { return Key.get(ParseURIFromListOrLocationHeaderIf20x.class); } else { throw new IllegalStateException("You must specify a ResponseParser annotation on: " + method.toString()); @@ -844,7 +807,7 @@ public class RestAnnotationProcessor { } else { if (postBinders[0] instanceof org.jclouds.rest.MapBinder) { throw new IllegalArgumentException("we currently do not support multiple varargs postBinders in: " - + method.getName()); + + method.getName()); } } } else if (arg instanceof org.jclouds.rest.MapBinder) { @@ -862,41 +825,23 @@ public class RestAnnotationProcessor { return null; } - private Multimap constants = LinkedHashMultimap.create(); - - public boolean isHttpMethod(Method method) { - return method.isAnnotationPresent(Path.class) || getHttpMethods(method) != null - || Sets.newHashSet(method.getParameterTypes()).contains(HttpRequest.class); - } - - public boolean isConstantDeclaration(Method method) { - return method.isAnnotationPresent(PathParam.class) && method.isAnnotationPresent(Named.class); - } - - public void bindConstant(Method method) { - String key = method.getAnnotation(PathParam.class).value(); - String value = injector.getInstance(Key.get(String.class, method.getAnnotation(Named.class))); - constants.put(key, value); - } - public static Set getHttpMethods(Method method) { - HashSet methods = new HashSet(); + Builder methodsBuilder = ImmutableSet. builder(); for (Annotation annotation : method.getAnnotations()) { HttpMethod http = annotation.annotationType().getAnnotation(HttpMethod.class); if (http != null) - methods.add(http.value()); + methodsBuilder.add(http.value()); } - if (methods.size() == 0) - return null; - return methods; + Set methods = methodsBuilder.build(); + return (methods.size() == 0) ? null : methods; } public String getHttpMethodOrConstantOrThrowException(Method method) { Set requests = getHttpMethods(method); if (requests == null || requests.size() != 1) { throw new IllegalStateException( - "You must use at least one, but no more than one http method or pathparam annotation on: " - + method.toString()); + "You must use at least one, but no more than one http method or pathparam annotation on: " + + method.toString()); } return requests.iterator().next(); } @@ -908,17 +853,25 @@ public class RestAnnotationProcessor { return false; } + private static final Predicate> notEmpty = new Predicate>() { + public boolean apply(Set input) { + return input.size() >= 1; + } + }; + public GeneratedHttpRequest decorateRequest(GeneratedHttpRequest request) { - OUTER: for (Entry> entry : filterValues( - methodToIndexOfParamToDecoratorParamAnnotation.get(request.getJavaMethod()), - new Predicate>() { - public boolean apply(Set input) { - return input.size() >= 1; - } - }).entrySet()) { + OUTER: for (Entry> entry : concat(// + filterValues(methodToIndexOfParamToBinderParamAnnotation.get(request.getJavaMethod()), notEmpty) + .entrySet(), // + filterValues(methodToIndexOfParamToWrapWithAnnotation.get(request.getJavaMethod()), notEmpty).entrySet())) { boolean shouldBreak = false; - BinderParam payloadAnnotation = (BinderParam) entry.getValue().iterator().next(); - Binder binder = injector.getInstance(payloadAnnotation.value()); + Annotation annotation = Iterables.get(entry.getValue(), 0); + Binder binder; + if (annotation instanceof BinderParam) + binder = injector.getInstance(BinderParam.class.cast(annotation).value()); + else + binder = injector.getInstance(BindToJsonPayloadWrappedWith.Factory.class).create( + WrapWith.class.cast(annotation).value()); if (request.getArgs().size() >= entry.getKey() + 1 && request.getArgs().get(entry.getKey()) != null) { Object input; Class parameterType = request.getJavaMethod().getParameterTypes()[entry.getKey()]; @@ -951,24 +904,24 @@ public class RestAnnotationProcessor { } public static Map> indexWithOnlyOneAnnotation(Method method, String description, - Map>> toRefine) { + Map>> toRefine) { Map> indexToPayloadAnnotation = indexWithAtLeastOneAnnotation(method, toRefine); if (indexToPayloadAnnotation.size() > 1) { throw new IllegalStateException(String.format( - "You must not specify more than one %s annotation on: %s; found %s", description, method.toString(), - indexToPayloadAnnotation)); + "You must not specify more than one %s annotation on: %s; found %s", description, method.toString(), + indexToPayloadAnnotation)); } return indexToPayloadAnnotation; } private static Map> indexWithAtLeastOneAnnotation(Method method, - Map>> toRefine) { + Map>> toRefine) { Map> indexToPayloadAnnotation = filterValues(toRefine.get(method), - new Predicate>() { - public boolean apply(Set input) { - return input.size() == 1; - } - }); + new Predicate>() { + public boolean apply(Set input) { + return input.size() == 1; + } + }); return indexToPayloadAnnotation; } @@ -987,7 +940,7 @@ public class RestAnnotationProcessor { } else { if (options[0] instanceof HttpRequestOptions) { throw new IllegalArgumentException("we currently do not support multiple varargs options in: " - + method.getName()); + + method.getName()); } } } else { @@ -999,7 +952,7 @@ public class RestAnnotationProcessor { } public Multimap buildHeaders(Collection> tokenValues, Method method, - final Object... args) { + final Object... args) { Multimap headers = LinkedHashMultimap.create(); addHeaderIfAnnotationPresentOnMethod(headers, method, tokenValues); Map> indexToHeaderParam = methodToIndexOfParamToHeaderParamAnnotations.get(method); @@ -1046,7 +999,7 @@ public class RestAnnotationProcessor { } public void addHeaderIfAnnotationPresentOnMethod(Multimap headers, Method method, - Collection> tokenValues) { + Collection> tokenValues) { if (declaring.isAnnotationPresent(Headers.class)) { Headers header = declaring.getAnnotation(Headers.class); addHeader(headers, header, tokenValues); @@ -1058,7 +1011,7 @@ public class RestAnnotationProcessor { } private void addHeader(Multimap headers, Headers header, - Collection> tokenValues) { + Collection> tokenValues) { for (int i = 0; i < header.keys().length; i++) { String value = header.values()[i]; value = Strings2.replaceTokens(value, tokenValues); @@ -1067,14 +1020,6 @@ public class RestAnnotationProcessor { } - private Map convertUnsafe(Multimap in) { - Map out = newLinkedHashMap(); - for (Entry entry : in.entries()) { - out.put(entry.getKey(), entry.getValue()); - } - return out; - } - List getParts(Method method, Object[] args, Iterable> iterable) { List parts = newLinkedList(); Map> indexToPartParam = methodToIndexOfParamToPartParamAnnotations.get(method); @@ -1115,7 +1060,6 @@ public class RestAnnotationProcessor { private Multimap getPathParamKeyValues(Method method, Object... args) { Multimap pathParamValues = LinkedHashMultimap.create(); - pathParamValues.putAll(constants); Map> indexToPathParam = methodToIndexOfParamToPathParamAnnotations.get(method); Map> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method); @@ -1153,7 +1097,6 @@ public class RestAnnotationProcessor { private Multimap getMatrixParamKeyValues(Method method, Object... args) { Multimap matrixParamValues = LinkedHashMultimap.create(); - matrixParamValues.putAll(constants); Map> indexToMatrixParam = methodToIndexOfParamToMatrixParamAnnotations.get(method); Map> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method); @@ -1183,7 +1126,6 @@ public class RestAnnotationProcessor { private Multimap getFormParamKeyValues(Method method, Object... args) { Multimap formParamValues = LinkedHashMultimap.create(); - formParamValues.putAll(constants); Map> indexToFormParam = methodToIndexOfParamToFormParamAnnotations.get(method); Map> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method); @@ -1213,7 +1155,6 @@ public class RestAnnotationProcessor { private Multimap getQueryParamKeyValues(Method method, Object... args) { Multimap queryParamValues = LinkedHashMultimap.create(); - queryParamValues.putAll(constants); Map> indexToQueryParam = methodToIndexOfParamToQueryParamAnnotations.get(method); Map> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method); diff --git a/core/src/main/java/org/jclouds/rest/internal/SeedAnnotationCache.java b/core/src/main/java/org/jclouds/rest/internal/SeedAnnotationCache.java new file mode 100644 index 0000000000..65919dd8d9 --- /dev/null +++ b/core/src/main/java/org/jclouds/rest/internal/SeedAnnotationCache.java @@ -0,0 +1,126 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.rest.internal; + +import static com.google.common.collect.Sets.difference; +import static org.jclouds.rest.internal.RestAnnotationProcessor.delegationMap; +import static org.jclouds.rest.internal.RestAnnotationProcessor.getHttpMethods; +import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexOfParamToBinderParamAnnotation; +import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexOfParamToEndpointAnnotations; +import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexOfParamToEndpointParamAnnotations; +import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexOfParamToFormParamAnnotations; +import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexOfParamToHeaderParamAnnotations; +import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexOfParamToMatrixParamAnnotations; +import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexOfParamToParamParserAnnotations; +import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexOfParamToPartParamAnnotations; +import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexOfParamToPathParamAnnotations; +import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexOfParamToPostParamAnnotations; +import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexOfParamToQueryParamAnnotations; +import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexOfParamToWrapWithAnnotation; +import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexesOfOptions; + +import java.lang.reflect.Method; + +import javax.annotation.Resource; +import javax.inject.Named; +import javax.inject.Singleton; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; + +import org.jclouds.http.HttpRequest; +import org.jclouds.logging.Logger; +import org.jclouds.rest.annotations.Delegate; +import org.jclouds.rest.internal.RestAnnotationProcessor.MethodKey; + +import com.google.common.base.Function; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Multimap; +import com.google.inject.Inject; +import com.google.inject.Injector; +import com.google.inject.Key; +import com.google.inject.Provides; + +/** + * seeds the annotation cache + * + * @author Adrian Cole + */ + +@Singleton +public class SeedAnnotationCache implements Function, Boolean> { + @Resource + protected Logger logger = Logger.NULL; + + protected final Multimap constants; + protected final Injector injector; + + @Inject + public SeedAnnotationCache(Injector injector, @Named("CONSTANTS") Multimap constants) { + this.injector = injector; + this.constants = constants; + } + + public void bindConstant(Method method) { + String key = method.getAnnotation(PathParam.class).value(); + String value = injector.getInstance(Key.get(String.class, method.getAnnotation(Named.class))); + constants.put(key, value); + } + + public Boolean apply(Class declaring) { + for (Method method : difference(ImmutableSet.copyOf(declaring.getMethods()), ImmutableSet.copyOf(Object.class + .getMethods()))) { + if (isHttpMethod(method) || method.isAnnotationPresent(Delegate.class)) { + for (int index = 0; index < method.getParameterTypes().length; index++) { + methodToIndexOfParamToBinderParamAnnotation.get(method).get(index); + methodToIndexOfParamToWrapWithAnnotation.get(method).get(index); + methodToIndexOfParamToHeaderParamAnnotations.get(method).get(index); + methodToIndexOfParamToMatrixParamAnnotations.get(method).get(index); + methodToIndexOfParamToFormParamAnnotations.get(method).get(index); + methodToIndexOfParamToQueryParamAnnotations.get(method).get(index); + methodToIndexOfParamToEndpointAnnotations.get(method).get(index); + methodToIndexOfParamToEndpointParamAnnotations.get(method).get(index); + methodToIndexOfParamToPathParamAnnotations.get(method).get(index); + methodToIndexOfParamToPostParamAnnotations.get(method).get(index); + methodToIndexOfParamToParamParserAnnotations.get(method).get(index); + methodToIndexOfParamToPartParamAnnotations.get(method).get(index); + methodToIndexesOfOptions.get(method); + } + delegationMap.put(new MethodKey(method), method); + } else if (isConstantDeclaration(method)) { + bindConstant(method); + } else if (!method.getDeclaringClass().equals(declaring)) { + logger.trace("skipping potentially overridden method %s", method); + } else if (method.isAnnotationPresent(Provides.class)) { + logger.trace("skipping provider method %s", method); + } else { + logger.trace("Method is not annotated as either http or constant: %s", method); + } + } + return true; + } + + public static boolean isHttpMethod(Method method) { + return method.isAnnotationPresent(Path.class) || getHttpMethods(method) != null + || ImmutableSet.copyOf(method.getParameterTypes()).contains(HttpRequest.class); + } + + public static boolean isConstantDeclaration(Method method) { + return method.isAnnotationPresent(PathParam.class) && method.isAnnotationPresent(Named.class); + } +} \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/util/Maps2.java b/core/src/main/java/org/jclouds/util/Maps2.java index cc80dfb2fe..869f6b644d 100644 --- a/core/src/main/java/org/jclouds/util/Maps2.java +++ b/core/src/main/java/org/jclouds/util/Maps2.java @@ -23,14 +23,16 @@ import static com.google.common.base.Predicates.equalTo; import static com.google.common.base.Predicates.not; import static com.google.common.collect.Maps.filterKeys; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import com.google.common.base.Function; import com.google.common.base.Supplier; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableMap.Builder; import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.google.common.collect.ImmutableMap.Builder; /** * General utilities used in jclouds code for {@link Map}s. @@ -39,9 +41,17 @@ import com.google.common.collect.Maps; */ public class Maps2 { + public static Map convertUnsafe(Multimap in) { + LinkedHashMap out = Maps.newLinkedHashMap(); + for (Entry entry : in.entries()) { + out.put(entry.getKey(), entry.getValue()); + } + return ImmutableMap.copyOf(out); + } + /** - * If the supplied map contains the key {@code k1}, its value will be assigned to the key - * {@code k2}. Note that this doesn't modify the input map. + * If the supplied map contains the key {@code k1}, its value will be assigned to the key {@code + * k2}. Note that this doesn't modify the input map. * * @param * type of value the map holds diff --git a/core/src/main/java/org/jclouds/util/PasswordGenerator.java b/core/src/main/java/org/jclouds/util/PasswordGenerator.java new file mode 100644 index 0000000000..7a632b1b93 --- /dev/null +++ b/core/src/main/java/org/jclouds/util/PasswordGenerator.java @@ -0,0 +1,60 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.util; + +import java.security.SecureRandom; + +import com.google.common.base.Supplier; + +/** + * Cheap, lightweight, low-security password generator. + * + * @see + */ +public enum PasswordGenerator implements Supplier { + + INSTANCE; + + /** Minimum length for a decent password */ + public static final int MIN_LENGTH = 10; + + /** The random number generator. */ + protected static final SecureRandom r = new SecureRandom(); + + /* + * Set of characters that is valid. Must be printable, memorable, and + * "won't break HTML" (i.e., not ' <', '>', '&', '=', ...). or break shell + * commands (i.e., not ' <', '>', '$', '!', ...). I, L and O are good to + * leave out, as are numeric zero and one. + */ + public final static char[] goodChar = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'm', 'n', 'p', 'q', 'r', + 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'M', 'N', 'P', 'Q', + 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '2', '3', '4', '5', '6', '7', '8', '9', '+', '-', '@', }; + + @Override + public String get() { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < MIN_LENGTH; i++) { + sb.append(goodChar[r.nextInt(goodChar.length)]); + } + return sb.toString(); + } +} \ No newline at end of file diff --git a/core/src/main/resources/rest.properties b/core/src/main/resources/rest.properties index cfba086eed..664ee1076a 100644 --- a/core/src/main/resources/rest.properties +++ b/core/src/main/resources/rest.properties @@ -160,6 +160,9 @@ softlayer.propertiesbuilder=org.jclouds.softlayer.SoftLayerPropertiesBuilder savvis-symphonyvpdc.contextbuilder=org.jclouds.savvis.vpdc.VPDCContextBuilder savvis-symphonyvpdc.propertiesbuilder=org.jclouds.savvis.vpdc.VPDCPropertiesBuilder +cloudloadbalancers-us.contextbuilder=org.jclouds.cloudloadbalancers.CloudLoadBalancersContextBuilder +cloudloadbalancers-us.propertiesbuilder=org.jclouds.cloudloadbalancers.CloudLoadBalancersUSPropertiesBuilder + cloudfiles-us.contextbuilder=org.jclouds.cloudfiles.CloudFilesContextBuilder cloudfiles-us.propertiesbuilder=org.jclouds.rackspace.cloudfiles.CloudFilesUSPropertiesBuilder diff --git a/core/src/test/java/org/jclouds/PerformanceTest.java b/core/src/test/java/org/jclouds/PerformanceTest.java index 067a7a4256..f9e8c506cf 100644 --- a/core/src/test/java/org/jclouds/PerformanceTest.java +++ b/core/src/test/java/org/jclouds/PerformanceTest.java @@ -25,8 +25,8 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; +import org.jclouds.concurrent.DynamicExecutors; import org.jclouds.date.DateService; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeTest; @@ -44,7 +44,7 @@ public abstract class PerformanceTest { @BeforeTest public void setupExecutorService() { - exec = Executors.newCachedThreadPool(); + exec = DynamicExecutors.newScalingThreadPool(1, THREAD_COUNT, 1000); } @AfterTest diff --git a/core/src/test/java/org/jclouds/concurrent/internal/SyncProxyTest.java b/core/src/test/java/org/jclouds/concurrent/internal/SyncProxyTest.java index c8771ab749..ebdefbc122 100644 --- a/core/src/test/java/org/jclouds/concurrent/internal/SyncProxyTest.java +++ b/core/src/test/java/org/jclouds/concurrent/internal/SyncProxyTest.java @@ -46,15 +46,15 @@ import com.google.inject.Provides; * * @author Adrian Cole */ -@Test(groups = "unit", sequential = true) +@Test(groups = "unit", singleThreaded = true) public class SyncProxyTest { @Test void testConvertNanos() { - assertEquals(SyncProxy.convertToNanos(Sync.class.getAnnotation(Timeout.class)), 30000000); + assertEquals(SyncProxy.convertToNanos(Sync.class.getAnnotation(Timeout.class)), 40000000); } - @Timeout(duration = 30, timeUnit = TimeUnit.MILLISECONDS) + @Timeout(duration = 40, timeUnit = TimeUnit.MILLISECONDS) private static interface Sync { String getString(); @@ -69,10 +69,10 @@ public class SyncProxyTest { String take20Milliseconds(); - String take100MillisecondsAndTimeout(); + String take200MillisecondsAndTimeout(); @Timeout(duration = 300, timeUnit = TimeUnit.MILLISECONDS) - String take100MillisecondsAndOverride(); + String take200MillisecondsAndOverride(); } @@ -137,12 +137,12 @@ public class SyncProxyTest { }), executorService); } - public ListenableFuture take100MillisecondsAndTimeout() { + public ListenableFuture take200MillisecondsAndTimeout() { return Futures.makeListenable(executorService.submit(new Callable() { public String call() { try { - Thread.sleep(100); + Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } @@ -152,8 +152,8 @@ public class SyncProxyTest { }), executorService); } - public ListenableFuture take100MillisecondsAndOverride() { - return take100MillisecondsAndTimeout(); + public ListenableFuture take200MillisecondsAndOverride() { + return take200MillisecondsAndTimeout(); } } @@ -164,6 +164,8 @@ public class SyncProxyTest { public void setUp() throws IllegalArgumentException, SecurityException, NoSuchMethodException { sync = SyncProxy.proxy(Sync.class, new SyncProxy(Sync.class, new Async(), new ConcurrentHashMap(), ImmutableMap., Class> of())); + // just to warm up + sync.string(); } @Test @@ -184,14 +186,13 @@ public class SyncProxyTest { } @Test(expectedExceptions = RuntimeException.class) - public void testTake100MillisecondsAndTimeout() { - assertEquals(sync.take100MillisecondsAndTimeout(), "foo"); - + public void testTake200MillisecondsAndTimeout() { + assertEquals(sync.take200MillisecondsAndTimeout(), "foo"); } @Test - public void testTake100MillisecondsAndOverride() { - assertEquals(sync.take100MillisecondsAndOverride(), "foo"); + public void testTake200MillisecondsAndOverride() { + assertEquals(sync.take200MillisecondsAndOverride(), "foo"); } @Test diff --git a/core/src/test/java/org/jclouds/crypto/Sha512CryptTest.java b/core/src/test/java/org/jclouds/crypto/Sha512CryptTest.java new file mode 100644 index 0000000000..07dea0c86a --- /dev/null +++ b/core/src/test/java/org/jclouds/crypto/Sha512CryptTest.java @@ -0,0 +1,77 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.crypto; + +import static org.testng.Assert.assertEquals; + +import org.testng.annotations.BeforeTest; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import com.google.inject.Guice; +import com.google.inject.Injector; + +/** + * @author Adrian Cole + */ +@Test(groups = "unit") +public class Sha512CryptTest { + + protected Crypto crypto; + + @BeforeTest + protected void createCrypto() { + Injector i = Guice.createInjector(); + crypto = i.getInstance(Crypto.class); + } + + public final static Object[][] TEST_DATA = { + { "Hello world!", "$6$saltstring", + "$6$saltstring$svn8UoSVapNtMuq1ukKS4tPQd8iKwSMHWjl/O817G3uBnIFNjnQJuesI68u4OTLiBFdcbYEdFCoEOfaS35inz1" }, + { + "Hello world!", + "$6$rounds=10000$saltstringsaltstring", + "$6$rounds=10000$saltstringsaltst$OW1/O6BYHV6BcXZu8QVeXbDWra3Oeqh0sbHbbMCVNSnCM/UrjmM0Dp8vOuZeHBy/YTBmSK6H9qs/y3RnOaw5v." }, + { "This is just a test", "$6$rounds=5000$toolongsaltstring", + "$6$toolongsaltstrin$lQ8jolhgVRVhY4b5pZKaysCLi0QBxGoNeKQzQ3glMhwllF7oGDZxUhx1yxdYcz/e1JSbq3y6JMxxl8audkUEm0" }, + { + "a very much longer text to encrypt. This one even stretches over morethan one line.", + "$6$rounds=1400$anotherlongsaltstring", + "$6$rounds=1400$anotherlongsalts$POfYwTEok97VWcjxIiSOjiykti.o/pQs.wPvMxQ6Fm7I6IoYN3CmLs66x9t0oSwbtEW7o7UmJEiDwGqd8p4ur1" }, + + { + "a short string", + "$6$rounds=123456$asaltof16chars..", + "$6$rounds=123456$asaltof16chars..$BtCwjqMJGx5hrJhZywWvt0RLE8uZ4oPwcelCjmw2kSYu.Ec6ycULevoBK25fs2xXgMNrCzIMVcgEJAstJeonj1" }, + { "the minimum number is still observed", "$6$rounds=10$roundstoolow", + "$6$rounds=1000$roundstoolow$kUMsbe306n21p9R.FRkW3IGn.S9NPN0x50YhH1xhLsPuWGsUSklZt58jaTfF4ZEQpyUNGc0dqbpBYYBaHHrsX." } }; + + @DataProvider(name = "data") + public Object[][] createData1() { + return TEST_DATA; + } + + /** + * Validate our implementation using test data from Ulrich Drepper's C implementation. + */ + @Test(dataProvider = "data") + public void testMakeCryptedPasswordHash(String password, String salt, String expected) { + assertEquals(Sha512Crypt.makeShadowLine(password, salt, crypto), expected); + } +} diff --git a/core/src/test/java/org/jclouds/crypto/SshKeysTest.java b/core/src/test/java/org/jclouds/crypto/SshKeysTest.java new file mode 100644 index 0000000000..fc9487d51d --- /dev/null +++ b/core/src/test/java/org/jclouds/crypto/SshKeysTest.java @@ -0,0 +1,65 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.crypto; + +import static org.testng.Assert.assertEquals; + +import java.io.IOException; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.interfaces.RSAPrivateCrtKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.InvalidKeySpecException; +import java.util.Map; + +import org.jclouds.io.Payloads; +import org.testng.annotations.Test; + +/** + * + * @author Adrian Cole + */ +@Test(groups = "unit", singleThreaded = true) +public class SshKeysTest { + + public static final String SSH_PUBLIC_KEY = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDJvZkkmoabQopH7yd9Ak2xJ34X21c0xXsJ85xbqOyqzwRmCJVHT2EPUhg6PhiozSok42WDKDjFFZ7B1IbtBM+PWUmlUCJ1r2xfLb6TPJqBkDUCbQ5lxupvmGh4gOBxf54Nrv2zS7QOiaNx870QqG8csHP7Mz7dCo9GQ9XydhNt+z4eNXPM5ToPUHRdHf4g9lmXYCdazZ3SqGdK0dwNS+dFVDQ/jzZjA31WBx45m2k/PQMIoQnlPHlJO5vyTT/O39UAwdBp8tK5Awt3azhku45mm03/+4CxObGKt6p3fvP7xlN0FsnwsSkn6IJe5J+blpSHuLDqjG9OhjYAvnf6MrZR"; + public static final String SSH_PRIVATE_KEY = "-----BEGIN RSA PRIVATE KEY-----\\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDJvZkkmoabQopH\\n7yd9Ak2xJ34X21c0xXsJ85xbqOyqzwRmCJVHT2EPUhg6PhiozSok42WDKDjFFZ7B\\n1IbtBM+PWUmlUCJ1r2xfLb6TPJqBkDUCbQ5lxupvmGh4gOBxf54Nrv2zS7QOiaNx\\n870QqG8csHP7Mz7dCo9GQ9XydhNt+z4eNXPM5ToPUHRdHf4g9lmXYCdazZ3SqGdK\\n0dwNS+dFVDQ/jzZjA31WBx45m2k/PQMIoQnlPHlJO5vyTT/O39UAwdBp8tK5Awt3\\nazhku45mm03/+4CxObGKt6p3fvP7xlN0FsnwsSkn6IJe5J+blpSHuLDqjG9OhjYA\\nvnf6MrZRAgMBAAECggEBAMDzwHeL/FafS9cFXEVqYJih5y42MbBdeRLJl7DrXoD4\\nQ4K7jtuHhpO6t0VtgvRgVoC1pa/OVo3Z4eANv4cO5N58Tb35aRwaTpKyE+aLPlPR\\nc4IAgJbDrBJUOQeYbBLiNm9sAWbtbyfAaT1iHGDEWJGeCzAlkWik4ugXlZeza13y\\nC4hg2yUymju27nSIWcEfAo7Zmw8CTy32edLGUNq8qu7KYSaocKvf522Kjl5p6cly\\nnd+OhcFA4pSTeKkgjb1dEHw2p4JVX/srHCpySo4ERtHAFyVyTIhsP5GICAq856U5\\nGMSIT1Hja+6q/FEDqT2R32ju62VfQBS2kSfeBlzot6kCgYEA/1p6Xljvn6wrYklQ\\n046E7xp7jlx2yKWnbIjRf5QFpK5Czb53I+mSkQcDF3imaZK+f/xz1OURmGv6phsA\\nFdU+UOdzGki7DYCNuTuz9LhdpIqxGqlcvnvOP9A6ouRGTz45VX2rqUx5A2ou0SPW\\n7ok++JfvRGj4VC1G2005ht90/98CgYEAykBeNHnChXmxRv5ESD28S5ijxFLI5qvG\\nooUIhdVQ2ws7H7bb4Arj4JClu55Y3uOi2uVgPq5Pqy9aLcAALuSJjRQj9+NU9EJS\\nLy1WhBQTRNpTlUshNpg4H5b9sFoMTgz3nrTU24NY73RtoDl19TjiK7O9rTasYlcr\\n36dLejyeT88CgYEAs4vp2OcN7ibABobolyhx3jGvyOTI/MJFm7IEJIFvCmEhRctz\\nuEOms+TLTridwkPVQObAh2Rd39+kySDZCYD8JSTosQWMyKyoeiM5oIv2BBkk+Es3\\nlBQ3bHU8lYaOzW9CHxOTHSJRQI5rxtA9c1H7fg5OxbpNSdrgJJkDJwt+F98CgYEA\\niCBWx58EK+5CQXQ15SGYMJFl+Gd3zLnlEdHUcK+ooiWm/6uFxf/ObIEu616iljJE\\nlGw6ITYVbTSLz6sg9G7hndDmfJvHvDc/NX2gc3lHltoT07IjgqllbO2lhiK1kXrs\\n1ycC9VQscc69UlAacph8scliaskXsYDWiMwC4x0VuMUCgYA/FSHHAXLyBqoFqEpQ\\nRmfgXzUcU/mKEaGRPhAvoAU87Z74mEgaO5Hbv7zmFwWpD4cdW0WybzVv9r5Hs/hS\\n4JGBzx4KPFAygnVeXiftVuM9scycg3RHK907hmlYNZicI7TbUlM5RQ4ngn/LTjXo\\nG+TDTPn3Luw1fvAMEEcdsr9cJw==\\n-----END RSA PRIVATE KEY-----"; + + @Test + public void testCanGenerate() { + Map map = SshKeys.generate(); + assert map.get("public").startsWith("ssh-rsa ") : map; + assert map.get("private").startsWith("-----BEGIN RSA PRIVATE KEY-----") : map; + } + + @Test + public void testEncodeAsPem() throws IOException, InvalidKeySpecException, NoSuchAlgorithmException { + String encoded = SshKeys.encodeAsPem((RSAPrivateCrtKey) KeyFactory.getInstance("RSA").generatePrivate( + Pems.privateKeySpec(Payloads.newStringPayload(PemsTest.PRIVATE_KEY)))); + assertEquals(encoded.replace("\n", "\\n").trim(), SSH_PRIVATE_KEY); + } + + @Test + public void testEncodeAsOpenSSH() throws IOException, InvalidKeySpecException, NoSuchAlgorithmException { + String encoded = SshKeys.encodeAsOpenSSH((RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic( + Pems.publicKeySpec(Payloads.newStringPayload(PemsTest.PUBLIC_KEY)))); + assertEquals(encoded, SSH_PUBLIC_KEY); + } + +} diff --git a/core/src/test/java/org/jclouds/http/handlers/BaseHttpErrorHandlerTest.java b/core/src/test/java/org/jclouds/http/handlers/BaseHttpErrorHandlerTest.java new file mode 100644 index 0000000000..1f3dad37ae --- /dev/null +++ b/core/src/test/java/org/jclouds/http/handlers/BaseHttpErrorHandlerTest.java @@ -0,0 +1,95 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.http.handlers; + +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.reportMatcher; +import static org.easymock.classextension.EasyMock.createMock; +import static org.easymock.classextension.EasyMock.replay; +import static org.easymock.classextension.EasyMock.verify; + +import java.net.URI; + +import org.easymock.IArgumentMatcher; +import org.jclouds.http.HttpCommand; +import org.jclouds.http.HttpErrorHandler; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpResponse; +import org.jclouds.io.Payloads; +import org.jclouds.util.Strings2; +import org.testng.annotations.Test; + +import com.google.inject.Guice; + +/** + * + * @author Adrian Cole + */ +@Test(groups = { "unit" }) +public abstract class BaseHttpErrorHandlerTest { + abstract protected Class getClassToTest(); + + protected void assertCodeMakes(String method, URI uri, int statusCode, String message, String content, + Class expected, String exceptionMessage) { + assertCodeMakes(method, uri, statusCode, message, "text/xml", content, expected, exceptionMessage); + } + + private void assertCodeMakes(String method, URI uri, int statusCode, String message, String contentType, + String content, Class expected, String exceptionMessage) { + + T function = Guice.createInjector().getInstance(getClassToTest()); + + HttpCommand command = createMock(HttpCommand.class); + HttpRequest request = new HttpRequest(method, uri); + HttpResponse response = new HttpResponse(statusCode, message, Payloads.newInputStreamPayload(Strings2 + .toInputStream(content))); + response.getPayload().getContentMetadata().setContentType(contentType); + + expect(command.getCurrentRequest()).andReturn(request).atLeastOnce(); + command.setException(exceptionEq(expected, exceptionMessage)); + + replay(command); + + function.handleError(command, response); + + verify(command); + } + + public static Exception exceptionEq(final Class in, final String exceptionMessage) { + reportMatcher(new IArgumentMatcher() { + + @Override + public void appendTo(StringBuffer buffer) { + buffer.append("exceptionEq("); + buffer.append(in); + buffer.append(","); + buffer.append(exceptionMessage); + buffer.append(")"); + } + + @Override + public boolean matches(Object arg) { + return arg.getClass() == in && exceptionMessage.equals(Exception.class.cast(arg).getMessage()); + } + + }); + return null; + } + +} diff --git a/core/src/test/java/org/jclouds/http/handlers/RedirectionRetryHandlerTest.java b/core/src/test/java/org/jclouds/http/handlers/RedirectionRetryHandlerTest.java index 0ca239b33c..3017c6ed23 100644 --- a/core/src/test/java/org/jclouds/http/handlers/RedirectionRetryHandlerTest.java +++ b/core/src/test/java/org/jclouds/http/handlers/RedirectionRetryHandlerTest.java @@ -25,6 +25,8 @@ import static org.easymock.classextension.EasyMock.verify; import java.net.URI; +import javax.inject.Named; +import javax.inject.Singleton; import javax.ws.rs.core.HttpHeaders; import org.jclouds.http.HttpCommand; @@ -35,7 +37,12 @@ import org.jclouds.rest.config.RestModule; import org.testng.annotations.Test; import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.LinkedHashMultimap; +import com.google.common.collect.Multimap; +import com.google.inject.AbstractModule; import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.Provides; /** * Tests behavior of {@code RedirectionRetryHandler} @@ -44,6 +51,19 @@ import com.google.inject.Guice; */ @Test(groups = "unit") public class RedirectionRetryHandlerTest { + Injector injector = Guice.createInjector(new MockModule(), new RestModule(), new AbstractModule() { + @SuppressWarnings("unused") + @Provides + @Singleton + @Named("CONSTANTS") + protected Multimap constants() { + return LinkedHashMultimap.create(); + } + + @Override + protected void configure() { + } + }); @Test public void test302DoesNotRetry() { @@ -55,8 +75,7 @@ public class RedirectionRetryHandlerTest { replay(command); - RedirectionRetryHandler retry = Guice.createInjector(new MockModule(), new RestModule()).getInstance( - RedirectionRetryHandler.class); + RedirectionRetryHandler retry = injector.getInstance(RedirectionRetryHandler.class); assert !retry.shouldRetryRequest(command, response); @@ -69,14 +88,13 @@ public class RedirectionRetryHandlerTest { HttpCommand command = createMock(HttpCommand.class); HttpResponse response = new HttpResponse(302, "HTTP/1.1 302 Found", null, ImmutableMultimap.of( - HttpHeaders.LOCATION, "/api/v0.8b-ext2.5/Error.aspx?aspxerrorpath=/api/v0.8b-ext2.5/org.svc/1906645")); + HttpHeaders.LOCATION, "/api/v0.8b-ext2.5/Error.aspx?aspxerrorpath=/api/v0.8b-ext2.5/org.svc/1906645")); expect(command.incrementRedirectCount()).andReturn(5); replay(command); - RedirectionRetryHandler retry = Guice.createInjector(new MockModule(), new RestModule()).getInstance( - RedirectionRetryHandler.class); + RedirectionRetryHandler retry = injector.getInstance(RedirectionRetryHandler.class); assert !retry.shouldRetryRequest(command, response); @@ -88,66 +106,64 @@ public class RedirectionRetryHandlerTest { public void test302WithPathOnlyHeader() { verifyRedirectRoutes( - new HttpRequest("GET", - URI.create("https://services.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645")), - new HttpResponse(302, "HTTP/1.1 302 Found", null, ImmutableMultimap.of(HttpHeaders.LOCATION, - "/api/v0.8b-ext2.5/Error.aspx?aspxerrorpath=/api/v0.8b-ext2.5/org.svc/1906645")), - new HttpRequest( - "GET", - URI.create("https://services.enterprisecloud.terremark.com/api/v0.8b-ext2.5/Error.aspx?aspxerrorpath=/api/v0.8b-ext2.5/org.svc/1906645"))); + new HttpRequest("GET", URI + .create("https://services.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645")), + new HttpResponse(302, "HTTP/1.1 302 Found", null, ImmutableMultimap.of(HttpHeaders.LOCATION, + "/api/v0.8b-ext2.5/Error.aspx?aspxerrorpath=/api/v0.8b-ext2.5/org.svc/1906645")), + new HttpRequest( + "GET", + URI + .create("https://services.enterprisecloud.terremark.com/api/v0.8b-ext2.5/Error.aspx?aspxerrorpath=/api/v0.8b-ext2.5/org.svc/1906645"))); } @Test public void test302ToHttps() { - verifyRedirectRoutes( - new HttpRequest("GET", - URI.create("http://services.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645")), - new HttpResponse(302, "HTTP/1.1 302 Found", null, ImmutableMultimap.of(HttpHeaders.LOCATION, - "https://services.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645")),// - new HttpRequest("GET", URI - .create("https://services.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645"))); + verifyRedirectRoutes(new HttpRequest("GET", URI + .create("http://services.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645")), + new HttpResponse(302, "HTTP/1.1 302 Found", null, ImmutableMultimap.of(HttpHeaders.LOCATION, + "https://services.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645")),// + new HttpRequest("GET", URI + .create("https://services.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645"))); } @Test public void test302ToDifferentPort() { - verifyRedirectRoutes( - new HttpRequest("GET", - URI.create("http://services.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645")), - new HttpResponse(302, "HTTP/1.1 302 Found", null, ImmutableMultimap.of(HttpHeaders.LOCATION, - "http://services.enterprisecloud.terremark.com:3030/api/v0.8b-ext2.5/org/1906645")),// - new HttpRequest("GET", URI - .create("http://services.enterprisecloud.terremark.com:3030/api/v0.8b-ext2.5/org/1906645"))); + verifyRedirectRoutes(new HttpRequest("GET", URI + .create("http://services.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645")), + new HttpResponse(302, "HTTP/1.1 302 Found", null, ImmutableMultimap.of(HttpHeaders.LOCATION, + "http://services.enterprisecloud.terremark.com:3030/api/v0.8b-ext2.5/org/1906645")),// + new HttpRequest("GET", URI + .create("http://services.enterprisecloud.terremark.com:3030/api/v0.8b-ext2.5/org/1906645"))); } @Test public void test302WithHeader() { - verifyRedirectRoutes( - new HttpRequest("GET", - URI.create("https://services.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645")), - new HttpResponse(302, "HTTP/1.1 302 Found", null, ImmutableMultimap.of(HttpHeaders.LOCATION, - "https://services1.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645")), new HttpRequest( - "GET", URI.create("https://services1.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645"))); + verifyRedirectRoutes(new HttpRequest("GET", URI + .create("https://services.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645")), + new HttpResponse(302, "HTTP/1.1 302 Found", null, ImmutableMultimap.of(HttpHeaders.LOCATION, + "https://services1.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645")), + new HttpRequest("GET", URI + .create("https://services1.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645"))); } @Test public void test302WithHeaderReplacesHostHeader() { - verifyRedirectRoutes( - new HttpRequest("GET", - URI.create("https://services.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645"), - ImmutableMultimap.of(HttpHeaders.HOST, "services.enterprisecloud.terremark.com")), - new HttpResponse(302, "HTTP/1.1 302 Found", null, ImmutableMultimap.of(HttpHeaders.LOCATION, - "https://services1.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645")),// - new HttpRequest("GET", URI - .create("https://services1.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645"), - ImmutableMultimap.of(HttpHeaders.HOST, "services1.enterprisecloud.terremark.com"))); + verifyRedirectRoutes(new HttpRequest("GET", URI + .create("https://services.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645"), + ImmutableMultimap.of(HttpHeaders.HOST, "services.enterprisecloud.terremark.com")), new HttpResponse(302, + "HTTP/1.1 302 Found", null, ImmutableMultimap.of(HttpHeaders.LOCATION, + "https://services1.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645")),// + new HttpRequest("GET", URI + .create("https://services1.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645"), + ImmutableMultimap.of(HttpHeaders.HOST, "services1.enterprisecloud.terremark.com"))); } @@ -160,8 +176,7 @@ public class RedirectionRetryHandlerTest { replay(command); - RedirectionRetryHandler retry = Guice.createInjector(new MockModule(), new RestModule()).getInstance( - RedirectionRetryHandler.class); + RedirectionRetryHandler retry = injector.getInstance(RedirectionRetryHandler.class); assert retry.shouldRetryRequest(command, response); verify(command); diff --git a/core/src/test/java/org/jclouds/io/CryptoTest.java b/core/src/test/java/org/jclouds/io/CryptoTest.java index e14345dd03..bdaed52388 100644 --- a/core/src/test/java/org/jclouds/io/CryptoTest.java +++ b/core/src/test/java/org/jclouds/io/CryptoTest.java @@ -45,7 +45,7 @@ import com.google.inject.Injector; * * @author Adrian Cole */ -//NOTE:without testName, this will not call @Before* and fail w/NPE during surefire +// NOTE:without testName, this will not call @Before* and fail w/NPE during surefire @Test(groups = "performance", sequential = true, timeOut = 2 * 60 * 1000, testName = "CryptoTest") public class CryptoTest extends PerformanceTest { @@ -122,4 +122,15 @@ public class CryptoTest extends PerformanceTest { assertEquals(hexMD5Digest, b64); } + public void testSHA1() { + crypto.sha1(); + } + + public void testSHA256() { + crypto.sha256(); + } + + public void testSHA512() { + crypto.sha512(); + } } diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/VersionStatus.java b/core/src/test/java/org/jclouds/json/BaseItemParserTest.java similarity index 69% rename from sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/VersionStatus.java rename to core/src/test/java/org/jclouds/json/BaseItemParserTest.java index f3270887dd..61e47bc0b2 100644 --- a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/VersionStatus.java +++ b/core/src/test/java/org/jclouds/json/BaseItemParserTest.java @@ -16,22 +16,13 @@ * limitations under the License. * ==================================================================== */ -package org.jclouds.openstack.nova.domain; +package org.jclouds.json; -public enum VersionStatus { - BETA, CURRENT, DEPRECATED, UNRECOGNIZED; - - public String value() { - return name(); - } - - public static VersionStatus fromValue(String v) { - try { - return valueOf(v); - } catch (IllegalArgumentException e) { - return UNRECOGNIZED; - } - } +/** + * + * @author Adrian Cole + */ +public abstract class BaseItemParserTest extends BaseParserTest { } diff --git a/core/src/test/java/org/jclouds/json/BaseParserTest.java b/core/src/test/java/org/jclouds/json/BaseParserTest.java new file mode 100644 index 0000000000..c9fd77926e --- /dev/null +++ b/core/src/test/java/org/jclouds/json/BaseParserTest.java @@ -0,0 +1,81 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.json; + +import static org.testng.Assert.assertEquals; + +import org.jclouds.http.HttpResponse; +import org.jclouds.http.functions.UnwrapOnlyNestedJsonValue; +import org.jclouds.io.Payloads; +import org.jclouds.json.config.GsonModule; +import org.testng.annotations.Test; + +import com.google.common.base.Function; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.Key; +import com.google.inject.TypeLiteral; +import com.google.inject.util.Types; + +/** + * + * @author Adrian Cole + */ +public abstract class BaseParserTest { + + @Test + public void test() { + + T expects = expected(); + + Function parser = getParser(getInjector()); + T response = parser.apply(new HttpResponse(200, "ok", Payloads.newInputStreamPayload(getClass() + .getResourceAsStream(resource())))); + compare(expects, response); + } + + public void compare(T expects, T response) { + assertEquals(response.toString(), expects.toString()); + } + + protected Injector getInjector() { + return Guice.createInjector(new GsonModule() { + + @Override + protected void configure() { + bind(DateAdapter.class).to(Iso8601DateAdapter.class); + super.configure(); + } + + }); + + } + + @SuppressWarnings("unchecked") + protected Function getParser(Injector i) { + return (Function) i.getInstance(Key.get(TypeLiteral.get( + Types.newParameterizedType(UnwrapOnlyNestedJsonValue.class, type())).getType())); + } + + public abstract Class type(); + + public abstract String resource(); + + public abstract T expected(); +} diff --git a/core/src/test/java/org/jclouds/json/BaseSetParserTest.java b/core/src/test/java/org/jclouds/json/BaseSetParserTest.java new file mode 100644 index 0000000000..b966abd745 --- /dev/null +++ b/core/src/test/java/org/jclouds/json/BaseSetParserTest.java @@ -0,0 +1,54 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.json; + +import static org.testng.Assert.assertEquals; + +import java.util.Set; + +import org.jclouds.http.HttpResponse; +import org.jclouds.http.functions.UnwrapOnlyNestedJsonValue; + +import com.google.common.base.Function; +import com.google.common.collect.ImmutableSortedSet; +import com.google.inject.Injector; +import com.google.inject.Key; +import com.google.inject.TypeLiteral; +import com.google.inject.util.Types; + +/** + * + * @author Adrian Cole + */ +public abstract class BaseSetParserTest extends BaseParserTest, T> { + + @SuppressWarnings("unchecked") + // crazy stuff due to type erasure + @Override + protected Function> getParser(Injector i) { + return (Function>) i.getInstance(Key.get(TypeLiteral.get( + Types.newParameterizedType(UnwrapOnlyNestedJsonValue.class, Types + .newParameterizedType(Set.class, type()))).getType())); + } + + public void compare(Set expects, Set response) { + assertEquals(ImmutableSortedSet.copyOf(response).toString(), ImmutableSortedSet.copyOf(expects).toString()); + } + +} diff --git a/core/src/test/java/org/jclouds/json/JsonTest.java b/core/src/test/java/org/jclouds/json/JsonTest.java index 5b7d8bcb49..9224822a84 100644 --- a/core/src/test/java/org/jclouds/json/JsonTest.java +++ b/core/src/test/java/org/jclouds/json/JsonTest.java @@ -21,6 +21,7 @@ package org.jclouds.json; import static org.testng.Assert.assertEquals; import java.util.Map; +import java.util.Properties; import org.jclouds.json.config.GsonModule; import org.testng.annotations.Test; @@ -44,6 +45,19 @@ public class JsonTest { private Test enumValue; } + public void testPropertiesSerializesDefaults() { + Properties props = new Properties(); + props.put("string", "string"); + props.put("number", "1"); + props.put("boolean", "true"); + assertEquals(json.toJson(props), "{\"string\":\"string\",\"boolean\":\"true\",\"number\":\"1\"}"); + Properties props3 = new Properties(props); + assertEquals(json.toJson(props3), "{\"string\":\"string\",\"boolean\":\"true\",\"number\":\"1\"}"); + Properties props2 = json.fromJson(json.toJson(props), Properties.class); + assertEquals(props2, props); + assertEquals(json.toJson(props2), json.toJson(props)); + } + public void testMapStringObjectWithAllValidValuesOneDeep() { Map map = Maps.newHashMap(); map.put("string", "string"); diff --git a/core/src/test/java/org/jclouds/providers/JcloudsTestBlobStoreProviderMetadata.java b/core/src/test/java/org/jclouds/providers/JcloudsTestBlobStoreProviderMetadata.java new file mode 100644 index 0000000000..7ddc6f7310 --- /dev/null +++ b/core/src/test/java/org/jclouds/providers/JcloudsTestBlobStoreProviderMetadata.java @@ -0,0 +1,70 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.providers; + +import java.net.URI; + +/** + * Implementation of {@ link org.jclouds.types.ProviderMetadata} for testing. + * + * @author Jeremy Whitlock + */ +public class JcloudsTestBlobStoreProviderMetadata implements ProviderMetadata { + + /** + * {@ see org.jclouds.types.ProviderMetadata#getId()} + */ + @Override + public String getId() { + return "test-blobstore-provider"; + } + + /** + * {@ see org.jclouds.types.ProviderMetadata#getType()} + */ + @Override + public String getType() { + return ProviderMetadata.BLOBSTORE_TYPE; + } + + /** + * {@ see org.jclouds.types.ProviderMetadata#getName()} + */ + @Override + public String getName() { + return "Test Blobstore Provider"; + } + + /** + * {@ see org.jclouds.types.ProviderMetadata#getHomepage()} + */ + @Override + public URI getHomepage() { + return URI.create("http://jclouds.org"); + } + + /** + * {@ see org.jclouds.types.ProviderMetadata#getConsole()} + */ + @Override + public URI getConsole() { + return URI.create("http://jclouds.org/console"); + } + +} \ No newline at end of file diff --git a/core/src/test/java/org/jclouds/providers/JcloudsTestComputeProviderMetadata.java b/core/src/test/java/org/jclouds/providers/JcloudsTestComputeProviderMetadata.java new file mode 100644 index 0000000000..93289c4dab --- /dev/null +++ b/core/src/test/java/org/jclouds/providers/JcloudsTestComputeProviderMetadata.java @@ -0,0 +1,70 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.providers; + +import java.net.URI; + +/** + * Implementation of {@ link org.jclouds.types.ProviderMetadata} for testing. + * + * @author Jeremy Whitlock + */ +public class JcloudsTestComputeProviderMetadata implements ProviderMetadata { + + /** + * {@ see org.jclouds.types.ProviderMetadata#getId()} + */ + @Override + public String getId() { + return "test-compute-provider"; + } + + /** + * {@ see org.jclouds.types.ProviderMetadata#getType()} + */ + @Override + public String getType() { + return ProviderMetadata.COMPUTE_TYPE; + } + + /** + * {@ see org.jclouds.types.ProviderMetadata#getName()} + */ + @Override + public String getName() { + return "Test Compute Provider"; + } + + /** + * {@ see org.jclouds.types.ProviderMetadata#getHomepage()} + */ + @Override + public URI getHomepage() { + return URI.create("http://jclouds.org"); + } + + /** + * {@ see org.jclouds.types.ProviderMetadata#getConsole()} + */ + @Override + public URI getConsole() { + return URI.create("http://jclouds.org/console"); + } + +} \ No newline at end of file diff --git a/core/src/test/java/org/jclouds/providers/ProvidersTest.java b/core/src/test/java/org/jclouds/providers/ProvidersTest.java new file mode 100644 index 0000000000..d4f957d7e3 --- /dev/null +++ b/core/src/test/java/org/jclouds/providers/ProvidersTest.java @@ -0,0 +1,100 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.providers; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; + +import java.util.NoSuchElementException; + +import org.testng.annotations.Test; + +/** + * The ProvidersTest tests the org.jclouds.providers.Providers class. + * + * @author Jeremy Whitlock + */ +@Test( groups = "unit" ) +public class ProvidersTest { + + @Test + public void testWithId() { + ProviderMetadata providerMetadata; + + try { + providerMetadata = Providers.withId("fake-id"); + fail("Looking for a provider with an id that doesn't exist should " + + "throw an exceptoin."); + } catch (NoSuchElementException nsee) { + ; // Expected + } + + providerMetadata = Providers.withId("test-blobstore-provider"); + + assertEquals("Test Blobstore Provider", providerMetadata.getName()); + } + + @Test + public void testOfType() { + Iterable providersMetadata = Providers.ofType(ProviderMetadata.BLOBSTORE_TYPE); + + for (ProviderMetadata providerMetadata : providersMetadata) { + assertEquals("Test Blobstore Provider", providerMetadata.getName()); + assertEquals("test-blobstore-provider", providerMetadata.getId()); + assertEquals(ProviderMetadata.BLOBSTORE_TYPE, providerMetadata.getType()); + assertEquals("http://jclouds.org", providerMetadata.getHomepage().toString()); + assertEquals("http://jclouds.org/console", providerMetadata.getConsole().toString()); + } + + providersMetadata = Providers.ofType(ProviderMetadata.COMPUTE_TYPE); + + for (ProviderMetadata providerMetadata : providersMetadata) { + assertEquals("Test Compute Provider", providerMetadata.getName()); + assertEquals("test-compute-provider", providerMetadata.getId()); + assertEquals(ProviderMetadata.COMPUTE_TYPE, providerMetadata.getType()); + assertEquals("http://jclouds.org", providerMetadata.getHomepage().toString()); + assertEquals("http://jclouds.org/console", providerMetadata.getConsole().toString()); + } + + providersMetadata = Providers.ofType("fake-type"); + + assertEquals(false, providersMetadata.iterator().hasNext()); + } + + @Test + public void testAll() { + Iterable providersMetadata = Providers.all(); + + for (ProviderMetadata providerMetadata : providersMetadata) { + if (providerMetadata.getName().equals("Test Blobstore Provider")) { + assertEquals("test-blobstore-provider", providerMetadata.getId()); + assertEquals(ProviderMetadata.BLOBSTORE_TYPE, providerMetadata.getType()); + assertEquals("http://jclouds.org", providerMetadata.getHomepage().toString()); + assertEquals("http://jclouds.org/console", providerMetadata.getConsole().toString()); + } else { + assertEquals("Test Compute Provider", providerMetadata.getName()); + assertEquals("test-compute-provider", providerMetadata.getId()); + assertEquals(ProviderMetadata.COMPUTE_TYPE, providerMetadata.getType()); + assertEquals("http://jclouds.org", providerMetadata.getHomepage().toString()); + assertEquals("http://jclouds.org/console", providerMetadata.getConsole().toString()); + } + } + } + +} diff --git a/core/src/test/java/org/jclouds/rest/binders/BindToJsonPayloadWrappedWithTest.java b/core/src/test/java/org/jclouds/rest/binders/BindToJsonPayloadWrappedWithTest.java new file mode 100644 index 0000000000..4d3ebda0b0 --- /dev/null +++ b/core/src/test/java/org/jclouds/rest/binders/BindToJsonPayloadWrappedWithTest.java @@ -0,0 +1,73 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.rest.binders; + +import static org.testng.Assert.assertEquals; + +import java.net.URI; + +import org.jclouds.http.HttpRequest; +import org.jclouds.json.config.GsonModule; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMap; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.assistedinject.FactoryModuleBuilder; + +/** + * Tests behavior of {@code BindToJsonPayloadWrappedWith} + * + * @author Adrian Cole + */ +@Test(groups = "unit") +public class BindToJsonPayloadWrappedWithTest { + + Injector injector = Guice.createInjector(new GsonModule(), new FactoryModuleBuilder() + .build(BindToJsonPayloadWrappedWith.Factory.class)); + + @Test + public void testCorrect() throws SecurityException, NoSuchMethodException { + BindToJsonPayloadWrappedWith binder = new BindToJsonPayloadWrappedWith(injector + .getInstance(BindToJsonPayload.class), "envelope"); + + HttpRequest request = HttpRequest.builder().method("GET").endpoint(URI.create("http://momma")).build(); + request = binder.bindToRequest(request, ImmutableMap.of("imageName", "foo", "serverId", "2")); + assertEquals(request.getPayload().getRawContent(), "{\"envelope\":{\"imageName\":\"foo\",\"serverId\":\"2\"}}"); + + } + + @Test + public void testFactoryCorrect() throws SecurityException, NoSuchMethodException { + BindToJsonPayloadWrappedWith binder = injector.getInstance(BindToJsonPayloadWrappedWith.Factory.class).create( + "envelope"); + + HttpRequest request = HttpRequest.builder().method("GET").endpoint(URI.create("http://momma")).build(); + request = binder.bindToRequest(request, ImmutableMap.of("imageName", "foo", "serverId", "2")); + assertEquals(request.getPayload().getRawContent(), "{\"envelope\":{\"imageName\":\"foo\",\"serverId\":\"2\"}}"); + + } + + @Test(expectedExceptions = NullPointerException.class) + public void testNullIsBad() { + BindToJsonPayloadWrappedWith binder = new BindToJsonPayloadWrappedWith(injector + .getInstance(BindToJsonPayload.class), "envelope"); + binder.bindToRequest(HttpRequest.builder().method("GET").endpoint(URI.create("http://momma")).build(), null); + } +} diff --git a/core/src/test/java/org/jclouds/rest/internal/RestAnnotationProcessorTest.java b/core/src/test/java/org/jclouds/rest/internal/RestAnnotationProcessorTest.java index 5d3bf32375..7b1a94c379 100755 --- a/core/src/test/java/org/jclouds/rest/internal/RestAnnotationProcessorTest.java +++ b/core/src/test/java/org/jclouds/rest/internal/RestAnnotationProcessorTest.java @@ -132,6 +132,7 @@ import org.jclouds.rest.annotations.ResponseParser; import org.jclouds.rest.annotations.SkipEncoding; import org.jclouds.rest.annotations.Unwrap; import org.jclouds.rest.annotations.VirtualHost; +import org.jclouds.rest.annotations.WrapWith; import org.jclouds.rest.binders.BindAsHostPrefix; import org.jclouds.rest.binders.BindMapToMatrixParams; import org.jclouds.rest.binders.BindToJsonPayload; @@ -200,6 +201,9 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest { @Delegate public Callee getCallee(); + + @Delegate + public Callee getCallee(@EndpointParam URI endpoint); } @Timeout(duration = 10, timeUnit = TimeUnit.NANOSECONDS) @@ -212,6 +216,9 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest { @Delegate public AsyncCallee getCallee(); + + @Delegate + public AsyncCallee getCallee(@EndpointParam URI endpoint); } @SuppressWarnings("unchecked") @@ -284,6 +291,31 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest { } + public void testDelegateWithOverridingEndpointOnMethod() throws SecurityException, NoSuchMethodException, + InterruptedException, ExecutionException { + Injector child = injectorForClient(); + TransformingHttpCommandExecutorService mock = child.getInstance(TransformingHttpCommandExecutorService.class); + + ReleasePayloadAndReturn function = child.getInstance(ReleasePayloadAndReturn.class); + + try { + child.getInstance(Callee.class); + assert false : "Callee shouldn't be bound yet"; + } catch (ConfigurationException e) { + + } + + Caller caller = child.getInstance(Caller.class); + expect(mock.submit(requestLineEquals("GET http://howdyboys/client/1/foo HTTP/1.1"), eq(function))) + .andReturn(Futures. immediateFuture(null)).atLeastOnce(); + replay(mock); + + caller.getCallee(URI.create("http://howdyboys")).onePath("foo"); + + verify(mock); + + } + private Injector injectorForClient() { RestContextSpec contextSpec = contextSpec("test", "http://localhost:9999", "1", "", @@ -768,6 +800,10 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest { @Consumes(MediaType.APPLICATION_JSON) String testUnwrap(); + @POST + @Path("/") + String testWrapWith(@WrapWith("foo") String param); + @GET @Path("/") @Unwrap @@ -928,6 +964,12 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest { } + public void testWrapWith() throws SecurityException, NoSuchMethodException, IOException { + Method method = TestPut.class.getMethod("testWrapWith", String.class); + HttpRequest request = factory(TestPut.class).createRequest(method, "bar"); + assertPayloadEquals(request, "{\"foo\":\"bar\"}", "application/json", false); + } + @SuppressWarnings("unchecked") public void testUnwrap2() throws SecurityException, NoSuchMethodException, IOException { Method method = TestPut.class.getMethod("testUnwrap2"); diff --git a/core/src/test/java/org/jclouds/util/Throwables2Test.java b/core/src/test/java/org/jclouds/util/Throwables2Test.java index 8e06f6ec03..4acd527ae2 100644 --- a/core/src/test/java/org/jclouds/util/Throwables2Test.java +++ b/core/src/test/java/org/jclouds/util/Throwables2Test.java @@ -24,6 +24,8 @@ import static org.jclouds.util.Throwables2.getFirstThrowableOfType; import static org.jclouds.util.Throwables2.returnFirstExceptionIfInListOrThrowStandardExceptionOrCause; import static org.testng.Assert.assertEquals; +import java.io.IOException; +import java.net.SocketException; import java.util.concurrent.TimeoutException; import org.jclouds.http.HttpCommand; @@ -51,6 +53,11 @@ public class Throwables2Test { assertEquals(getFirstThrowableOfType(pex, AuthorizationException.class), aex); } + public void testGetFirstThrowableOfTypeSubclass() { + SocketException aex = createMock(SocketException.class); + assertEquals(getFirstThrowableOfType(aex, IOException.class), aex); + } + public void testGetFirstThrowableOfTypeOuter() { AuthorizationException aex = createMock(AuthorizationException.class); assertEquals(getFirstThrowableOfType(aex, AuthorizationException.class), aex); diff --git a/core/src/test/resources/META-INF/services/org.jclouds.providers.ProviderMetadata b/core/src/test/resources/META-INF/services/org.jclouds.providers.ProviderMetadata new file mode 100644 index 0000000000..5bcd1ef273 --- /dev/null +++ b/core/src/test/resources/META-INF/services/org.jclouds.providers.ProviderMetadata @@ -0,0 +1,2 @@ +org.jclouds.providers.JcloudsTestBlobStoreProviderMetadata +org.jclouds.providers.JcloudsTestComputeProviderMetadata diff --git a/drivers/enterprise/src/test/java/org/jclouds/enterprise/config/EnterpriseConfigurationModuleTest.java b/drivers/enterprise/src/test/java/org/jclouds/enterprise/config/EnterpriseConfigurationModuleTest.java deleted file mode 100644 index 9711a76aa5..0000000000 --- a/drivers/enterprise/src/test/java/org/jclouds/enterprise/config/EnterpriseConfigurationModuleTest.java +++ /dev/null @@ -1,51 +0,0 @@ -/** - * - * Copyright (C) 2011 Cloud Conscious, LLC. - * - * ==================================================================== - * 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.enterprise.config; - -import static org.jclouds.Constants.PROPERTY_IO_WORKER_THREADS; -import static org.jclouds.Constants.PROPERTY_MAX_CONNECTIONS_PER_CONTEXT; -import static org.jclouds.Constants.PROPERTY_MAX_CONNECTIONS_PER_HOST; -import static org.jclouds.Constants.PROPERTY_USER_THREADS; - -import java.util.Properties; - -import org.jclouds.http.BaseHttpCommandExecutorServiceIntegrationTest; - -import com.google.inject.Module; - -/** - * Tests the functionality of the {@link EnterpriseConfigurationModule} - * - * @author Adrian Cole - */ -public class EnterpriseConfigurationModuleTest extends BaseHttpCommandExecutorServiceIntegrationTest { - - protected Module createConnectionModule() { - return new EnterpriseConfigurationModule(); - } - - protected void addConnectionProperties(Properties props) { - props.setProperty(PROPERTY_MAX_CONNECTIONS_PER_CONTEXT, 50 + ""); - props.setProperty(PROPERTY_MAX_CONNECTIONS_PER_HOST, 50 + ""); - // IO workers not used in this executor - props.setProperty(PROPERTY_IO_WORKER_THREADS, 0 + ""); - props.setProperty(PROPERTY_USER_THREADS, 5 + ""); - } - -} \ No newline at end of file diff --git a/drivers/enterprise/src/test/resources/test.jks b/drivers/enterprise/src/test/resources/test.jks deleted file mode 100644 index e641fb5470..0000000000 Binary files a/drivers/enterprise/src/test/resources/test.jks and /dev/null differ diff --git a/drivers/pom.xml b/drivers/pom.xml index 977babb3a0..a624a8ceb6 100644 --- a/drivers/pom.xml +++ b/drivers/pom.xml @@ -30,7 +30,7 @@ jclouds-drivers-project pom - jclouds extensions project + jclouds drivers project gae apachehc diff --git a/pom.xml b/pom.xml index ed04521b97..a4061982d2 100644 --- a/pom.xml +++ b/pom.xml @@ -48,6 +48,7 @@ scriptbuilder allcompute allblobstore + allloadbalancer all diff --git a/project/pom.xml b/project/pom.xml index 3892467151..d1054b8655 100644 --- a/project/pom.xml +++ b/project/pom.xml @@ -100,9 +100,8 @@ https://oss.sonatype.org/content/repositories/snapshots - website - website - file://${basedir}/target/dist/site/jclouds-testing/ + jclouds-googlecode-site + ${maven.site.url.base}/${project.version} @@ -180,6 +179,7 @@ 1.6 true true + davs://jclouds.googlecode.com/svn/maven-sites http://mirror.cloudera.com/apache/maven/binaries/apache-maven-2.2.1-bin.tar.bz2 @@ -501,7 +501,17 @@ pageTracker._trackPageview(); maven-site-plugin - 2.2 + + ${project.distributionManagement.site.id} + ${project.distributionManagement.site.url} + + + + org.apache.maven.wagon + wagon-webdav-jackrabbit + 1.0-beta-8-WAGON-319 + + maven-deploy-plugin @@ -681,6 +691,16 @@ pageTracker._trackPageview(); ${basedir} + + + + + maven-site-plugin + 2.2 + + + + @@ -712,7 +732,7 @@ pageTracker._trackPageview(); maven-javadoc-plugin - 2.6.1 + 2.7 ${project.build.sourceEncoding} true @@ -766,24 +786,11 @@ pageTracker._trackPageview(); - - - maven-site-plugin - - - attach-descriptor - - attach-descriptor - - - - - maven-site-plugin - 3.0-beta-3 + 3.0-beta-4-SNAPSHOT @@ -808,6 +815,7 @@ pageTracker._trackPageview(); ${project.build.sourceEncoding} true + -J-Xmx256m @@ -868,7 +876,15 @@ pageTracker._trackPageview(); src/etc/header.txt - + + + + + + jclouds-googlecode-site + ${maven.site.url.base}/${project.version}/${project.artifactId} + + diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeService.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeService.java index 8b7bf063c5..63ffece204 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeService.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeService.java @@ -40,6 +40,7 @@ import org.jclouds.compute.domain.Hardware; import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.TemplateBuilder; +import org.jclouds.compute.internal.PersistNodeCredentials; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts; import org.jclouds.compute.strategy.CreateNodesInGroupThenAddToSet; @@ -56,6 +57,7 @@ import org.jclouds.ec2.compute.EC2ComputeService; import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.compute.options.EC2TemplateOptions; import org.jclouds.ec2.domain.KeyPair; +import org.jclouds.scriptbuilder.functions.InitAdminAccess; import org.jclouds.util.Preconditions2; import com.google.common.annotations.VisibleForTesting; @@ -83,7 +85,8 @@ public class AWSEC2ComputeService extends EC2ComputeService { @Named("NODE_RUNNING") Predicate nodeRunning, @Named("NODE_TERMINATED") Predicate nodeTerminated, @Named("NODE_SUSPENDED") Predicate nodeSuspended, - InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, Timeouts timeouts, + InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, InitAdminAccess initAdminAccess, + PersistNodeCredentials persistNodeCredentials, Timeouts timeouts, @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, AWSEC2Client ec2Client, Map credentialsMap, @Named("SECURITY") Map securityGroupMap, @Named("PLACEMENT") Map placementGroupMap, @@ -91,7 +94,8 @@ public class AWSEC2ComputeService extends EC2ComputeService { super(context, credentialStore, images, sizes, locations, listNodesStrategy, getNodeMetadataStrategy, runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy, startNodeStrategy, stopNodeStrategy, templateBuilderProvider, templateOptionsProvider, nodeRunning, nodeTerminated, nodeSuspended, - initScriptRunnerFactory, timeouts, executor, ec2Client, credentialsMap, securityGroupMap); + initScriptRunnerFactory, initAdminAccess, persistNodeCredentials, timeouts, executor, ec2Client, + credentialsMap, securityGroupMap); this.ec2Client = ec2Client; this.placementGroupMap = placementGroupMap; this.placementGroupDeleted = placementGroupDeleted; diff --git a/providers/aws-s3/src/main/java/org/jclouds/aws/s3/AWSS3AsyncClient.java b/providers/aws-s3/src/main/java/org/jclouds/aws/s3/AWSS3AsyncClient.java index 5c5ed564b2..1a0ea3e205 100644 --- a/providers/aws-s3/src/main/java/org/jclouds/aws/s3/AWSS3AsyncClient.java +++ b/providers/aws-s3/src/main/java/org/jclouds/aws/s3/AWSS3AsyncClient.java @@ -18,29 +18,13 @@ */ package org.jclouds.aws.s3; -/** - * - * - * ==================================================================== - * 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. - * ==================================================================== - */ - import static org.jclouds.blobstore.attr.BlobScopes.CONTAINER; import java.util.Map; +import javax.annotation.Nullable; import javax.ws.rs.DELETE; +import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; @@ -56,6 +40,7 @@ import org.jclouds.blobstore.attr.BlobScope; import org.jclouds.http.functions.ParseETagHeader; import org.jclouds.io.Payload; import org.jclouds.rest.annotations.BinderParam; +import org.jclouds.rest.annotations.Endpoint; import org.jclouds.rest.annotations.ExceptionParser; import org.jclouds.rest.annotations.ParamParser; import org.jclouds.rest.annotations.ParamValidators; @@ -63,14 +48,20 @@ import org.jclouds.rest.annotations.QueryParams; import org.jclouds.rest.annotations.RequestFilters; import org.jclouds.rest.annotations.ResponseParser; import org.jclouds.rest.annotations.SkipEncoding; +import org.jclouds.rest.annotations.XMLResponseParser; import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404; import org.jclouds.s3.Bucket; import org.jclouds.s3.S3AsyncClient; +import org.jclouds.s3.S3Client; import org.jclouds.s3.binders.BindAsHostPrefixIfConfigured; import org.jclouds.s3.domain.ObjectMetadata; import org.jclouds.s3.filters.RequestAuthorizeSignature; +import org.jclouds.s3.functions.BindRegionToXmlPayload; +import org.jclouds.s3.functions.ReturnFalseIfBucketAlreadyOwnedByYouOrIllegalState; +import org.jclouds.s3.options.PutBucketOptions; import org.jclouds.s3.options.PutObjectOptions; import org.jclouds.s3.predicates.validators.BucketNameValidator; +import org.jclouds.s3.xml.LocationConstraintHandler; import com.google.common.util.concurrent.ListenableFuture; @@ -83,6 +74,31 @@ import com.google.common.util.concurrent.ListenableFuture; @RequestFilters(RequestAuthorizeSignature.class) @BlobScope(CONTAINER) public interface AWSS3AsyncClient extends S3AsyncClient { + /** + * @see S3Client#putBucketInRegion + */ + @Override + @PUT + @Path("/") + @Endpoint(Bucket.class) + @ExceptionParser(ReturnFalseIfBucketAlreadyOwnedByYouOrIllegalState.class) + ListenableFuture putBucketInRegion( + @BinderParam(BindRegionToXmlPayload.class) @Nullable String region, + @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName, + PutBucketOptions... options); + + /** + * @see S3Client#getBucketLocation + */ + @Override + @GET + @QueryParams(keys = "location") + @Path("/") + @Endpoint(Bucket.class) + @XMLResponseParser(LocationConstraintHandler.class) + ListenableFuture getBucketLocation( + @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName); + /** * @see AWSS3Client#initiateMultipartUpload */ @@ -91,9 +107,9 @@ public interface AWSS3AsyncClient extends S3AsyncClient { @Path("/{key}") @ResponseParser(UploadIdFromHttpResponseViaRegex.class) ListenableFuture initiateMultipartUpload( - @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName, - @PathParam("key") @ParamParser(ObjectMetadataKey.class) @BinderParam(BindObjectMetadataToRequest.class) ObjectMetadata objectMetadata, - PutObjectOptions... options); + @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName, + @PathParam("key") @ParamParser(ObjectMetadataKey.class) @BinderParam(BindObjectMetadataToRequest.class) ObjectMetadata objectMetadata, + PutObjectOptions... options); /** * @see AWSS3Client#abortMultipartUpload @@ -102,8 +118,8 @@ public interface AWSS3AsyncClient extends S3AsyncClient { @Path("/{key}") @ExceptionParser(ReturnVoidOnNotFoundOr404.class) ListenableFuture abortMultipartUpload( - @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName, - @PathParam("key") String key, @QueryParam("uploadId") String uploadId); + @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName, + @PathParam("key") String key, @QueryParam("uploadId") String uploadId); /** * @see AWSS3Client#uploadPart @@ -112,9 +128,9 @@ public interface AWSS3AsyncClient extends S3AsyncClient { @Path("/{key}") @ResponseParser(ParseETagHeader.class) ListenableFuture uploadPart( - @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName, - @PathParam("key") String key, @QueryParam("partNumber") int partNumber, - @QueryParam("uploadId") String uploadId, Payload part); + @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName, + @PathParam("key") String key, @QueryParam("partNumber") int partNumber, + @QueryParam("uploadId") String uploadId, Payload part); /** * @see AWSS3Client#completeMultipartUpload @@ -123,8 +139,8 @@ public interface AWSS3AsyncClient extends S3AsyncClient { @Path("/{key}") @ResponseParser(ETagFromHttpResponseViaRegex.class) ListenableFuture completeMultipartUpload( - @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName, - @PathParam("key") String key, @QueryParam("uploadId") String uploadId, - @BinderParam(BindPartIdsAndETagsToRequest.class) Map parts); + @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName, + @PathParam("key") String key, @QueryParam("uploadId") String uploadId, + @BinderParam(BindPartIdsAndETagsToRequest.class) Map parts); } diff --git a/providers/aws-s3/src/main/java/org/jclouds/aws/s3/binders/AssignCorrectHostnameAndBindAsHostPrefixIfConfigured.java b/providers/aws-s3/src/main/java/org/jclouds/aws/s3/binders/AssignCorrectHostnameAndBindAsHostPrefixIfConfigured.java new file mode 100644 index 0000000000..20c5abd042 --- /dev/null +++ b/providers/aws-s3/src/main/java/org/jclouds/aws/s3/binders/AssignCorrectHostnameAndBindAsHostPrefixIfConfigured.java @@ -0,0 +1,73 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.aws.s3.binders; + +import static org.jclouds.s3.reference.S3Constants.PROPERTY_S3_SERVICE_PATH; +import static org.jclouds.s3.reference.S3Constants.PROPERTY_S3_VIRTUAL_HOST_BUCKETS; + +import java.net.URI; +import java.util.Map; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; +import javax.ws.rs.core.UriBuilder; + +import org.jclouds.http.HttpRequest; +import org.jclouds.http.utils.ModifyRequest; +import org.jclouds.location.functions.RegionToEndpointOrProviderIfNull; +import org.jclouds.rest.binders.BindAsHostPrefix; +import org.jclouds.s3.Bucket; +import org.jclouds.s3.binders.BindAsHostPrefixIfConfigured; + +/** + * + * @author Adrian Cole + */ +@Singleton +public class AssignCorrectHostnameAndBindAsHostPrefixIfConfigured extends BindAsHostPrefixIfConfigured { + private final Map bucketToRegion; + private final RegionToEndpointOrProviderIfNull r2; + + @Inject + public AssignCorrectHostnameAndBindAsHostPrefixIfConfigured(BindAsHostPrefix bindAsHostPrefix, + @Named(PROPERTY_S3_VIRTUAL_HOST_BUCKETS) boolean isVhostStyle, + @Named(PROPERTY_S3_SERVICE_PATH) String servicePath, RegionToEndpointOrProviderIfNull r2, + Provider uriBuilderProvider, @Bucket Map bucketToRegion) { + super(bindAsHostPrefix, isVhostStyle, servicePath, uriBuilderProvider); + this.bucketToRegion = bucketToRegion; + this.r2 = r2; + } + + @Override + public R bindToRequest(R request, Object payload) { + String bucket = payload.toString(); + String region = bucketToRegion.get(bucket); + if (region != null) { + URI endpoint = r2.apply(region); + request = ModifyRequest.endpoint( + request, + uriBuilderProvider.get().uri(endpoint).path(request.getEndpoint().getPath()) + .replaceQuery(request.getEndpoint().getQuery()).build()); + } + return super.bindToRequest(request, payload); + } +} diff --git a/providers/aws-s3/src/main/java/org/jclouds/aws/s3/config/AWSS3RestClientModule.java b/providers/aws-s3/src/main/java/org/jclouds/aws/s3/config/AWSS3RestClientModule.java index fbd3933472..daccd2d7ed 100644 --- a/providers/aws-s3/src/main/java/org/jclouds/aws/s3/config/AWSS3RestClientModule.java +++ b/providers/aws-s3/src/main/java/org/jclouds/aws/s3/config/AWSS3RestClientModule.java @@ -18,14 +18,25 @@ */ package org.jclouds.aws.s3.config; +import static org.jclouds.aws.domain.Region.US_STANDARD; +import static org.jclouds.location.reference.LocationConstants.ENDPOINT; +import static org.jclouds.location.reference.LocationConstants.PROPERTY_REGION; + +import java.net.URI; + +import javax.inject.Named; import javax.inject.Singleton; import org.jclouds.aws.s3.AWSS3AsyncClient; import org.jclouds.aws.s3.AWSS3Client; +import org.jclouds.aws.s3.binders.AssignCorrectHostnameAndBindAsHostPrefixIfConfigured; import org.jclouds.http.RequiresHttp; +import org.jclouds.location.Region; import org.jclouds.rest.ConfiguresRestClient; +import org.jclouds.s3.Bucket; import org.jclouds.s3.S3AsyncClient; import org.jclouds.s3.S3Client; +import org.jclouds.s3.binders.BindAsHostPrefixIfConfigured; import org.jclouds.s3.config.S3RestClientModule; import com.google.inject.Provides; @@ -39,6 +50,24 @@ import com.google.inject.Provides; @ConfiguresRestClient public class AWSS3RestClientModule extends S3RestClientModule { + @Provides + @Singleton + @Bucket + protected URI provideBucketURI(@Named(PROPERTY_REGION + "." + US_STANDARD + "." + ENDPOINT) String endpoint) { + return URI.create(endpoint); + } + + @Override + protected String defaultRegionForBucket(@Region String defaultRegion) { + return US_STANDARD; + } + + @Override + protected void configure() { + bind(BindAsHostPrefixIfConfigured.class).to(AssignCorrectHostnameAndBindAsHostPrefixIfConfigured.class); + super.configure(); + } + public AWSS3RestClientModule() { super(AWSS3Client.class, AWSS3AsyncClient.class); } diff --git a/providers/aws-s3/src/test/java/org/jclouds/aws/s3/AWSS3AsyncClientTest.java b/providers/aws-s3/src/test/java/org/jclouds/aws/s3/AWSS3AsyncClientTest.java index 865c31c3a9..137777a4f2 100644 --- a/providers/aws-s3/src/test/java/org/jclouds/aws/s3/AWSS3AsyncClientTest.java +++ b/providers/aws-s3/src/test/java/org/jclouds/aws/s3/AWSS3AsyncClientTest.java @@ -21,8 +21,10 @@ package org.jclouds.aws.s3; import java.io.IOException; import java.lang.reflect.Array; import java.lang.reflect.Method; +import java.net.URI; import java.util.Map; import java.util.Properties; +import java.util.concurrent.ConcurrentMap; import org.jclouds.aws.s3.config.AWSS3RestClientModule; import org.jclouds.aws.s3.functions.ETagFromHttpResponseViaRegex; @@ -31,6 +33,7 @@ import org.jclouds.date.TimeStamp; import org.jclouds.http.HttpRequest; import org.jclouds.http.RequiresHttp; import org.jclouds.http.functions.ParseETagHeader; +import org.jclouds.http.functions.ParseSax; import org.jclouds.http.functions.ReleasePayloadAndReturn; import org.jclouds.http.functions.ReturnTrueIf2xx; import org.jclouds.io.Payload; @@ -45,17 +48,20 @@ import org.jclouds.s3.domain.ObjectMetadataBuilder; import org.jclouds.s3.functions.ReturnFalseIfBucketAlreadyOwnedByYouOrIllegalState; import org.jclouds.s3.options.PutBucketOptions; import org.jclouds.s3.options.PutObjectOptions; +import org.jclouds.s3.xml.LocationConstraintHandler; import org.testng.annotations.Test; import com.google.common.base.Supplier; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; import com.google.inject.Module; import com.google.inject.TypeLiteral; /** * @author Adrian Cole */ -// NOTE:without testName, this will not call @Before* and fail w/NPE during surefire +// NOTE:without testName, this will not call @Before* and fail w/NPE during +// surefire @Test(groups = "unit", testName = "AWSS3AsyncClientTest") public class AWSS3AsyncClientTest extends org.jclouds.s3.S3AsyncClientTest { @@ -63,6 +69,63 @@ public class AWSS3AsyncClientTest extends org.jclouds.s3.S3AsyncClientTest> createTypeLiteral() { return new TypeLiteral>() { @@ -75,26 +138,26 @@ public class AWSS3AsyncClientTest extends org.jclouds.s3.S3AsyncClientTest of(1, "\"a54357aff0632cce46d942af68356b38\"")); + String.class, Map.class); + HttpRequest request = processor.createRequest(method, "bucket", "foo", "asdsadasdas", + ImmutableMap. of(1, "\"a54357aff0632cce46d942af68356b38\"")); assertRequestLineEquals(request, "POST https://bucket." + url + "/foo?uploadId=asdsadasdas HTTP/1.1"); assertNonPayloadHeadersEqual(request, "Host: bucket." + url + "\n"); assertPayloadEquals( - request, - "1\"a54357aff0632cce46d942af68356b38\"", - "text/xml", false); + request, + "1\"a54357aff0632cce46d942af68356b38\"", + "text/xml", false); assertResponseParserClassEquals(method, request, ETagFromHttpResponseViaRegex.class); assertSaxResponseParserClassEquals(method, null); @@ -160,16 +223,16 @@ public class AWSS3AsyncClientTest extends org.jclouds.s3.S3AsyncClientTestEU", - "text/xml", false); + "EU", + "text/xml", false); assertResponseParserClassEquals(method, request, ReturnTrueIf2xx.class); assertSaxResponseParserClassEquals(method, null); @@ -186,6 +249,18 @@ public class AWSS3AsyncClientTest extends org.jclouds.s3.S3AsyncClientTest bucketToRegion() { + ConcurrentMap returnVal = Maps.newConcurrentMap(); + returnVal.put("eubucket", "EU"); + return returnVal; + } + + @Override + protected URI provideBucketURI(String endpoint) { + return URI.create("https://bucketendpoint"); + } + @Override protected String provideTimeStamp(@TimeStamp Supplier cache) { return "2009-11-08T15:54:08.897Z"; diff --git a/providers/cloudloadbalancers-us/pom.xml b/providers/cloudloadbalancers-us/pom.xml new file mode 100644 index 0000000000..d75be50da9 --- /dev/null +++ b/providers/cloudloadbalancers-us/pom.xml @@ -0,0 +1,125 @@ + + + + 4.0.0 + + org.jclouds + jclouds-project + 1.0-SNAPSHOT + ../../project/pom.xml + + org.jclouds.provider + cloudloadbalancers-us + jclouds cloudloadbalancers-us core + jclouds components to access cloudloadbalancers-us + + + https://auth.api.rackspacecloud.com + 1.0 + ${test.rackspace-us.identity} + ${test.rackspace-us.credential} + + + + org.jclouds + jclouds-loadbalancer + ${project.version} + + + org.jclouds.common + openstack-common + ${project.version} + + + org.jclouds + jclouds-core + ${project.version} + test-jar + test + + + org.jclouds + jclouds-loadbalancer + ${project.version} + test-jar + test + + + org.jclouds.common + openstack-common + ${project.version} + test-jar + test + + + org.jclouds.driver + jclouds-log4j + ${project.version} + test + + + + + live + + + + org.apache.maven.plugins + maven-surefire-plugin + + + integration + integration-test + + test + + + + + test.cloudloadbalancers-us.endpoint + ${test.cloudloadbalancers-us.endpoint} + + + test.cloudloadbalancers-us.apiversion + ${test.cloudloadbalancers-us.apiversion} + + + test.cloudloadbalancers-us.identity + ${test.cloudloadbalancers-us.identity} + + + test.cloudloadbalancers-us.credential + ${test.cloudloadbalancers-us.credential} + + + + + + + + + + + diff --git a/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/CloudLoadBalancersAsyncClient.java b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/CloudLoadBalancersAsyncClient.java new file mode 100644 index 0000000000..ee32d3dac4 --- /dev/null +++ b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/CloudLoadBalancersAsyncClient.java @@ -0,0 +1,58 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers; + +import java.util.Set; + +import org.jclouds.cloudloadbalancers.features.LoadBalancerAsyncClient; +import org.jclouds.location.Region; +import org.jclouds.location.functions.RegionToEndpointOrProviderIfNull; +import org.jclouds.rest.annotations.Delegate; +import org.jclouds.rest.annotations.EndpointParam; + +import com.google.inject.Provides; + +/** + * Provides asynchronous access to CloudLoadBalancers via their REST API. + *

+ * + * @see CloudLoadBalancersClient + * @see + * @author Adrian Cole + */ +public interface CloudLoadBalancersAsyncClient { + + /** + * + * @return the region codes configured + */ + @Provides + @Region + Set getConfiguredRegions(); + + /** + * Provides asynchronous access to LoadBalancer features. + */ + @Delegate + LoadBalancerAsyncClient getLoadBalancerClient( + @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) String region); + +} diff --git a/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/CloudLoadBalancersClient.java b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/CloudLoadBalancersClient.java new file mode 100644 index 0000000000..7c8b7edd61 --- /dev/null +++ b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/CloudLoadBalancersClient.java @@ -0,0 +1,60 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers; + +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import org.jclouds.cloudloadbalancers.features.LoadBalancerClient; +import org.jclouds.concurrent.Timeout; +import org.jclouds.location.Region; +import org.jclouds.location.functions.RegionToEndpointOrProviderIfNull; +import org.jclouds.rest.annotations.Delegate; +import org.jclouds.rest.annotations.EndpointParam; + +import com.google.inject.Provides; + +/** + * Provides synchronous access to CloudLoadBalancers. + *

+ * + * @see CloudLoadBalancersAsyncClient + * @see + * @author Adrian Cole + */ +@Timeout(duration = 60, timeUnit = TimeUnit.SECONDS) +public interface CloudLoadBalancersClient { + /** + * + * @return the region codes configured + */ + @Provides + @Region + Set getConfiguredRegions(); + + /** + * Provides synchronous access to LoadBalancer features. + */ + @Delegate + LoadBalancerClient getLoadBalancerClient( + @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) String region); + +} diff --git a/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/CloudLoadBalancersContextBuilder.java b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/CloudLoadBalancersContextBuilder.java new file mode 100644 index 0000000000..014bbf3cf8 --- /dev/null +++ b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/CloudLoadBalancersContextBuilder.java @@ -0,0 +1,50 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers; + +import java.util.List; +import java.util.Properties; + +import org.jclouds.cloudloadbalancers.config.CloudLoadBalancersRestClientModule; +import org.jclouds.cloudloadbalancers.loadbalancer.config.CloudLoadBalancersLoadBalancerContextModule; +import org.jclouds.loadbalancer.LoadBalancerServiceContextBuilder; + +import com.google.inject.Module; + +/** + * + * @author Adrian Cole + */ +public class CloudLoadBalancersContextBuilder extends + LoadBalancerServiceContextBuilder { + + public CloudLoadBalancersContextBuilder(Properties props) { + super(CloudLoadBalancersClient.class, CloudLoadBalancersAsyncClient.class, props); + } + + @Override + protected void addContextModule(List modules) { + modules.add(new CloudLoadBalancersLoadBalancerContextModule()); + } + + protected void addClientModule(List modules) { + modules.add(new CloudLoadBalancersRestClientModule()); + } + +} diff --git a/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/CloudLoadBalancersUSPropertiesBuilder.java b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/CloudLoadBalancersUSPropertiesBuilder.java new file mode 100644 index 0000000000..e934304f19 --- /dev/null +++ b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/CloudLoadBalancersUSPropertiesBuilder.java @@ -0,0 +1,68 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers; + +import static org.jclouds.Constants.PROPERTY_API_VERSION; +import static org.jclouds.Constants.PROPERTY_ENDPOINT; +import static org.jclouds.Constants.PROPERTY_ISO3166_CODES; +import static org.jclouds.cloudloadbalancers.reference.RackspaceConstants.PROPERTY_ACCOUNT_ID; +import static org.jclouds.cloudloadbalancers.reference.Region.DFW; +import static org.jclouds.cloudloadbalancers.reference.Region.ORD; +import static org.jclouds.location.reference.LocationConstants.ENDPOINT; +import static org.jclouds.location.reference.LocationConstants.ISO3166_CODES; +import static org.jclouds.location.reference.LocationConstants.PROPERTY_REGION; +import static org.jclouds.location.reference.LocationConstants.PROPERTY_REGIONS; + +import java.util.Properties; + +import org.jclouds.PropertiesBuilder; + +import com.google.common.base.Joiner; + +/** + * Builds properties used inRackspace Cloud Load Balancers Clients + * + * @author Adrian Cole + */ +public class CloudLoadBalancersUSPropertiesBuilder extends PropertiesBuilder { + @Override + protected Properties defaultProperties() { + Properties properties = super.defaultProperties(); + properties.setProperty(PROPERTY_API_VERSION, "1.0"); + properties.setProperty(PROPERTY_ENDPOINT, "https://auth.api.rackspacecloud.com"); + properties.setProperty(PROPERTY_REGIONS, Joiner.on(',').join(ORD, DFW)); + properties.setProperty(PROPERTY_ISO3166_CODES, "US-IL,US-TX"); + + properties.setProperty(PROPERTY_REGION + "." + ORD + "." + ISO3166_CODES, "US-IL"); + properties.setProperty(PROPERTY_REGION + "." + ORD + "." + ENDPOINT, String + .format("https://ord.loadbalancers.api.rackspacecloud.com/v{%s}/{%s}", PROPERTY_API_VERSION, + PROPERTY_ACCOUNT_ID)); + + properties.setProperty(PROPERTY_REGION + "." + DFW + "." + ISO3166_CODES, "US-TX"); + properties.setProperty(PROPERTY_REGION + "." + DFW + "." + ENDPOINT, String + .format("https://dfw.loadbalancers.api.rackspacecloud.com/v{%s}/{%s}", PROPERTY_API_VERSION, + PROPERTY_ACCOUNT_ID)); + return properties; + } + + public CloudLoadBalancersUSPropertiesBuilder(Properties properties) { + super(properties); + } + +} diff --git a/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/LoadBalancer.java b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/LoadBalancer.java new file mode 100644 index 0000000000..f98d598380 --- /dev/null +++ b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/LoadBalancer.java @@ -0,0 +1,40 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import javax.inject.Qualifier; + +/** + * Represents a Load Balancer endpoint + * + * @author Adrian Cole + * + */ +@Retention(value = RetentionPolicy.RUNTIME) +@Target(value = { ElementType.TYPE, ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD }) +@Qualifier +public @interface LoadBalancer { + +} \ No newline at end of file diff --git a/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/config/CloudLoadBalancersRestClientModule.java b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/config/CloudLoadBalancersRestClientModule.java new file mode 100644 index 0000000000..95a268991e --- /dev/null +++ b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/config/CloudLoadBalancersRestClientModule.java @@ -0,0 +1,131 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers.config; + +import java.net.URI; +import java.util.Map; +import java.util.Set; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.jclouds.cloudloadbalancers.CloudLoadBalancersAsyncClient; +import org.jclouds.cloudloadbalancers.CloudLoadBalancersClient; +import org.jclouds.cloudloadbalancers.features.LoadBalancerAsyncClient; +import org.jclouds.cloudloadbalancers.features.LoadBalancerClient; +import org.jclouds.cloudloadbalancers.handlers.ParseCloudLoadBalancersErrorFromHttpResponse; +import org.jclouds.cloudloadbalancers.reference.RackspaceConstants; +import org.jclouds.http.HttpErrorHandler; +import org.jclouds.http.RequiresHttp; +import org.jclouds.http.annotation.ClientError; +import org.jclouds.http.annotation.Redirection; +import org.jclouds.http.annotation.ServerError; +import org.jclouds.json.config.GsonModule.DateAdapter; +import org.jclouds.json.config.GsonModule.Iso8601DateAdapter; +import org.jclouds.location.Region; +import org.jclouds.location.config.ProvideRegionToURIViaProperties; +import org.jclouds.openstack.OpenStackAuthAsyncClient.AuthenticationResponse; +import org.jclouds.openstack.config.OpenStackAuthenticationModule; +import org.jclouds.openstack.reference.AuthHeaders; +import org.jclouds.rest.ConfiguresRestClient; +import org.jclouds.rest.config.RestClientModule; + +import com.google.common.base.Supplier; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Multimap; +import com.google.inject.Injector; +import com.google.inject.Provides; +import com.google.inject.Scopes; +import com.google.inject.TypeLiteral; + +/** + * Configures theRackspace Cloud Load Balancers connection. + * + * @author Adrian Cole + */ +@RequiresHttp +@ConfiguresRestClient +public class CloudLoadBalancersRestClientModule extends + RestClientModule { + + public static final Map, Class> DELEGATE_MAP = ImmutableMap., Class> builder()// + .put(LoadBalancerClient.class, LoadBalancerAsyncClient.class)// + .build(); + + public CloudLoadBalancersRestClientModule() { + super(CloudLoadBalancersClient.class, CloudLoadBalancersAsyncClient.class, DELEGATE_MAP); + } + + protected void bindRegionsToProvider() { + bindRegionsToProvider(ProvideRegionToURIViaPropertiesWithAccountID.class); + } + + @Singleton + public static class ProvideRegionToURIViaPropertiesWithAccountID extends ProvideRegionToURIViaProperties { + + @Inject + protected ProvideRegionToURIViaPropertiesWithAccountID(Injector injector, + @Named("CONSTANTS") Multimap constants, + @Named(RackspaceConstants.PROPERTY_ACCOUNT_ID) String accountID) { + super(injector, constants); + constants.replaceValues(RackspaceConstants.PROPERTY_ACCOUNT_ID, ImmutableSet.of(accountID)); + } + } + + protected void bindRegionsToProvider(Class>> providerClass) { + bind(new TypeLiteral>() { + }).annotatedWith(Region.class).toProvider(providerClass).in(Scopes.SINGLETON); + } + + @Override + protected void configure() { + install(new OpenStackAuthenticationModule()); + bind(DateAdapter.class).to(Iso8601DateAdapter.class); + bindRegionsToProvider(); + super.configure(); + } + + @Provides + @Singleton + @Named(RackspaceConstants.PROPERTY_ACCOUNT_ID) + protected String accountID(Supplier in) { + URI serverURL = in.get().getServices().get(AuthHeaders.SERVER_MANAGEMENT_URL); + return serverURL.getPath().substring(serverURL.getPath().lastIndexOf('/') + 1); + } + + @Provides + @Singleton + @Region + public Set regions(@Region Map endpoints) { + return endpoints.keySet(); + } + + @Override + protected void bindErrorHandlers() { + bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to( + ParseCloudLoadBalancersErrorFromHttpResponse.class); + bind(HttpErrorHandler.class).annotatedWith(ClientError.class).to( + ParseCloudLoadBalancersErrorFromHttpResponse.class); + bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to( + ParseCloudLoadBalancersErrorFromHttpResponse.class); + } + +} diff --git a/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/domain/LoadBalancer.java b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/domain/LoadBalancer.java new file mode 100644 index 0000000000..757ecc092a --- /dev/null +++ b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/domain/LoadBalancer.java @@ -0,0 +1,310 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers.domain; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Date; +import java.util.Set; + +import org.jclouds.cloudloadbalancers.domain.internal.BaseLoadBalancer; + +import com.google.common.collect.ImmutableSet; + +/** + * + * @author Adrian Cole + * @see + */ +public class LoadBalancer extends BaseLoadBalancer { + + @SuppressWarnings("unchecked") + public static Builder builder() { + return new Builder(); + } + + /** + * {@inheritDoc} + */ + @Override + public Builder toBuilder() { + return new Builder().from(this); + } + + public static class Builder extends BaseLoadBalancer.Builder { + private String region; + private int id = -1; + private Status status; + private Set virtualIPs = ImmutableSet. of(); + private String sessionPersistenceType; + private String clusterName; + private Date created; + private Date updated; + private boolean connectionLoggingEnabled; + + public Builder region(String region) { + this.region = region; + return this; + } + + public Builder id(int id) { + this.id = id; + return this; + } + + public Builder status(Status status) { + this.status = status; + return this; + } + + public Builder virtualIPs(Iterable virtualIPs) { + this.virtualIPs = ImmutableSet. copyOf(checkNotNull(virtualIPs, "virtualIPs")); + return this; + } + + public Builder sessionPersistenceType(String sessionPersistenceType) { + this.sessionPersistenceType = sessionPersistenceType; + return this; + } + + public Builder clusterName(String clusterName) { + this.clusterName = clusterName; + return this; + } + + public Builder created(Date created) { + this.created = created; + return this; + } + + public Builder updated(Date updated) { + this.updated = updated; + return this; + } + + public Builder connectionLoggingEnabled(boolean connectionLoggingEnabled) { + this.connectionLoggingEnabled = connectionLoggingEnabled; + return this; + } + + public LoadBalancer build() { + return new LoadBalancer(region, id, name, protocol, port, algorithm, status, virtualIPs, nodes, + sessionPersistenceType, clusterName, created, updated, connectionLoggingEnabled); + } + + @Override + public Builder nodes(Iterable nodes) { + this.nodes = ImmutableSet. copyOf(checkNotNull(nodes, "nodes")); + return this; + } + + @Override + public Builder node(Node nodes) { + this.nodes.add(checkNotNull(nodes, "nodes")); + return this; + } + + @Override + public Builder algorithm(String algorithm) { + return Builder.class.cast(super.algorithm(algorithm)); + } + + @Override + public Builder from(LoadBalancer in) { + return Builder.class.cast(super.from(in)).id(in.getId()).status(in.getStatus()).virtualIPs(in.getVirtualIPs()) + .clusterName(in.getClusterName()).created(in.getCreated()).updated(in.getUpdated()) + .connectionLoggingEnabled(in.isConnectionLoggingEnabled()); + } + + @Override + public Builder name(String name) { + return Builder.class.cast(super.name(name)); + } + + @Override + public Builder port(Integer port) { + return Builder.class.cast(super.port(port)); + } + + @Override + public Builder protocol(String protocol) { + return Builder.class.cast(super.protocol(protocol)); + } + + } + + /** + * All load balancers also have a status attribute to signify the current configuration status of + * the device. This status is immutable by the caller and is updated automatically based on state + * changes within the service. When a load balancer is first created, it will be placed into a + * BUILD status while the configuration is being generated and applied based on the request. Once + * the configuration is applied and finalized, it will be in an ACTIVE status. In the event of a + * configuration change or update, the status of the load balancer will change to PENDING_UPDATE + * to signify configuration changes are in progress but have not yet been finalized. Load + * balancers in a SUSPENDED status are configured to reject traffic and will not forward requests + * to back-end nodess. + */ + public static enum Status { + /** + * Load balancer is being provisioned for the first time and configuration is being applied to + * bring the service online. The service will not yet be ready to serve incoming requests. + */ + BUILD, + /** + * Load balancer is configured properly and ready to serve traffic to incoming requests via + * the configured virtual IPs. + */ + ACTIVE, + /** + * Load balancer is online but configuration changes are being applied to update the service + * based on a previous request. + */ + PENDING_UPDATE, + /** + * Load balancer has been taken offline and disabled; contact Support. + */ + SUSPENDED, + /** + * Load balancer is online but configuration changes are being applied to begin deletion of + * the service based on a previous request. + */ + PENDING_DELETE, + /** + * Load balancers in DELETED status can be displayed for at least 90 days after deletion. + */ + DELETED, + /** + * The system encountered an error when attempting to configure the load balancer; contact + * Support. + */ + ERROR, UNRECOGNIZED; + + public static Status fromValue(String status) { + try { + return valueOf(checkNotNull(status, "status")); + } catch (IllegalArgumentException e) { + return UNRECOGNIZED; + } + } + + } + + private final String region; + private final int id; + private final Status status; + private final Set virtualIPs; + private final String sessionPersistenceType; + private final String clusterName; + private final Date created; + private final Date updated; + private final boolean connectionLoggingEnabled; + + public LoadBalancer(String region, int id, String name, String protocol, Integer port, String algorithm, Status status, + Iterable virtualIPs, Iterable nodes, String sessionPersistenceType, String clusterName, + Date created, Date updated, boolean connectionLoggingEnabled) { + super(name, protocol, port, algorithm, nodes); + this.region = checkNotNull(region, "region"); + checkArgument(id != -1, "id must be specified"); + this.id = id; + this.status = checkNotNull(status, "status"); + this.virtualIPs = ImmutableSet.copyOf(checkNotNull(virtualIPs, "virtualIPs")); + this.sessionPersistenceType = sessionPersistenceType; + this.clusterName = clusterName; + this.created = checkNotNull(created, "created"); + this.updated = checkNotNull(updated, "updated"); + this.connectionLoggingEnabled = connectionLoggingEnabled; + } + + public String getRegion() { + return region; + } + + public int getId() { + return id; + } + + public Status getStatus() { + return status; + } + + public Set getVirtualIPs() { + return virtualIPs; + } + + public String getClusterName() { + return clusterName; + } + + public String getSessionPersistenceType() { + return sessionPersistenceType; + } + + public Date getCreated() { + return created; + } + + public Date getUpdated() { + return updated; + } + + public boolean isConnectionLoggingEnabled() { + return connectionLoggingEnabled; + } + + @Override + public String toString() { + return String + .format( + "[region=%s, id=%s, name=%s, protocol=%s, port=%s, algorithm=%s, status=%s, virtualIPs=%s, nodes=%s, sessionPersistenceType=%s, created=%s, updated=%s, clusterName=%s, connectionLoggingEnabled=%s]", + region, id, name, protocol, port, algorithm, status, virtualIPs, nodes, sessionPersistenceType, + created, updated, clusterName, connectionLoggingEnabled); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + id; + result = prime * result + ((region == null) ? 0 : region.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + LoadBalancer other = (LoadBalancer) obj; + if (id != other.id) + return false; + if (region == null) { + if (other.region != null) + return false; + } else if (!region.equals(other.region)) + return false; + return true; + } + +} diff --git a/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/domain/LoadBalancerAttributes.java b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/domain/LoadBalancerAttributes.java new file mode 100644 index 0000000000..f606e3c42a --- /dev/null +++ b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/domain/LoadBalancerAttributes.java @@ -0,0 +1,125 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers.domain; + +import org.jclouds.cloudloadbalancers.domain.internal.BaseLoadBalancer; + +/** + * + * @author Adrian Cole + * @see + */ +public class LoadBalancerAttributes { + protected String name; + protected String protocol; + protected Integer port; + protected String algorithm; + + public LoadBalancerAttributes name(String name) { + this.name = name; + return this; + } + + public LoadBalancerAttributes protocol(String protocol) { + this.protocol = protocol; + return this; + } + + public LoadBalancerAttributes port(int port) { + this.port = port; + return this; + } + + public LoadBalancerAttributes algorithm(String algorithm) { + this.algorithm = algorithm; + return this; + } + + public static > LoadBalancerAttributes fromLoadBalancer(T lb) { + return Builder.name(lb.getName()).port(lb.getPort()).protocol(lb.getProtocol()).algorithm(lb.getAlgorithm()); + } + + public static class Builder { + public static LoadBalancerAttributes name(String name) { + return new LoadBalancerAttributes().name(name); + } + + public static LoadBalancerAttributes protocol(String protocol) { + return new LoadBalancerAttributes().protocol(protocol); + } + + public static LoadBalancerAttributes port(int port) { + return new LoadBalancerAttributes().port(port); + } + + public static LoadBalancerAttributes algorithm(String algorithm) { + return new LoadBalancerAttributes().algorithm(algorithm); + } + } + + @Override + public String toString() { + return String.format("[algorithm=%s, name=%s, port=%s, protocol=%s]", algorithm, name, port, protocol); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((algorithm == null) ? 0 : algorithm.hashCode()); + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((port == null) ? 0 : port.hashCode()); + result = prime * result + ((protocol == null) ? 0 : protocol.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; + LoadBalancerAttributes other = (LoadBalancerAttributes) obj; + if (algorithm == null) { + if (other.algorithm != null) + return false; + } else if (!algorithm.equals(other.algorithm)) + return false; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + if (port == null) { + if (other.port != null) + return false; + } else if (!port.equals(other.port)) + return false; + if (protocol == null) { + if (other.protocol != null) + return false; + } else if (!protocol.equals(other.protocol)) + return false; + return true; + } +} diff --git a/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/domain/LoadBalancerRequest.java b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/domain/LoadBalancerRequest.java new file mode 100644 index 0000000000..9d7bbec8a5 --- /dev/null +++ b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/domain/LoadBalancerRequest.java @@ -0,0 +1,149 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers.domain; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.List; +import java.util.Map; + +import org.jclouds.cloudloadbalancers.domain.internal.BaseLoadBalancer; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; + +/** + * + * @author Adrian Cole + * @see + */ +public class LoadBalancerRequest extends BaseLoadBalancer { + + @SuppressWarnings("unchecked") + public static Builder builder() { + return new Builder(); + } + + /** + * {@inheritDoc} + */ + @Override + public Builder toBuilder() { + return new Builder().from(this); + } + + public static class Builder extends BaseLoadBalancer.Builder { + private VirtualIP.Type virtualIPType; + private Integer virtualIPId; + private List> virtualIps; + + public Builder virtualIPId(Integer virtualIPId) { + this.virtualIPId = virtualIPId; + return this; + } + + public Builder virtualIPType(VirtualIP.Type virtualIPType) { + this.virtualIPType = virtualIPType; + return this; + } + + public LoadBalancerRequest build() { + return virtualIps == null ? new LoadBalancerRequest(name, protocol, port, algorithm, nodes, virtualIPType, + virtualIPId) : new LoadBalancerRequest(name, protocol, port, algorithm, nodes, virtualIps); + } + + @Override + public Builder nodes(Iterable nodes) { + this.nodes = ImmutableSet. copyOf(checkNotNull(nodes, "nodes")); + return this; + } + + @Override + public Builder node(NodeRequest nodes) { + this.nodes.add(checkNotNull(nodes, "nodes")); + return this; + } + + private Builder virtualIPs(List> virtualIPs) { + this.virtualIps = virtualIPs; + return this; + } + + @Override + public Builder algorithm(String algorithm) { + return Builder.class.cast(super.algorithm(algorithm)); + } + + @Override + public Builder from(LoadBalancerRequest in) { + return Builder.class.cast(super.from(in)).virtualIPs(in.virtualIps); + } + + @Override + public Builder name(String name) { + return Builder.class.cast(super.name(name)); + } + + @Override + public Builder port(Integer port) { + return Builder.class.cast(super.port(port)); + } + + @Override + public Builder protocol(String protocol) { + return Builder.class.cast(super.protocol(protocol)); + } + + } + + private final List> virtualIps; + + public LoadBalancerRequest(String name, String protocol, int port, String algorithm, Iterable nodes, + VirtualIP.Type virtualIPType, Integer virtualIPId) { + this(name, protocol, port, algorithm, nodes, getVirtualIPsFromOptions(virtualIPType, virtualIPId)); + } + + private LoadBalancerRequest(String name, String protocol, int port, String algorithm, Iterable nodes, + List> virtualIPsFromOptions) { + super(name, protocol, port, algorithm, nodes); + this.virtualIps = checkNotNull(virtualIPsFromOptions, "virtualIPsFromOptions"); + } + + static List> getVirtualIPsFromOptions(VirtualIP.Type virtualIPType, Integer virtualIPId) { + checkArgument(virtualIPType == null || virtualIPId == null, + "virtualIPType and virtualIPId cannot both be specified"); + if (virtualIPType != null) + return ImmutableList.> of(ImmutableMap.of("type", virtualIPType.name())); + else if (virtualIPId != null) + return ImmutableList.> of(ImmutableMap.of("id", virtualIPId.toString())); + else + throw new IllegalArgumentException("virtualIPType or virtualIPId must be specified"); + } + + @Override + public String toString() { + return String.format("[algorithm=%s, name=%s, nodes=%s, port=%s, protocol=%s, virtualIps=%s]", algorithm, name, + nodes, port, protocol, virtualIps); + } + +} diff --git a/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/domain/Node.java b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/domain/Node.java new file mode 100644 index 0000000000..4a7113423b --- /dev/null +++ b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/domain/Node.java @@ -0,0 +1,200 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers.domain; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import org.jclouds.cloudloadbalancers.domain.internal.BaseNode; + +/** + * The nodes defined by the load balancer are responsible for servicing the requests received + * through the load balancer's virtual IP. By default, the load balancer employs a basic health + * check that ensures the node is listening on its defined port. The node is checked at the time of + * addition and at regular intervals as defined by the load balancer health check configuration. If + * a back-end node is not listening on its port or does not meet the conditions of the defined + * active health check for the load balancer, then the load balancer will not forward connections + * and its status will be listed as OFFLINE. Only nodes that are in an ONLINE status will receive + * and be able to service traffic from the load balancer. + *

+ * All nodes have an associated status that indicates whether the node is ONLINE, OFFLINE, or + * DRAINING. Only nodes that are in ONLINE status will receive and be able to service traffic from + * the load balancer. The OFFLINE status represents a node that cannot accept or service traffic. A + * node in DRAINING status represents a node that stops the traffic manager from sending any + * additional new connections to the node, but honors established sessions. If the traffic manager + * receives a request and session persistence requires that the node is used, the traffic manager + * will use it. The status is determined by the passive or active health monitors. + *

+ * If the WEIGHTED_ROUND_ROBIN load balancer algorithm mode is selected, then the caller should + * assign the relevant weights to the node as part of the weight attribute of the node element. When + * the algorithm of the load balancer is changed to WEIGHTED_ROUND_ROBIN and the nodes do not + * already have an assigned weight, the service will automatically set the weight to "1" for all + * nodes. + * + * @author Adrian Cole + * @see + */ +public class Node extends BaseNode { + + @SuppressWarnings("unchecked") + public static Builder builder() { + return new Builder(); + } + + /** + * {@inheritDoc} + */ + @Override + public Builder toBuilder() { + return new Builder().from(this); + } + + public static class Builder extends BaseNode.Builder { + private int id = -1; + private Status status; + + public Builder id(int id) { + this.id = id; + return this; + } + + public Builder status(Status status) { + this.status = status; + return this; + } + + @Override + public Node build() { + return new Node(id, address, port, condition, status, weight); + } + + @Override + public Builder address(String address) { + return Builder.class.cast(super.address(address)); + } + + @Override + public Builder condition(Condition condition) { + return Builder.class.cast(super.condition(condition)); + } + + @Override + public Builder from(Node in) { + return Builder.class.cast(super.from(in)).id(in.getId()).status(in.getStatus()); + } + + @Override + public Builder port(int port) { + return Builder.class.cast(super.port(port)); + } + + @Override + public Builder weight(Integer weight) { + return Builder.class.cast(super.weight(weight)); + } + + } + + /** + * The status is determined by the passive or active health monitors. + * + */ + public static enum Status { + /** + * Only nodes that are in an ONLINE status will receive and be able to service traffic from + * the load balancer. + */ + ONLINE, + + /** + * represents a node that cannot accept or service traffic + */ + OFFLINE, + + /** + * represents a node that stops the traffic manager from sending any additional new + * connections to the node, but honors established sessions. + */ + DRAINING, + + UNRECOGNIZED; + + public static Status fromValue(String status) { + try { + return valueOf(checkNotNull(status, "status")); + } catch (IllegalArgumentException e) { + return UNRECOGNIZED; + } + } + + } + + private int id; + private Status status; + + // for serialization only + Node() { + + } + + public Node(int id, String address, int port, Condition condition, Status status, Integer weight) { + super(address, port, condition, weight); + checkArgument(id != -1, "id must be specified"); + this.id = id; + this.status = checkNotNull(status, "status"); + } + + public int getId() { + return id; + } + + public Status getStatus() { + return status; + } + + @Override + public String toString() { + return String.format("[id=%s, address=%s, condition=%s, port=%s, weight=%s,status=%s]", id, address, condition, + port, weight, status); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + id; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + Node other = (Node) obj; + if (id != other.id) + return false; + return true; + } + +} diff --git a/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/domain/NodeRequest.java b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/domain/NodeRequest.java new file mode 100644 index 0000000000..4ddadb8864 --- /dev/null +++ b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/domain/NodeRequest.java @@ -0,0 +1,109 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers.domain; + +import org.jclouds.cloudloadbalancers.domain.internal.BaseNode; + +/** + * The nodes defined by the load balancer are responsible for servicing the requests received + * through the load balancer's virtual IP. By default, the load balancer employs a basic health + * check that ensures the node is listening on its defined port. The node is checked at the time of + * addition and at regular intervals as defined by the load balancer health check configuration. If + * a back-end node is not listening on its port or does not meet the conditions of the defined + * active health check for the load balancer, then the load balancer will not forward connections + * and its status will be listed as OFFLINE. Only nodes that are in an ONLINE status will receive + * and be able to service traffic from the load balancer. + *

+ * All nodes have an associated status that indicates whether the node is ONLINE, OFFLINE, or + * DRAINING. Only nodes that are in ONLINE status will receive and be able to service traffic from + * the load balancer. The OFFLINE status represents a node that cannot accept or service traffic. A + * node in DRAINING status represents a node that stops the traffic manager from sending any + * additional new connections to the node, but honors established sessions. If the traffic manager + * receives a request and session persistence requires that the node is used, the traffic manager + * will use it. The status is determined by the passive or active health monitors. + *

+ * If the WEIGHTED_ROUND_ROBIN load balancer algorithm mode is selected, then the caller should + * assign the relevant weights to the node as part of the weight attribute of the node element. When + * the algorithm of the load balancer is changed to WEIGHTED_ROUND_ROBIN and the nodes do not + * already have an assigned weight, the service will automatically set the weight to "1" for all + * nodes. + * + * @author Adrian Cole + * @see + */ +public class NodeRequest extends BaseNode { + + @SuppressWarnings("unchecked") + public static Builder builder() { + return new Builder(); + } + + /** + * {@inheritDoc} + */ + @Override + public Builder toBuilder() { + return new Builder().from(this); + } + + public static class Builder extends BaseNode.Builder { + + @Override + public NodeRequest build() { + return new NodeRequest(address, port, condition, weight); + } + + @Override + public Builder address(String address) { + return Builder.class.cast(super.address(address)); + } + + @Override + public Builder condition(Condition condition) { + return Builder.class.cast(super.condition(condition)); + } + + @Override + public Builder from(NodeRequest in) { + return Builder.class.cast(super.from(in)); + } + + @Override + public Builder port(int port) { + return Builder.class.cast(super.port(port)); + } + + @Override + public Builder weight(Integer weight) { + return Builder.class.cast(super.weight(weight)); + } + + } + + // for serialization only + NodeRequest() { + + } + + public NodeRequest(String address, int port, Condition condition, Integer weight) { + super(address, port, condition, weight); + } + +} diff --git a/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/domain/VirtualIP.java b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/domain/VirtualIP.java new file mode 100644 index 0000000000..a6d368bc6e --- /dev/null +++ b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/domain/VirtualIP.java @@ -0,0 +1,176 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers.domain; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * A virtual IP (VIP) makes a load balancer accessible by clients. The load balancing service + * supports either a public VIP, routable on the public Internet, or a ServiceNet address, routable + * only within the region in which the load balancer resides. + * + * @author Adrian Cole + * @see + */ +public class VirtualIP implements Comparable { + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private int id = -1; + private String address; + private Type type; + private IPVersion ipVersion = IPVersion.IPV4; + + public Builder id(int id) { + this.id = id; + return this; + } + + public Builder address(String address) { + this.address = address; + return this; + } + + public Builder type(Type type) { + this.type = type; + return this; + } + + public Builder ipVersion(IPVersion ipVersion) { + this.ipVersion = ipVersion; + return this; + } + + public VirtualIP build() { + return new VirtualIP(id, address, type, ipVersion); + } + } + + /** + * Virtual IP Types + */ + public static enum Type { + /** + * An address that is routable on the public Internet. + */ + PUBLIC, + /** + * An address that is routable only on ServiceNet. + */ + SERVICENET, UNRECOGNIZED; + + public static Type fromValue(String type) { + try { + return valueOf(checkNotNull(type, "type")); + } catch (IllegalArgumentException e) { + return UNRECOGNIZED; + } + } + + } + + /** + * Virtual IP Versions + */ + public static enum IPVersion { + + IPV4, IPV6, UNRECOGNIZED; + + public static IPVersion fromValue(String ipVersion) { + try { + return valueOf(checkNotNull(ipVersion, "ipVersion")); + } catch (IllegalArgumentException e) { + return UNRECOGNIZED; + } + } + + } + + // for serialization only + VirtualIP() { + + } + + private int id; + private String address; + private Type type; + private IPVersion ipVersion; + + public VirtualIP(int id, String address, Type type, IPVersion ipVersion) { + checkArgument(id != -1, "id must be specified"); + this.id = id; + this.address = checkNotNull(address, "address"); + this.type = checkNotNull(type, "type"); + this.ipVersion = checkNotNull(ipVersion, "ipVersion"); + } + + public int getId() { + return id; + } + + public String getAddress() { + return address; + } + + public Type getType() { + return type; + } + + public IPVersion getIpVersion() { + return ipVersion; + } + + @Override + public int compareTo(VirtualIP arg0) { + return address.compareTo(arg0.address); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + id; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + VirtualIP other = (VirtualIP) obj; + if (id != other.id) + return false; + return true; + } + + @Override + public String toString() { + return String.format("[address=%s, id=%s, ipVersion=%s, type=%s]", address, id, ipVersion, type); + } + +} diff --git a/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/domain/internal/BaseLoadBalancer.java b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/domain/internal/BaseLoadBalancer.java new file mode 100644 index 0000000000..0826e28a5d --- /dev/null +++ b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/domain/internal/BaseLoadBalancer.java @@ -0,0 +1,173 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers.domain.internal; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Set; +import java.util.SortedSet; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSortedSet; +import com.google.common.collect.Sets; + +/** + * + * @author Adrian Cole + * @see + */ +public class BaseLoadBalancer, T extends BaseLoadBalancer> implements + Comparable> { + + public static , T extends BaseLoadBalancer> Builder builder() { + return new Builder(); + } + + @SuppressWarnings("unchecked") + public Builder toBuilder() { + return new Builder().from((T) this); + } + + public static class Builder, T extends BaseLoadBalancer> { + protected String name; + protected String protocol; + protected Integer port; + protected String algorithm; + protected Set nodes = Sets.newLinkedHashSet(); + + public Builder name(String name) { + this.name = name; + return this; + } + + public Builder protocol(String protocol) { + this.protocol = protocol; + return this; + } + + public Builder port(Integer port) { + this.port = port; + return this; + } + + public Builder algorithm(String algorithm) { + this.algorithm = algorithm; + return this; + } + + public Builder nodes(Iterable nodes) { + this.nodes = ImmutableSet. copyOf(checkNotNull(nodes, "nodes")); + return this; + } + + @SuppressWarnings("unchecked") + public Builder node(N node) { + this.nodes.add((N) checkNotNull(nodes, "nodes")); + return this; + } + + public BaseLoadBalancer build() { + return new BaseLoadBalancer(name, protocol, port, algorithm, nodes); + } + + public Builder from(T baseLoadBalancer) { + return name(baseLoadBalancer.getName()).port(baseLoadBalancer.getPort()).protocol( + baseLoadBalancer.getProtocol()).algorithm(baseLoadBalancer.getAlgorithm()).nodes( + baseLoadBalancer.getNodes()); + } + } + + // for serialization only + protected BaseLoadBalancer() { + + } + + protected String name; + protected String protocol; + protected Integer port; + protected String algorithm; + // so tests will come out consistently + protected SortedSet nodes = ImmutableSortedSet.of(); + + public BaseLoadBalancer(String name, String protocol, Integer port, String algorithm, Iterable nodes) { + this.name = checkNotNull(name, "name"); + this.protocol = protocol;// null on deleted LB + this.port = port;// null on deleted LB + this.algorithm = algorithm;// null on deleted LB + this.nodes = ImmutableSortedSet.copyOf(checkNotNull(nodes, "nodes")); + } + + @Override + public int compareTo(BaseLoadBalancer arg0) { + return name.compareTo(arg0.name); + } + + public String getName() { + return name; + } + + public String getProtocol() { + return protocol; + } + + public Integer getPort() { + return port; + } + + public String getAlgorithm() { + return algorithm; + } + + public Set getNodes() { + return nodes; + } + + @Override + public int hashCode() { + final Integer prime = 31; + Integer result = 1; + result = prime * result + ((name == null) ? 0 : name.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; + BaseLoadBalancer other = (BaseLoadBalancer) obj; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + return true; + } + + @Override + public String toString() { + return String.format("[name=%s, port=%s, protocol=%s, algorithm=%s, nodes=%s]", name, port, protocol, algorithm, + nodes); + } +} diff --git a/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/domain/internal/BaseNode.java b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/domain/internal/BaseNode.java new file mode 100644 index 0000000000..b8382e40c8 --- /dev/null +++ b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/domain/internal/BaseNode.java @@ -0,0 +1,210 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers.domain.internal; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * The nodes defined by the load balancer are responsible for servicing the requests received + * through the load balancer's virtual IP. By default, the load balancer employs a basic health + * check that ensures the node is listening on its defined port. The node is checked at the time of + * addition and at regular intervals as defined by the load balancer health check configuration. If + * a back-end node is not listening on its port or does not meet the conditions of the defined + * active health check for the load balancer, then the load balancer will not forward connections + * and its status will be listed as OFFLINE. Only nodes that are in an ONLINE status will receive + * and be able to service traffic from the load balancer. + *

+ * All nodes have an associated status that indicates whether the node is ONLINE, OFFLINE, or + * DRAINING. Only nodes that are in ONLINE status will receive and be able to service traffic from + * the load balancer. The OFFLINE status represents a node that cannot accept or service traffic. A + * node in DRAINING status represents a node that stops the traffic manager from sending any + * additional new connections to the node, but honors established sessions. If the traffic manager + * receives a request and session persistence requires that the node is used, the traffic manager + * will use it. The status is determined by the passive or active health monitors. + *

+ * If the WEIGHTED_ROUND_ROBIN load balancer algorithm mode is selected, then the caller should + * assign the relevant weights to the node as part of the weight attribute of the node element. When + * the algorithm of the load balancer is changed to WEIGHTED_ROUND_ROBIN and the nodes do not + * already have an assigned weight, the service will automatically set the weight to "1" for all + * nodes. + * + * @author Adrian Cole + * @see + */ +public class BaseNode> implements Comparable> { + + public static > Builder builder() { + return new Builder(); + } + + @SuppressWarnings("unchecked") + public Builder toBuilder() { + return new Builder().from((T) this); + } + + public static class Builder> { + protected String address; + protected int port = -1; + protected Condition condition = Condition.ENABLED; + protected Integer weight; + + public Builder address(String address) { + this.address = address; + return this; + } + + public Builder port(int port) { + this.port = port; + return this; + } + + public Builder condition(Condition condition) { + this.condition = condition; + return this; + } + + public Builder weight(Integer weight) { + this.weight = weight; + return this; + } + + public BaseNode build() { + return new BaseNode(address, port, condition, weight); + } + + public Builder from(T in) { + return address(in.getAddress()).port(in.getPort()).condition(in.getCondition()).weight(in.getWeight()); + } + } + + /** + * Virtual IP Conditions + */ + public static enum Condition { + /** + * Node is permitted to accept new connections. + */ + ENABLED, + /** + * Node is not permitted to accept any new connections regardless of session persistence + * configuration. Existing connections are forcibly terminated. + */ + DISABLED, + /** + * Node is allowed to service existing established connections and connections that are being + * directed to it as a result of the session persistence configuration. + */ + DRAINING, + + UNRECOGNIZED; + + public static Condition fromValue(String condition) { + try { + return valueOf(checkNotNull(condition, "condition")); + } catch (IllegalArgumentException e) { + return UNRECOGNIZED; + } + } + + } + + protected String address; + protected int port; + protected Condition condition; + protected Integer weight; + + // for serialization only + protected BaseNode() { + + } + + public BaseNode(String address, int port, Condition condition, Integer weight) { + this.address = checkNotNull(address, "address"); + checkArgument(port != -1, "port must be specified"); + this.port = port; + this.condition = checkNotNull(condition, "condition"); + this.weight = weight; + } + + public String getAddress() { + return address; + } + + public int getPort() { + return port; + } + + public Condition getCondition() { + return condition; + } + + /** + * the maximum weight of a node is 100. + */ + public Integer getWeight() { + return weight; + } + + @Override + public int compareTo(BaseNode arg0) { + return address.compareTo(arg0.address); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((address == null) ? 0 : address.hashCode()); + result = prime * result + ((condition == null) ? 0 : condition.hashCode()); + result = prime * result + port; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + BaseNode other = (BaseNode) obj; + if (address == null) { + if (other.address != null) + return false; + } else if (!address.equals(other.address)) + return false; + if (condition == null) { + if (other.condition != null) + return false; + } else if (!condition.equals(other.condition)) + return false; + if (port != other.port) + return false; + return true; + } + + @Override + public String toString() { + return String.format("[address=%s, condition=%s, port=%s, weight=%s]", address, condition, port, weight); + } + +} diff --git a/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/features/LoadBalancerAsyncClient.java b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/features/LoadBalancerAsyncClient.java new file mode 100644 index 0000000000..ee50ea4b04 --- /dev/null +++ b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/features/LoadBalancerAsyncClient.java @@ -0,0 +1,112 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers.features; + +import java.util.Set; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.core.MediaType; + +import org.jclouds.cloudloadbalancers.domain.LoadBalancer; +import org.jclouds.cloudloadbalancers.domain.LoadBalancerAttributes; +import org.jclouds.cloudloadbalancers.domain.LoadBalancerRequest; +import org.jclouds.cloudloadbalancers.functions.UnwrapLoadBalancer; +import org.jclouds.cloudloadbalancers.functions.UnwrapLoadBalancers; +import org.jclouds.openstack.filters.AuthenticateRequest; +import org.jclouds.rest.annotations.ExceptionParser; +import org.jclouds.rest.annotations.RequestFilters; +import org.jclouds.rest.annotations.ResponseParser; +import org.jclouds.rest.annotations.SkipEncoding; +import org.jclouds.rest.annotations.WrapWith; +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; + +/** + * Provides asynchronous access toRackspace Cloud Load Balancers via their REST API. + *

+ * + * @see LoadBalancerClient + * @see + * @author Adrian Cole + */ +@SkipEncoding('/') +@RequestFilters(AuthenticateRequest.class) +public interface LoadBalancerAsyncClient { + + /** + * @see LoadBalancerClient#createLoadBalancer + */ + @POST + @ResponseParser(UnwrapLoadBalancer.class) + @Consumes(MediaType.APPLICATION_JSON) + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + @Path("/loadbalancers") + ListenableFuture createLoadBalancer(@WrapWith("loadBalancer") LoadBalancerRequest lb); + + /** + * @see LoadBalancerClient#updateLoadBalancerAttributes + */ + @PUT + @ResponseParser(UnwrapLoadBalancer.class) + @Consumes(MediaType.APPLICATION_JSON) + @Path("/loadbalancers/{id}") + ListenableFuture updateLoadBalancerAttributes(@PathParam("id") int id, + @WrapWith("loadBalancer") LoadBalancerAttributes attrs); + + /** + * @see CloudServersClient#listLoadBalancers + */ + @GET + @ResponseParser(UnwrapLoadBalancers.class) + @Consumes(MediaType.APPLICATION_JSON) + @Path("/loadbalancers") + @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class) + ListenableFuture> listLoadBalancers(); + + /** + * @see LoadBalancerClient#getLoadBalancer + */ + @GET + @ResponseParser(UnwrapLoadBalancer.class) + @Consumes(MediaType.APPLICATION_JSON) + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + @Path("/loadbalancers/{id}") + ListenableFuture getLoadBalancer(@PathParam("id") int id); + + /** + * @see LoadBalancerClient#removeLoadBalancer + */ + @DELETE + @ExceptionParser(ReturnVoidOnNotFoundOr404.class) + @Path("/loadbalancers/{id}") + @Consumes("*/*") + ListenableFuture removeLoadBalancer(@PathParam("id") int id); + +} diff --git a/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/features/LoadBalancerClient.java b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/features/LoadBalancerClient.java new file mode 100644 index 0000000000..f9b6a46861 --- /dev/null +++ b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/features/LoadBalancerClient.java @@ -0,0 +1,110 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers.features; + +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import org.jclouds.cloudloadbalancers.domain.LoadBalancer; +import org.jclouds.cloudloadbalancers.domain.LoadBalancerAttributes; +import org.jclouds.cloudloadbalancers.domain.LoadBalancerRequest; +import org.jclouds.concurrent.Timeout; +import org.jclouds.http.HttpResponseException; + +/** + * Provides synchronous access to CloudLoadBalancers LoadBalancer features. + *

+ * + * @see LoadBalancerAsyncClient + * @see + * @author Adrian Cole + */ +@Timeout(duration = 60, timeUnit = TimeUnit.SECONDS) +public interface LoadBalancerClient { + /** + * Create a new load balancer with the configuration defined by the request. + * + *

+ * This operation asynchronously provisions a new load balancer based on the configuration + * defined in the request object. Once the request is validated and progress has started on the + * provisioning process, a response object will be returned. + * + * + * @param lb + * configuration to create + * @return The object will contain a unique identifier and status of the request. Using the + * identifier, the caller can check on the progress of the operation by performing a + * {@link LoadBalancerClient#getLoadBalancer}. + * @throws HttpResponseException + * If the corresponding request cannot be fulfilled due to insufficient or invalid + * data + * + */ + LoadBalancer createLoadBalancer(LoadBalancerRequest lb); + + /** + * + * Update the properties of a load balancer. + * + *

+ * This operation asynchronously updates the attributes of the specified load balancer. Upon + * successful validation of the request, the service will return a 202 (Accepted) response code. + * A caller can poll the load balancer with its ID to wait for the changes to be applied and the + * load balancer to return to an ACTIVE status. + * + * @param id + * id of the loadbalancer to change + * @param attrs + * what to change + * @return The object will contain a unique identifier and status of the request. Using the + * identifier, the caller can check on the progress of the operation by performing a + * {@link LoadBalancerClient#getLoadBalancer}. + * @see LoadBalancerAttributes#fromLoadBalancer + */ + void updateLoadBalancerAttributes(int id, LoadBalancerAttributes attrs); + + /** + * + * @return all load balancers configured for the account, or empty set if none available + */ + Set listLoadBalancers(); + + /** + * + * + * @param id + * id of the loadbalancer to retrieve + * @return details of the specified load balancer, or null if not found + */ + LoadBalancer getLoadBalancer(int id); + + /** + * Remove a load balancer from the account. + *

+ * The remove load balancer function removes the specified load balancer and its associated + * configuration from the account. Any and all configuration data is immediately purged and is + * not recoverable. + * + * @param id + * to remove + */ + void removeLoadBalancer(int id); +} diff --git a/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/functions/ConvertLB.java b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/functions/ConvertLB.java new file mode 100644 index 0000000000..75bbddb87f --- /dev/null +++ b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/functions/ConvertLB.java @@ -0,0 +1,56 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers.functions; + +import org.jclouds.cloudloadbalancers.domain.LoadBalancer; +import org.jclouds.cloudloadbalancers.domain.LoadBalancer.Builder; + +import com.google.common.base.Function; +import com.google.common.collect.Iterables; + +/** + * @author Adrian Cole + */ +public class ConvertLB implements Function { + + private final String region; + + ConvertLB(String region) { + this.region = region.toUpperCase(); + } + + @Override + public LoadBalancer apply(LB lb) { + Builder builder = LoadBalancer.builder().region(region).name(lb.getName()).port(lb.getPort()).protocol( + lb.getProtocol()).algorithm(lb.getAlgorithm()).nodes(lb.getNodes()).id(lb.id).status(lb.status) + .virtualIPs(lb.virtualIps); + if (lb.cluster.size() == 1) + builder.clusterName(Iterables.get(lb.cluster.values(), 0)); + if (lb.sessionPersistence.size() == 1) + builder.sessionPersistenceType(Iterables.get(lb.sessionPersistence.values(), 0)); + if (lb.created.size() == 1) + builder.created(Iterables.get(lb.created.values(), 0)); + if (lb.updated.size() == 1) + builder.updated(Iterables.get(lb.updated.values(), 0)); + if (lb.connectionLogging.size() == 1) + builder.connectionLoggingEnabled(Iterables.get(lb.connectionLogging.values(), 0)); + return builder.build(); + } + +} \ No newline at end of file diff --git a/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/functions/LB.java b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/functions/LB.java new file mode 100644 index 0000000000..dea0500a81 --- /dev/null +++ b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/functions/LB.java @@ -0,0 +1,46 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers.functions; + +import java.util.Date; +import java.util.Map; +import java.util.Set; + +import org.jclouds.cloudloadbalancers.domain.Node; +import org.jclouds.cloudloadbalancers.domain.VirtualIP; +import org.jclouds.cloudloadbalancers.domain.LoadBalancer.Status; +import org.jclouds.cloudloadbalancers.domain.internal.BaseLoadBalancer; + +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + +/** + * only here as the datatype for cloudloadbalancers is awkward. + * + **/ +class LB extends BaseLoadBalancer { + int id; + Status status; + Set virtualIps = Sets.newLinkedHashSet(); + Map sessionPersistence = Maps.newLinkedHashMap(); + Map cluster = Maps.newLinkedHashMap(); + Map created = Maps.newLinkedHashMap(); + Map updated = Maps.newLinkedHashMap(); + Map connectionLogging = Maps.newLinkedHashMap(); +} \ No newline at end of file diff --git a/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/functions/UnwrapLoadBalancer.java b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/functions/UnwrapLoadBalancer.java new file mode 100644 index 0000000000..4871978d4b --- /dev/null +++ b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/functions/UnwrapLoadBalancer.java @@ -0,0 +1,66 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers.functions; + +import java.util.Map; + +import javax.inject.Inject; + +import org.jclouds.cloudloadbalancers.domain.LoadBalancer; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpResponse; +import org.jclouds.http.functions.ParseJson; +import org.jclouds.rest.InvocationContext; + +import com.google.common.base.Function; +import com.google.common.collect.Iterables; + +/** + * @author Adrian Cole + */ +public class UnwrapLoadBalancer implements Function, InvocationContext { + + private final ParseJson> json; + private ConvertLB convertLB; + + @Inject + UnwrapLoadBalancer(ParseJson> json) { + this.json = json; + } + + @Override + public LoadBalancer apply(HttpResponse arg0) { + Map map = json.apply(arg0); + if (map == null || map.size() == 0) + return null; + LB lb = Iterables.get(map.values(), 0); + return convertLB.apply(lb); + } + + @Override + public UnwrapLoadBalancer setContext(HttpRequest request) { + return setRegion(request.getEndpoint().getHost().substring(0, request.getEndpoint().getHost().indexOf('.'))); + } + + UnwrapLoadBalancer setRegion(String region) { + this.convertLB = new ConvertLB(region); + return this; + } + +} \ No newline at end of file diff --git a/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/functions/UnwrapLoadBalancers.java b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/functions/UnwrapLoadBalancers.java new file mode 100644 index 0000000000..3b20480f2f --- /dev/null +++ b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/functions/UnwrapLoadBalancers.java @@ -0,0 +1,69 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers.functions; + +import java.util.Map; +import java.util.Set; + +import javax.inject.Inject; + +import org.jclouds.cloudloadbalancers.domain.LoadBalancer; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpResponse; +import org.jclouds.http.functions.ParseJson; +import org.jclouds.rest.InvocationContext; + +import com.google.common.base.Function; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; + +/** + * @author Adrian Cole + */ +public class UnwrapLoadBalancers implements Function>, + InvocationContext { + + private final ParseJson>> json; + private ConvertLB convertLB; + + @Inject + UnwrapLoadBalancers(ParseJson>> json) { + this.json = json; + } + + @Override + public Set apply(HttpResponse arg0) { + Map> map = json.apply(arg0); + if (map.size() == 0) + return ImmutableSet. of(); + ; + return ImmutableSet.copyOf(Iterables.transform(Iterables.get(map.values(), 0), convertLB)); + } + + @Override + public UnwrapLoadBalancers setContext(HttpRequest request) { + return setRegion(request.getEndpoint().getHost().substring(0, request.getEndpoint().getHost().indexOf('.'))); + } + + UnwrapLoadBalancers setRegion(String region) { + this.convertLB = new ConvertLB(region); + return this; + } + +} \ No newline at end of file diff --git a/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/handlers/ParseCloudLoadBalancersErrorFromHttpResponse.java b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/handlers/ParseCloudLoadBalancersErrorFromHttpResponse.java new file mode 100644 index 0000000000..4d5839aabe --- /dev/null +++ b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/handlers/ParseCloudLoadBalancersErrorFromHttpResponse.java @@ -0,0 +1,95 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers.handlers; + +import static org.jclouds.http.HttpUtils.releasePayload; + +import java.io.IOException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.annotation.Resource; +import javax.inject.Singleton; + +import org.jclouds.http.HttpCommand; +import org.jclouds.http.HttpErrorHandler; +import org.jclouds.http.HttpResponse; +import org.jclouds.http.HttpResponseException; +import org.jclouds.logging.Logger; +import org.jclouds.rest.AuthorizationException; +import org.jclouds.rest.ResourceNotFoundException; +import org.jclouds.util.Strings2; + +/** + * + * @author Adrian Cole + * + */ +@Singleton +public class ParseCloudLoadBalancersErrorFromHttpResponse implements HttpErrorHandler { + @Resource + protected Logger logger = Logger.NULL; + public static final Pattern RESOURCE_PATTERN = Pattern.compile("^/v1[^/]*/[0-9]+/([^/]+)/([0-9]+)"); + + public void handleError(HttpCommand command, HttpResponse response) { + Exception exception = new HttpResponseException(command, response); + try { + String content = parseErrorFromContentOrNull(command, response); + exception = content != null ? new HttpResponseException(command, response, content) : exception; + switch (response.getStatusCode()) { + case 401: + exception = new AuthorizationException(exception.getMessage(), exception); + break; + case 404: + if (!command.getCurrentRequest().getMethod().equals("DELETE")) { + String path = command.getCurrentRequest().getEndpoint().getPath(); + Matcher matcher = RESOURCE_PATTERN.matcher(path); + String message; + if (matcher.find()) { + message = String.format("%s %s not found", matcher.group(1), matcher.group(2)); + } else { + message = path; + } + exception = new ResourceNotFoundException(message); + } + break; + case 409: + exception = new IllegalStateException(content); + break; + default: + exception = new HttpResponseException(command, response, content); + break; + } + } finally { + releasePayload(response); + command.setException(exception); + } + } + + String parseErrorFromContentOrNull(HttpCommand command, HttpResponse response) { + if (response.getPayload() != null) { + try { + return Strings2.toStringAndClose(response.getPayload().getInput()); + } catch (IOException e) { + logger.warn(e, "exception reading error from response", response); + } + } + return null; + } +} diff --git a/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/loadbalancer/config/CloudLoadBalancersBindLoadBalancerStrategiesByClass.java b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/loadbalancer/config/CloudLoadBalancersBindLoadBalancerStrategiesByClass.java new file mode 100644 index 0000000000..cf365129ac --- /dev/null +++ b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/loadbalancer/config/CloudLoadBalancersBindLoadBalancerStrategiesByClass.java @@ -0,0 +1,55 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers.loadbalancer.config; + +import org.jclouds.cloudloadbalancers.loadbalancer.strategy.CloudLoadBalancersDestroyLoadBalancerStrategy; +import org.jclouds.cloudloadbalancers.loadbalancer.strategy.CloudLoadBalancersGetLoadBalancerMetadataStrategy; +import org.jclouds.cloudloadbalancers.loadbalancer.strategy.CloudLoadBalancersListLoadBalancersStrategy; +import org.jclouds.cloudloadbalancers.loadbalancer.strategy.CloudLoadBalancersLoadBalanceNodesStrategy; +import org.jclouds.loadbalancer.config.BindLoadBalancerStrategiesByClass; +import org.jclouds.loadbalancer.strategy.DestroyLoadBalancerStrategy; +import org.jclouds.loadbalancer.strategy.GetLoadBalancerMetadataStrategy; +import org.jclouds.loadbalancer.strategy.ListLoadBalancersStrategy; +import org.jclouds.loadbalancer.strategy.LoadBalanceNodesStrategy; + +/** + * @author Adrian Cole + */ +public class CloudLoadBalancersBindLoadBalancerStrategiesByClass extends BindLoadBalancerStrategiesByClass { + + @Override + protected Class defineLoadBalanceNodesStrategy() { + return CloudLoadBalancersLoadBalanceNodesStrategy.class; + } + + @Override + protected Class defineDestroyLoadBalancerStrategy() { + return CloudLoadBalancersDestroyLoadBalancerStrategy.class; + } + + @Override + protected Class defineGetLoadBalancerMetadataStrategy() { + return CloudLoadBalancersGetLoadBalancerMetadataStrategy.class; + } + + @Override + protected Class defineListLoadBalancersStrategy() { + return CloudLoadBalancersListLoadBalancersStrategy.class; + } +} \ No newline at end of file diff --git a/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/loadbalancer/config/CloudLoadBalancersBindLoadBalancerSuppliersByClass.java b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/loadbalancer/config/CloudLoadBalancersBindLoadBalancerSuppliersByClass.java new file mode 100644 index 0000000000..7fa787c5aa --- /dev/null +++ b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/loadbalancer/config/CloudLoadBalancersBindLoadBalancerSuppliersByClass.java @@ -0,0 +1,38 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers.loadbalancer.config; + +import java.util.Set; + +import org.jclouds.domain.Location; +import org.jclouds.loadbalancer.config.BindLoadBalancerSuppliersByClass; +import org.jclouds.location.suppliers.RegionToProviderOrJustProvider; + +import com.google.common.base.Supplier; + +/** + * @author Adrian Cole + */ +public class CloudLoadBalancersBindLoadBalancerSuppliersByClass extends BindLoadBalancerSuppliersByClass { + + @Override + protected Class>> defineLocationSupplier() { + return RegionToProviderOrJustProvider.class; + } +} \ No newline at end of file diff --git a/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/loadbalancer/config/CloudLoadBalancersLoadBalancerContextModule.java b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/loadbalancer/config/CloudLoadBalancersLoadBalancerContextModule.java new file mode 100644 index 0000000000..1b4587cd56 --- /dev/null +++ b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/loadbalancer/config/CloudLoadBalancersLoadBalancerContextModule.java @@ -0,0 +1,38 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers.loadbalancer.config; + +import org.jclouds.loadbalancer.config.BaseLoadBalancerServiceContextModule; + +/** + * Configures the CloudLoadBalancers connection. + * + * @author Adrian Cole + */ + +public class CloudLoadBalancersLoadBalancerContextModule extends BaseLoadBalancerServiceContextModule { + + @Override + protected void configure() { + install(new CloudLoadBalancersBindLoadBalancerSuppliersByClass()); + install(new CloudLoadBalancersBindLoadBalancerStrategiesByClass()); + install(new CloudLoadBalancersLoadBalancerServiceDependenciesModule()); + super.configure(); + } +} diff --git a/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/loadbalancer/config/CloudLoadBalancersLoadBalancerServiceDependenciesModule.java b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/loadbalancer/config/CloudLoadBalancersLoadBalancerServiceDependenciesModule.java new file mode 100644 index 0000000000..26abf0c4c7 --- /dev/null +++ b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/loadbalancer/config/CloudLoadBalancersLoadBalancerServiceDependenciesModule.java @@ -0,0 +1,41 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers.loadbalancer.config; + +import org.jclouds.cloudloadbalancers.domain.LoadBalancer; +import org.jclouds.cloudloadbalancers.loadbalancer.functions.LoadBalancerToLoadBalancerMetadata; +import org.jclouds.loadbalancer.domain.LoadBalancerMetadata; + +import com.google.common.base.Function; +import com.google.inject.AbstractModule; +import com.google.inject.TypeLiteral; + +/** + * + * @author Adrian Cole + */ +public class CloudLoadBalancersLoadBalancerServiceDependenciesModule extends AbstractModule { + + @Override + protected void configure() { + bind(new TypeLiteral>() { + }).to(LoadBalancerToLoadBalancerMetadata.class); + } + +} diff --git a/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/loadbalancer/functions/LoadBalancerToLoadBalancerMetadata.java b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/loadbalancer/functions/LoadBalancerToLoadBalancerMetadata.java new file mode 100644 index 0000000000..5c3f1fe1db --- /dev/null +++ b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/loadbalancer/functions/LoadBalancerToLoadBalancerMetadata.java @@ -0,0 +1,72 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers.loadbalancer.functions; + +import java.util.Map; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.jclouds.cloudloadbalancers.domain.LoadBalancer; +import org.jclouds.cloudloadbalancers.domain.VirtualIP; +import org.jclouds.domain.Location; +import org.jclouds.loadbalancer.domain.LoadBalancerMetadata; +import org.jclouds.loadbalancer.domain.LoadBalancerType; +import org.jclouds.loadbalancer.domain.internal.LoadBalancerMetadataImpl; + +import com.google.common.base.Function; +import com.google.common.base.Supplier; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; + +/** + * + * @author Adrian Cole + */ +@Singleton +public class LoadBalancerToLoadBalancerMetadata implements Function { + protected final Supplier> locationMap; + protected final Supplier defaultLocationSupplier; + + @Inject + public LoadBalancerToLoadBalancerMetadata(Supplier defaultLocationSupplier, + Supplier> locationMap) { + this.locationMap = locationMap; + this.defaultLocationSupplier = defaultLocationSupplier; + } + + @Override + public LoadBalancerMetadata apply(LoadBalancer input) { + + Location location = locationMap.get().get(input.getRegion()); + + String id = input.getRegion() + "/" + input.getId(); + // TODO Builder + return new LoadBalancerMetadataImpl(LoadBalancerType.LB, input.getName(), input.getName(), id, location, null, + ImmutableMap. of(), Iterables.transform(input.getVirtualIPs(), + new Function() { + + @Override + public String apply(VirtualIP arg0) { + return arg0.getAddress(); + } + + })); + } +} \ No newline at end of file diff --git a/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/loadbalancer/strategy/CloudLoadBalancersDestroyLoadBalancerStrategy.java b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/loadbalancer/strategy/CloudLoadBalancersDestroyLoadBalancerStrategy.java new file mode 100644 index 0000000000..99fe66410e --- /dev/null +++ b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/loadbalancer/strategy/CloudLoadBalancersDestroyLoadBalancerStrategy.java @@ -0,0 +1,63 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers.loadbalancer.strategy; + +import static com.google.common.base.Preconditions.checkNotNull; + +import javax.annotation.Resource; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.jclouds.cloudloadbalancers.CloudLoadBalancersClient; +import org.jclouds.loadbalancer.domain.LoadBalancerMetadata; +import org.jclouds.loadbalancer.reference.LoadBalancerConstants; +import org.jclouds.loadbalancer.strategy.DestroyLoadBalancerStrategy; +import org.jclouds.loadbalancer.strategy.GetLoadBalancerMetadataStrategy; +import org.jclouds.logging.Logger; + +/** + * + * @author Adrian Cole + */ +@Singleton +public class CloudLoadBalancersDestroyLoadBalancerStrategy implements DestroyLoadBalancerStrategy { + @Resource + @Named(LoadBalancerConstants.LOADBALANCER_LOGGER) + protected Logger logger = Logger.NULL; + + private final CloudLoadBalancersClient client; + private final GetLoadBalancerMetadataStrategy getLoadBalancer; + + @Inject + protected CloudLoadBalancersDestroyLoadBalancerStrategy(CloudLoadBalancersClient client, + GetLoadBalancerMetadataStrategy getLoadBalancer) { + this.client = checkNotNull(client, "client"); + this.getLoadBalancer = checkNotNull(getLoadBalancer, "getLoadBalancer"); + } + + @Override + public LoadBalancerMetadata destroyLoadBalancer(String id) { + String[] parts = checkNotNull(id, "id").split("/"); + String region = parts[0]; + int lbId = Integer.parseInt(parts[1]); + client.getLoadBalancerClient(region).removeLoadBalancer(lbId); + return getLoadBalancer.getLoadBalancer(id); + } +} diff --git a/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/loadbalancer/strategy/CloudLoadBalancersGetLoadBalancerMetadataStrategy.java b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/loadbalancer/strategy/CloudLoadBalancersGetLoadBalancerMetadataStrategy.java new file mode 100644 index 0000000000..6a1900cfac --- /dev/null +++ b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/loadbalancer/strategy/CloudLoadBalancersGetLoadBalancerMetadataStrategy.java @@ -0,0 +1,58 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers.loadbalancer.strategy; + +import static com.google.common.base.Preconditions.checkNotNull; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.jclouds.cloudloadbalancers.CloudLoadBalancersClient; +import org.jclouds.cloudloadbalancers.domain.LoadBalancer; +import org.jclouds.loadbalancer.domain.LoadBalancerMetadata; +import org.jclouds.loadbalancer.strategy.GetLoadBalancerMetadataStrategy; + +import com.google.common.base.Function; + +/** + * + * @author Adrian Cole + */ +@Singleton +public class CloudLoadBalancersGetLoadBalancerMetadataStrategy implements GetLoadBalancerMetadataStrategy { + + private final org.jclouds.cloudloadbalancers.CloudLoadBalancersClient client; + private final Function converter; + + @Inject + protected CloudLoadBalancersGetLoadBalancerMetadataStrategy(CloudLoadBalancersClient client, + Function converter) { + this.client = checkNotNull(client, "client"); + this.converter = checkNotNull(converter, "converter"); + } + + @Override + public LoadBalancerMetadata getLoadBalancer(String id) { + String[] parts = checkNotNull(id, "id").split("/"); + String region = parts[0]; + int lbId = Integer.parseInt(parts[1]); + return converter.apply(client.getLoadBalancerClient(region).getLoadBalancer(lbId)); + } + +} \ No newline at end of file diff --git a/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/loadbalancer/strategy/CloudLoadBalancersListLoadBalancersStrategy.java b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/loadbalancer/strategy/CloudLoadBalancersListLoadBalancersStrategy.java new file mode 100644 index 0000000000..8323f944e5 --- /dev/null +++ b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/loadbalancer/strategy/CloudLoadBalancersListLoadBalancersStrategy.java @@ -0,0 +1,84 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers.loadbalancer.strategy; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.concat; +import static com.google.common.collect.Iterables.transform; +import static org.jclouds.concurrent.FutureIterables.transformParallel; + +import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; + +import javax.annotation.Nullable; +import javax.annotation.Resource; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.jclouds.Constants; +import org.jclouds.cloudloadbalancers.CloudLoadBalancersAsyncClient; +import org.jclouds.cloudloadbalancers.domain.LoadBalancer; +import org.jclouds.loadbalancer.domain.LoadBalancerMetadata; +import org.jclouds.loadbalancer.reference.LoadBalancerConstants; +import org.jclouds.loadbalancer.strategy.ListLoadBalancersStrategy; +import org.jclouds.location.Region; +import org.jclouds.logging.Logger; + +import com.google.common.base.Function; +import com.google.common.util.concurrent.ListenableFuture; + +/** + * + * @author Adrian Cole + */ +@Singleton +public class CloudLoadBalancersListLoadBalancersStrategy implements ListLoadBalancersStrategy { + @Resource + @Named(LoadBalancerConstants.LOADBALANCER_LOGGER) + protected Logger logger = Logger.NULL; + + private final CloudLoadBalancersAsyncClient aclient; + private final Function converter; + private final ExecutorService executor; + private final Set regions; + + @Inject + protected CloudLoadBalancersListLoadBalancersStrategy(CloudLoadBalancersAsyncClient aclient, + Function converter, + @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, @Nullable @Region Set regions) { + this.aclient = checkNotNull(aclient, "aclient"); + this.regions = checkNotNull(regions, "regions"); + this.converter = checkNotNull(converter, "converter"); + this.executor = checkNotNull(executor, "executor"); + } + + @Override + public Iterable listLoadBalancers() { + return transform(concat(transformParallel(regions, new Function>>() { + + @Override + public ListenableFuture> apply(String from) { + return aclient.getLoadBalancerClient(from).listLoadBalancers(); + } + + }, executor, null, logger, "loadbalancers")), converter); + } +} diff --git a/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/loadbalancer/strategy/CloudLoadBalancersLoadBalanceNodesStrategy.java b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/loadbalancer/strategy/CloudLoadBalancersLoadBalanceNodesStrategy.java new file mode 100644 index 0000000000..6023abce69 --- /dev/null +++ b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/loadbalancer/strategy/CloudLoadBalancersLoadBalanceNodesStrategy.java @@ -0,0 +1,85 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers.loadbalancer.strategy; + +import static com.google.common.base.Preconditions.checkNotNull; + +import javax.annotation.Resource; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.jclouds.cloudloadbalancers.CloudLoadBalancersClient; +import org.jclouds.cloudloadbalancers.domain.LoadBalancer; +import org.jclouds.cloudloadbalancers.domain.LoadBalancerRequest; +import org.jclouds.cloudloadbalancers.domain.NodeRequest; +import org.jclouds.cloudloadbalancers.domain.VirtualIP.Type; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.domain.Location; +import org.jclouds.loadbalancer.domain.LoadBalancerMetadata; +import org.jclouds.loadbalancer.reference.LoadBalancerConstants; +import org.jclouds.loadbalancer.strategy.GetLoadBalancerMetadataStrategy; +import org.jclouds.loadbalancer.strategy.LoadBalanceNodesStrategy; +import org.jclouds.logging.Logger; + +import com.google.common.base.Function; +import com.google.common.collect.Iterables; + +/** + * + * @author Adrian Cole + */ +@Singleton +public class CloudLoadBalancersLoadBalanceNodesStrategy implements LoadBalanceNodesStrategy { + @Resource + @Named(LoadBalancerConstants.LOADBALANCER_LOGGER) + protected Logger logger = Logger.NULL; + protected final CloudLoadBalancersClient client; + protected final GetLoadBalancerMetadataStrategy getLB; + + @Inject + protected CloudLoadBalancersLoadBalanceNodesStrategy(CloudLoadBalancersClient client, + GetLoadBalancerMetadataStrategy getLB) { + this.client = checkNotNull(client, "client"); + this.getLB = checkNotNull(getLB, "getLB"); + } + + @Override + public LoadBalancerMetadata createLoadBalancerInLocation(Location location, String name, String protocol, + int loadBalancerPort, final int instancePort, Iterable nodes) { + String region = checkNotNull(location, "location").getId(); + + // TODO need to query and update the LB per current design. + LoadBalancer lb = client.getLoadBalancerClient(region).createLoadBalancer( + LoadBalancerRequest.builder().name(name).protocol(protocol.toUpperCase()).port(loadBalancerPort) + .virtualIPType(Type.PUBLIC).nodes( + Iterables.transform(nodes, new Function() { + + @Override + public NodeRequest apply(NodeMetadata arg0) { + return NodeRequest.builder().address( + Iterables.get(arg0.getPrivateAddresses(), 0)).port(instancePort) + .build(); + + } + + })).build()); + return getLB.getLoadBalancer(region + "/" + lb.getId()); + } +} diff --git a/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/options/ListOptions.java b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/options/ListOptions.java new file mode 100644 index 0000000000..1dc0537c1c --- /dev/null +++ b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/options/ListOptions.java @@ -0,0 +1,63 @@ +package org.jclouds.cloudloadbalancers.options; + +import static com.google.common.base.Preconditions.checkArgument; + +import org.jclouds.http.options.BaseHttpRequestOptions; + +import com.google.common.collect.ImmutableSet; + +/** + * To reduce load on the service, list operations will return a maximum of 100 items at a time. To + * navigate the collection, the limit and marker parameters (for example, ?limit=50&marker=1 ) can + * be set in the URI. If a marker beyond the end of a list is given, an empty list is returned. Note + * that list operations never return 404 (itemNotFound) faults. + * + * @see + * @author Adrian Cole + */ +public class ListOptions extends BaseHttpRequestOptions { + public static final ListOptions NONE = new ListOptions(); + + /** + * Indicates where to begin listing, if the previous list was larger than the limit. + */ + public ListOptions marker(String marker) { + checkArgument(marker != null, "marker cannot be null"); + queryParameters.replaceValues("marker", ImmutableSet.of(marker)); + return this; + } + + /** + * To reduce load on the service, list operations will return a maximum of 100 items at a time. + *

+ * Note that list operations never return itemNotFound (404) faults. + */ + public ListOptions limit(int limit) { + checkArgument(limit >= 0, "limit must be >= 0"); + checkArgument(limit <= 10000, "limit must be <= 10000"); + queryParameters.replaceValues("limit", ImmutableSet.of(limit + "")); + return this; + } + + public static class Builder { + + /** + * @see ListOptions#marker(marker) + */ + public static ListOptions marker(String marker) { + ListOptions options = new ListOptions(); + return options.marker(marker); + } + + /** + * @see ListOptions#limit(long) + */ + public static ListOptions limit(int limit) { + ListOptions options = new ListOptions(); + return options.limit(limit); + } + + } +} diff --git a/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/predicates/LoadBalancerActive.java b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/predicates/LoadBalancerActive.java new file mode 100644 index 0000000000..deefce8165 --- /dev/null +++ b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/predicates/LoadBalancerActive.java @@ -0,0 +1,68 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers.predicates; + +import static com.google.common.base.Preconditions.checkNotNull; + +import javax.annotation.Resource; +import javax.inject.Singleton; + +import org.jclouds.cloudloadbalancers.CloudLoadBalancersClient; +import org.jclouds.cloudloadbalancers.domain.LoadBalancer; +import org.jclouds.cloudloadbalancers.domain.LoadBalancer.Status; +import org.jclouds.logging.Logger; + +import com.google.common.base.Predicate; +import com.google.inject.Inject; + +/** + * + * Tests to see if a loadBalancer is running + * + * @author Adrian Cole + */ +@Singleton +public class LoadBalancerActive implements Predicate { + + private final CloudLoadBalancersClient client; + + @Resource + protected Logger logger = Logger.NULL; + + @Inject + public LoadBalancerActive(CloudLoadBalancersClient client) { + this.client = client; + } + + public boolean apply(LoadBalancer loadBalancer) { + logger.trace("looking for status on loadBalancer %s", checkNotNull(loadBalancer, "loadBalancer")); + loadBalancer = refresh(loadBalancer); + if (loadBalancer == null) + return false; + logger.trace("%s: looking for loadBalancer status %s: currently: %s", loadBalancer.getId(), Status.ACTIVE, + loadBalancer.getStatus()); + if (loadBalancer.getStatus() == Status.ERROR) + throw new IllegalStateException("loadBalancer in error status: " + loadBalancer); + return loadBalancer.getStatus() == Status.ACTIVE; + } + + private LoadBalancer refresh(LoadBalancer loadBalancer) { + return client.getLoadBalancerClient(loadBalancer.getRegion()).getLoadBalancer(loadBalancer.getId()); + } +} diff --git a/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/predicates/LoadBalancerDeleted.java b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/predicates/LoadBalancerDeleted.java new file mode 100644 index 0000000000..c85ea302f9 --- /dev/null +++ b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/predicates/LoadBalancerDeleted.java @@ -0,0 +1,66 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers.predicates; + +import static com.google.common.base.Preconditions.checkNotNull; + +import javax.annotation.Resource; +import javax.inject.Singleton; + +import org.jclouds.cloudloadbalancers.CloudLoadBalancersClient; +import org.jclouds.cloudloadbalancers.domain.LoadBalancer; +import org.jclouds.cloudloadbalancers.domain.LoadBalancer.Status; +import org.jclouds.logging.Logger; + +import com.google.common.base.Predicate; +import com.google.inject.Inject; + +/** + * + * Tests to see if a loadBalancer is deleted + * + * @author Adrian Cole + */ +@Singleton +public class LoadBalancerDeleted implements Predicate { + + private final CloudLoadBalancersClient client; + + @Resource + protected Logger logger = Logger.NULL; + + @Inject + public LoadBalancerDeleted(CloudLoadBalancersClient client) { + this.client = client; + } + + public boolean apply(LoadBalancer loadBalancer) { + logger.trace("looking for status on loadBalancer %s", checkNotNull(loadBalancer, "loadBalancer")); + loadBalancer = refresh(loadBalancer); + if (loadBalancer == null) + return true; + logger.trace("%s: looking for loadBalancer status %s: currently: %s", loadBalancer.getId(), Status.DELETED, + loadBalancer.getStatus()); + return loadBalancer.getStatus() == Status.DELETED; + } + + private LoadBalancer refresh(LoadBalancer loadBalancer) { + return client.getLoadBalancerClient(loadBalancer.getRegion()).getLoadBalancer(loadBalancer.getId()); + } +} diff --git a/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/reference/RackspaceConstants.java b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/reference/RackspaceConstants.java new file mode 100644 index 0000000000..6fd14aa627 --- /dev/null +++ b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/reference/RackspaceConstants.java @@ -0,0 +1,29 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers.reference; + +/** + * Configuration properties and constants used in rackspace connections. + * + * @author Adrian Cole + */ +public interface RackspaceConstants { + public static final String PROPERTY_ACCOUNT_ID = "jclouds.rackspace.account-id"; +} diff --git a/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/reference/Region.java b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/reference/Region.java new file mode 100644 index 0000000000..76caa078d1 --- /dev/null +++ b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/cloudloadbalancers/reference/Region.java @@ -0,0 +1,53 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers.reference; + + + +/** + * The load balancing service is a regionalized service. It allows the caller to select a region + * into which a load balancer is to be provisioned. + *

+ * If load balancing Cloud Servers, you can determine the appropriate region to select by viewing + * your Cloud Servers list and creating a load balancer within the same region as the data center in + * which your Cloud Server resides. When your resources reside in the same region as your load + * balancer, devices are in close proximity to each other and can take advantage of ServiceNet + * connectivity for free data transfer between services. + *

+ * If load balancing external servers, you can determine the appropriate region to select by + * choosing the region that is geographically as close to your external servers as possible. + * + * @see + * @author Adrian Cole + */ + +public interface Region { + /** + * Chicago (ORD) https://ord.loadbalancers.api.rackspacecloud.com/v1.0/1234/ + */ + public final static String ORD = "ORD"; + + /** + * Dallas/Ft. Worth (DFW) https://dfw.loadbalancers.api.rackspacecloud.com/v1.0/1234/ + */ + public final static String DFW = "DFW"; + +} diff --git a/providers/cloudloadbalancers-us/src/test/java/org/jclouds/cloudloadbalancers/CloudLoadBalancersAsyncClientTest.java b/providers/cloudloadbalancers-us/src/test/java/org/jclouds/cloudloadbalancers/CloudLoadBalancersAsyncClientTest.java new file mode 100644 index 0000000000..1d3a618ddf --- /dev/null +++ b/providers/cloudloadbalancers-us/src/test/java/org/jclouds/cloudloadbalancers/CloudLoadBalancersAsyncClientTest.java @@ -0,0 +1,70 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers; + +import java.io.IOException; +import java.util.concurrent.ExecutionException; + +import org.jclouds.cloudloadbalancers.features.BaseCloudLoadBalancersAsyncClientTest; +import org.jclouds.http.HttpRequest; +import org.jclouds.rest.internal.RestAnnotationProcessor; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import com.google.inject.TypeLiteral; + +/** + * Tests behavior of {@code CloudLoadBalancersAsyncClient} + * + * @author Adrian Cole + */ +// NOTE:without testName, this will not call @Before* and fail w/NPE during surefire +@Test(groups = "unit", testName = "CloudLoadBalancersAsyncClientTest") +public class CloudLoadBalancersAsyncClientTest extends BaseCloudLoadBalancersAsyncClientTest { + + private CloudLoadBalancersAsyncClient asyncClient; + private CloudLoadBalancersClient syncClient; + + public void testSync() throws SecurityException, NoSuchMethodException, InterruptedException, ExecutionException { + assert syncClient.getLoadBalancerClient("DFW") != null; + } + + public void testAsync() throws SecurityException, NoSuchMethodException, InterruptedException, ExecutionException { + assert asyncClient.getLoadBalancerClient("DFW") != null; + } + + @Override + protected TypeLiteral> createTypeLiteral() { + return new TypeLiteral>() { + }; + } + + @BeforeClass + @Override + protected void setupFactory() throws IOException { + super.setupFactory(); + asyncClient = injector.getInstance(CloudLoadBalancersAsyncClient.class); + syncClient = injector.getInstance(CloudLoadBalancersClient.class); + } + + @Override + protected void checkFilters(HttpRequest request) { + + } +} diff --git a/providers/cloudloadbalancers-us/src/test/java/org/jclouds/cloudloadbalancers/features/BaseCloudLoadBalancersAsyncClientTest.java b/providers/cloudloadbalancers-us/src/test/java/org/jclouds/cloudloadbalancers/features/BaseCloudLoadBalancersAsyncClientTest.java new file mode 100644 index 0000000000..941bbc18fe --- /dev/null +++ b/providers/cloudloadbalancers-us/src/test/java/org/jclouds/cloudloadbalancers/features/BaseCloudLoadBalancersAsyncClientTest.java @@ -0,0 +1,88 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers.features; + +import static org.testng.Assert.assertEquals; + +import java.io.IOException; +import java.util.Properties; + +import org.jclouds.cloudloadbalancers.CloudLoadBalancersAsyncClient; +import org.jclouds.cloudloadbalancers.CloudLoadBalancersClient; +import org.jclouds.cloudloadbalancers.config.CloudLoadBalancersRestClientModule; +import org.jclouds.cloudloadbalancers.reference.Region; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.RequiresHttp; +import org.jclouds.internal.ClassMethodArgs; +import org.jclouds.openstack.OpenStackAuthAsyncClient.AuthenticationResponse; +import org.jclouds.openstack.filters.AuthenticateRequest; +import org.jclouds.rest.ConfiguresRestClient; +import org.jclouds.rest.RestClientTest; +import org.jclouds.rest.RestContextFactory; +import org.jclouds.rest.RestContextSpec; +import org.testng.annotations.BeforeClass; + +import com.google.common.base.Supplier; +import com.google.common.base.Throwables; +import com.google.inject.Module; + +/** + * @author Adrian Cole + */ +public abstract class BaseCloudLoadBalancersAsyncClientTest extends RestClientTest { + + @RequiresHttp + @ConfiguresRestClient + public static class CloudLoadBalancersRestClientModuleExtension extends CloudLoadBalancersRestClientModule { + @Override + protected String accountID(Supplier in) { + return "1234"; + } + } + + @Override + protected void checkFilters(HttpRequest request) { + assertEquals(request.getFilters().size(), 1); + assertEquals(request.getFilters().get(0).getClass(), AuthenticateRequest.class); + } + + @Override + protected Module createModule() { + return new CloudLoadBalancersRestClientModuleExtension(); + } + + @Override + public RestContextSpec createContextSpec() { + Properties props = new Properties(); + return new RestContextFactory().createContextSpec("cloudloadbalancers-us", "email", "apikey", props); + } + + @BeforeClass + @Override + protected void setupFactory() throws IOException { + super.setupFactory(); + try { + processor.setCaller(new ClassMethodArgs(CloudLoadBalancersAsyncClient.class, + CloudLoadBalancersAsyncClient.class.getMethod("getLoadBalancerClient", String.class), + new Object[] { Region.DFW })); + } catch (Exception e) { + Throwables.propagate(e); + } + } +} \ No newline at end of file diff --git a/providers/cloudloadbalancers-us/src/test/java/org/jclouds/cloudloadbalancers/features/BaseCloudLoadBalancersClientLiveTest.java b/providers/cloudloadbalancers-us/src/test/java/org/jclouds/cloudloadbalancers/features/BaseCloudLoadBalancersClientLiveTest.java new file mode 100644 index 0000000000..96da07d44e --- /dev/null +++ b/providers/cloudloadbalancers-us/src/test/java/org/jclouds/cloudloadbalancers/features/BaseCloudLoadBalancersClientLiveTest.java @@ -0,0 +1,112 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers.features; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Properties; +import java.util.concurrent.TimeUnit; + +import org.jclouds.Constants; +import org.jclouds.cloudloadbalancers.CloudLoadBalancersAsyncClient; +import org.jclouds.cloudloadbalancers.CloudLoadBalancersClient; +import org.jclouds.cloudloadbalancers.domain.LoadBalancer; +import org.jclouds.cloudloadbalancers.predicates.LoadBalancerActive; +import org.jclouds.cloudloadbalancers.predicates.LoadBalancerDeleted; +import org.jclouds.loadbalancer.LoadBalancerServiceContextFactory; +import org.jclouds.logging.log4j.config.Log4JLoggingModule; +import org.jclouds.net.IPSocket; +import org.jclouds.predicates.RetryablePredicate; +import org.jclouds.rest.RestContext; +import org.testng.annotations.AfterGroups; +import org.testng.annotations.BeforeGroups; + +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableSet; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.Module; + +/** + * + * @author Adrian Cole + */ +public class BaseCloudLoadBalancersClientLiveTest { + protected String prefix = System.getProperty("user.name"); + + protected CloudLoadBalancersClient client; + protected RestContext context; + protected String provider = "cloudloadbalancers-us"; + protected String identity; + protected String credential; + protected String endpoint; + protected String apiversion; + protected Predicate socketTester; + protected RetryablePredicate loadBalancerActive; + protected RetryablePredicate loadBalancerDeleted; + + protected Injector injector; + + protected void setupCredentials() { + identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + + ".identity must be set. ex. apiKey"); + credential = checkNotNull(System.getProperty("test." + provider + ".credential"), "test." + provider + + ".credential must be set. ex. secretKey"); + endpoint = System.getProperty("test." + provider + ".endpoint"); + apiversion = System.getProperty("test." + provider + ".apiversion"); + } + + protected Properties setupProperties() { + Properties overrides = new Properties(); + overrides.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true"); + overrides.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true"); + overrides.setProperty(provider + ".identity", identity); + overrides.setProperty(provider + ".credential", credential); + if (endpoint != null) + overrides.setProperty(provider + ".endpoint", endpoint); + if (apiversion != null) + overrides.setProperty(provider + ".apiversion", apiversion); + return overrides; + } + + @BeforeGroups(groups = "live") + public void setupClient() { + setupCredentials(); + Properties overrides = setupProperties(); + context = new LoadBalancerServiceContextFactory().createContext(provider, ImmutableSet. of(new Log4JLoggingModule()), + overrides).getProviderSpecificContext(); + + client = context.getApi(); + + injector = Guice.createInjector(new Log4JLoggingModule()); + loadBalancerActive = new RetryablePredicate(new LoadBalancerActive(client), 30, 1, 1, + TimeUnit.SECONDS); + injector.injectMembers(loadBalancerActive); + loadBalancerDeleted = new RetryablePredicate(new LoadBalancerDeleted(client), 30, 1, 1, + TimeUnit.SECONDS); + injector.injectMembers(loadBalancerDeleted); + } + + @AfterGroups(groups = "live") + protected void tearDown() { + if (context != null) + context.close(); + } + +} \ No newline at end of file diff --git a/providers/cloudloadbalancers-us/src/test/java/org/jclouds/cloudloadbalancers/features/LoadBalancerAsyncClientTest.java b/providers/cloudloadbalancers-us/src/test/java/org/jclouds/cloudloadbalancers/features/LoadBalancerAsyncClientTest.java new file mode 100644 index 0000000000..16692d3763 --- /dev/null +++ b/providers/cloudloadbalancers-us/src/test/java/org/jclouds/cloudloadbalancers/features/LoadBalancerAsyncClientTest.java @@ -0,0 +1,166 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers.features; + +import java.io.IOException; +import java.lang.reflect.Method; + +import org.jclouds.cloudloadbalancers.domain.LoadBalancerAttributes; +import org.jclouds.cloudloadbalancers.domain.LoadBalancerRequest; +import org.jclouds.cloudloadbalancers.domain.LoadBalancerAttributes.Builder; +import org.jclouds.cloudloadbalancers.domain.VirtualIP.Type; +import org.jclouds.cloudloadbalancers.functions.UnwrapLoadBalancer; +import org.jclouds.cloudloadbalancers.functions.UnwrapLoadBalancers; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.functions.ReleasePayloadAndReturn; +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.RestAnnotationProcessor; +import org.testng.annotations.Test; + +import com.google.inject.TypeLiteral; + +/** + * Tests behavior of {@code LoadBalancerAsyncClient} + * + * @author Adrian Cole + */ +// NOTE:without testName, this will not call @Before* and fail w/NPE during surefire +@Test(groups = "unit", testName = "LoadBalancerAsyncClientTest") +public class LoadBalancerAsyncClientTest extends BaseCloudLoadBalancersAsyncClientTest { + + public void testListLoadBalancers() throws SecurityException, NoSuchMethodException, IOException { + Method method = LoadBalancerAsyncClient.class.getMethod("listLoadBalancers"); + HttpRequest httpRequest = processor.createRequest(method); + + assertRequestLineEquals(httpRequest, + "GET https://dfw.loadbalancers.api.rackspacecloud.com/v1.0/1234/loadbalancers HTTP/1.1"); + assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\n"); + assertPayloadEquals(httpRequest, null, null, false); + + assertResponseParserClassEquals(method, httpRequest, UnwrapLoadBalancers.class); + assertSaxResponseParserClassEquals(method, null); + assertExceptionParserClassEquals(method, ReturnEmptySetOnNotFoundOr404.class); + + checkFilters(httpRequest); + + } + + public void testGetLoadBalancer() throws SecurityException, NoSuchMethodException, IOException { + Method method = LoadBalancerAsyncClient.class.getMethod("getLoadBalancer", int.class); + HttpRequest httpRequest = processor.createRequest(method, 5); + + assertRequestLineEquals(httpRequest, + "GET https://dfw.loadbalancers.api.rackspacecloud.com/v1.0/1234/loadbalancers/5 HTTP/1.1"); + assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\n"); + assertPayloadEquals(httpRequest, null, null, false); + + assertResponseParserClassEquals(method, httpRequest, UnwrapLoadBalancer.class); + assertSaxResponseParserClassEquals(method, null); + assertExceptionParserClassEquals(method, ReturnNullOnNotFoundOr404.class); + + checkFilters(httpRequest); + + } + + public void testCreateLoadBalancerWithType() throws SecurityException, NoSuchMethodException, IOException { + Method method = LoadBalancerAsyncClient.class.getMethod("createLoadBalancer", LoadBalancerRequest.class); + HttpRequest httpRequest = processor.createRequest(method, LoadBalancerRequest.builder().name("goo").protocol( + "HTTP").port(80).virtualIPType(Type.PUBLIC).build()); + + assertRequestLineEquals(httpRequest, + "POST https://dfw.loadbalancers.api.rackspacecloud.com/v1.0/1234/loadbalancers HTTP/1.1"); + assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\n"); + assertPayloadEquals( + httpRequest, + "{\"loadBalancer\":{\"virtualIps\":[{\"type\":\"PUBLIC\"}],\"name\":\"goo\",\"protocol\":\"HTTP\",\"port\":80,\"nodes\":[]}}", + "application/json", false); + + assertResponseParserClassEquals(method, httpRequest, UnwrapLoadBalancer.class); + assertSaxResponseParserClassEquals(method, null); + assertExceptionParserClassEquals(method, ReturnNullOnNotFoundOr404.class); + + checkFilters(httpRequest); + + } + + public void testCreateLoadBalancerWithId() throws SecurityException, NoSuchMethodException, IOException { + Method method = LoadBalancerAsyncClient.class.getMethod("createLoadBalancer", LoadBalancerRequest.class); + HttpRequest httpRequest = processor.createRequest(method, LoadBalancerRequest.builder().name("goo").protocol( + "HTTP").port(80).virtualIPId(4).build()); + + assertRequestLineEquals(httpRequest, + "POST https://dfw.loadbalancers.api.rackspacecloud.com/v1.0/1234/loadbalancers HTTP/1.1"); + assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\n"); + assertPayloadEquals( + httpRequest, + "{\"loadBalancer\":{\"virtualIps\":[{\"id\":\"4\"}],\"name\":\"goo\",\"protocol\":\"HTTP\",\"port\":80,\"nodes\":[]}}", + "application/json", false); + + assertResponseParserClassEquals(method, httpRequest, UnwrapLoadBalancer.class); + assertSaxResponseParserClassEquals(method, null); + assertExceptionParserClassEquals(method, ReturnNullOnNotFoundOr404.class); + + checkFilters(httpRequest); + + } + + public void testUpdateLoadBalancerAttributes() throws SecurityException, NoSuchMethodException, IOException { + Method method = LoadBalancerAsyncClient.class.getMethod("updateLoadBalancerAttributes", int.class, + LoadBalancerAttributes.class); + HttpRequest httpRequest = processor.createRequest(method, 2, Builder.name("foo")); + + assertRequestLineEquals(httpRequest, + "PUT https://dfw.loadbalancers.api.rackspacecloud.com/v1.0/1234/loadbalancers/2 HTTP/1.1"); + assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\n"); + assertPayloadEquals(httpRequest, "{\"loadBalancer\":{\"name\":\"foo\"}}", "application/json", false); + + assertResponseParserClassEquals(method, httpRequest, UnwrapLoadBalancer.class); + assertSaxResponseParserClassEquals(method, null); + assertExceptionParserClassEquals(method, MapHttp4xxCodesToExceptions.class); + + checkFilters(httpRequest); + + } + + public void testRemoveLoadBalancer() throws SecurityException, NoSuchMethodException, IOException { + Method method = LoadBalancerAsyncClient.class.getMethod("removeLoadBalancer", int.class); + HttpRequest httpRequest = processor.createRequest(method, 5); + + assertRequestLineEquals(httpRequest, + "DELETE https://dfw.loadbalancers.api.rackspacecloud.com/v1.0/1234/loadbalancers/5 HTTP/1.1"); + assertNonPayloadHeadersEqual(httpRequest, "Accept: */*\n"); + assertPayloadEquals(httpRequest, null, null, false); + + assertResponseParserClassEquals(method, httpRequest, ReleasePayloadAndReturn.class); + assertSaxResponseParserClassEquals(method, null); + assertExceptionParserClassEquals(method, ReturnVoidOnNotFoundOr404.class); + + checkFilters(httpRequest); + + } + + @Override + protected TypeLiteral> createTypeLiteral() { + return new TypeLiteral>() { + }; + } +} diff --git a/providers/cloudloadbalancers-us/src/test/java/org/jclouds/cloudloadbalancers/features/LoadBalancerClientLiveTest.java b/providers/cloudloadbalancers-us/src/test/java/org/jclouds/cloudloadbalancers/features/LoadBalancerClientLiveTest.java new file mode 100644 index 0000000000..9eddff9a5d --- /dev/null +++ b/providers/cloudloadbalancers-us/src/test/java/org/jclouds/cloudloadbalancers/features/LoadBalancerClientLiveTest.java @@ -0,0 +1,134 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers.features; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.util.Set; +import java.util.logging.Logger; + +import org.jclouds.cloudloadbalancers.domain.LoadBalancer; +import org.jclouds.cloudloadbalancers.domain.LoadBalancerAttributes; +import org.jclouds.cloudloadbalancers.domain.LoadBalancerRequest; +import org.jclouds.cloudloadbalancers.domain.NodeRequest; +import org.jclouds.cloudloadbalancers.domain.VirtualIP.Type; +import org.testng.annotations.AfterGroups; +import org.testng.annotations.Test; + +import com.google.common.collect.Iterables; +import com.google.common.collect.Sets; + +/** + * Tests behavior of {@code LoadBalancerClientLiveTest} + * + * @author Adrian Cole + */ +@Test(groups = "live", singleThreaded = true, testName = "LoadBalancerClientLiveTest") +public class LoadBalancerClientLiveTest extends BaseCloudLoadBalancersClientLiveTest { + private Set lbs = Sets.newLinkedHashSet(); + + @AfterGroups(groups = "live") + protected void tearDown() { + for (LoadBalancer lb : lbs) { + client.getLoadBalancerClient(lb.getRegion()).removeLoadBalancer(lb.getId()); + assert loadBalancerDeleted.apply(lb) : lb; + } + super.tearDown(); + } + + public void testListLoadBalancers() throws Exception { + for (String region : client.getConfiguredRegions()) { + Set response = client.getLoadBalancerClient(region).listLoadBalancers(); + assert null != response; + assertTrue(response.size() >= 0); + for (LoadBalancer lb : response) { + if (lb.getStatus() == LoadBalancer.Status.DELETED) + continue; + assert lb.getRegion() != null : lb; + assert lb.getName() != null : lb; + assert lb.getId() != -1 : lb; + assert lb.getProtocol() != null : lb; + assert lb.getPort() != -1 : lb; + assert lb.getStatus() != null : lb; + assert lb.getCreated() != null : lb; + assert lb.getUpdated() != null : lb; + assert lb.getVirtualIPs().size() > 0 : lb; + // node info not available during list; + assert lb.getNodes().size() == 0 : lb; + + LoadBalancer getDetails = client.getLoadBalancerClient(region).getLoadBalancer(lb.getId()); + try { + assertEquals(getDetails.getRegion(), lb.getRegion()); + assertEquals(getDetails.getName(), lb.getName()); + assertEquals(getDetails.getId(), lb.getId()); + assertEquals(getDetails.getProtocol(), lb.getProtocol()); + assertEquals(getDetails.getPort(), lb.getPort()); + assertEquals(getDetails.getStatus(), lb.getStatus()); + assertEquals(getDetails.getCreated(), lb.getCreated()); + assertEquals(getDetails.getUpdated(), lb.getUpdated()); + assertEquals(getDetails.getVirtualIPs(), lb.getVirtualIPs()); + // node info not available during list; + assert getDetails.getNodes().size() > 0 : lb; + } catch (AssertionError e) { + throw new AssertionError(String.format("%s\n%s - %s", e.getMessage(),getDetails, lb)); + } + } + } + } + + public void testCreateLoadBalancer() throws Exception { + for (String region : client.getConfiguredRegions()) { + Logger.getAnonymousLogger().info("starting lb in region " + region); + LoadBalancer lb = client.getLoadBalancerClient(region).createLoadBalancer( + LoadBalancerRequest.builder().name(prefix + "-" + region).protocol("HTTP").port(80).virtualIPType( + Type.PUBLIC).node(NodeRequest.builder().address("192.168.1.1").port(8080).build()).build()); + checkLBInRegion(region, lb, prefix + "-" + region); + assertEquals(lb.getStatus(), LoadBalancer.Status.BUILD); + lbs.add(lb); + assert loadBalancerActive.apply(lb) : lb; + + LoadBalancer newLb = client.getLoadBalancerClient(region).getLoadBalancer(lb.getId()); + checkLBInRegion(region, newLb, prefix + "-" + region); + assertEquals(newLb.getStatus(), LoadBalancer.Status.ACTIVE); + } + } + + @Test(dependsOnMethods = "testCreateLoadBalancer") + public void testUpdateLoadBalancer() throws Exception { + for (LoadBalancer lb : lbs) { + client.getLoadBalancerClient(lb.getRegion()).updateLoadBalancerAttributes(lb.getId(), + LoadBalancerAttributes.Builder.name("foo" + "-" + lb.getRegion())); + assert loadBalancerActive.apply(lb) : lb; + + LoadBalancer newLb = client.getLoadBalancerClient(lb.getRegion()).getLoadBalancer(lb.getId()); + checkLBInRegion(newLb.getRegion(), newLb, "foo" + "-" + lb.getRegion()); + assertEquals(newLb.getStatus(), LoadBalancer.Status.ACTIVE); + } + } + + private void checkLBInRegion(String region, LoadBalancer lb, String name) { + assertEquals(lb.getRegion(), region); + assertEquals(lb.getName(), name); + assertEquals(lb.getProtocol(), "HTTP"); + assertEquals(lb.getPort(), new Integer(80)); + assertEquals(Iterables.get(lb.getVirtualIPs(), 0).getType(), Type.PUBLIC); + } + +} diff --git a/providers/cloudloadbalancers-us/src/test/java/org/jclouds/cloudloadbalancers/functions/UnwrapLoadBalancerTest.java b/providers/cloudloadbalancers-us/src/test/java/org/jclouds/cloudloadbalancers/functions/UnwrapLoadBalancerTest.java new file mode 100644 index 0000000000..e93fcae313 --- /dev/null +++ b/providers/cloudloadbalancers-us/src/test/java/org/jclouds/cloudloadbalancers/functions/UnwrapLoadBalancerTest.java @@ -0,0 +1,79 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers.functions; + +import org.jclouds.cloudloadbalancers.domain.LoadBalancer; +import org.jclouds.cloudloadbalancers.domain.Node; +import org.jclouds.cloudloadbalancers.domain.VirtualIP; +import org.jclouds.cloudloadbalancers.domain.LoadBalancer.Status; +import org.jclouds.cloudloadbalancers.domain.VirtualIP.IPVersion; +import org.jclouds.date.internal.SimpleDateFormatDateService; +import org.jclouds.http.HttpResponse; +import org.jclouds.json.BaseItemParserTest; +import org.testng.annotations.Test; + +import com.google.common.base.Function; +import com.google.common.collect.ImmutableSet; +import com.google.inject.Injector; + +/** + * + * @author Adrian Cole + */ +@Test(groups = "unit") +public class UnwrapLoadBalancerTest extends BaseItemParserTest { + + @Override + public Class type() { + return LoadBalancer.class; + } + + @Override + public String resource() { + return "/getloadbalancer.json"; + } + + @Override + public LoadBalancer expected() { + return LoadBalancer.builder().region("DFW").id(2000).name("sample-loadbalancer").protocol("HTTP").port(80) + .algorithm("RANDOM").status(Status.ACTIVE).connectionLoggingEnabled(true).virtualIPs( + ImmutableSet.of(VirtualIP.builder().id(1000).address("206.10.10.210").type( + VirtualIP.Type.PUBLIC).ipVersion(IPVersion.IPV4).build())) + + .nodes( + ImmutableSet.of(Node.builder().id(1041).address("10.1.1.1").port(80).condition( + Node.Condition.ENABLED).status(Node.Status.ONLINE).build(), Node.builder().id(1411) + .address("10.1.1.2").port(80).condition(Node.Condition.ENABLED).status( + Node.Status.ONLINE).build())).sessionPersistenceType("HTTP_COOKIE") + // connectionThrottle({ + // minConnections(10) + // maxConnections(100) + // maxConnectionRate(50) + // rateInterval(60 + // }) + .clusterName("c1.dfw1").created( + new SimpleDateFormatDateService().iso8601SecondsDateParse("2010-11-30T03:23:42Z")).updated( + new SimpleDateFormatDateService().iso8601SecondsDateParse("2010-11-30T03:23:44Z")).build(); + } + + @Override + protected Function getParser(Injector i) { + return i.getInstance(UnwrapLoadBalancer.class).setRegion("DFW"); + } +} diff --git a/providers/cloudloadbalancers-us/src/test/java/org/jclouds/cloudloadbalancers/functions/UnwrapLoadBalancersTest.java b/providers/cloudloadbalancers-us/src/test/java/org/jclouds/cloudloadbalancers/functions/UnwrapLoadBalancersTest.java new file mode 100644 index 0000000000..9a55b7a3fb --- /dev/null +++ b/providers/cloudloadbalancers-us/src/test/java/org/jclouds/cloudloadbalancers/functions/UnwrapLoadBalancersTest.java @@ -0,0 +1,76 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious) LLC. + * + * ==================================================================== + * 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.cloudloadbalancers.functions; + +import java.util.Set; + +import org.jclouds.cloudloadbalancers.domain.LoadBalancer; +import org.jclouds.cloudloadbalancers.domain.VirtualIP; +import org.jclouds.cloudloadbalancers.domain.LoadBalancer.Status; +import org.jclouds.date.internal.SimpleDateFormatDateService; +import org.jclouds.http.HttpResponse; +import org.jclouds.json.BaseSetParserTest; +import org.testng.annotations.Test; + +import com.google.common.base.Function; +import com.google.common.collect.ImmutableSet; +import com.google.inject.Injector; + +/** + * + * @author Adrian Cole + */ +@Test(groups = "unit") +public class UnwrapLoadBalancersTest extends BaseSetParserTest { + + @Override + public Class type() { + return LoadBalancer.class; + } + + @Override + public String resource() { + return "/listloadbalancers.json"; + } + + @Override + public Set expected() { + + return ImmutableSet.of( + + LoadBalancer.builder().region("DFW").name("lb-site1").id(71).protocol("HTTP").port(80).algorithm("RANDOM") + .status(Status.ACTIVE).virtualIPs( + ImmutableSet.of(VirtualIP.builder().id(403).address("206.55.130.1").type(VirtualIP.Type.PUBLIC) + .ipVersion(VirtualIP.IPVersion.IPV4).build())).created( + new SimpleDateFormatDateService().iso8601SecondsDateParse("2010-11-30T03:23:42Z")).updated( + new SimpleDateFormatDateService().iso8601SecondsDateParse("2010-11-30T03:23:44Z")).build(), + LoadBalancer.builder().region("DFW").name("lb-site2").id(166).protocol("HTTP").port(80).algorithm( + "RANDOM").status(Status.ACTIVE).virtualIPs( + ImmutableSet.of(VirtualIP.builder().id(401).address("206.55.130.2").type(VirtualIP.Type.PUBLIC) + .ipVersion(VirtualIP.IPVersion.IPV4).build())).created( + new SimpleDateFormatDateService().iso8601SecondsDateParse("2010-11-30T03:23:42Z")).updated( + new SimpleDateFormatDateService().iso8601SecondsDateParse("2010-11-30T03:23:44Z")).build()); + + } + + @Override + protected Function> getParser(Injector i) { + return i.getInstance(UnwrapLoadBalancers.class).setRegion("DFW"); + } +} diff --git a/providers/cloudloadbalancers-us/src/test/java/org/jclouds/cloudloadbalancers/handlers/ParseCloudLoadBalancersErrorFromHttpResponseTest.java b/providers/cloudloadbalancers-us/src/test/java/org/jclouds/cloudloadbalancers/handlers/ParseCloudLoadBalancersErrorFromHttpResponseTest.java new file mode 100644 index 0000000000..a4ba398bb0 --- /dev/null +++ b/providers/cloudloadbalancers-us/src/test/java/org/jclouds/cloudloadbalancers/handlers/ParseCloudLoadBalancersErrorFromHttpResponseTest.java @@ -0,0 +1,50 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers.handlers; + +import java.net.URI; + +import org.jclouds.http.handlers.BaseHttpErrorHandlerTest; +import org.jclouds.rest.ResourceNotFoundException; +import org.testng.annotations.Test; + +/** + * + * @author Adrian Cole + */ +@Test(groups = { "unit" }) +public class ParseCloudLoadBalancersErrorFromHttpResponseTest extends + BaseHttpErrorHandlerTest { + @Test + public void test404NotFound() { + assertCodeMakes( + "GET", + URI.create("https://ord.loadbalancers.api.rackspacecloud.com/v1.0/1234/loadbalancers/2000"), + 404, + "Not Found", + "\n Object not Found\n", + ResourceNotFoundException.class, "loadbalancers 2000 not found"); + } + + @Override + protected Class getClassToTest() { + return ParseCloudLoadBalancersErrorFromHttpResponse.class; + } + +} diff --git a/providers/cloudloadbalancers-us/src/test/java/org/jclouds/cloudloadbalancers/options/ListOptionsTest.java b/providers/cloudloadbalancers-us/src/test/java/org/jclouds/cloudloadbalancers/options/ListOptionsTest.java new file mode 100644 index 0000000000..aeab656844 --- /dev/null +++ b/providers/cloudloadbalancers-us/src/test/java/org/jclouds/cloudloadbalancers/options/ListOptionsTest.java @@ -0,0 +1,59 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudloadbalancers.options; + +import static org.jclouds.cloudloadbalancers.options.ListOptions.Builder.limit; +import static org.jclouds.cloudloadbalancers.options.ListOptions.Builder.marker; +import static org.testng.Assert.assertEquals; + +import org.jclouds.cloudloadbalancers.options.ListOptions; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableList; + +/** + * Tests behavior of {@code ListOptions} + * + * @author Adrian Cole + */ +@Test(groups = "unit") +public class ListOptionsTest { + + public void testMarker() { + ListOptions options = new ListOptions().marker("1"); + assertEquals(ImmutableList.of("1"), options.buildQueryParameters().get("marker")); + } + + public void testLimit() { + int limit = 1; + ListOptions options = new ListOptions().limit(limit); + assertEquals(ImmutableList.of("1"), options.buildQueryParameters().get("limit")); + } + + public void testMarkerStatic() { + ListOptions options = marker("1"); + assertEquals(ImmutableList.of("1"), options.buildQueryParameters().get("marker")); + } + + public void testLimitStatic() { + ListOptions options = limit(1); + assertEquals(ImmutableList.of("1"), options.buildQueryParameters().get("limit")); + } +} diff --git a/providers/cloudloadbalancers-us/src/test/resources/getloadbalancer.json b/providers/cloudloadbalancers-us/src/test/resources/getloadbalancer.json new file mode 100644 index 0000000000..4ef8b1baec --- /dev/null +++ b/providers/cloudloadbalancers-us/src/test/resources/getloadbalancer.json @@ -0,0 +1,55 @@ +{ + "loadBalancer":{ + "id": 2000, + "name":"sample-loadbalancer", + "protocol":"HTTP", + "port": 80, + "algorithm":"RANDOM", + "status":"ACTIVE", + "connectionLogging":{ + "enabled":"true" + }, + "virtualIps":[ + { + "id": 1000, + "address":"206.10.10.210", + "type":"PUBLIC", + "ipVersion":"IPV4" + } + ], + "nodes":[ + { + "id": 1041, + "address":"10.1.1.1", + "port": 80, + "condition":"ENABLED", + "status":"ONLINE" + }, + { + "id": 1411, + "address":"10.1.1.2", + "port": 80, + "condition":"ENABLED", + "status":"ONLINE" + } + ], + "sessionPersistence":{ + "persistenceType":"HTTP_COOKIE" + }, + "connectionThrottle":{ + "minConnections": 10, + "maxConnections": 100, + "maxConnectionRate": 50, + "rateInterval": 60 + }, + "cluster":{ + "name":"c1.dfw1" + }, + "created":{ + "time":"2010-11-30T03:23:42Z" + }, + "updated":{ + "time":"2010-11-30T03:23:44Z" + } + } +} \ No newline at end of file diff --git a/providers/cloudloadbalancers-us/src/test/resources/listloadbalancers.json b/providers/cloudloadbalancers-us/src/test/resources/listloadbalancers.json new file mode 100644 index 0000000000..7bc3f855f9 --- /dev/null +++ b/providers/cloudloadbalancers-us/src/test/resources/listloadbalancers.json @@ -0,0 +1,48 @@ +{ + "loadBalancers":[ + { + "name":"lb-site1", + "id":"71", + "protocol":"HTTP", + "port":"80", + "algorithm":"RANDOM", + "status":"ACTIVE", + "virtualIps":[ + { + "id":"403", + "address":"206.55.130.1", + "type":"PUBLIC", + "ipVersion":"IPV4" + } + ], + "created":{ + "time":"2010-11-30T03:23:42Z" + }, + "updated":{ + "time":"2010-11-30T03:23:44Z" + } + }, + { + "name":"lb-site2", + "id":"166", + "protocol":"HTTP", + "port":"80", + "algorithm":"RANDOM", + "status":"ACTIVE", + "virtualIps":[ + { + "id":"401", + "address":"206.55.130.2", + "type":"PUBLIC", + "ipVersion":"IPV4" + } + ], + "created":{ + "time":"2010-11-30T03:23:42Z" + }, + "updated":{ + "time":"2010-11-30T03:23:44Z" + } + } + ] +} \ No newline at end of file diff --git a/sandbox-apis/nova/src/test/resources/log4j.xml b/providers/cloudloadbalancers-us/src/test/resources/log4j.xml old mode 100755 new mode 100644 similarity index 87% rename from sandbox-apis/nova/src/test/resources/log4j.xml rename to providers/cloudloadbalancers-us/src/test/resources/log4j.xml index 5b548a0f48..8e7879bb45 --- a/sandbox-apis/nova/src/test/resources/log4j.xml +++ b/providers/cloudloadbalancers-us/src/test/resources/log4j.xml @@ -70,17 +70,7 @@ --> - - - - - - - - - - - + @@ -128,7 +118,7 @@ - + @@ -141,9 +131,6 @@ - - - @@ -157,19 +144,21 @@ - + - + - - + - + + diff --git a/providers/gogrid/src/main/java/org/jclouds/gogrid/compute/GoGridComputeService.java b/providers/gogrid/src/main/java/org/jclouds/gogrid/compute/GoGridComputeService.java new file mode 100644 index 0000000000..4946eeedd6 --- /dev/null +++ b/providers/gogrid/src/main/java/org/jclouds/gogrid/compute/GoGridComputeService.java @@ -0,0 +1,89 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.gogrid.compute; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ExecutorService; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.jclouds.Constants; +import org.jclouds.collect.Memoized; +import org.jclouds.compute.ComputeServiceContext; +import org.jclouds.compute.domain.Hardware; +import org.jclouds.compute.domain.Image; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.domain.TemplateBuilder; +import org.jclouds.compute.internal.BaseComputeService; +import org.jclouds.compute.internal.PersistNodeCredentials; +import org.jclouds.compute.options.TemplateOptions; +import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts; +import org.jclouds.compute.strategy.CreateNodesInGroupThenAddToSet; +import org.jclouds.compute.strategy.DestroyNodeStrategy; +import org.jclouds.compute.strategy.GetNodeMetadataStrategy; +import org.jclouds.compute.strategy.InitializeRunScriptOnNodeOrPlaceInBadMap; +import org.jclouds.compute.strategy.ListNodesStrategy; +import org.jclouds.compute.strategy.RebootNodeStrategy; +import org.jclouds.compute.strategy.ResumeNodeStrategy; +import org.jclouds.compute.strategy.SuspendNodeStrategy; +import org.jclouds.domain.Credentials; +import org.jclouds.domain.Location; +import org.jclouds.gogrid.compute.options.GoGridTemplateOptions; +import org.jclouds.scriptbuilder.functions.InitAdminAccess; + +import com.google.common.base.Predicate; +import com.google.common.base.Supplier; + +/** + * @author Andrew Kennedy + */ +@Singleton +public class GoGridComputeService extends BaseComputeService { + @Inject + protected GoGridComputeService(ComputeServiceContext context, Map credentialStore, + @Memoized Supplier> images, @Memoized Supplier> hardwareProfiles, + @Memoized Supplier> locations, ListNodesStrategy listNodesStrategy, + GetNodeMetadataStrategy getNodeMetadataStrategy, CreateNodesInGroupThenAddToSet runNodesAndAddToSetStrategy, + RebootNodeStrategy rebootNodeStrategy, DestroyNodeStrategy destroyNodeStrategy, + ResumeNodeStrategy resumeNodeStrategy, SuspendNodeStrategy suspendNodeStrategy, + Provider templateBuilderProvider, Provider templateOptionsProvider, + @Named("NODE_RUNNING") Predicate nodeRunning, + @Named("NODE_TERMINATED") Predicate nodeTerminated, + @Named("NODE_SUSPENDED") Predicate nodeSuspended, + InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, InitAdminAccess initAdminAccess, + PersistNodeCredentials persistNodeCredentials, Timeouts timeouts, + @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor) { + super(context, credentialStore, images, hardwareProfiles, locations, listNodesStrategy, getNodeMetadataStrategy, + runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy, resumeNodeStrategy, + suspendNodeStrategy, templateBuilderProvider, templateOptionsProvider, nodeRunning, nodeTerminated, + nodeSuspended, initScriptRunnerFactory, initAdminAccess, persistNodeCredentials, timeouts, executor); + } + + /** + * Returns template options, except of type {@link GoGridTemplateOptions}. + */ + @Override + public GoGridTemplateOptions templateOptions() { + return GoGridTemplateOptions.class.cast(super.templateOptions()); + } +} diff --git a/providers/gogrid/src/main/java/org/jclouds/gogrid/compute/config/GoGridBindComputeStrategiesByClass.java b/providers/gogrid/src/main/java/org/jclouds/gogrid/compute/config/GoGridBindComputeStrategiesByClass.java index 2f34306fc0..b45d6c92ec 100644 --- a/providers/gogrid/src/main/java/org/jclouds/gogrid/compute/config/GoGridBindComputeStrategiesByClass.java +++ b/providers/gogrid/src/main/java/org/jclouds/gogrid/compute/config/GoGridBindComputeStrategiesByClass.java @@ -26,21 +26,19 @@ import org.jclouds.compute.strategy.ListNodesStrategy; import org.jclouds.compute.strategy.RebootNodeStrategy; import org.jclouds.compute.strategy.ResumeNodeStrategy; import org.jclouds.compute.strategy.SuspendNodeStrategy; -import org.jclouds.gogrid.compute.strategy.FindPublicIpThenCreateNodeInGroup; +import org.jclouds.gogrid.compute.strategy.FindIpThenCreateNodeInGroup; import org.jclouds.gogrid.compute.strategy.GoGridDestroyNodeStrategy; import org.jclouds.gogrid.compute.strategy.GoGridGetNodeMetadataStrategy; import org.jclouds.gogrid.compute.strategy.GoGridLifeCycleStrategy; import org.jclouds.gogrid.compute.strategy.GoGridListNodesStrategy; /** - * * @author Adrian Cole - * */ public class GoGridBindComputeStrategiesByClass extends BindComputeStrategiesByClass { @Override protected Class defineAddNodeWithTagStrategy() { - return FindPublicIpThenCreateNodeInGroup.class; + return FindIpThenCreateNodeInGroup.class; } @Override diff --git a/providers/gogrid/src/main/java/org/jclouds/gogrid/compute/config/GoGridComputeServiceDependenciesModule.java b/providers/gogrid/src/main/java/org/jclouds/gogrid/compute/config/GoGridComputeServiceDependenciesModule.java index 7e04543be8..387f3ed215 100644 --- a/providers/gogrid/src/main/java/org/jclouds/gogrid/compute/config/GoGridComputeServiceDependenciesModule.java +++ b/providers/gogrid/src/main/java/org/jclouds/gogrid/compute/config/GoGridComputeServiceDependenciesModule.java @@ -18,21 +18,24 @@ */ package org.jclouds.gogrid.compute.config; -import static org.jclouds.compute.util.ComputeServiceUtils.getCores; -import static org.jclouds.compute.util.ComputeServiceUtils.getSpace; +import static org.jclouds.compute.util.ComputeServiceUtils.*; import java.util.Map; import javax.inject.Singleton; +import org.jclouds.compute.ComputeService; import org.jclouds.compute.ComputeServiceContext; import org.jclouds.compute.domain.Hardware; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.NodeState; import org.jclouds.compute.internal.ComputeServiceContextImpl; +import org.jclouds.compute.options.TemplateOptions; import org.jclouds.gogrid.GoGridAsyncClient; import org.jclouds.gogrid.GoGridClient; +import org.jclouds.gogrid.compute.GoGridComputeService; import org.jclouds.gogrid.compute.functions.ServerToNodeMetadata; +import org.jclouds.gogrid.compute.options.GoGridTemplateOptions; import org.jclouds.gogrid.domain.Server; import org.jclouds.gogrid.domain.ServerState; import org.jclouds.rest.RestContext; @@ -49,11 +52,12 @@ import com.google.inject.TypeLiteral; /** * @author Oleksiy Yarmula * @author Adrian Cole + * @author Andrew Kennedy */ public class GoGridComputeServiceDependenciesModule extends AbstractModule { - - @Override protected void configure() { + bind(TemplateOptions.class).to(GoGridTemplateOptions.class); + bind(ComputeService.class).to(GoGridComputeService.class); bind(new TypeLiteral>() { }).to(ServerToNodeMetadata.class); bind(new TypeLiteral() { diff --git a/providers/gogrid/src/main/java/org/jclouds/gogrid/compute/options/GoGridTemplateOptions.java b/providers/gogrid/src/main/java/org/jclouds/gogrid/compute/options/GoGridTemplateOptions.java new file mode 100644 index 0000000000..91417c3460 --- /dev/null +++ b/providers/gogrid/src/main/java/org/jclouds/gogrid/compute/options/GoGridTemplateOptions.java @@ -0,0 +1,196 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.gogrid.compute.options; + +import org.jclouds.compute.ComputeService; +import org.jclouds.compute.options.TemplateOptions; +import org.jclouds.io.Payload; + +/** + * Contains options supported by the {@link ComputeService#createNodesInGroup(String, int, TemplateOptions)} + * and {@link ComputeService#runNodesWithTag(String, int, TemplateOptions)} operations on + * the gogrid provider. + * + *

Usage

+ * The recommended way to instantiate a {@link GoGridTemplateOptions} object is to statically + * import {@code GoGridTemplateOptions.*} and invoke a static creation method followed by an + * instance mutator (if needed): + *

+ *

+ * import static org.jclouds.compute.options.GoGridTemplateOptions.Builder.*;
+ * ComputeService client = // get connection
+ * templateBuilder.options(inboundPorts(22, 80, 8080, 443));
+ * Set<? extends NodeMetadata> set = client.runNodesWithTag(tag, 2, templateBuilder.build());
+ * 
+ * + * TODO add GoGrid specific options + * + * @author Adrian Cole + * @author Andrew Kennedy + */ +public class GoGridTemplateOptions extends TemplateOptions implements Cloneable { + @Override + public GoGridTemplateOptions clone() { + GoGridTemplateOptions options = new GoGridTemplateOptions(); + copyTo(options); + return options; + } + + @Override + public void copyTo(TemplateOptions to) { + super.copyTo(to); + if (to instanceof GoGridTemplateOptions) { + @SuppressWarnings("unused") + GoGridTemplateOptions eTo = GoGridTemplateOptions.class.cast(to); + } + } + + public static final GoGridTemplateOptions NONE = new GoGridTemplateOptions(); + + public static class Builder { + // methods that only facilitate returning the correct object type + + /** + * @see TemplateOptions#inboundPorts(int...) + */ + public static GoGridTemplateOptions inboundPorts(int... ports) { + GoGridTemplateOptions options = new GoGridTemplateOptions(); + return GoGridTemplateOptions.class.cast(options.inboundPorts(ports)); + } + + /** + * @see TemplateOptions#blockOnPort(int, int) + */ + public static GoGridTemplateOptions blockOnPort(int port, int seconds) { + GoGridTemplateOptions options = new GoGridTemplateOptions(); + return GoGridTemplateOptions.class.cast(options.blockOnPort(port, seconds)); + } + + /** + * @see TemplateOptions#runScript(Payload) + */ + public static GoGridTemplateOptions runScript(Payload script) { + GoGridTemplateOptions options = new GoGridTemplateOptions(); + return GoGridTemplateOptions.class.cast(options.runScript(script)); + } + + /** + * @see TemplateOptions#installPrivateKey(Payload) + */ + @Deprecated + public static GoGridTemplateOptions installPrivateKey(Payload rsaKey) { + GoGridTemplateOptions options = new GoGridTemplateOptions(); + return GoGridTemplateOptions.class.cast(options.installPrivateKey(rsaKey)); + } + + /** + * @see TemplateOptions#authorizePublicKey(Payload) + */ + @Deprecated + public static GoGridTemplateOptions authorizePublicKey(Payload rsaKey) { + GoGridTemplateOptions options = new GoGridTemplateOptions(); + return GoGridTemplateOptions.class.cast(options.authorizePublicKey(rsaKey)); + } + + /** + * @see TemplateOptions#withMetadata() + */ + public static GoGridTemplateOptions withMetadata() { + GoGridTemplateOptions options = new GoGridTemplateOptions(); + return GoGridTemplateOptions.class.cast(options.withMetadata()); + } + } + + // methods that only facilitate returning the correct object type + + /** + * @see TemplateOptions#blockOnPort(int, int) + */ + @Override + public GoGridTemplateOptions blockOnPort(int port, int seconds) { + return GoGridTemplateOptions.class.cast(super.blockOnPort(port, seconds)); + } + + /** + * @see TemplateOptions#inboundPorts(int...) + */ + @Override + public GoGridTemplateOptions inboundPorts(int... ports) { + return GoGridTemplateOptions.class.cast(super.inboundPorts(ports)); + } + + /** + * @see TemplateOptions#authorizePublicKey(String) + */ + @Override + public GoGridTemplateOptions authorizePublicKey(String publicKey) { + return GoGridTemplateOptions.class.cast(super.authorizePublicKey(publicKey)); + } + + /** + * @see TemplateOptions#authorizePublicKey(Payload) + */ + @Override + @Deprecated + public GoGridTemplateOptions authorizePublicKey(Payload publicKey) { + return GoGridTemplateOptions.class.cast(super.authorizePublicKey(publicKey)); + } + + /** + * @see TemplateOptions#installPrivateKey(String) + */ + @Override + public GoGridTemplateOptions installPrivateKey(String privateKey) { + return GoGridTemplateOptions.class.cast(super.installPrivateKey(privateKey)); + } + + /** + * @see TemplateOptions#installPrivateKey(Payload) + */ + @Override + @Deprecated + public GoGridTemplateOptions installPrivateKey(Payload privateKey) { + return GoGridTemplateOptions.class.cast(super.installPrivateKey(privateKey)); + } + + /** + * @see TemplateOptions#runScript(Payload) + */ + @Override + public GoGridTemplateOptions runScript(Payload script) { + return GoGridTemplateOptions.class.cast(super.runScript(script)); + } + + /** + * @see TemplateOptions#runScript(byte[]) + */ + @Override + @Deprecated + public GoGridTemplateOptions runScript(byte[] script) { + return GoGridTemplateOptions.class.cast(super.runScript(script)); + } + + /** + * @see TemplateOptions#withMetadata() + */ + @Override + public GoGridTemplateOptions withMetadata() { + return GoGridTemplateOptions.class.cast(super.withMetadata()); + } +} diff --git a/providers/gogrid/src/main/java/org/jclouds/gogrid/compute/strategy/FindPublicIpThenCreateNodeInGroup.java b/providers/gogrid/src/main/java/org/jclouds/gogrid/compute/strategy/FindIpThenCreateNodeInGroup.java similarity index 85% rename from providers/gogrid/src/main/java/org/jclouds/gogrid/compute/strategy/FindPublicIpThenCreateNodeInGroup.java rename to providers/gogrid/src/main/java/org/jclouds/gogrid/compute/strategy/FindIpThenCreateNodeInGroup.java index f71d330178..5c4ccad148 100644 --- a/providers/gogrid/src/main/java/org/jclouds/gogrid/compute/strategy/FindPublicIpThenCreateNodeInGroup.java +++ b/providers/gogrid/src/main/java/org/jclouds/gogrid/compute/strategy/FindIpThenCreateNodeInGroup.java @@ -18,7 +18,7 @@ */ package org.jclouds.gogrid.compute.strategy; -import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.*; import java.security.SecureRandom; import java.util.Set; @@ -26,8 +26,8 @@ import java.util.Set; import javax.inject.Inject; import javax.inject.Singleton; -import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.Hardware; +import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.Template; import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts; import org.jclouds.compute.strategy.CreateNodeWithGroupEncodedIntoName; @@ -48,22 +48,23 @@ import com.google.common.collect.Iterables; * @author Oleksiy Yarmula */ @Singleton -public class FindPublicIpThenCreateNodeInGroup implements CreateNodeWithGroupEncodedIntoName { +public class FindIpThenCreateNodeInGroup implements CreateNodeWithGroupEncodedIntoName { private final GoGridClient client; private final Function sizeToRam; private final Function serverToNodeMetadata; private RetryablePredicate serverLatestJobCompleted; private RetryablePredicate serverLatestJobCompletedShort; - + @Inject - protected FindPublicIpThenCreateNodeInGroup(GoGridClient client, + protected FindIpThenCreateNodeInGroup(GoGridClient client, Function serverToNodeMetadata, Function sizeToRam, Timeouts timeouts) { this.client = client; this.serverToNodeMetadata = serverToNodeMetadata; this.sizeToRam = sizeToRam; - this.serverLatestJobCompleted = new RetryablePredicate(new ServerLatestJobCompleted( - client.getJobServices()), timeouts.nodeRunning * 9l / 10l); + this.serverLatestJobCompleted = new RetryablePredicate( + new ServerLatestJobCompleted(client.getJobServices()), + timeouts.nodeRunning * 9l / 10l); this.serverLatestJobCompletedShort = new RetryablePredicate( new ServerLatestJobCompleted(client.getJobServices()), timeouts.nodeRunning * 1l / 10l); @@ -74,15 +75,16 @@ public class FindPublicIpThenCreateNodeInGroup implements CreateNodeWithGroupEnc Server addedServer = null; boolean notStarted = true; int numOfRetries = 20; + GetIpListOptions unassignedIps = new GetIpListOptions() + .onlyUnassigned() + .inDatacenter(template.getLocation().getId()) + .onlyWithType(IpType.PUBLIC); // lock-free consumption of a shared resource: IP address pool while (notStarted) { // TODO: replace with Predicate-based thread - // collision avoidance for - // simplicity - Set availableIps = client.getIpServices().getIpList( - new GetIpListOptions().onlyUnassigned().onlyWithType(IpType.PUBLIC).inDatacenter( - template.getLocation().getId())); - if (availableIps.size() == 0) - throw new RuntimeException("No public IPs available on this identity."); + // collision avoidance for simplicity + Set availableIps = client.getIpServices().getIpList(unassignedIps); + if (availableIps.isEmpty()) + throw new RuntimeException("No IPs available on this identity."); int ipIndex = new SecureRandom().nextInt(availableIps.size()); Ip availableIp = Iterables.get(availableIps, ipIndex); try { diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/ShareIp.java b/providers/gogrid/src/test/java/org/jclouds/gogrid/compute/options/GoGridTemplateOptionsTest.java similarity index 60% rename from sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/ShareIp.java rename to providers/gogrid/src/test/java/org/jclouds/gogrid/compute/options/GoGridTemplateOptionsTest.java index d8284f6aad..b6ded698f8 100644 --- a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/ShareIp.java +++ b/providers/gogrid/src/test/java/org/jclouds/gogrid/compute/options/GoGridTemplateOptionsTest.java @@ -16,27 +16,22 @@ * limitations under the License. * ==================================================================== */ -package org.jclouds.openstack.nova.domain; +package org.jclouds.gogrid.compute.options; -public class ShareIp { +import static org.testng.Assert.*; - private boolean configureServer; - private int sharedIpGroupId; +import org.jclouds.compute.options.TemplateOptions; +import org.testng.annotations.Test; - public void setConfigureServer(boolean configureServer) { - this.configureServer = configureServer; +/** + * Tests possible uses of {@code GoGridTemplateOptions} and {@code GoGridTemplateOptions.Builder.*}. + * + * @author Andrew Kennedy + */ +public class GoGridTemplateOptionsTest { + @Test + public void testAs() { + TemplateOptions options = new GoGridTemplateOptions(); + assertEquals(options.as(GoGridTemplateOptions.class), options); } - - public boolean isConfigureServer() { - return configureServer; - } - - public void setSharedIpGroupId(int sharedIpGroupId) { - this.sharedIpGroupId = sharedIpGroupId; - } - - public int getSharedIpGroupId() { - return sharedIpGroupId; - } - } diff --git a/providers/pom.xml b/providers/pom.xml index 4393bbcc28..59bb182b5f 100644 --- a/providers/pom.xml +++ b/providers/pom.xml @@ -52,6 +52,7 @@ cloudservers-uk cloudfiles-us cloudfiles-uk + cloudloadbalancers-us bluelock-vcdirector trmk-ecloud trmk-vcloudexpress diff --git a/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/binders/BindFirewallRuleToXmlPayload.java b/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/binders/BindFirewallRuleToXmlPayload.java new file mode 100644 index 0000000000..92aed4ec58 --- /dev/null +++ b/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/binders/BindFirewallRuleToXmlPayload.java @@ -0,0 +1,121 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.savvis.vpdc.binders; + +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.util.Map; +import java.util.Properties; + +import javax.inject.Singleton; +import javax.ws.rs.core.MediaType; +import javax.xml.parsers.FactoryConfigurationError; +import javax.xml.parsers.ParserConfigurationException; + +import org.jclouds.http.HttpRequest; +import org.jclouds.rest.MapBinder; +import org.jclouds.rest.binders.BindToStringPayload; +import org.jclouds.rest.internal.GeneratedHttpRequest; +import org.jclouds.savvis.vpdc.domain.FirewallRule; + +import com.jamesmurty.utils.XMLBuilder; + +/** + * + * @author Kedar Dave + * + */ +@Singleton +public class BindFirewallRuleToXmlPayload extends BindToStringPayload implements MapBinder { + @Override + public R bindToRequest(R request, Object toBind) { + throw new IllegalStateException("BindFirewallRuleToXmlPayload needs parameters"); + + } + + protected FirewallRule findRuleInArgsOrNull(GeneratedHttpRequest gRequest) { + for (Object arg : gRequest.getArgs()) { + if (arg instanceof FirewallRule) { + return (FirewallRule) arg; + } else if (arg instanceof FirewallRule[]) { + FirewallRule[] rules = (FirewallRule[]) arg; + return (rules.length > 0) ? rules[0] : null; + } + } + return null; + } + + @Override + public R bindToRequest(R request, Map postParams) { + checkArgument(checkNotNull(request, "request") instanceof GeneratedHttpRequest, + "this binder is only valid for GeneratedHttpRequests!"); + GeneratedHttpRequest gRequest = (GeneratedHttpRequest) request; + checkState(gRequest.getArgs() != null, "args should be initialized at this point"); + + request = super.bindToRequest(request, + generateXml(findRuleInArgsOrNull(gRequest))); + request.getPayload().getContentMetadata().setContentType(MediaType.APPLICATION_XML); + return request; + } + + public String generateXml(FirewallRule firewallRule) { + checkNotNull(firewallRule, "FirewallRule"); + + try { + XMLBuilder rootBuilder = buildRoot(); + addFirewallRuleSection(rootBuilder, firewallRule); + Properties outputProperties = new Properties(); + outputProperties.put(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes"); + return rootBuilder.asString(outputProperties); + } catch (Exception e) { + return null; + } + } + + void addFirewallRuleSection(XMLBuilder rootBuilder, FirewallRule firewallRule) { + XMLBuilder firewallRuleBuilder = rootBuilder.e("svvs:FirewallRule"); + firewallRuleBuilder.e("svvs:IsEnabled").t(firewallRule.isEnabled() ? "true" : "false"); + firewallRuleBuilder.e("svvs:Description").t("Server Tier Firewall Rule"); + firewallRuleBuilder.e("svvs:Type").t(firewallRule.getFirewallType()); + firewallRuleBuilder.e("svvs:Log").t(firewallRule.isLogged() ? "yes" : "no"); + firewallRuleBuilder.e("svvs:Policy").t(firewallRule.getPolicy()); + firewallRuleBuilder.e("svvs:Protocols").e("svvs:"+firewallRule.getProtocol()).t("true").up().up(); + firewallRuleBuilder.e("svvs:Port").t(firewallRule.getPort()); + firewallRuleBuilder.e("svvs:Destination").t(firewallRule.getDestination()); + firewallRuleBuilder.e("svvs:Source").t(firewallRule.getSource()); + } + + protected XMLBuilder buildRoot() throws ParserConfigurationException, FactoryConfigurationError { + XMLBuilder rootBuilder = XMLBuilder.create("svvs:FirewallService") + .a("xmlns:common", "http://schemas.dmtf.org/wbem/wscim/1/common") + .a("xmlns:vApp", "http://www.vmware.com/vcloud/v0.8") + .a("xmlns:rasd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData") + .a("xmlns:vssd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData") + .a("xmlns:ovf", "http://schemas.dmtf.org/ovf/envelope/1") + .a("xmlns:svvs", "http://schemas.api.sandbox.symphonyVPDC.savvis.net/vpdci"); + return rootBuilder; + } + + protected String ifNullDefaultTo(String value, String defaultValue) { + return value != null ? value : checkNotNull(defaultValue, "defaultValue"); + } + +} diff --git a/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/features/FirewallAsyncClient.java b/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/features/FirewallAsyncClient.java index 8920b4f85e..ee44b53e55 100644 --- a/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/features/FirewallAsyncClient.java +++ b/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/features/FirewallAsyncClient.java @@ -18,10 +18,27 @@ */ package org.jclouds.savvis.vpdc.features; +import javax.annotation.Nullable; +import javax.ws.rs.DELETE; +import javax.ws.rs.PUT; import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import org.jclouds.rest.annotations.ExceptionParser; +import org.jclouds.rest.annotations.MapBinder; +import org.jclouds.rest.annotations.ParamParser; +import org.jclouds.rest.annotations.PayloadParam; import org.jclouds.rest.annotations.RequestFilters; +import org.jclouds.rest.annotations.XMLResponseParser; +import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; +import org.jclouds.savvis.vpdc.binders.BindFirewallRuleToXmlPayload; +import org.jclouds.savvis.vpdc.domain.FirewallRule; +import org.jclouds.savvis.vpdc.domain.Task; import org.jclouds.savvis.vpdc.filters.SetVCloudTokenCookie; +import org.jclouds.savvis.vpdc.functions.DefaultOrgIfNull; +import org.jclouds.savvis.vpdc.xml.TaskHandler; + +import com.google.common.util.concurrent.ListenableFuture; /** * Provides access to Symphony VPDC resources via their REST API. @@ -31,8 +48,29 @@ import org.jclouds.savvis.vpdc.filters.SetVCloudTokenCookie; * @author Adrian Cole */ @RequestFilters(SetVCloudTokenCookie.class) -@Path("v{jclouds.api-version}") public interface FirewallAsyncClient { + /** + * @see FirewallClient#addFirewallRule + */ + @PUT + @XMLResponseParser(TaskHandler.class) + @Path("v{jclouds.api-version}/org/{billingSiteId}/vdc/{vpdcId}/FirewallService/") + @MapBinder(BindFirewallRuleToXmlPayload.class) + ListenableFuture addFirewallRule( + @PathParam("billingSiteId") @Nullable @ParamParser(DefaultOrgIfNull.class) String billingSiteId, + @PathParam("vpdcId") String vpdcId, @PayloadParam("firewallRule") FirewallRule firewallRule); + + /** + * @see FirewallClient#deleteFirewallRule + */ + @DELETE + @XMLResponseParser(TaskHandler.class) + @Path("v{jclouds.api-version}/org/{billingSiteId}/vdc/{vpdcId}/FirewallService/") + @MapBinder(BindFirewallRuleToXmlPayload.class) + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + ListenableFuture deleteFirewallRule( + @PathParam("billingSiteId") @Nullable @ParamParser(DefaultOrgIfNull.class) String billingSiteId, + @PathParam("vpdcId") String vpdcId, @PayloadParam("firewallRule") FirewallRule firewallRule); } diff --git a/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/features/FirewallClient.java b/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/features/FirewallClient.java index c681918a7b..da26f301f6 100644 --- a/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/features/FirewallClient.java +++ b/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/features/FirewallClient.java @@ -21,6 +21,8 @@ package org.jclouds.savvis.vpdc.features; import java.util.concurrent.TimeUnit; import org.jclouds.concurrent.Timeout; +import org.jclouds.savvis.vpdc.domain.FirewallRule; +import org.jclouds.savvis.vpdc.domain.Task; /** * Provides access to Symphony VPDC resources via their REST API. @@ -31,5 +33,30 @@ import org.jclouds.concurrent.Timeout; */ @Timeout(duration = 300, timeUnit = TimeUnit.SECONDS) public interface FirewallClient { - + + /** + * Add a new firewall rule + * + * @param billingSiteId + * billing site Id, or null for default + * @param vpdcId + * vpdc Id + * @param firewallRule + * firewall rule to be added + * @return + */ + Task addFirewallRule(String billingSiteId, String vpdcId, FirewallRule firewallRule); + + /** + * Delete a firewall rule + * + * @param billingSiteId + * billing site Id, or null for default + * @param vpdcId + * vpdc Id + * @param firewallRule + * firewall rule to be deleted + * @return + */ + Task deleteFirewallRule(String billingSiteId, String vpdcId, FirewallRule firewallRule); } \ No newline at end of file diff --git a/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/features/VMAsyncClient.java b/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/features/VMAsyncClient.java index a72703462b..e060dbf8e4 100644 --- a/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/features/VMAsyncClient.java +++ b/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/features/VMAsyncClient.java @@ -23,6 +23,7 @@ import java.net.URI; import javax.annotation.Nullable; 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; @@ -93,4 +94,22 @@ public interface VMAsyncClient { @XMLResponseParser(TaskHandler.class) @ExceptionParser(ReturnNullOnNotFoundOr404.class) ListenableFuture removeVM(@EndpointParam URI vm); + + /** + * @see VMClient#powerOffVM + */ + @POST + @XMLResponseParser(TaskHandler.class) + @Path("action/powerOff") + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + ListenableFuture powerOffVM(@EndpointParam URI vm); + + /** + * @see VMClient#powerOnVM + */ + @POST + @XMLResponseParser(TaskHandler.class) + @Path("action/powerOn") + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + ListenableFuture powerOnVM(@EndpointParam URI vm); } diff --git a/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/features/VMClient.java b/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/features/VMClient.java index 2cf5724cf2..57e9549b1a 100644 --- a/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/features/VMClient.java +++ b/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/features/VMClient.java @@ -89,4 +89,22 @@ public interface VMClient { * @see #removeVMFromVDC */ Task removeVM(URI vm); + + /** + * Power off a VM + * + * @param vm + * href of the vm + * @return + */ + Task powerOffVM(URI vm); + + /** + * Power on a VM + * + * @param vm + * href of the vm + * @return + */ + Task powerOnVM(URI vm); } diff --git a/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/binders/BindFirewallRuleToXmlPayloadTest.java b/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/binders/BindFirewallRuleToXmlPayloadTest.java new file mode 100644 index 0000000000..a3d245ae03 --- /dev/null +++ b/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/binders/BindFirewallRuleToXmlPayloadTest.java @@ -0,0 +1,45 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.savvis.vpdc.binders; + +import static org.testng.Assert.assertEquals; + +import java.io.IOException; + +import org.jclouds.savvis.vpdc.domain.FirewallRule; +import org.jclouds.util.Strings2; +import org.testng.annotations.Test; + +/** + * Tests behavior of {@code BindVMSpecToXmlPayload} + * + * @author Kedar Dave + */ +@Test(groups = "unit") +public class BindFirewallRuleToXmlPayloadTest { + + public void test() throws IOException { + String expected = Strings2.toStringAndClose(getClass().getResourceAsStream("/firewallService-default.xml")); + + FirewallRule firewallRule = FirewallRule.builder().firewallType("SERVER_TIER_FIREWALL").isEnabled(true).source("internet") + .destination("VM Tier01").port("22").protocol("Tcp").policy("allow").description("Server Tier Firewall Rule").isLogged(false).build(); + + assertEquals(new BindFirewallRuleToXmlPayload().generateXml(firewallRule), expected); + } +} diff --git a/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/features/BaseVPDCClientLiveTest.java b/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/features/BaseVPDCClientLiveTest.java index d33a359a24..4bf96cd284 100644 --- a/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/features/BaseVPDCClientLiveTest.java +++ b/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/features/BaseVPDCClientLiveTest.java @@ -89,7 +89,7 @@ public class BaseVPDCClientLiveTest { context = new ComputeServiceContextFactory().createContext(provider, ImmutableSet. of( new Log4JLoggingModule(), new JschSshClientModule()), overrides); restContext = context.getProviderSpecificContext(); - taskTester = new RetryablePredicate(new TaskSuccess(restContext.getApi()), 650, 10, TimeUnit.SECONDS); + taskTester = new RetryablePredicate(new TaskSuccess(restContext.getApi()), 1200, 10, TimeUnit.SECONDS); } @AfterGroups(groups = "live") diff --git a/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/features/FirewallAsyncClientTest.java b/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/features/FirewallAsyncClientTest.java index 8391a796cd..764ce18ab9 100644 --- a/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/features/FirewallAsyncClientTest.java +++ b/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/features/FirewallAsyncClientTest.java @@ -18,7 +18,16 @@ */ package org.jclouds.savvis.vpdc.features; +import java.io.IOException; +import java.lang.reflect.Method; + +import org.jclouds.http.HttpRequest; +import org.jclouds.http.functions.ParseSax; +import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; import org.jclouds.rest.internal.RestAnnotationProcessor; +import org.jclouds.savvis.vpdc.domain.FirewallRule; +import org.jclouds.savvis.vpdc.xml.TaskHandler; +import org.jclouds.util.Strings2; import org.testng.annotations.Test; import com.google.inject.TypeLiteral; @@ -31,6 +40,38 @@ import com.google.inject.TypeLiteral; @Test(groups = "unit") public class FirewallAsyncClientTest extends BaseVPDCAsyncClientTest { + public void testAddFirewallRule() throws NoSuchMethodException, IOException{ + Method method = FirewallAsyncClient.class.getMethod("addFirewallRule", String.class, String.class, FirewallRule.class); + HttpRequest request = processor.createRequest(method, "11", "22", FirewallRule.builder().firewallType("SERVER_TIER_FIREWALL").isEnabled(true).source("internet") + .destination("VM Tier01").port("22").protocol("Tcp").policy("allow").description("Server Tier Firewall Rule").isLogged(false).build()); + + assertRequestLineEquals(request, "PUT https://api.symphonyvpdc.savvis.net/rest/api/v0.8/org/11/vdc/22/FirewallService/ HTTP/1.1"); + assertNonPayloadHeadersEqual(request, ""); + assertPayloadEquals(request, Strings2.toStringAndClose(getClass().getResourceAsStream("/firewallService-default.xml")), + "application/xml", false); + + assertResponseParserClassEquals(method, request, ParseSax.class); + assertSaxResponseParserClassEquals(method, TaskHandler.class); + + checkFilters(request); + } + + public void testDeleteFirewallRule() throws NoSuchMethodException, IOException{ + Method method = FirewallAsyncClient.class.getMethod("deleteFirewallRule", String.class, String.class, FirewallRule.class); + HttpRequest request = processor.createRequest(method, "11", "22", FirewallRule.builder().firewallType("SERVER_TIER_FIREWALL").isEnabled(true).source("internet") + .destination("VM Tier01").port("22").protocol("Tcp").policy("allow").description("Server Tier Firewall Rule").isLogged(false).build()); + + assertRequestLineEquals(request, "DELETE https://api.symphonyvpdc.savvis.net/rest/api/v0.8/org/11/vdc/22/FirewallService/ HTTP/1.1"); + assertNonPayloadHeadersEqual(request, ""); + assertPayloadEquals(request, Strings2.toStringAndClose(getClass().getResourceAsStream("/firewallService-default.xml")), + "application/xml", false); + + assertResponseParserClassEquals(method, request, ParseSax.class); + assertSaxResponseParserClassEquals(method, TaskHandler.class); + assertExceptionParserClassEquals(method, ReturnNullOnNotFoundOr404.class); + + checkFilters(request); + } @Override protected TypeLiteral> createTypeLiteral() { diff --git a/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/features/FirewallClientLiveTest.java b/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/features/FirewallClientLiveTest.java index f9054cbe0c..14bb39307e 100644 --- a/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/features/FirewallClientLiveTest.java +++ b/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/features/FirewallClientLiveTest.java @@ -18,14 +18,21 @@ */ package org.jclouds.savvis.vpdc.features; +import org.jclouds.savvis.vpdc.domain.FirewallRule; +import org.jclouds.savvis.vpdc.domain.Resource; +import org.jclouds.savvis.vpdc.domain.Task; import org.testng.annotations.AfterGroups; import org.testng.annotations.BeforeGroups; import org.testng.annotations.Test; -@Test(groups = "live") +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; + public class FirewallClientLiveTest extends BaseVPDCClientLiveTest { private FirewallClient client; + private String billingSiteId; + private String vpdcId; @Override @BeforeGroups(groups = { "live" }) @@ -33,7 +40,76 @@ public class FirewallClientLiveTest extends BaseVPDCClientLiveTest { super.setupClient(); client = restContext.getApi().getFirewallClient(); } + + @Test(groups = "live") + public void testAddFirewallRule() throws Exception { + billingSiteId = restContext.getApi().getBrowsingClient().getOrg(null).getId();// default + vpdcId = Iterables.find(restContext.getApi().getBrowsingClient().getOrg(billingSiteId).getVDCs(), + new Predicate() { + + // try to find the first VDC owned by the current user + // check here for what the email property might be, or in + // the jclouds-wire.log + @Override + public boolean apply(Resource arg0) { + String description = restContext.getApi().getBrowsingClient().getVDCInOrg(billingSiteId, + arg0.getId()).getDescription(); + return description.indexOf(email) != -1; + } + + }).getId(); + + String networkTierName = Iterables.get( + restContext.getApi().getBrowsingClient().getVDCInOrg(billingSiteId, vpdcId).getAvailableNetworks(), 0) + .getName(); + + FirewallRule firewallRule = FirewallRule.builder().firewallType("SERVER_TIER_FIREWALL").isEnabled(true).source("internet") + .destination(networkTierName).port("10000").protocol("Tcp").policy("allow").description("Server Tier Firewall Rule").isLogged(false).build(); + + System.out.printf("adding firewall rule:%s %n", firewallRule.toString()); + + Task task = client.addFirewallRule(billingSiteId, vpdcId, firewallRule); + + // make sure there's no error + assert task.getId() != null && task.getError() == null : task; + + assert this.taskTester.apply(task.getId()); + } + + @Test(groups = "live", dependsOnMethods = {"testAddFirewallRule"}) + public void testDeleteFirewallRule() throws Exception { + billingSiteId = restContext.getApi().getBrowsingClient().getOrg(null).getId();// default + vpdcId = Iterables.find(restContext.getApi().getBrowsingClient().getOrg(billingSiteId).getVDCs(), + new Predicate() { + // try to find the first VDC owned by the current user + // check here for what the email property might be, or in + // the jclouds-wire.log + @Override + public boolean apply(Resource arg0) { + String description = restContext.getApi().getBrowsingClient().getVDCInOrg(billingSiteId, + arg0.getId()).getDescription(); + return description.indexOf(email) != -1; + } + + }).getId(); + + String networkTierName = Iterables.get( + restContext.getApi().getBrowsingClient().getVDCInOrg(billingSiteId, vpdcId).getAvailableNetworks(), 0) + .getName(); + + FirewallRule firewallRule = FirewallRule.builder().firewallType("SERVER_TIER_FIREWALL").isEnabled(true).source("internet") + .destination(networkTierName).port("10000").protocol("Tcp").policy("allow").description("Server Tier Firewall Rule").isLogged(false).build(); + + System.out.printf("deleting firewall rule:%s %n", firewallRule.toString()); + + Task task = client.deleteFirewallRule(billingSiteId, vpdcId, firewallRule); + + // make sure there's no error + assert task.getId() != null && task.getError() == null : task; + + assert this.taskTester.apply(task.getId()); + } @AfterGroups(groups = "live") protected void tearDown() { diff --git a/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/features/VMAsyncClientTest.java b/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/features/VMAsyncClientTest.java index e8890bf6dd..7542ef1801 100644 --- a/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/features/VMAsyncClientTest.java +++ b/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/features/VMAsyncClientTest.java @@ -141,6 +141,40 @@ public class VMAsyncClientTest extends BaseVPDCAsyncClientTest { checkFilters(request); } + + public void testPowerOffVM() throws SecurityException, NoSuchMethodException, IOException { + Method method = VMAsyncClient.class.getMethod("powerOffVM", URI.class); + HttpRequest request = processor.createRequest(method, URI + .create("https://api.symphonyvpdc.savvis.net/rest/api/v0.8/org/11/vdc/22/vApp/33")); + + assertRequestLineEquals(request, + "POST https://api.symphonyvpdc.savvis.net/rest/api/v0.8/org/11/vdc/22/vApp/33/action/powerOff HTTP/1.1"); + assertNonPayloadHeadersEqual(request, ""); + assertPayloadEquals(request, null, null, false); + + assertResponseParserClassEquals(method, request, ParseSax.class); + assertSaxResponseParserClassEquals(method, TaskHandler.class); + assertExceptionParserClassEquals(method, ReturnNullOnNotFoundOr404.class); + + checkFilters(request); + } + + public void testPowerOnVM() throws SecurityException, NoSuchMethodException, IOException { + Method method = VMAsyncClient.class.getMethod("powerOnVM", URI.class); + HttpRequest request = processor.createRequest(method, URI + .create("https://api.symphonyvpdc.savvis.net/rest/api/v0.8/org/11/vdc/22/vApp/33")); + + assertRequestLineEquals(request, + "POST https://api.symphonyvpdc.savvis.net/rest/api/v0.8/org/11/vdc/22/vApp/33/action/powerOn HTTP/1.1"); + assertNonPayloadHeadersEqual(request, ""); + assertPayloadEquals(request, null, null, false); + + assertResponseParserClassEquals(method, request, ParseSax.class); + assertSaxResponseParserClassEquals(method, TaskHandler.class); + assertExceptionParserClassEquals(method, ReturnNullOnNotFoundOr404.class); + + checkFilters(request); + } @Override protected TypeLiteral> createTypeLiteral() { diff --git a/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/features/VMClientLiveTest.java b/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/features/VMClientLiveTest.java index ed22798413..3b7a37ca54 100644 --- a/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/features/VMClientLiveTest.java +++ b/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/features/VMClientLiveTest.java @@ -21,6 +21,7 @@ package org.jclouds.savvis.vpdc.features; import static com.google.common.base.Preconditions.checkNotNull; import static org.testng.Assert.assertEquals; +import java.net.URI; import java.util.concurrent.TimeUnit; import org.jclouds.cim.OSType; @@ -34,7 +35,10 @@ import org.jclouds.savvis.vpdc.domain.Resource; import org.jclouds.savvis.vpdc.domain.Task; import org.jclouds.savvis.vpdc.domain.VDC; import org.jclouds.savvis.vpdc.domain.VM; +import org.jclouds.savvis.vpdc.domain.VM.Status; import org.jclouds.savvis.vpdc.domain.VMSpec; +import org.jclouds.savvis.vpdc.options.GetVMOptions; +import org.jclouds.savvis.vpdc.reference.VCloudMediaType; import org.jclouds.ssh.SshClient; import org.jclouds.util.InetAddresses2; import org.testng.annotations.AfterGroups; @@ -114,7 +118,7 @@ public class VMClientLiveTest extends BaseVPDCClientLiveTest { .operatingSystem(os).memoryInGig(2).addDataDrive("/data01", 25).build()); // make sure there's no error - assert task.getId() != null && task.getError() != null : task; + assert task.getId() != null && task.getError() == null : task; assert this.taskTester.apply(task.getId()); vm = restContext.getApi().getBrowsingClient().getVMInVDC(billingSiteId, vpdcId, task.getOwner().getId()); @@ -148,6 +152,88 @@ public class VMClientLiveTest extends BaseVPDCClientLiveTest { } } + public void testPowerOffVM() throws Exception { + billingSiteId = restContext.getApi().getBrowsingClient().getOrg(null).getId();// default + vpdcId = Iterables.find(restContext.getApi().getBrowsingClient().getOrg(billingSiteId).getVDCs(), + new Predicate() { + + // try to find the first VDC owned by the current user + // check here for what the email property might be, or in + // the jclouds-wire.log + @Override + public boolean apply(Resource arg0) { + String description = restContext.getApi().getBrowsingClient().getVDCInOrg(billingSiteId, + arg0.getId()).getDescription(); + return description.indexOf(email) != -1; + } + + }).getId(); + + VDC vpdc = restContext.getApi().getBrowsingClient().getVDCInOrg(billingSiteId, vpdcId); + URI vmURI = Iterables.find(vpdc.getResourceEntities(), new Predicate() { + @Override + public boolean apply(Resource arg0) { + if(VCloudMediaType.VAPP_XML.equals(arg0.getType())){ + VM response1 = restContext.getApi().getBrowsingClient().getVM(arg0.getHref(), (GetVMOptions[]) null); + System.out.printf("powering off vm - %s%n", response1.getName()); + if(response1.getStatus().equals(Status.ON)){ + return true; + } + } + return false; + } + + }).getHref(); + + Task task = client.powerOffVM(vmURI); + + // make sure there's no error + assert task.getId() != null && task.getError() == null : task; + + assert this.taskTester.apply(task.getId()); + } + + public void testPowerOnVM() throws Exception { + billingSiteId = restContext.getApi().getBrowsingClient().getOrg(null).getId();// default + vpdcId = Iterables.find(restContext.getApi().getBrowsingClient().getOrg(billingSiteId).getVDCs(), + new Predicate() { + + // try to find the first VDC owned by the current user + // check here for what the email property might be, or in + // the jclouds-wire.log + @Override + public boolean apply(Resource arg0) { + String description = restContext.getApi().getBrowsingClient().getVDCInOrg(billingSiteId, + arg0.getId()).getDescription(); + return description.indexOf(email) != -1; + } + + }).getId(); + + VDC vpdc = restContext.getApi().getBrowsingClient().getVDCInOrg(billingSiteId, vpdcId); + URI vmURI = Iterables.find(vpdc.getResourceEntities(), new Predicate() { + @Override + public boolean apply(Resource arg0) { + if(VCloudMediaType.VAPP_XML.equals(arg0.getType())){ + VM response1 = restContext.getApi().getBrowsingClient().getVM(arg0.getHref(), (GetVMOptions[]) null); + System.out.printf("powering on vm - %s%n", response1.getName()); + if(response1.getStatus().equals(Status.OFF)){ + return true; + } + } + return false; + } + + }).getHref(); + + Task task = client.powerOnVM(vmURI); + + // make sure there's no error + assert task.getId() != null && task.getError() == null : task; + + assert this.taskTester.apply(task.getId()); + } + @AfterGroups(groups = "live") protected void tearDown() { if (vm != null) { diff --git a/providers/savvis-symphonyvpdc/src/test/resources/firewallService-default.xml b/providers/savvis-symphonyvpdc/src/test/resources/firewallService-default.xml new file mode 100644 index 0000000000..5a0e642ced --- /dev/null +++ b/providers/savvis-symphonyvpdc/src/test/resources/firewallService-default.xml @@ -0,0 +1 @@ +trueServer Tier Firewall RuleSERVER_TIER_FIREWALLnoallowtrue22VM Tier01internet \ No newline at end of file diff --git a/providers/slicehost/src/test/java/org/jclouds/slicehost/compute/SlicehostComputeServiceLiveTest.java b/providers/slicehost/src/test/java/org/jclouds/slicehost/compute/SlicehostComputeServiceLiveTest.java index add4ebab51..fc8c04d84b 100644 --- a/providers/slicehost/src/test/java/org/jclouds/slicehost/compute/SlicehostComputeServiceLiveTest.java +++ b/providers/slicehost/src/test/java/org/jclouds/slicehost/compute/SlicehostComputeServiceLiveTest.java @@ -37,7 +37,7 @@ import org.testng.annotations.Test; * * @author Adrian Cole */ -@Test(groups = "live", enabled = true, sequential = true) +@Test(groups = "live", enabled = true, singleThreaded = true) public class SlicehostComputeServiceLiveTest extends BaseComputeServiceLiveTest { public SlicehostComputeServiceLiveTest() { provider = "slicehost"; @@ -49,7 +49,7 @@ public class SlicehostComputeServiceLiveTest extends BaseComputeServiceLiveTest assertEquals(defaultTemplate.getImage().getOperatingSystem().is64Bit(), true); assertEquals(defaultTemplate.getImage().getOperatingSystem().getVersion(), "10.04"); assertEquals(defaultTemplate.getImage().getOperatingSystem().getFamily(), OsFamily.UBUNTU); - assertEquals(defaultTemplate.getLocation().getId(), "DFW1"); + assertEquals(defaultTemplate.getLocation().getId(), "slicehost"); assertEquals(getCores(defaultTemplate.getHardware()), 0.25d); } diff --git a/providers/trmk-ecloud/src/test/java/org/jclouds/vcloud/terremark/TerremarkECloudAsyncClientTest.java b/providers/trmk-ecloud/src/test/java/org/jclouds/vcloud/terremark/TerremarkECloudAsyncClientTest.java index 34b49a0880..6bf0e59cc5 100644 --- a/providers/trmk-ecloud/src/test/java/org/jclouds/vcloud/terremark/TerremarkECloudAsyncClientTest.java +++ b/providers/trmk-ecloud/src/test/java/org/jclouds/vcloud/terremark/TerremarkECloudAsyncClientTest.java @@ -48,9 +48,9 @@ import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404; import org.jclouds.rest.internal.RestAnnotationProcessor; import org.jclouds.util.Strings2; import org.jclouds.vcloud.CommonVCloudClient; -import org.jclouds.vcloud.VCloudVersionsAsyncClient; import org.jclouds.vcloud.VCloudExpressAsyncClientTest.VCloudRestClientModuleExtension.TestOrgCatalogItemSupplier; import org.jclouds.vcloud.VCloudExpressAsyncClientTest.VCloudRestClientModuleExtension.TestOrgCatalogSupplier; +import org.jclouds.vcloud.VCloudVersionsAsyncClient; import org.jclouds.vcloud.config.CommonVCloudRestClientModule.OrgVDCSupplier; import org.jclouds.vcloud.domain.AllocationModel; import org.jclouds.vcloud.domain.Capacity; @@ -62,6 +62,7 @@ import org.jclouds.vcloud.domain.VDCStatus; import org.jclouds.vcloud.domain.internal.ReferenceTypeImpl; import org.jclouds.vcloud.domain.network.NetworkConfig; import org.jclouds.vcloud.filters.SetVCloudTokenCookie; +import org.jclouds.vcloud.functions.ParseTaskFromLocationHeader; import org.jclouds.vcloud.options.InstantiateVAppTemplateOptions; import org.jclouds.vcloud.terremark.config.TerremarkECloudRestClientModule; import org.jclouds.vcloud.terremark.domain.Protocol; @@ -104,8 +105,8 @@ import com.google.inject.TypeLiteral; public class TerremarkECloudAsyncClientTest extends RestClientTest { public void testNetwork() throws SecurityException, NoSuchMethodException, IOException { Method method = TerremarkECloudAsyncClient.class.getMethod("getNetwork", URI.class); - HttpRequest request = processor.createRequest(method, URI - .create("https://vcloud.safesecureweb.com/api/v0.8/vdcItem/2")); + HttpRequest request = processor.createRequest(method, + URI.create("https://vcloud.safesecureweb.com/api/v0.8/vdcItem/2")); assertRequestLineEquals(request, "GET https://vcloud.safesecureweb.com/api/v0.8/vdcItem/2 HTTP/1.1"); assertNonPayloadHeadersEqual(request, "Accept: application/vnd.vmware.vcloud.network+xml\n"); @@ -118,9 +119,25 @@ public class TerremarkECloudAsyncClientTest extends RestClientTestnametrueeggs", - "application/vnd.tmrk.vCloud.nodeService+xml", false); + request, + "nametrueeggs", + "application/vnd.tmrk.vCloud.nodeService+xml", false); assertResponseParserClassEquals(method, request, ParseSax.class); assertSaxResponseParserClassEquals(method, NodeHandler.class); assertExceptionParserClassEquals(method, null); @@ -390,16 +414,16 @@ public class TerremarkECloudAsyncClientTest extends RestClientTestnametrue", - "application/vnd.tmrk.vCloud.nodeService+xml", false); + request, + "nametrue", + "application/vnd.tmrk.vCloud.nodeService+xml", false); assertResponseParserClassEquals(method, request, ParseSax.class); assertSaxResponseParserClassEquals(method, NodeHandler.class); assertExceptionParserClassEquals(method, null); @@ -443,7 +467,7 @@ public class TerremarkECloudAsyncClientTest extends RestClientTest get() { - return ImmutableMap. of("org", new TerremarkOrgImpl("org", null, URI - .create("https://vcloud.safesecureweb.com/api/v0.8/org/1"), null, ImmutableMap - . of("catalog", new ReferenceTypeImpl("catalog", - TerremarkECloudMediaType.CATALOG_XML, URI - .create("https://vcloud.safesecureweb.com/api/v0.8/catalog/1"))), ImmutableMap - . of("vdc", new ReferenceTypeImpl("vdc", TerremarkECloudMediaType.VDC_XML, - URI.create("https://vcloud.safesecureweb.com/api/v0.8/vdc/1"))), ImmutableMap - . of(), new ReferenceTypeImpl("tasksList", - TerremarkECloudMediaType.TASKSLIST_XML, URI - .create("https://vcloud.safesecureweb.com/api/v0.8/tasksList/1")), new ReferenceTypeImpl( - "keysList", TerremarkECloudMediaType.KEYSLIST_XML, URI + return ImmutableMap. of( + "org", + new TerremarkOrgImpl("org", null, URI.create("https://vcloud.safesecureweb.com/api/v0.8/org/1"), + null, ImmutableMap. of( + "catalog", + new ReferenceTypeImpl("catalog", TerremarkECloudMediaType.CATALOG_XML, URI + .create("https://vcloud.safesecureweb.com/api/v0.8/catalog/1"))), ImmutableMap + . of( + "vdc", + new ReferenceTypeImpl("vdc", TerremarkECloudMediaType.VDC_XML, URI + .create("https://vcloud.safesecureweb.com/api/v0.8/vdc/1"))), ImmutableMap + . of(), new ReferenceTypeImpl("tasksList", + TerremarkECloudMediaType.TASKSLIST_XML, URI + .create("https://vcloud.safesecureweb.com/api/v0.8/tasksList/1")), + new ReferenceTypeImpl("keysList", TerremarkECloudMediaType.KEYSLIST_XML, URI .create("https://vcloud.safesecureweb.com/api/v0.8/keysList/1")))); } } @@ -643,7 +670,7 @@ public class TerremarkECloudAsyncClientTest extends RestClientTest> vDCtoOrgSupplier) { + @org.jclouds.vcloud.endpoints.VDC Supplier> vDCtoOrgSupplier) { return "vdc"; } @@ -664,21 +691,25 @@ public class TerremarkECloudAsyncClientTest extends RestClientTest> get() { return ImmutableMap.> of("org", - ImmutableMap. of("vdc", new TerremarkVDCImpl("vdc", null, URI - .create("https://vcloud.safesecureweb.com/api/v0.8/vdc/1"), VDCStatus.READY, null, "description", - ImmutableSet. of(), AllocationModel.UNRECOGNIZED, new Capacity("MB", 0, 0, 0, 0), new Capacity( - "MB", 0, 0, 0, 0), new Capacity("MB", 0, 0, 0, 0), ImmutableMap. of( - "vapp", new ReferenceTypeImpl("vapp", "application/vnd.vmware.vcloud.vApp+xml", URI - .create("https://vcloud.safesecureweb.com/api/v0.8/vApp/188849-1")), "network", + ImmutableMap. of( + "vdc", + new TerremarkVDCImpl("vdc", null, URI.create("https://vcloud.safesecureweb.com/api/v0.8/vdc/1"), + VDCStatus.READY, null, "description", ImmutableSet. of(), AllocationModel.UNRECOGNIZED, + new Capacity("MB", 0, 0, 0, 0), new Capacity("MB", 0, 0, 0, 0), new Capacity("MB", 0, 0, 0, 0), + ImmutableMap. of( + "vapp", + new ReferenceTypeImpl("vapp", "application/vnd.vmware.vcloud.vApp+xml", URI + .create("https://vcloud.safesecureweb.com/api/v0.8/vApp/188849-1")), + "network", new ReferenceTypeImpl("network", "application/vnd.vmware.vcloud.vAppTemplate+xml", URI - .create("https://vcloud.safesecureweb.com/api/v0.8/vdcItem/2"))), ImmutableMap + .create("https://vcloud.safesecureweb.com/api/v0.8/vdcItem/2"))), ImmutableMap . of(), 0, 0, 0, false, new ReferenceTypeImpl("catalog", TerremarkVCloudMediaType.CATALOG_XML, URI - .create("https://vcloud.safesecureweb.com/api/v0.8/catalog/1")), - new ReferenceTypeImpl("publicIps", TerremarkVCloudMediaType.PUBLICIPSLIST_XML, URI + .create("https://vcloud.safesecureweb.com/api/v0.8/catalog/1")), + new ReferenceTypeImpl("publicIps", TerremarkVCloudMediaType.PUBLICIPSLIST_XML, URI .create("https://vcloud.safesecureweb.com/api/v0.8/publicIps/1")), new ReferenceTypeImpl( "internetServices", TerremarkVCloudMediaType.INTERNETSERVICESLIST_XML, URI - .create("https://vcloud.safesecureweb.com/api/v0.8/internetServices/1"))))); + .create("https://vcloud.safesecureweb.com/api/v0.8/internetServices/1"))))); } } } diff --git a/providers/trmk-vcloudexpress/src/test/java/org/jclouds/vcloud/terremark/TerremarkVCloudExpressAsyncClientTest.java b/providers/trmk-vcloudexpress/src/test/java/org/jclouds/vcloud/terremark/TerremarkVCloudExpressAsyncClientTest.java index 08bbd70143..f92608bdb6 100644 --- a/providers/trmk-vcloudexpress/src/test/java/org/jclouds/vcloud/terremark/TerremarkVCloudExpressAsyncClientTest.java +++ b/providers/trmk-vcloudexpress/src/test/java/org/jclouds/vcloud/terremark/TerremarkVCloudExpressAsyncClientTest.java @@ -48,9 +48,9 @@ import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404; import org.jclouds.rest.internal.RestAnnotationProcessor; import org.jclouds.util.Strings2; import org.jclouds.vcloud.CommonVCloudClient; -import org.jclouds.vcloud.VCloudVersionsAsyncClient; import org.jclouds.vcloud.VCloudExpressAsyncClientTest.VCloudRestClientModuleExtension.TestOrgCatalogItemSupplier; import org.jclouds.vcloud.VCloudExpressAsyncClientTest.VCloudRestClientModuleExtension.TestOrgCatalogSupplier; +import org.jclouds.vcloud.VCloudVersionsAsyncClient; import org.jclouds.vcloud.domain.AllocationModel; import org.jclouds.vcloud.domain.Capacity; import org.jclouds.vcloud.domain.Catalog; @@ -62,6 +62,7 @@ import org.jclouds.vcloud.domain.VDCStatus; import org.jclouds.vcloud.domain.internal.ReferenceTypeImpl; import org.jclouds.vcloud.domain.network.NetworkConfig; import org.jclouds.vcloud.filters.SetVCloudTokenCookie; +import org.jclouds.vcloud.functions.ParseTaskFromLocationHeader; import org.jclouds.vcloud.options.InstantiateVAppTemplateOptions; import org.jclouds.vcloud.terremark.config.TerremarkVCloudExpressRestClientModule; import org.jclouds.vcloud.terremark.domain.Protocol; @@ -104,8 +105,8 @@ public class TerremarkVCloudExpressAsyncClientTest extends RestClientTestnametrueeggs", - "application/vnd.tmrk.vCloud.nodeService+xml", false); + request, + "nametrueeggs", + "application/vnd.tmrk.vCloud.nodeService+xml", false); assertResponseParserClassEquals(method, request, ParseSax.class); assertSaxResponseParserClassEquals(method, NodeHandler.class); assertExceptionParserClassEquals(method, null); @@ -404,16 +436,16 @@ public class TerremarkVCloudExpressAsyncClientTest extends RestClientTestnametrue", - "application/vnd.tmrk.vCloud.nodeService+xml", false); + request, + "nametrue", + "application/vnd.tmrk.vCloud.nodeService+xml", false); assertResponseParserClassEquals(method, request, ParseSax.class); assertSaxResponseParserClassEquals(method, NodeHandler.class); assertExceptionParserClassEquals(method, null); @@ -453,12 +485,12 @@ public class TerremarkVCloudExpressAsyncClientTest extends RestClientTest createContextSpec() { return new RestContextFactory().createContextSpec("trmk-vcloudexpress", "identity", "credential", - new Properties()); + new Properties()); } @RequiresHttp @@ -604,7 +636,7 @@ public class TerremarkVCloudExpressAsyncClientTest extends RestClientTest get() { - return ImmutableMap. of("org", new TerremarkOrgImpl("org", null, URI - .create("https://vcloud.safesecureweb.com/api/v0.8/org/1"), null, ImmutableMap - . of("catalog", new ReferenceTypeImpl("catalog", - TerremarkVCloudExpressMediaType.CATALOG_XML, URI - .create("https://vcloud.safesecureweb.com/api/v0.8/catalog/1"))), ImmutableMap - . of("vdc", new ReferenceTypeImpl("vdc", - TerremarkVCloudExpressMediaType.VDC_XML, URI - .create("https://vcloud.safesecureweb.com/api/v0.8/vdc/1"))), ImmutableMap - . of(), new ReferenceTypeImpl("tasksList", - TerremarkVCloudExpressMediaType.TASKSLIST_XML, URI - .create("https://vcloud.safesecureweb.com/api/v0.8/tasksList/1")), new ReferenceTypeImpl( - "keysList", TerremarkVCloudExpressMediaType.KEYSLIST_XML, URI + return ImmutableMap. of( + "org", + new TerremarkOrgImpl("org", null, URI.create("https://vcloud.safesecureweb.com/api/v0.8/org/1"), + null, ImmutableMap. of( + "catalog", + new ReferenceTypeImpl("catalog", TerremarkVCloudExpressMediaType.CATALOG_XML, URI + .create("https://vcloud.safesecureweb.com/api/v0.8/catalog/1"))), ImmutableMap + . of( + "vdc", + new ReferenceTypeImpl("vdc", TerremarkVCloudExpressMediaType.VDC_XML, URI + .create("https://vcloud.safesecureweb.com/api/v0.8/vdc/1"))), ImmutableMap + . of(), new ReferenceTypeImpl("tasksList", + TerremarkVCloudExpressMediaType.TASKSLIST_XML, URI + .create("https://vcloud.safesecureweb.com/api/v0.8/tasksList/1")), + new ReferenceTypeImpl("keysList", TerremarkVCloudExpressMediaType.KEYSLIST_XML, URI .create("https://vcloud.safesecureweb.com/api/v0.8/keysList/1")))); } } @@ -703,22 +738,25 @@ public class TerremarkVCloudExpressAsyncClientTest extends RestClientTest> get() { return ImmutableMap.> of("org", - ImmutableMap. of("vdc", new TerremarkVDCImpl("vdc", null, URI - .create("https://vcloud.safesecureweb.com/api/v0.8/vdc/1"), VDCStatus.READY, null, "description", - ImmutableSet. of(), AllocationModel.UNRECOGNIZED, new Capacity("MB", 0, 0, 0, 0), - new Capacity("MB", 0, 0, 0, 0), new Capacity("MB", 0, 0, 0, 0), - ImmutableMap. of("vapp", new ReferenceTypeImpl("vapp", - "application/vnd.vmware.vcloud.vApp+xml", URI - .create("https://vcloud.safesecureweb.com/api/v0.8/vApp/188849-1")), "network", + ImmutableMap. of( + "vdc", + new TerremarkVDCImpl("vdc", null, URI.create("https://vcloud.safesecureweb.com/api/v0.8/vdc/1"), + VDCStatus.READY, null, "description", ImmutableSet. of(), AllocationModel.UNRECOGNIZED, + new Capacity("MB", 0, 0, 0, 0), new Capacity("MB", 0, 0, 0, 0), new Capacity("MB", 0, 0, 0, 0), + ImmutableMap. of( + "vapp", + new ReferenceTypeImpl("vapp", "application/vnd.vmware.vcloud.vApp+xml", URI + .create("https://vcloud.safesecureweb.com/api/v0.8/vApp/188849-1")), + "network", new ReferenceTypeImpl("network", "application/vnd.vmware.vcloud.vAppTemplate+xml", URI - .create("https://vcloud.safesecureweb.com/api/v0.8/vdcItem/2"))), ImmutableMap + .create("https://vcloud.safesecureweb.com/api/v0.8/vdcItem/2"))), ImmutableMap . of(), 0, 0, 0, false, new ReferenceTypeImpl("catalog", TerremarkVCloudExpressMediaType.CATALOG_XML, URI - .create("https://vcloud.safesecureweb.com/api/v0.8/catalog/1")), - new ReferenceTypeImpl("publicIps", TerremarkVCloudExpressMediaType.PUBLICIPSLIST_XML, URI + .create("https://vcloud.safesecureweb.com/api/v0.8/catalog/1")), + new ReferenceTypeImpl("publicIps", TerremarkVCloudExpressMediaType.PUBLICIPSLIST_XML, URI .create("https://vcloud.safesecureweb.com/api/v0.8/publicIps/1")), new ReferenceTypeImpl( "internetServices", TerremarkVCloudExpressMediaType.INTERNETSERVICESLIST_XML, URI - .create("https://vcloud.safesecureweb.com/api/v0.8/internetServices/1"))))); + .create("https://vcloud.safesecureweb.com/api/v0.8/internetServices/1"))))); } } @@ -734,7 +772,7 @@ public class TerremarkVCloudExpressAsyncClientTest extends RestClientTest> vDCtoOrgSupplier) { + @org.jclouds.vcloud.endpoints.VDC Supplier> vDCtoOrgSupplier) { return "vdc"; } diff --git a/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackAsyncClient.java b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackAsyncClient.java index 8c37b6a3e6..efd5c11d28 100644 --- a/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackAsyncClient.java +++ b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackAsyncClient.java @@ -18,6 +18,7 @@ */ package org.jclouds.cloudstack; +import org.jclouds.cloudstack.features.AccountAsyncClient; import org.jclouds.cloudstack.features.AddressAsyncClient; import org.jclouds.cloudstack.features.AsyncJobAsyncClient; import org.jclouds.cloudstack.features.ConfigurationAsyncClient; @@ -127,4 +128,10 @@ public interface CloudStackAsyncClient { */ @Delegate ConfigurationAsyncClient getConfigurationClient(); + + /** + * Provides asynchronous access to Account features. + */ + @Delegate + AccountAsyncClient getAccountClient(); } diff --git a/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackClient.java b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackClient.java index 6d6e91fefa..51fe909a5e 100644 --- a/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackClient.java +++ b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackClient.java @@ -20,6 +20,7 @@ package org.jclouds.cloudstack; import java.util.concurrent.TimeUnit; +import org.jclouds.cloudstack.features.AccountClient; import org.jclouds.cloudstack.features.AddressClient; import org.jclouds.cloudstack.features.AsyncJobClient; import org.jclouds.cloudstack.features.ConfigurationClient; @@ -130,4 +131,10 @@ public interface CloudStackClient { */ @Delegate ConfigurationClient getConfigurationClient(); + + /** + * Provides synchronous access to Account features. + */ + @Delegate + AccountClient getAccountClient(); } diff --git a/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackParserModule.java b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackParserModule.java new file mode 100644 index 0000000000..d04ecc06a0 --- /dev/null +++ b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackParserModule.java @@ -0,0 +1,140 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.cloudstack.config; + +import java.lang.reflect.Type; +import java.util.Map; +import java.util.Set; + +import javax.inject.Singleton; + +import org.jclouds.cloudstack.domain.Account; +import org.jclouds.cloudstack.domain.Account.State; +import org.jclouds.cloudstack.domain.User; + +import com.google.common.collect.ImmutableMap; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import com.google.gson.annotations.SerializedName; +import com.google.inject.AbstractModule; +import com.google.inject.TypeLiteral; + +/** + * Configures the cloudstack parsers. + * + * @author Adrian Cole + */ +public class CloudStackParserModule extends AbstractModule { + + @Singleton + public static class BreakGenericSetAdapter implements JsonSerializer, JsonDeserializer { + + public JsonElement serialize(Account src, Type typeOfSrc, JsonSerializationContext context) { + return context.serialize(src); + } + + public Account deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + return apply(context. deserialize(json, AccountInternal.class)); + } + + public Account apply(AccountInternal in) { + return Account.builder().id(in.id).type(in.type).domain(in.domain).domainId(in.domainId) + .IPsAvailable(nullIfUnlimited(in.IPsAvailable)).IPLimit(nullIfUnlimited(in.IPLimit)).IPs(in.IPs) + .cleanupRequired(in.cleanupRequired).name(in.name).receivedBytes(in.receivedBytes) + .sentBytes(in.sentBytes).snapshotsAvailable(nullIfUnlimited(in.snapshotsAvailable)) + .snapshotLimit(nullIfUnlimited(in.snapshotLimit)).snapshots(in.snapshots).state(in.state) + .templatesAvailable(nullIfUnlimited(in.templatesAvailable)) + .templateLimit(nullIfUnlimited(in.templateLimit)).templates(in.templates) + .VMsAvailable(nullIfUnlimited(in.VMsAvailable)).VMLimit(nullIfUnlimited(in.VMLimit)) + .VMsRunning(in.VMsRunning).VMsStopped(in.VMsStopped).VMs(in.VMs) + .volumesAvailable(nullIfUnlimited(in.volumesAvailable)).volumeLimit(nullIfUnlimited(in.volumeLimit)) + .volumes(in.volumes).users(in.users).build(); + } + + static final class AccountInternal { + private long id; + @SerializedName("accounttype") + private Account.Type type; + private String domain; + @SerializedName("domainid") + private long domainId; + @SerializedName("ipavailable") + private String IPsAvailable; + @SerializedName("iplimit") + private String IPLimit; + @SerializedName("iptotal") + private long IPs; + @SerializedName("iscleanuprequired") + private boolean cleanupRequired; + private String name; + @SerializedName("receivedbytes") + private long receivedBytes; + @SerializedName("sentBytes") + private long sentBytes; + @SerializedName("snapshotavailable") + private String snapshotsAvailable; + @SerializedName("snapshotlimit") + private String snapshotLimit; + @SerializedName("snapshottotal") + private long snapshots; + @SerializedName("state") + private State state; + @SerializedName("templateavailable") + private String templatesAvailable; + @SerializedName("templatelimit") + private String templateLimit; + @SerializedName("templatetotal") + private long templates; + @SerializedName("vmavailable") + private String VMsAvailable; + @SerializedName("vmlimit") + private String VMLimit; + @SerializedName("vmrunning") + private long VMsRunning; + @SerializedName("vmstopped") + private long VMsStopped; + @SerializedName("vmtotal") + private long VMs; + @SerializedName("volumeavailable") + private String volumesAvailable; + @SerializedName("volumelimit") + private String volumeLimit; + @SerializedName("volumetotal") + private long volumes; + @SerializedName("user") + private Set users; + } + + private static Long nullIfUnlimited(String in) { + return in == null || "Unlimited".equals(in) ? null : new Long(in); + } + } + + @Override + protected void configure() { + bind(new TypeLiteral>() { + }).toInstance(ImmutableMap. of(Account.class, new BreakGenericSetAdapter())); + } + +} diff --git a/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackRestClientModule.java b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackRestClientModule.java index 33f7d30657..4c493495c8 100644 --- a/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackRestClientModule.java +++ b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackRestClientModule.java @@ -22,6 +22,8 @@ import java.util.Map; import org.jclouds.cloudstack.CloudStackAsyncClient; import org.jclouds.cloudstack.CloudStackClient; +import org.jclouds.cloudstack.features.AccountAsyncClient; +import org.jclouds.cloudstack.features.AccountClient; import org.jclouds.cloudstack.features.AddressAsyncClient; import org.jclouds.cloudstack.features.AddressClient; import org.jclouds.cloudstack.features.AsyncJobAsyncClient; @@ -75,21 +77,22 @@ import com.google.common.collect.ImmutableMap; public class CloudStackRestClientModule extends RestClientModule { public static final Map, Class> DELEGATE_MAP = ImmutableMap., Class> builder()// - .put(ZoneClient.class, ZoneAsyncClient.class)// - .put(TemplateClient.class, TemplateAsyncClient.class)// - .put(OfferingClient.class, OfferingAsyncClient.class)// - .put(NetworkClient.class, NetworkAsyncClient.class)// - .put(VirtualMachineClient.class, VirtualMachineAsyncClient.class)// - .put(SecurityGroupClient.class, SecurityGroupAsyncClient.class)// - .put(AsyncJobClient.class, AsyncJobAsyncClient.class)// - .put(AddressClient.class, AddressAsyncClient.class)// - .put(NATClient.class, NATAsyncClient.class)// - .put(FirewallClient.class, FirewallAsyncClient.class)// - .put(LoadBalancerClient.class, LoadBalancerAsyncClient.class)// - .put(GuestOSClient.class, GuestOSAsyncClient.class)// - .put(HypervisorClient.class, HypervisorAsyncClient.class)// - .put(ConfigurationClient.class, ConfigurationAsyncClient.class)// - .build(); + .put(ZoneClient.class, ZoneAsyncClient.class)// + .put(TemplateClient.class, TemplateAsyncClient.class)// + .put(OfferingClient.class, OfferingAsyncClient.class)// + .put(NetworkClient.class, NetworkAsyncClient.class)// + .put(VirtualMachineClient.class, VirtualMachineAsyncClient.class)// + .put(SecurityGroupClient.class, SecurityGroupAsyncClient.class)// + .put(AsyncJobClient.class, AsyncJobAsyncClient.class)// + .put(AddressClient.class, AddressAsyncClient.class)// + .put(NATClient.class, NATAsyncClient.class)// + .put(FirewallClient.class, FirewallAsyncClient.class)// + .put(LoadBalancerClient.class, LoadBalancerAsyncClient.class)// + .put(GuestOSClient.class, GuestOSAsyncClient.class)// + .put(HypervisorClient.class, HypervisorAsyncClient.class)// + .put(ConfigurationClient.class, ConfigurationAsyncClient.class)// + .put(AccountClient.class, AccountAsyncClient.class)// + .build(); public CloudStackRestClientModule() { super(CloudStackClient.class, CloudStackAsyncClient.class, DELEGATE_MAP); @@ -106,6 +109,7 @@ public class CloudStackRestClientModule extends RestClientModule + * + * ==================================================================== + * 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.cloudstack.domain; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Map; +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.base.CaseFormat; +import com.google.common.base.Function; +import com.google.common.collect.ForwardingSet; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Maps; + +/** + * + * @author Adrian Cole + */ +public class Account extends ForwardingSet implements Comparable { + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private long id; + private Type type; + private String domain; + private long domainId; + private Long IPsAvailable; + private Long IPLimit; + private long IPs; + private boolean cleanupRequired; + private String name; + private long receivedBytes; + private long sentBytes; + private Long snapshotsAvailable; + private Long snapshotLimit; + private long snapshots; + private State state; + private Long templatesAvailable; + private Long templateLimit; + private long templates; + private Long VMsAvailable; + private Long VMLimit; + private long VMsRunning; + private long VMsStopped; + private long VMs; + private Long volumesAvailable; + private Long volumeLimit; + private long volumes; + private Set users = ImmutableSet.of(); + + public Builder id(long id) { + this.id = id; + return this; + } + + public Builder type(Type type) { + this.type = type; + return this; + } + + public Builder domain(String domain) { + this.domain = domain; + return this; + } + + public Builder domainId(long domainId) { + this.domainId = domainId; + return this; + } + + public Builder IPsAvailable(Long IPsAvailable) { + this.IPsAvailable = IPsAvailable; + return this; + } + + public Builder IPLimit(Long IPLimit) { + this.IPLimit = IPLimit; + return this; + } + + public Builder IPs(long IPs) { + this.IPs = IPs; + return this; + } + + public Builder cleanupRequired(boolean cleanupRequired) { + this.cleanupRequired = cleanupRequired; + return this; + } + + public Builder name(String name) { + this.name = name; + return this; + } + + public Builder receivedBytes(long receivedBytes) { + this.receivedBytes = receivedBytes; + return this; + } + + public Builder sentBytes(long sentBytes) { + this.sentBytes = sentBytes; + return this; + } + + public Builder snapshotsAvailable(Long snapshotsAvailable) { + this.snapshotsAvailable = snapshotsAvailable; + return this; + } + + public Builder snapshotLimit(Long snapshotLimit) { + this.snapshotLimit = snapshotLimit; + return this; + } + + public Builder snapshots(long snapshots) { + this.snapshots = snapshots; + return this; + } + + public Builder state(State state) { + this.state = state; + return this; + } + + public Builder templatesAvailable(Long templatesAvailable) { + this.templatesAvailable = templatesAvailable; + return this; + } + + public Builder templateLimit(Long templateLimit) { + this.templateLimit = templateLimit; + return this; + } + + public Builder templates(long templates) { + this.templates = templates; + return this; + } + + public Builder VMsAvailable(Long VMsAvailable) { + this.VMsAvailable = VMsAvailable; + return this; + } + + public Builder VMLimit(Long VMLimit) { + this.VMLimit = VMLimit; + return this; + } + + public Builder VMsRunning(long VMsRunning) { + this.VMsRunning = VMsRunning; + return this; + } + + public Builder VMsStopped(long VMsStopped) { + this.VMsStopped = VMsStopped; + return this; + } + + public Builder VMs(long VMs) { + this.VMs = VMs; + return this; + } + + public Builder volumesAvailable(Long volumesAvailable) { + this.volumesAvailable = volumesAvailable; + return this; + } + + public Builder volumeLimit(Long volumeLimit) { + this.volumeLimit = volumeLimit; + return this; + } + + public Builder volumes(long volumes) { + this.volumes = volumes; + return this; + } + + public Builder users(Set users) { + this.users = ImmutableSet.copyOf(checkNotNull(users, "users")); + return this; + } + + public Account build() { + return new Account(id, type, domain, domainId, IPsAvailable, IPLimit, IPs, cleanupRequired, name, + receivedBytes, sentBytes, snapshotsAvailable, snapshotLimit, snapshots, state, templatesAvailable, + templateLimit, templates, VMsAvailable, VMLimit, VMsRunning, VMsStopped, VMs, volumesAvailable, + volumeLimit, volumes, users); + } + + } + + public static enum State { + ENABLED, DISABLED, LOCKED, UNRECOGNIZED; + @Override + public String toString() { + return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_HYPHEN, name()); + } + + public static State fromValue(String state) { + try { + return valueOf(CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_UNDERSCORE, checkNotNull(state, "state"))); + } catch (IllegalArgumentException e) { + return UNRECOGNIZED; + } + } + + } + + public static enum Type { + /** + * API access for all the resources associated with their account. There + * may be many users in a domain, many domains in a deployment, and many + * users in a deployment. This is typically the end user + */ + USER(0), + /** + * full API access. This is typically a service administrator or code that + * executes with complete trust in the service operator's environment. + */ + ADMIN(1), + /** + * full API access within a domain. This is the most privileged user that + * a given customer has. This may be a reseller for the service provider. + */ + DOMAIN_ADMIN(2), UNRECOGNIZED(Integer.MAX_VALUE); + + private int code; + + private static final Map INDEX = Maps.uniqueIndex(ImmutableSet.copyOf(Type.values()), + new Function() { + + @Override + public Integer apply(Type input) { + return input.code; + } + + }); + + Type(int code) { + this.code = code; + } + + @Override + public String toString() { + return name(); + } + + public static Type fromValue(String type) { + Integer code = new Integer(checkNotNull(type, "type")); + return INDEX.containsKey(code) ? INDEX.get(code) : UNRECOGNIZED; + } + + } + + private long id; + private Type type; + private String domain; + private long domainId; + private Long IPsAvailable; + private Long IPLimit; + private long IPs; + private boolean cleanupRequired; + private String name; + private long receivedBytes; + private long sentBytes; + private Long snapshotsAvailable; + private Long snapshotLimit; + private long snapshots; + private State state; + private Long templatesAvailable; + private Long templateLimit; + private long templates; + private Long VMsAvailable; + private Long VMLimit; + private long VMsRunning; + private long VMsStopped; + private long VMs; + private Long volumesAvailable; + private Long volumeLimit; + private long volumes; + private Set users; + + public Account(long id, Type type, String domain, long domainId, Long IPsAvailable, Long IPLimit, long iPs, + boolean cleanupRequired, String name, long receivedBytes, long sentBytes, Long snapshotsAvailable, + Long snapshotLimit, long snapshots, org.jclouds.cloudstack.domain.Account.State state, + Long templatesAvailable, Long templateLimit, long templates, Long VMsAvailable, Long VMLimit, long vMsRunning, + long vMsStopped, long vMs, Long volumesAvailable, Long volumeLimit, long volumes, Set users) { + this.id = id; + this.type = type; + this.domain = domain; + this.domainId = domainId; + this.IPsAvailable = IPsAvailable; + this.IPLimit = IPLimit; + this.IPs = iPs; + this.cleanupRequired = cleanupRequired; + this.name = name; + this.receivedBytes = receivedBytes; + this.sentBytes = sentBytes; + this.snapshotsAvailable = snapshotsAvailable; + this.snapshotLimit = snapshotLimit; + this.snapshots = snapshots; + this.state = state; + this.templatesAvailable = templatesAvailable; + this.templateLimit = templateLimit; + this.templates = templates; + this.VMsAvailable = VMsAvailable; + this.VMLimit = VMLimit; + this.VMsRunning = vMsRunning; + this.VMsStopped = vMsStopped; + this.VMs = vMs; + this.volumesAvailable = volumesAvailable; + this.volumeLimit = volumeLimit; + this.volumes = volumes; + this.users = ImmutableSet.copyOf(checkNotNull(users, "users")); + } + + /** + * present only for serializer + * + */ + Account() { + + } + + /** + * + * @return the id of the account + */ + public long getId() { + return id; + } + + /** + * + * @return the name of the account + */ + + public String getName() { + return name; + } + + /** + * + * @return account type (admin, domain-admin, user) + */ + public Type getType() { + return type; + } + + /** + * + * @return name of the Domain the account belongs to + */ + public String getDomain() { + return domain; + } + + /** + * + * @return id of the Domain the account belongs to + */ + public long getDomainId() { + return domainId; + } + + /** + * + * @return true if the account requires cleanup + */ + public boolean isCleanupRequired() { + return cleanupRequired; + } + + /** + * + * @return the list of users associated with account + */ + public Set getUsers() { + return users; + } + + /** + * + * @return the total number of public ip addresses available for this account + * to acquire, or null if unlimited + */ + @Nullable + public Long getIPsAvailable() { + return IPsAvailable; + } + + /** + * + * @return the total number of public ip addresses this account can acquire, + * or null if unlimited + */ + @Nullable + public Long getIPLimit() { + return IPLimit; + } + + /** + * + * @return the total number of public ip addresses allocated for this account + */ + public long getIPs() { + return IPs; + } + + /** + * + * @return the total number of network traffic bytes received + */ + public long getReceivedBytes() { + return receivedBytes; + } + + /** + * + * @return the total number of network traffic bytes sent + */ + public long getSentBytes() { + return sentBytes; + } + + /** + * + * @return the total number of snapshots available for this account, or null + * if unlimited + */ + @Nullable + public Long getSnapshotsAvailable() { + return snapshotsAvailable; + } + + /** + * + * @return the total number of snapshots which can be stored by this account, + * or null if unlimited + */ + @Nullable + public Long getSnapshotLimit() { + return snapshotLimit; + } + + /** + * + * @return the total number of snapshots stored by this account + */ + public long getSnapshots() { + return snapshots; + } + + /** + * + * @return the state of the account + */ + public State getState() { + return state; + } + + /** + * + * @return the total number of templates available to be created by this + * account, or null if unlimited + */ + @Nullable + public Long getTemplatesAvailable() { + return templatesAvailable; + } + + /** + * + * @return the total number of templates which can be created by this + * account, or null if unlimited + */ + @Nullable + public Long getTemplateLimit() { + return templateLimit; + } + + /** + * + * @return the total number of templates which have been created by this + * account + */ + public long getTemplates() { + return templates; + } + + /** + * + * @return the total number of virtual machines available for this account to + * acquire, or null if unlimited + */ + @Nullable + public Long getVMsAvailable() { + return VMsAvailable; + } + + /** + * + * @return the total number of virtual machines that can be deployed by this + * account, or null if unlimited + */ + @Nullable + public Long getVMLimit() { + return VMLimit; + } + + /** + * + * @return the total number of virtual machines running for this account + */ + public long getVMsRunning() { + return VMsRunning; + } + + /** + * + * @return the total number of virtual machines stopped for this account + */ + public long getVMsStopped() { + return VMsStopped; + } + + /** + * + * @return the total number of virtual machines deployed by this account + */ + public long getVMs() { + return VMs; + } + + /** + * + * @return the total volume available for this account, or null if unlimited + */ + @Nullable + public Long getVolumesAvailable() { + return volumesAvailable; + } + + /** + * + * @return the total volume which can be used by this account, or null if + * unlimited + */ + @Nullable + public Long getVolumeLimit() { + return volumeLimit; + } + + /** + * + * @return the total volume being used by this account + */ + public long getVolumes() { + return volumes; + } + + @Override + public int compareTo(Account arg0) { + return new Long(id).compareTo(arg0.getId()); + } + + @Override + public String toString() { + return String + .format( + "[id=%s, name=%s, type=%s, state=%s, domain=%s, domainId=%s, cleanupRequired=%s, sentBytes=%s, receivedBytes=%s, IPs=%s, IPsAvailable=%s, IPLimit=%s, VMs=%s, VMsAvailable=%s, VMsRunning=%s, VMsStopped=%s, VMLimit=%s, snapshots=%s, snapshotLimit=%s, snapshotsAvailable=%s, templateLimit=%s, templates=%s, templatesAvailable=%s, volumes=%s, volumeLimit=%s, volumesAvailable=%s, users=%s]", + id, name, type, state, domain, domainId, cleanupRequired, sentBytes, receivedBytes, IPs, + IPsAvailable, IPLimit, VMs, VMsAvailable, VMsRunning, VMsStopped, VMLimit, snapshots, snapshotLimit, + snapshotsAvailable, templateLimit, templates, templatesAvailable, volumes, volumeLimit, + volumesAvailable, users); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (int) (domainId ^ (domainId >>> 32)); + result = prime * result + (int) (id ^ (id >>> 32)); + result = prime * result + ((name == null) ? 0 : name.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; + Account other = (Account) obj; + if (domainId != other.domainId) + 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; + return true; + } + + @Override + protected Set delegate() { + return users; + } +} diff --git a/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Network.java b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Network.java index 2ad9c7c472..dfce71a44b 100644 --- a/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Network.java +++ b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Network.java @@ -23,11 +23,13 @@ import static com.google.common.base.Preconditions.checkNotNull; import java.net.URI; import java.util.List; import java.util.Set; +import java.util.SortedSet; import javax.annotation.Nullable; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSortedSet; import com.google.gson.annotations.SerializedName; /** @@ -267,7 +269,8 @@ public class Network implements Comparable { @SerializedName("zoneid") private long zoneId; @SerializedName("service") - private Set services = ImmutableSet. of(); + // so tests and serialization comes out expected + private SortedSet services = ImmutableSortedSet. of(); /** * present only for serializer @@ -311,7 +314,7 @@ public class Network implements Comparable { this.VLAN = vLAN; this.trafficType = trafficType; this.zoneId = zoneId; - this.services = ImmutableSet.copyOf(checkNotNull(services, "services")); + this.services = ImmutableSortedSet.copyOf(checkNotNull(services, "services")); } /** diff --git a/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/NetworkOffering.java b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/NetworkOffering.java index 6f4581126a..cd70eea1a6 100644 --- a/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/NetworkOffering.java +++ b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/NetworkOffering.java @@ -50,6 +50,7 @@ public class NetworkOffering implements Comparable { private boolean isDefault; private boolean supportsVLAN; private TrafficType trafficType; + private GuestIPType guestIPType; private Set tags = ImmutableSet.of(); public Builder id(long id) { @@ -102,6 +103,11 @@ public class NetworkOffering implements Comparable { return this; } + public Builder guestIPType(GuestIPType guestIPType) { + this.guestIPType = guestIPType; + return this; + } + public Builder tags(Set tags) { this.tags = ImmutableSet.copyOf(checkNotNull(tags, "tags")); return this; @@ -109,7 +115,7 @@ public class NetworkOffering implements Comparable { public NetworkOffering build() { return new NetworkOffering(id, name, displayText, created, availability, supportsVLAN, maxConnections, - isDefault, trafficType, networkRate, tags); + isDefault, trafficType, guestIPType, networkRate, tags); } } @@ -127,13 +133,15 @@ public class NetworkOffering implements Comparable { private boolean supportsVLAN; @SerializedName("traffictype") private TrafficType trafficType; + @SerializedName("guestiptype") + private GuestIPType guestIPType; @SerializedName("networkrate") private int networkRate = -1; private String tags; public NetworkOffering(long id, String name, String displayText, @Nullable Date created, String availability, boolean supportsVLAN, @Nullable Integer maxConnections, boolean isDefault, TrafficType trafficType, - int networkRate, Set tags) { + GuestIPType guestIPType, int networkRate, Set tags) { this.id = id; this.name = name; this.displayText = displayText; @@ -143,6 +151,7 @@ public class NetworkOffering implements Comparable { this.maxConnections = maxConnections; this.isDefault = isDefault; this.trafficType = trafficType; + this.guestIPType = guestIPType; this.networkRate = networkRate; this.tags = tags.size() == 0 ? null : Joiner.on(',').join(tags); } @@ -207,7 +216,8 @@ public class NetworkOffering implements Comparable { /** * - * @return the max number of concurrent connection the network offering supports + * @return the max number of concurrent connection the network offering + * supports */ @Nullable public Integer getMaxConnections() { @@ -230,6 +240,14 @@ public class NetworkOffering implements Comparable { return trafficType; } + /** + * + * @return the guest ip type for this network offering + */ + public GuestIPType getGuestIPType() { + return guestIPType; + } + /** * * @return data transfer rate in megabits per second allowed. diff --git a/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/NetworkService.java b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/NetworkService.java index b85b5ed11d..76a5038636 100644 --- a/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/NetworkService.java +++ b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/NetworkService.java @@ -22,20 +22,21 @@ import static com.google.common.base.Preconditions.checkNotNull; import java.util.Map; import java.util.Map.Entry; -import java.util.Set; +import java.util.SortedSet; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap.Builder; -import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSortedMap; +import com.google.common.collect.ImmutableSortedSet; import com.google.gson.annotations.SerializedName; /** * * @author Adrian Cole */ -public class NetworkService { +public class NetworkService implements Comparable { // internal only to match json type - private static class Capability { + private static class Capability implements Comparable { private String name; private String value; @@ -85,11 +86,17 @@ public class NetworkService { return "[name=" + name + ", value=" + value + "]"; } + @Override + public int compareTo(Capability o) { + return name.compareTo(o.name); + } + } private String name; @SerializedName("capability") - private Set capabilities = ImmutableSet.of(); + // so tests and serialization comes out expected + private SortedSet capabilities = ImmutableSortedSet.of(); NetworkService() { @@ -101,7 +108,7 @@ public class NetworkService { public NetworkService(String name, Map capabilities) { this.name = checkNotNull(name, "name"); - ImmutableSet.Builder internal = ImmutableSet. builder(); + ImmutableSortedSet.Builder internal = ImmutableSortedSet. naturalOrder(); for (Entry capabililty : checkNotNull(capabilities, "capabilities").entrySet()) internal.add(new Capability(capabililty.getKey(), capabililty.getValue())); this.capabilities = internal.build(); @@ -112,7 +119,8 @@ public class NetworkService { } public Map getCapabilities() { - Builder returnVal = ImmutableMap. builder(); + // so tests and serialization comes out expected + Builder returnVal = ImmutableSortedMap. naturalOrder(); for (Capability capability : capabilities) { returnVal.put(capability.name, capability.value); } @@ -154,4 +162,9 @@ public class NetworkService { public String toString() { return "[name=" + name + ", capabilities=" + capabilities + "]"; } + + @Override + public int compareTo(NetworkService o) { + return name.compareTo(o.getName()); + } } \ No newline at end of file diff --git a/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/SecurityGroup.java b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/SecurityGroup.java index 86e5f798dc..c647454570 100644 --- a/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/SecurityGroup.java +++ b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/SecurityGroup.java @@ -21,8 +21,10 @@ package org.jclouds.cloudstack.domain; import static com.google.common.base.Preconditions.checkNotNull; import java.util.Set; +import java.util.SortedSet; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSortedSet; import com.google.gson.annotations.SerializedName; /** @@ -91,7 +93,8 @@ public class SecurityGroup implements Comparable { @SerializedName("domainid") private long domainId; @SerializedName("ingressrule") - private Set ingressRules = ImmutableSet.of(); + // so that tests and serialization come out expected + private SortedSet ingressRules = ImmutableSortedSet. of(); public SecurityGroup(long id, String account, String name, String description, String domain, long domainId, Set ingressRules) { @@ -101,7 +104,7 @@ public class SecurityGroup implements Comparable { this.description = description; this.domain = domain; this.domainId = domainId; - this.ingressRules = ImmutableSet.copyOf(checkNotNull(ingressRules, "ingressRules")); + this.ingressRules = ImmutableSortedSet.copyOf(checkNotNull(ingressRules, "ingressRules")); } /** @@ -173,13 +176,8 @@ public class SecurityGroup implements Comparable { public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + ((account == null) ? 0 : account.hashCode()); - result = prime * result + ((description == null) ? 0 : description.hashCode()); - result = prime * result + ((domain == null) ? 0 : domain.hashCode()); result = prime * result + (int) (domainId ^ (domainId >>> 32)); result = prime * result + (int) (id ^ (id >>> 32)); - result = prime * result + ((ingressRules == null) ? 0 : ingressRules.hashCode()); - result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @@ -192,35 +190,10 @@ public class SecurityGroup implements Comparable { if (getClass() != obj.getClass()) return false; SecurityGroup other = (SecurityGroup) obj; - if (account == null) { - if (other.account != null) - return false; - } else if (!account.equals(other.account)) - return false; - if (description == null) { - if (other.description != null) - return false; - } else if (!description.equals(other.description)) - return false; - if (domain == null) { - if (other.domain != null) - return false; - } else if (!domain.equals(other.domain)) - return false; if (domainId != other.domainId) return false; if (id != other.id) return false; - if (ingressRules == null) { - if (other.ingressRules != null) - return false; - } else if (!ingressRules.equals(other.ingressRules)) - return false; - if (name == null) { - if (other.name != null) - return false; - } else if (!name.equals(other.name)) - return false; return true; } diff --git a/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Template.java b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Template.java index 834294f948..719052852a 100644 --- a/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Template.java +++ b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Template.java @@ -37,24 +37,16 @@ public class Template implements Comparable