diff --git a/sandbox-providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/RimuHostingContextBuilder.java b/sandbox-providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/RimuHostingContextBuilder.java index 40de024330..75256d8b0d 100644 --- a/sandbox-providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/RimuHostingContextBuilder.java +++ b/sandbox-providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/RimuHostingContextBuilder.java @@ -28,6 +28,7 @@ import org.jclouds.compute.internal.ComputeServiceContextImpl; import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule; import org.jclouds.logging.jdk.config.JDKLoggingModule; import org.jclouds.rimuhosting.miro.compute.config.RimuHostingComputeServiceContextModule; +import org.jclouds.rimuhosting.miro.config.RimuHostingRestClientModule; import com.google.inject.Injector; import com.google.inject.Key; @@ -47,8 +48,7 @@ import com.google.inject.TypeLiteral; * @author Adrian Cole * @see RimuHostingComputeServiceContext */ -public class RimuHostingContextBuilder extends - ComputeServiceContextBuilder { +public class RimuHostingContextBuilder extends ComputeServiceContextBuilder { public RimuHostingContextBuilder(Properties props) { super(RimuHostingClient.class, RimuHostingAsyncClient.class, props); @@ -62,11 +62,13 @@ public class RimuHostingContextBuilder extends @Override public ComputeServiceContext buildComputeServiceContext() { // need the generic type information - return (ComputeServiceContext) this - .buildInjector() - .getInstance( - Key - .get(new TypeLiteral>() { - })); + return (ComputeServiceContext) this.buildInjector().getInstance( + Key.get(new TypeLiteral>() { + })); + } + + @Override + protected void addClientModule(List modules) { + modules.add(new RimuHostingRestClientModule()); } } diff --git a/sandbox-providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/compute/functions/ServerToNodeMetadata.java b/sandbox-providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/compute/functions/ServerToNodeMetadata.java index 3014fa7430..7834d1ec24 100644 --- a/sandbox-providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/compute/functions/ServerToNodeMetadata.java +++ b/sandbox-providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/compute/functions/ServerToNodeMetadata.java @@ -101,7 +101,11 @@ public class ServerToNodeMetadata implements Function { builder.imageId(from.getImageId() + ""); builder.operatingSystem(parseOperatingSystem(from, location)); builder.hardware(null);// TODO - builder.state(runningStateToNodeState.get(from.getState())); + if (from.getBillingData() != null && from.getBillingData().getDateCancelled() != null + && RunningState.NOTRUNNING == from.getState()) + builder.state(NodeState.TERMINATED); + else + builder.state(runningStateToNodeState.get(from.getState())); builder.publicAddresses(getPublicAddresses.apply(from)); builder.credentials(credentialStore.get("node#" + from.getId())); return builder.build(); diff --git a/sandbox-providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/config/RimuHostingRestClientModule.java b/sandbox-providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/config/RimuHostingRestClientModule.java new file mode 100644 index 0000000000..af41df5114 --- /dev/null +++ b/sandbox-providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/config/RimuHostingRestClientModule.java @@ -0,0 +1,87 @@ +/** + * + * Copyright (C) 2010 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.rimuhosting.miro.config; + +import java.lang.reflect.Type; +import java.util.Date; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.jclouds.date.DateService; +import org.jclouds.http.RequiresHttp; +import org.jclouds.json.Json; +import org.jclouds.json.config.GsonModule.DateAdapter; +import org.jclouds.rest.ConfiguresRestClient; +import org.jclouds.rest.config.RestClientModule; +import org.jclouds.rimuhosting.miro.RimuHostingAsyncClient; +import org.jclouds.rimuhosting.miro.RimuHostingClient; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSerializationContext; + +/** + * + * @author Adrian Cole + */ +@RequiresHttp +@ConfiguresRestClient +public class RimuHostingRestClientModule extends RestClientModule { + + public RimuHostingRestClientModule() { + super(RimuHostingClient.class, RimuHostingAsyncClient.class); + } + + @Override + protected void configure() { + bind(DateAdapter.class).to(RimuIso8601DateAdapter.class); + super.configure(); + } + + @Singleton + public static class RimuIso8601DateAdapter implements DateAdapter { + private final DateService dateService; + private final Json json; + + private static class DateHolder { + String iso_format; + } + + @Inject + private RimuIso8601DateAdapter(DateService dateService, Json json) { + this.dateService = dateService; + this.json = json; + } + + public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) { + throw new UnsupportedOperationException(); + } + + public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + String toParse = json.toString(); + DateHolder dateHolder = this.json.fromJson(toParse, DateHolder.class); + return (dateHolder.iso_format != null) ? dateService.iso8601SecondsDateParse(dateHolder.iso_format) : null; + } + + } +} diff --git a/sandbox-providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/data/NewServerData.java b/sandbox-providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/data/NewServerData.java index 03b2b7bf0e..6c6df966af 100644 --- a/sandbox-providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/data/NewServerData.java +++ b/sandbox-providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/data/NewServerData.java @@ -29,100 +29,91 @@ import org.jclouds.rimuhosting.miro.domain.PricingPlan; /** * This structure defines the VPS to be setup. - * + * * @author Ivan Meredith */ -public class NewServerData implements PostData{ - public NewServerData(){} - - public NewServerData(CreateOptions createOptions, PricingPlan pricingPlan){ - this.createOptions = createOptions; - if(pricingPlan != null){ - this.planId = pricingPlan.getId(); - } - } - - public NewServerData(CreateOptions createOptions, String pricingPlanId){ - this.createOptions = createOptions; - this.planId = pricingPlanId; - } - - public NewServerData(CloneOptions cloneOptions){ - this.cloneOptions = cloneOptions; - } +public class NewServerData implements PostData { + public NewServerData() { + } + + public NewServerData(CreateOptions createOptions, PricingPlan pricingPlan) { + this.createOptions = createOptions; + if (pricingPlan != null) { + this.planId = pricingPlan.getId(); + } + } + + public NewServerData(CreateOptions createOptions, String pricingPlanId) { + this.createOptions = createOptions; + this.planId = pricingPlanId; + } + + public NewServerData(CloneOptions cloneOptions) { + this.cloneOptions = cloneOptions; + } + /** - * Set the billing id if you want to control how it is billed.  - * Else we will, for example, try to use, say, the credit card you used - * on your last order that had a credit card.  Or use a wire - * transfer method if you are using that on other orders.  See the - * billing methods resource for how to find what billing methods/ids you - * have setup on your identity. + * Set the billing id if you want to control how it is billed.  Else we will, for example, + * try to use, say, the credit card you used on your last order that had a credit card.  Or + * use a wire transfer method if you are using that on other orders.  See the billing + * methods resource for how to find what billing methods/ids you have setup on your identity. */ @SerializedName("billing_oid") private Long billingId; /** - * The host server on which to setup the server.  Typically you - * will want to leave this blank and let the API decide what is - * best/available.  And exception may be if you are a customer with - * a dedicated server that is a VPS host with us.  And in that case - * you may want to force a VPS to be setup on a particular server of - * yours. + * The host server on which to setup the server.  Typically you will want to leave this + * blank and let the API decide what is best/available.  And exception may be if you are a + * customer with a dedicated server that is a VPS host with us.  And in that case you may + * want to force a VPS to be setup on a particular server of yours. */ @SerializedName("host_server_oid") private String hostServerId; /** - * These are the instantiation options.  e.g. domain name, - * password, etc.  Only provide these if you are not cloning a VPS - * (the vps_order_oid_to_clone setting). i.e. utually exclusive to - * instantiation_via_clone_options + * These are the instantiation options.  e.g. domain name, password, etc.  Only provide + * these if you are not cloning a VPS (the vps_order_oid_to_clone setting). i.e. utually + * exclusive to instantiation_via_clone_options */ @SerializedName("instantiation_options") private CreateOptions createOptions; /** - * These are the instantiation options if you are creating a new VPS as - * a clone of an existing VPS. Mutually exclusive to - * instantiation_options. + * These are the instantiation options if you are creating a new VPS as a clone of an existing + * VPS. Mutually exclusive to instantiation_options. */ @SerializedName("instantiation_via_clone_options") private CloneOptions cloneOptions; /** - * The number of IPs you need on the VPS and a justification for having - * more than one.  Just leave blank for a single IP (which is all - * most servers need). + * The number of IPs you need on the VPS and a justification for having more than one.  Just + * leave blank for a single IP (which is all most servers need). */ @SerializedName("ip_request") private IpRequestData ipRequest; /** - * The pricing plan code you want to use.  Per the pricing plans - * resource. + * The pricing plan code you want to use.  Per the pricing plans resource. */ @SerializedName("pricing_plan_code") private String planId; /** - * To whom will the order belong? Leave this blank and we will assign it - * to you.  If you set it and you do not have permissions on that - * user's identity you will get an error. + * To whom will the order belong? Leave this blank and we will assign it to you.  If you set + * it and you do not have permissions on that user's identity you will get an error. */ @SerializedName("user_oid") private Long userId; /** - * Any particular memory/disk size overrides you want to make.  If - * they are compatible with the pricing plan you selected we will use - * them.  We will calculate the cost based on the resources we - * setup you up with.  We can provision VPSs in most sizes, - * provided that the host has space for them.  The low contention - * plans are an exception.  You will likely need to use the - * provided memory and disk sizes.  Since those plans are designed - * so there is a specific (small) number of VPSs per host.  And - * having VPSs with 'odd' sizes stops them all fitting in 'neatly' - * (that's not a problem on the bigger-, non-low contention-plans. + * Any particular memory/disk size overrides you want to make.  If they are compatible with + * the pricing plan you selected we will use them.  We will calculate the cost based on the + * resources we setup you up with.  We can provision VPSs in most sizes, provided that the + * host has space for them.  The low contention plans are an exception.  You will + * likely need to use the provided memory and disk sizes.  Since those plans are designed so + * there is a specific (small) number of VPSs per host.  And having VPSs with 'odd' sizes + * stops them all fitting in 'neatly' (that's not a problem on the bigger-, non-low + * contention-plans. */ @SerializedName("vps_paramters") private ServerParameters serverParameters; @SerializedName("meta_data") private List metaData; - + public Long getBillingId() { return billingId; } @@ -186,25 +177,25 @@ public class NewServerData implements PostData{ public void setInstanceParameters(ServerParameters serverParameters) { this.serverParameters = serverParameters; } - - public void validate(){ - //bitwise XOR, works with boolean :) - assert(this.cloneOptions == null ^ this.createOptions == null); - if(this.cloneOptions != null){ - this.cloneOptions.validate(); - } - if(this.createOptions != null){ - this.createOptions.validate(); - assert(this.planId != null && this.planId.length() == 0); - } - - if(this.ipRequest != null){ - this.ipRequest.validate(); - } - - if(this.serverParameters != null){ - this.serverParameters.validate(); - } + + public void validate() { + // bitwise XOR, works with boolean :) + assert (this.cloneOptions == null ^ this.createOptions == null); + if (this.cloneOptions != null) { + this.cloneOptions.validate(); + } + if (this.createOptions != null) { + this.createOptions.validate(); + assert (this.planId != null && this.planId.length() == 0); + } + + if (this.ipRequest != null) { + this.ipRequest.validate(); + } + + if (this.serverParameters != null) { + this.serverParameters.validate(); + } } public void setMetaData(List metaData) { diff --git a/sandbox-providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/domain/Server.java b/sandbox-providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/domain/Server.java index abad1a72f9..796dcf1c91 100644 --- a/sandbox-providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/domain/Server.java +++ b/sandbox-providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/domain/Server.java @@ -57,6 +57,8 @@ public class Server implements Comparable { private String slug; @SerializedName("vps_parameters") private ServerParameters serverParameters; + @SerializedName("billing_info") + private BillingData billingData; private DataCenter location; @@ -190,4 +192,37 @@ public class Server implements Comparable { public List getMetaData() { return metaData; } + + public Boolean getOnDedicatedHardware() { + return onDedicatedHardware; + } + + public BillingData getBillingData() { + return billingData; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((id == null) ? 0 : id.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; + Server other = (Server) obj; + if (id == null) { + if (other.id != null) + return false; + } else if (!id.equals(other.id)) + return false; + return true; + } } diff --git a/sandbox-providers/rimuhosting/src/test/java/org/jclouds/rimuhosting/miro/RimuHostingAsyncClientTest.java b/sandbox-providers/rimuhosting/src/test/java/org/jclouds/rimuhosting/miro/RimuHostingAsyncClientTest.java index 9ff421c026..fee442f088 100644 --- a/sandbox-providers/rimuhosting/src/test/java/org/jclouds/rimuhosting/miro/RimuHostingAsyncClientTest.java +++ b/sandbox-providers/rimuhosting/src/test/java/org/jclouds/rimuhosting/miro/RimuHostingAsyncClientTest.java @@ -35,6 +35,7 @@ import org.jclouds.rest.internal.RestAnnotationProcessor; import org.jclouds.rimuhosting.miro.binder.CreateServerOptions; import org.jclouds.rimuhosting.miro.filters.RimuHostingAuthentication; import org.jclouds.rimuhosting.miro.functions.ParseRimuHostingException; +import org.jclouds.rimuhosting.miro.functions.ParseServersFromJsonResponse; import org.testng.annotations.Test; import com.google.inject.TypeLiteral; @@ -50,16 +51,16 @@ public class RimuHostingAsyncClientTest extends RestClientTest httpRequest = processor.createRequest(method, "test.ivan.api.com", - "lenny", "MIRO4B"); + "lenny", "MIRO4B"); assertRequestLineEquals(httpRequest, "POST https://api.rimuhosting.com/r/orders/new-vps HTTP/1.1"); assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\nHost: api.rimuhosting.com\n"); assertPayloadEquals( - httpRequest, - "{\"request\":{\"instantiation_options\":{\"distro\":\"lenny\",\"domain_name\":\"test.ivan.api.com\"},\"pricing_plan_code\":\"MIRO4B\",\"meta_data\":[]}}", - "application/json", false); + httpRequest, + "{\"request\":{\"instantiation_options\":{\"distro\":\"lenny\",\"domain_name\":\"test.ivan.api.com\"},\"pricing_plan_code\":\"MIRO4B\",\"meta_data\":[]}}", + "application/json", false); assertResponseParserClassEquals(method, httpRequest, UnwrapOnlyJsonValue.class); assertSaxResponseParserClassEquals(method, null); assertExceptionParserClassEquals(method, ParseRimuHostingException.class); @@ -68,6 +69,21 @@ public class RimuHostingAsyncClientTest extends RestClientTest httpRequest = processor.createRequest(method); + + assertRequestLineEquals(httpRequest, "GET https://api.rimuhosting.com/r/orders;include_inactive=N HTTP/1.1"); + assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\nHost: api.rimuhosting.com\n"); + assertPayloadEquals(httpRequest, null, null, false); + assertResponseParserClassEquals(method, httpRequest, ParseServersFromJsonResponse.class); + assertSaxResponseParserClassEquals(method, null); + assertExceptionParserClassEquals(method, ParseRimuHostingException.class); + + checkFilters(httpRequest); + + } + @Override protected void checkFilters(HttpRequest request) { assertEquals(request.getFilters().size(), 1); diff --git a/sandbox-providers/rimuhosting/src/test/java/org/jclouds/rimuhosting/miro/functions/ParseServerFromJsonResponseTest.java b/sandbox-providers/rimuhosting/src/test/java/org/jclouds/rimuhosting/miro/functions/ParseServerFromJsonResponseTest.java new file mode 100644 index 0000000000..22b1336792 --- /dev/null +++ b/sandbox-providers/rimuhosting/src/test/java/org/jclouds/rimuhosting/miro/functions/ParseServerFromJsonResponseTest.java @@ -0,0 +1,65 @@ +/** + * + * Copyright (C) 2010 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.rimuhosting.miro.functions; + +import static org.testng.Assert.assertEquals; + +import java.io.InputStream; + +import org.jclouds.date.internal.SimpleDateFormatDateService; +import org.jclouds.http.HttpResponse; +import org.jclouds.io.Payloads; +import org.jclouds.json.config.GsonModule; +import org.jclouds.rimuhosting.miro.config.RimuHostingRestClientModule.RimuIso8601DateAdapter; +import org.jclouds.rimuhosting.miro.domain.Server; +import org.testng.annotations.Test; + +import com.google.inject.Guice; +import com.google.inject.Injector; + +/** + * + * @author Adrian Cole + */ +@Test(groups = "unit") +public class ParseServerFromJsonResponseTest { + Injector i = Guice.createInjector(new GsonModule() { + + @Override + protected void configure() { + bind(DateAdapter.class).to(RimuIso8601DateAdapter.class); + super.configure(); + } + + }); + + public void testCancelled() { + InputStream is = getClass().getResourceAsStream("/cancelled.json"); + + ParseServerFromJsonResponse parser = i.getInstance(ParseServerFromJsonResponse.class); + + Server response = parser.apply(new HttpResponse(200, "ok", Payloads.newInputStreamPayload(is))); + + assertEquals(response.getBillingData().getDateSuspended(), null); + assertEquals(response.getBillingData().getDateCancelled(), new SimpleDateFormatDateService() + .iso8601SecondsDateParse("2011-04-02T03:30:28Z")); + } + +} diff --git a/sandbox-providers/rimuhosting/src/test/resources/cancelled.json b/sandbox-providers/rimuhosting/src/test/resources/cancelled.json new file mode 100644 index 0000000000..f0f390bb31 --- /dev/null +++ b/sandbox-providers/rimuhosting/src/test/resources/cancelled.json @@ -0,0 +1,38 @@ +{ "get_order_response" : { "status_message" : null + , "status_code" : 200 + , "response_created_time" : { "ms_since_epoch": 1301716709422, "iso_format" : "2011-04-02T03:58:29Z", "users_tz_offset_ms" : 46800000} + , "response_created_time_epoch_ms" : 1301716709422 + , "error_info" : null + , "extended_error_infos" : null + , "response_type" : "OK" + , "human_readable_message" : "Information about rimuhosting.jcloudsr-eed" + , "redirect_uri" : null + , "response_display_duration_type" : "REGULAR" + , "about_order" : { "order_oid" : 721950181 + , "domain_name" : "rimuhosting.jcloudsr-eed" + , "slug" : "order-721950181-rimuhosting-jcloudsr-e" + , "billing_oid" : 50081656 + , "is_on_customers_own_physical_server" : false + , "vps_parameters" : { "memory_mb" : 1150 + , "disk_space_mb" : 4096 + , "disk_space_2_mb" : 0} + , "host_server_oid" : "324" + , "server_type" : "VPS" + , "data_transfer_allowance" : { "data_transfer_gb" : 75 + , "data_transfer" : ""} + , "billing_info" : { "monthly_recurring_fee" : 44.95 + , "monthly_recurring_amt" : { "amt" : 44.95 + , "currency" : "CUR_USD" + , "amt_usd" : 44.95} + , "suspended_date" : null + , "prepaid_until" : null + , "order_date" : { "ms_since_epoch": 1301710569000, "iso_format" : "2011-04-02T02:16:09Z", "users_tz_offset_ms" : 46800000} + , "cancellation_date" : { "ms_since_epoch": 1301715028000, "iso_format" : "2011-04-02T03:30:28Z", "users_tz_offset_ms" : 46800000}} + , "location" : { "data_center_location_code" : "DCDALLAS" + , "data_center_location_name" : "Dallas" + , "data_center_location_country_2ltr" : "US"} + , "allocated_ips" : { "primary_ip" : "206.123.115.150" + , "secondary_ips" : []} + , "running_state" : "NOTRUNNING" + , "distro" : "ubuntu1004.64" + , "meta_data" : []}}} \ No newline at end of file