Issue 158: Property/Predicate/finder to test that the product order got created. Initial timeout of 5s

This commit is contained in:
Jason King 2011-09-29 21:50:57 +01:00
parent e4d002a162
commit 607ff1c5e4
6 changed files with 213 additions and 87 deletions

View File

@ -39,6 +39,7 @@ public class SoftLayerPropertiesBuilder extends PropertiesBuilder {
properties.setProperty(PROPERTY_ENDPOINT, "https://api.softlayer.com/rest"); properties.setProperty(PROPERTY_ENDPOINT, "https://api.softlayer.com/rest");
properties.setProperty(PROPERTY_API_VERSION, "3"); properties.setProperty(PROPERTY_API_VERSION, "3");
properties.setProperty(SoftLayerConstants.PROPERTY_SOFTLAYER_VIRTUALGUEST_PACKAGE_NAME, "Cloud Server"); properties.setProperty(SoftLayerConstants.PROPERTY_SOFTLAYER_VIRTUALGUEST_PACKAGE_NAME, "Cloud Server");
properties.setProperty(SoftLayerConstants.PROPERTY_SOFTLAYER_VIRTUALGUEST_ORDER_DELAY, "5000");
properties.setProperty(PROPERTY_ISO3166_CODES, "SG,US-CA,US-TX,US-VA,US-WA,US-TX"); properties.setProperty(PROPERTY_ISO3166_CODES, "SG,US-CA,US-TX,US-VA,US-WA,US-TX");
return properties; return properties;
} }

View File

