node is terminated when order is cancelled in rimu

This commit is contained in:
Adrian Cole 2011-04-01 21:31:18 -07:00
parent 4f7a58b498
commit c50fddfc7a
8 changed files with 328 additions and 90 deletions

View File

@ -28,6 +28,7 @@ import org.jclouds.compute.internal.ComputeServiceContextImpl;
import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule; import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule;
import org.jclouds.logging.jdk.config.JDKLoggingModule; import org.jclouds.logging.jdk.config.JDKLoggingModule;
import org.jclouds.rimuhosting.miro.compute.config.RimuHostingComputeServiceContextModule; import org.jclouds.rimuhosting.miro.compute.config.RimuHostingComputeServiceContextModule;
import org.jclouds.rimuhosting.miro.config.RimuHostingRestClientModule;
import com.google.inject.Injector; import com.google.inject.Injector;
import com.google.inject.Key; import com.google.inject.Key;
@ -47,8 +48,7 @@ import com.google.inject.TypeLiteral;
* @author Adrian Cole * @author Adrian Cole
* @see RimuHostingComputeServiceContext * @see RimuHostingComputeServiceContext
*/ */
public class RimuHostingContextBuilder extends public class RimuHostingContextBuilder extends ComputeServiceContextBuilder<RimuHostingClient, RimuHostingAsyncClient> {
ComputeServiceContextBuilder<RimuHostingClient, RimuHostingAsyncClient> {
public RimuHostingContextBuilder(Properties props) { public RimuHostingContextBuilder(Properties props) {
super(RimuHostingClient.class, RimuHostingAsyncClient.class, props); super(RimuHostingClient.class, RimuHostingAsyncClient.class, props);
@ -62,11 +62,13 @@ public class RimuHostingContextBuilder extends
@Override @Override
public ComputeServiceContext buildComputeServiceContext() { public ComputeServiceContext buildComputeServiceContext() {
// need the generic type information // need the generic type information
return (ComputeServiceContext) this return (ComputeServiceContext) this.buildInjector().getInstance(
.buildInjector() Key.get(new TypeLiteral<ComputeServiceContextImpl<RimuHostingClient, RimuHostingAsyncClient>>() {
.getInstance( }));
Key }
.get(new TypeLiteral<ComputeServiceContextImpl<RimuHostingClient, RimuHostingAsyncClient>>() {
})); @Override
protected void addClientModule(List<Module> modules) {
modules.add(new RimuHostingRestClientModule());
} }
} }

View File

@ -101,7 +101,11 @@ public class ServerToNodeMetadata implements Function<Server, NodeMetadata> {
builder.imageId(from.getImageId() + ""); builder.imageId(from.getImageId() + "");
builder.operatingSystem(parseOperatingSystem(from, location)); builder.operatingSystem(parseOperatingSystem(from, location));
builder.hardware(null);// TODO 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.publicAddresses(getPublicAddresses.apply(from));
builder.credentials(credentialStore.get("node#" + from.getId())); builder.credentials(credentialStore.get("node#" + from.getId()));
return builder.build(); return builder.build();

View File

@ -0,0 +1,87 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*/
package org.jclouds.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<RimuHostingClient, RimuHostingAsyncClient> {
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;
}
}
}

View File

@ -29,100 +29,91 @@ import org.jclouds.rimuhosting.miro.domain.PricingPlan;
/** /**
* This structure defines the VPS to be setup. * This structure defines the VPS to be setup.
* *
* @author Ivan Meredith * @author Ivan Meredith
*/ */
public class NewServerData implements PostData{ public class NewServerData implements PostData {
public NewServerData(){} public NewServerData() {
}
public NewServerData(CreateOptions createOptions, PricingPlan pricingPlan){
this.createOptions = createOptions; public NewServerData(CreateOptions createOptions, PricingPlan pricingPlan) {
if(pricingPlan != null){ this.createOptions = createOptions;
this.planId = pricingPlan.getId(); if (pricingPlan != null) {
} this.planId = pricingPlan.getId();
} }
}
public NewServerData(CreateOptions createOptions, String pricingPlanId){
this.createOptions = createOptions; public NewServerData(CreateOptions createOptions, String pricingPlanId) {
this.planId = pricingPlanId; this.createOptions = createOptions;
} this.planId = pricingPlanId;
}
public NewServerData(CloneOptions cloneOptions){
this.cloneOptions = cloneOptions; public NewServerData(CloneOptions cloneOptions) {
} this.cloneOptions = cloneOptions;
}
/** /**
* Set the billing id if you want to control how it is billed.&nbsp; * Set the billing id if you want to control how it is billed.&nbsp; Else we will, for example,
* Else we will, for example, try to use, say, the credit card you used * try to use, say, the credit card you used on your last order that had a credit card.&nbsp; Or
* on your last order that had a credit card.&nbsp; Or use a wire * use a wire transfer method if you are using that on other orders.&nbsp; See the billing
* transfer method if you are using that on other orders.&nbsp; See the * methods resource for how to find what billing methods/ids you have setup on your identity.
* billing methods resource for how to find what billing methods/ids you
* have setup on your identity.
*/ */
@SerializedName("billing_oid") @SerializedName("billing_oid")
private Long billingId; private Long billingId;
/** /**
* The host server on which to setup the server.&nbsp; Typically you * The host server on which to setup the server.&nbsp; Typically you will want to leave this
* will want to leave this blank and let the API decide what is * blank and let the API decide what is best/available.&nbsp; And exception may be if you are a
* best/available.&nbsp; And exception may be if you are a customer with * customer with a dedicated server that is a VPS host with us.&nbsp; And in that case you may
* a dedicated server that is a VPS host with us.&nbsp; And in that case * want to force a VPS to be setup on a particular server of yours.
* you may want to force a VPS to be setup on a particular server of
* yours.
*/ */
@SerializedName("host_server_oid") @SerializedName("host_server_oid")
private String hostServerId; private String hostServerId;
/** /**
* These are the instantiation options.&nbsp; e.g. domain name, * These are the instantiation options.&nbsp; e.g. domain name, password, etc.&nbsp; Only provide
* password, etc.&nbsp; Only provide these if you are not cloning a VPS * these if you are not cloning a VPS (the vps_order_oid_to_clone setting). i.e. utually
* (the vps_order_oid_to_clone setting). i.e. utually exclusive to * exclusive to instantiation_via_clone_options
* instantiation_via_clone_options
*/ */
@SerializedName("instantiation_options") @SerializedName("instantiation_options")
private CreateOptions createOptions; private CreateOptions createOptions;
/** /**
* These are the instantiation options if you are creating a new VPS as * These are the instantiation options if you are creating a new VPS as a clone of an existing
* a clone of an existing VPS. Mutually exclusive to * VPS. Mutually exclusive to instantiation_options.
* instantiation_options.
*/ */
@SerializedName("instantiation_via_clone_options") @SerializedName("instantiation_via_clone_options")
private CloneOptions cloneOptions; private CloneOptions cloneOptions;
/** /**
* The number of IPs you need on the VPS and a justification for having * The number of IPs you need on the VPS and a justification for having more than one.&nbsp; Just
* more than one.&nbsp; Just leave blank for a single IP (which is all * leave blank for a single IP (which is all most servers need).
* most servers need).
*/ */
@SerializedName("ip_request") @SerializedName("ip_request")
private IpRequestData ipRequest; private IpRequestData ipRequest;
/** /**
* The pricing plan code you want to use.&nbsp; Per the pricing plans * The pricing plan code you want to use.&nbsp; Per the pricing plans resource.
* resource.
*/ */
@SerializedName("pricing_plan_code") @SerializedName("pricing_plan_code")
private String planId; private String planId;
/** /**
* To whom will the order belong? Leave this blank and we will assign it * To whom will the order belong? Leave this blank and we will assign it to you.&nbsp; If you set
* to you.&nbsp; If you set it and you do not have permissions on that * it and you do not have permissions on that user's identity you will get an error.
* user's identity you will get an error.
*/ */
@SerializedName("user_oid") @SerializedName("user_oid")
private Long userId; private Long userId;
/** /**
* Any particular memory/disk size overrides you want to make.&nbsp; If * Any particular memory/disk size overrides you want to make.&nbsp; If they are compatible with
* they are compatible with the pricing plan you selected we will use * the pricing plan you selected we will use them.&nbsp; We will calculate the cost based on the
* them.&nbsp; We will calculate the cost based on the resources we * resources we setup you up with.&nbsp; We can provision VPSs in most sizes, provided that the
* setup you up with.&nbsp; We can provision VPSs in most sizes, * host has space for them.&nbsp; The low contention plans are an exception.&nbsp; You will
* provided that the host has space for them.&nbsp; The low contention * likely need to use the provided memory and disk sizes.&nbsp; Since those plans are designed so
* plans are an exception.&nbsp; You will likely need to use the * there is a specific (small) number of VPSs per host.&nbsp; And having VPSs with 'odd' sizes
* provided memory and disk sizes.&nbsp; Since those plans are designed * stops them all fitting in 'neatly' (that's not a problem on the bigger-, non-low
* so there is a specific (small) number of VPSs per host.&nbsp; And * contention-plans.
* 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") @SerializedName("vps_paramters")
private ServerParameters serverParameters; private ServerParameters serverParameters;
@SerializedName("meta_data") @SerializedName("meta_data")
private List<MetaData> metaData; private List<MetaData> metaData;
public Long getBillingId() { public Long getBillingId() {
return billingId; return billingId;
} }
@ -186,25 +177,25 @@ public class NewServerData implements PostData{
public void setInstanceParameters(ServerParameters serverParameters) { public void setInstanceParameters(ServerParameters serverParameters) {
this.serverParameters = serverParameters; this.serverParameters = serverParameters;
} }
public void validate(){ public void validate() {
//bitwise XOR, works with boolean :) // bitwise XOR, works with boolean :)
assert(this.cloneOptions == null ^ this.createOptions == null); assert (this.cloneOptions == null ^ this.createOptions == null);
if(this.cloneOptions != null){ if (this.cloneOptions != null) {
this.cloneOptions.validate(); this.cloneOptions.validate();
} }
if(this.createOptions != null){ if (this.createOptions != null) {
this.createOptions.validate(); this.createOptions.validate();
assert(this.planId != null && this.planId.length() == 0); assert (this.planId != null && this.planId.length() == 0);
} }
if(this.ipRequest != null){ if (this.ipRequest != null) {
this.ipRequest.validate(); this.ipRequest.validate();
} }
if(this.serverParameters != null){ if (this.serverParameters != null) {
this.serverParameters.validate(); this.serverParameters.validate();
} }
} }
public void setMetaData(List<MetaData> metaData) { public void setMetaData(List<MetaData> metaData) {

View File

@ -57,6 +57,8 @@ public class Server implements Comparable<Server> {
private String slug; private String slug;
@SerializedName("vps_parameters") @SerializedName("vps_parameters")
private ServerParameters serverParameters; private ServerParameters serverParameters;
@SerializedName("billing_info")
private BillingData billingData;
private DataCenter location; private DataCenter location;
@ -190,4 +192,37 @@ public class Server implements Comparable<Server> {
public List<MetaData> getMetaData() { public List<MetaData> getMetaData() {
return metaData; 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;
}
} }

View File

@ -35,6 +35,7 @@ import org.jclouds.rest.internal.RestAnnotationProcessor;
import org.jclouds.rimuhosting.miro.binder.CreateServerOptions; import org.jclouds.rimuhosting.miro.binder.CreateServerOptions;
import org.jclouds.rimuhosting.miro.filters.RimuHostingAuthentication; import org.jclouds.rimuhosting.miro.filters.RimuHostingAuthentication;
import org.jclouds.rimuhosting.miro.functions.ParseRimuHostingException; import org.jclouds.rimuhosting.miro.functions.ParseRimuHostingException;
import org.jclouds.rimuhosting.miro.functions.ParseServersFromJsonResponse;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.inject.TypeLiteral; import com.google.inject.TypeLiteral;
@ -50,16 +51,16 @@ public class RimuHostingAsyncClientTest extends RestClientTest<RimuHostingAsyncC
public void testCreateServer() throws SecurityException, NoSuchMethodException, IOException { public void testCreateServer() throws SecurityException, NoSuchMethodException, IOException {
Method method = RimuHostingAsyncClient.class.getMethod("createServer", String.class, String.class, String.class, Method method = RimuHostingAsyncClient.class.getMethod("createServer", String.class, String.class, String.class,
CreateServerOptions[].class); CreateServerOptions[].class);
GeneratedHttpRequest<RimuHostingAsyncClient> httpRequest = processor.createRequest(method, "test.ivan.api.com", GeneratedHttpRequest<RimuHostingAsyncClient> 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"); assertRequestLineEquals(httpRequest, "POST https://api.rimuhosting.com/r/orders/new-vps HTTP/1.1");
assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\nHost: api.rimuhosting.com\n"); assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\nHost: api.rimuhosting.com\n");
assertPayloadEquals( assertPayloadEquals(
httpRequest, httpRequest,
"{\"request\":{\"instantiation_options\":{\"distro\":\"lenny\",\"domain_name\":\"test.ivan.api.com\"},\"pricing_plan_code\":\"MIRO4B\",\"meta_data\":[]}}", "{\"request\":{\"instantiation_options\":{\"distro\":\"lenny\",\"domain_name\":\"test.ivan.api.com\"},\"pricing_plan_code\":\"MIRO4B\",\"meta_data\":[]}}",
"application/json", false); "application/json", false);
assertResponseParserClassEquals(method, httpRequest, UnwrapOnlyJsonValue.class); assertResponseParserClassEquals(method, httpRequest, UnwrapOnlyJsonValue.class);
assertSaxResponseParserClassEquals(method, null); assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, ParseRimuHostingException.class); assertExceptionParserClassEquals(method, ParseRimuHostingException.class);
@ -68,6 +69,21 @@ public class RimuHostingAsyncClientTest extends RestClientTest<RimuHostingAsyncC
} }
public void testGetServerList() throws SecurityException, NoSuchMethodException, IOException {
Method method = RimuHostingAsyncClient.class.getMethod("getServerList");
GeneratedHttpRequest<RimuHostingAsyncClient> 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 @Override
protected void checkFilters(HttpRequest request) { protected void checkFilters(HttpRequest request) {
assertEquals(request.getFilters().size(), 1); assertEquals(request.getFilters().size(), 1);

View File

@ -0,0 +1,65 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*/
package org.jclouds.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"));
}
}

View File

@ -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" : []}}}