@ -18,46 +18,36 @@
*/ */
package org.jclouds.softlayer.compute.strategy; package org.jclouds.softlayer.compute.strategy;
import static com.google.common.base.Preconditions.checkArgument; import com.google.common.base.Predicate;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.jclouds.softlayer.predicates.ProductItemPredicates.categoryCode;
import static org.jclouds.softlayer.predicates.ProductItemPredicates.matches;
import static org.jclouds.softlayer.predicates.ProductItemPredicates.units;
import static org.jclouds.softlayer.predicates.ProductPackagePredicates.named;
import java.util.Map;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.jclouds.compute.ComputeService;
import org.jclouds.compute.ComputeServiceAdapter;
import org.jclouds.compute.domain.Template;
import org.jclouds.domain.Credentials;
import org.jclouds.softlayer.SoftLayerClient;
import org.jclouds.softlayer.compute.functions.ProductItems;
import org.jclouds.softlayer.compute.options.SoftLayerTemplateOptions;
import org.jclouds.softlayer.domain.BillingItemVirtualGuest;
import org.jclouds.softlayer.domain.Datacenter;
import org.jclouds.softlayer.domain.OperatingSystem;
import org.jclouds.softlayer.domain.Password;
import org.jclouds.softlayer.domain.ProductItem;
import org.jclouds.softlayer.domain.ProductItemPrice;
import org.jclouds.softlayer.domain.ProductOrder;
import org.jclouds.softlayer.domain.ProductPackage;
import org.jclouds.softlayer.domain.VirtualGuest;
import org.jclouds.softlayer.features.AccountClient;
import org.jclouds.softlayer.features.ProductPackageClient;
import org.jclouds.softlayer.reference.SoftLayerConstants;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import com.google.common.base.Splitter; import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import org.jclouds.compute.ComputeService;
import org.jclouds.compute.ComputeServiceAdapter;
import org.jclouds.compute.domain.Template;
import org.jclouds.domain.Credentials;
import org.jclouds.predicates.RetryablePredicate;
import org.jclouds.softlayer.SoftLayerClient;
import org.jclouds.softlayer.compute.functions.ProductItems;
import org.jclouds.softlayer.compute.options.SoftLayerTemplateOptions;
import org.jclouds.softlayer.domain.*;
import org.jclouds.softlayer.features.AccountClient;
import org.jclouds.softlayer.features.ProductPackageClient;
import org.jclouds.softlayer.reference.SoftLayerConstants;
import org.jclouds.softlayer.util.SoftLayerUtils;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import java.util.Map;
import java.util.Set;
import static com.google.common.base.Preconditions.*;
import static org.jclouds.softlayer.predicates.ProductItemPredicates.*;
import static org.jclouds.softlayer.predicates.ProductPackagePredicates.named;
/** /**
* defines the connection between the {@link SoftLayerClient} implementation and the jclouds * defines the connection between the {@link SoftLayerClient} implementation and the jclouds
@ -66,19 +56,27 @@ import com.google.common.collect.Sets;
*/ */
@Singleton @Singleton
public class SoftLayerComputeServiceAdapter implements public class SoftLayerComputeServiceAdapter implements
ComputeServiceAdapter<VirtualGuest, Set<ProductItem>, ProductItem, Datacenter> { ComputeServiceAdapter<VirtualGuest, Set<ProductItem>, ProductItem, Datacenter> {
public static final String SAN_DESCRIPTION_REGEX=".*GB \\(SAN\\).*"; public static final String SAN_DESCRIPTION_REGEX = ".*GB \\(SAN\\).*";
private static final Float BOOT_VOLUME_CAPACITY = 100F; private static final Float BOOT_VOLUME_CAPACITY = 100F;
private final SoftLayerClient client; private final SoftLayerClient client;
private final String virtualGuestPackageName; private final String virtualGuestPackageName;
private final RetryablePredicate<VirtualGuest> orderTester;
private final long guestOrderDelay;
@Inject @Inject
public SoftLayerComputeServiceAdapter(SoftLayerClient client, public SoftLayerComputeServiceAdapter(SoftLayerClient client,
@Named(SoftLayerConstants.PROPERTY_SOFTLAYER_VIRTUALGUEST_PACKAGE_NAME) String virtualGuestPackageName) { @Named(SoftLayerConstants.PROPERTY_SOFTLAYER_VIRTUALGUEST_PACKAGE_NAME) String virtualGuestPackageName,
OnlyOneVirtualGuestPresentWithHostAndDomainName onlyOneVirtualGuestPresentWithHostAndDomainName,
@Named(SoftLayerConstants.PROPERTY_SOFTLAYER_VIRTUALGUEST_ORDER_DELAY) long guestOrderDelay) {
this.client = checkNotNull(client, "client"); this.client = checkNotNull(client, "client");
this.virtualGuestPackageName = checkNotNull(virtualGuestPackageName, "virtualGuestPackageName"); this.virtualGuestPackageName = checkNotNull(virtualGuestPackageName, "virtualGuestPackageName");
checkArgument(guestOrderDelay > 500, "guestOrderDelay must be in milliseconds and greater than 500");
this.orderTester = new RetryablePredicate<VirtualGuest>(onlyOneVirtualGuestPresentWithHostAndDomainName,
guestOrderDelay);
this.guestOrderDelay=guestOrderDelay;
} }
@Override @Override
@ -87,61 +85,55 @@ public class SoftLayerComputeServiceAdapter implements
checkNotNull(template, "template was null"); checkNotNull(template, "template was null");
checkNotNull(template.getOptions(), "template options was null"); checkNotNull(template.getOptions(), "template options was null");
checkArgument(template.getOptions().getClass().isAssignableFrom(SoftLayerTemplateOptions.class), checkArgument(template.getOptions().getClass().isAssignableFrom(SoftLayerTemplateOptions.class),
"options class %s should have been assignable from SoftLayerTemplateOptions", template.getOptions() "options class %s should have been assignable from SoftLayerTemplateOptions", template.getOptions()
.getClass()); .getClass());
Iterable<VirtualGuest> existing = findVirtualGuests(name,group); String domainName = template.getOptions().as(SoftLayerTemplateOptions.class).getDomainName();
if(!Iterables.isEmpty(existing)) {
throw new IllegalStateException(
"VirtualGuest(s) already exist with hostname:"+name+", group:"+group+". Existing:"+existing);
}
VirtualGuest newGuest = VirtualGuest.builder() VirtualGuest newGuest = VirtualGuest.builder().domain(domainName).hostname(name).build();
.domain(template.getOptions().as(SoftLayerTemplateOptions.class).getDomainName())
.hostname(name)
.build();
ProductOrder order = ProductOrder.builder() ProductOrder order = ProductOrder.builder().packageId(getProductPackage().getId()).location(
.packageId(getProductPackage().getId()) template.getLocation().getId()).quantity(1).useHourlyPricing(true).prices(getPrices(template))
.location(template.getLocation().getId()) .virtualGuest(newGuest).build();
.quantity(1)
.useHourlyPricing(true)
.prices(getPrices(template))
.virtualGuest(newGuest)
.build();
client.getVirtualGuestClient().orderVirtualGuest(order); client.getVirtualGuestClient().orderVirtualGuest(order);
boolean orderInSystem = orderTester.apply(newGuest);
checkState(orderInSystem, "order for guest %s didn't complete within %dms", newGuest, guestOrderDelay);
Iterable<VirtualGuest> allGuests = client.getVirtualGuestClient().listVirtualGuests();
VirtualGuest result = SoftLayerUtils.findVirtualGuest(allGuests, name, domainName);
VirtualGuest result = Iterables.getOnlyElement(findVirtualGuests(name, group)); Credentials credentials = new Credentials(null, null);
Credentials credentials = new Credentials(null,null);
// This information is not always available. // This information is not always available.
OperatingSystem os = result.getOperatingSystem(); OperatingSystem os = result.getOperatingSystem();
if(os!=null) { if (os != null) {
Set<Password> passwords = os.getPasswords(); Set<Password> passwords = os.getPasswords();
if(passwords.size()>0) { if (passwords.size() > 0) {
Password pw = Iterables.get(passwords,0); Password pw = Iterables.get(passwords, 0);
credentials = new Credentials(pw.getUsername(),pw.getPassword()); credentials = new Credentials(pw.getUsername(), pw.getPassword());
} }
} }
credentialStore.put("node#"+result.getId(),credentials); credentialStore.put("node#" + result.getId(), credentials);
return result; return result;
} }
private Iterable<VirtualGuest> findVirtualGuests(String hostname,String domain) { public static class OnlyOneVirtualGuestPresentWithHostAndDomainName implements Predicate<VirtualGuest> {
checkNotNull(hostname,"hostname"); private final SoftLayerClient client;
checkNotNull(domain,"domain");
Set<VirtualGuest> result = Sets.newLinkedHashSet(); @Inject
public OnlyOneVirtualGuestPresentWithHostAndDomainName(SoftLayerClient client) {
for( VirtualGuest guest : client.getVirtualGuestClient().listVirtualGuests()) { this.client = checkNotNull(client, "client was null");
if ( guest.getHostname().equals(hostname) && guest.getDomain().equals(domain)) { }
result.add(guest);
} @Override
public boolean apply(VirtualGuest guest) {
checkNotNull(guest, "virtual guest was null");
Iterable<VirtualGuest> allGuests = client.getVirtualGuestClient().listVirtualGuests();
return SoftLayerUtils.findVirtualGuest(allGuests, guest.getHostname(), guest.getDomain()) != null;
} }
return result;
} }
private Iterable<ProductItemPrice> getPrices(Template template) { private Iterable<ProductItemPrice> getPrices(Template template) {
@ -151,7 +143,7 @@ public class SoftLayerComputeServiceAdapter implements
result.add(ProductItemPrice.builder().id(imageId).build()); result.add(ProductItemPrice.builder().id(imageId).build());
Iterable<String> hardwareIds = Splitter.on(",").split(template.getHardware().getId()); Iterable<String> hardwareIds = Splitter.on(",").split(template.getHardware().getId());
for(String hardwareId: hardwareIds) { for (String hardwareId : hardwareIds) {
int id = Integer.parseInt(hardwareId); int id = Integer.parseInt(hardwareId);
result.add(ProductItemPrice.builder().id(id).build()); result.add(ProductItemPrice.builder().id(id).build());
} }
@ -161,32 +153,32 @@ public class SoftLayerComputeServiceAdapter implements
return result; return result;
} }
@Override @Override
public Iterable<Set<ProductItem>> listHardwareProfiles() { public Iterable<Set<ProductItem>> listHardwareProfiles() {
ProductPackage productPackage = getProductPackage(); ProductPackage productPackage = getProductPackage();
Set<ProductItem> items = productPackage.getItems(); Set<ProductItem> items = productPackage.getItems();
Iterable<ProductItem> cpuItems = Iterables.filter(items, units("PRIVATE_CORE")); Iterable<ProductItem> cpuItems = Iterables.filter(items, units("PRIVATE_CORE"));
Iterable<ProductItem> ramItems = Iterables.filter(items,categoryCode("ram")); Iterable<ProductItem> ramItems = Iterables.filter(items, categoryCode("ram"));
Iterable<ProductItem> sanItems = Iterables.filter(items, Predicates.and(matches(SAN_DESCRIPTION_REGEX),categoryCode("one_time_charge"))); Iterable<ProductItem> sanItems = Iterables.filter(items, Predicates.and(matches(SAN_DESCRIPTION_REGEX),
categoryCode("one_time_charge")));
Map<Float, ProductItem> cpuMap = Maps.uniqueIndex(cpuItems, ProductItems.capacity()); Map<Float, ProductItem> cpuMap = Maps.uniqueIndex(cpuItems, ProductItems.capacity());
Map<Float, ProductItem> ramMap = Maps.uniqueIndex(ramItems, ProductItems.capacity()); Map<Float, ProductItem> ramMap = Maps.uniqueIndex(ramItems, ProductItems.capacity());
Map<Float, ProductItem> sanMap = Maps.uniqueIndex(sanItems, ProductItems.capacity()); Map<Float, ProductItem> sanMap = Maps.uniqueIndex(sanItems, ProductItems.capacity());
final ProductItem bootVolume = sanMap.get(BOOT_VOLUME_CAPACITY); final ProductItem bootVolume = sanMap.get(BOOT_VOLUME_CAPACITY);
assert bootVolume!=null : "Boot volume capacity not found:"+BOOT_VOLUME_CAPACITY+", available:"+sanItems; assert bootVolume != null : "Boot volume capacity not found:" + BOOT_VOLUME_CAPACITY + ", available:" + sanItems;
Set<Set<ProductItem>> result = Sets.newLinkedHashSet(); Set<Set<ProductItem>> result = Sets.newLinkedHashSet();
for(Map.Entry<Float, ProductItem> coresEntry : cpuMap.entrySet()) { for (Map.Entry<Float, ProductItem> coresEntry : cpuMap.entrySet()) {
Float cores = coresEntry.getKey(); Float cores = coresEntry.getKey();
ProductItem ramItem = ramMap.get(cores); ProductItem ramItem = ramMap.get(cores);
//Amount of RAM and number of cores must match. // Amount of RAM and number of cores must match.
if(ramItem==null) continue; if (ramItem == null)
continue;
result.add(ImmutableSet.of(coresEntry.getValue(),ramItem,bootVolume)); result.add(ImmutableSet.of(coresEntry.getValue(), ramItem, bootVolume));
} }
return result; return result;
@ -211,7 +203,7 @@ public class SoftLayerComputeServiceAdapter implements
AccountClient accountClient = client.getAccountClient(); AccountClient accountClient = client.getAccountClient();
ProductPackageClient productPackageClient = client.getProductPackageClient(); ProductPackageClient productPackageClient = client.getProductPackageClient();
ProductPackage p = Iterables.find(accountClient.getActivePackages(),named(virtualGuestPackageName)); ProductPackage p = Iterables.find(accountClient.getActivePackages(), named(virtualGuestPackageName));
return productPackageClient.getProductPackage(p.getId()); return productPackageClient.getProductPackage(p.getId());
} }
@ -224,10 +216,12 @@ public class SoftLayerComputeServiceAdapter implements
@Override @Override
public void destroyNode(String id) { public void destroyNode(String id) {
VirtualGuest guest = getNode(id); VirtualGuest guest = getNode(id);
if(guest==null) return; if (guest == null)
return;
BillingItemVirtualGuest billingItem = guest.getBillingItem(); BillingItemVirtualGuest billingItem = guest.getBillingItem();
if (billingItem==null) return; if (billingItem == null)
return;
client.getVirtualGuestClient().cancelService(billingItem.getId()); client.getVirtualGuestClient().cancelService(billingItem.getId());
} }

View File

@ -33,6 +33,12 @@ public interface SoftLayerConstants {
* Name of the product package corresponding to cloud servers * Name of the product package corresponding to cloud servers
*/ */
public static final String PROPERTY_SOFTLAYER_VIRTUALGUEST_PACKAGE_NAME = "jclouds.softlayer.virtualguest.package-name"; public static final String PROPERTY_SOFTLAYER_VIRTUALGUEST_PACKAGE_NAME = "jclouds.softlayer.virtualguest.package-name";
/**
* number of milliseconds to wait for an order to arrive on the api.
*/
public static final String PROPERTY_SOFTLAYER_VIRTUALGUEST_ORDER_DELAY = "jclouds.softlayer.virtualguest.order-delay";
public static final Set<ProductItemPrice> DEFAULT_VIRTUAL_GUEST_PRICES = ImmutableSet.<ProductItemPrice>builder() public static final Set<ProductItemPrice> DEFAULT_VIRTUAL_GUEST_PRICES = ImmutableSet.<ProductItemPrice>builder()
.add(ProductItemPrice.builder().id(1639).build()) // 100 GB (SAN) .add(ProductItemPrice.builder().id(1639).build()) // 100 GB (SAN)

View File

@ -0,0 +1,67 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.softlayer.util;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import org.jclouds.softlayer.domain.VirtualGuest;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Utils for the SoftLayer project
* @author Jason King
*/
public class SoftLayerUtils {
/**
* Finds the virtual guest with given hostname and domainName
* @param allGuests the VirtualGuests to search
* @param hostname the virtual guest's hostname
* @param domainName the virtual guest's domain name
* @return the matching virtual guest or null if not found
* @throws IllegalStateException if more than one match is found
*/
public static VirtualGuest findVirtualGuest(Iterable<VirtualGuest> allGuests,
final String hostname, final String domainName) {
checkNotNull(allGuests,"allGuests");
checkNotNull(hostname,"hostname");
checkNotNull(domainName,"domainName");
Iterable<VirtualGuest> guests = Iterables.filter(allGuests, new Predicate<VirtualGuest>() {
@Override
public boolean apply(VirtualGuest arg0) {
return hostname.equals(arg0.getHostname()) && domainName.equals(arg0.getDomain());
}
});
switch (Iterables.size(guests)) {
case 0:
return null;
case 1:
return Iterables.get(guests, 0);
default:
throw new IllegalStateException(String.format(
"expected only 1 virtual guest with hostname %s and domainname %s: %s", hostname, domainName,
guests));
}
}
}

View File

@ -32,6 +32,7 @@ import org.jclouds.domain.Credentials;
import org.jclouds.net.IPSocket; import org.jclouds.net.IPSocket;
import org.jclouds.softlayer.compute.options.SoftLayerTemplateOptions; import org.jclouds.softlayer.compute.options.SoftLayerTemplateOptions;
import org.jclouds.softlayer.compute.strategy.SoftLayerComputeServiceAdapter; import org.jclouds.softlayer.compute.strategy.SoftLayerComputeServiceAdapter;
import org.jclouds.softlayer.compute.strategy.SoftLayerComputeServiceAdapter.OnlyOneVirtualGuestPresentWithHostAndDomainName;
import org.jclouds.softlayer.domain.ProductItem; import org.jclouds.softlayer.domain.ProductItem;
import org.jclouds.softlayer.domain.VirtualGuest; import org.jclouds.softlayer.domain.VirtualGuest;
import org.jclouds.softlayer.features.BaseSoftLayerClientLiveTest; import org.jclouds.softlayer.features.BaseSoftLayerClientLiveTest;
@ -55,7 +56,7 @@ public class SoftLayerComputeServiceAdapterLiveTest extends BaseSoftLayerClientL
public void setupClient() { public void setupClient() {
super.setupClient(); super.setupClient();
adapter = new SoftLayerComputeServiceAdapter(context.getApi(), adapter = new SoftLayerComputeServiceAdapter(context.getApi(),
ProductPackageClientLiveTest.CLOUD_SERVER_PACKAGE_NAME); ProductPackageClientLiveTest.CLOUD_SERVER_PACKAGE_NAME, new OnlyOneVirtualGuestPresentWithHostAndDomainName(context.getApi()), 5000l);
} }
@Test @Test

View File

@ -0,0 +1,57 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.softlayer.util;
import com.google.common.collect.ImmutableSet;
import org.jclouds.softlayer.domain.VirtualGuest;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
import static org.testng.AssertJUnit.assertNull;
@Test(groups = "unit")
public class SoftLayerUtilsTest {
@Test
public void testMatch() {
VirtualGuest guest1 = new VirtualGuest.Builder().id(1).hostname("host1").domain("domain").build();
VirtualGuest guest2 = new VirtualGuest.Builder().id(2).hostname("host2").domain("domain").build();
VirtualGuest result = SoftLayerUtils.findVirtualGuest(ImmutableSet.of(guest1,guest2),"host1","domain");
assertEquals(guest1,result);
}
@Test
public void testMissing() {
VirtualGuest guest1 = new VirtualGuest.Builder().id(1).hostname("host1").domain("domain").build();
VirtualGuest guest2 = new VirtualGuest.Builder().id(2).hostname("host2").domain("domain").build();
VirtualGuest result = SoftLayerUtils.findVirtualGuest(ImmutableSet.of(guest1,guest2),"missing","domain");
assertNull(result);
}
@Test(expectedExceptions = IllegalStateException.class)
public void testDuplicate() {
VirtualGuest guest1 = new VirtualGuest.Builder().id(1).hostname("host1").domain("domain").build();
VirtualGuest guest2 = new VirtualGuest.Builder().id(2).hostname("host1").domain("domain").build();
SoftLayerUtils.findVirtualGuest(ImmutableSet.of(guest1,guest2),"host1","domain");
}
}