added expect test

This commit is contained in:
David Ribeiro Alves 2012-05-10 01:33:31 +01:00
parent 082158ac3f
commit 176647110a
6 changed files with 306 additions and 49 deletions

View File

@ -43,12 +43,10 @@ import org.jclouds.concurrent.Futures;
import org.jclouds.logging.Logger;
import org.jclouds.openstack.nova.v1_1.NovaClient;
import org.jclouds.openstack.nova.v1_1.domain.Server;
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ImageInZone;
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ZoneAndId;
import org.jclouds.predicates.PredicateWithResult;
import org.jclouds.predicates.Retryables;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.util.concurrent.ListenableFuture;
@ -61,21 +59,22 @@ public class NovaImageExtension implements ImageExtension {
protected Logger logger = Logger.NULL;
private final NovaClient novaClient;
private final Function<ImageInZone, Image> imageInZoneToImage;
private final ExecutorService executor;
@com.google.inject.Inject(optional = true)
@Named("IMAGE_MAX_WAIT")
long maxWait = 3600;
private long maxWait = 3600;
@com.google.inject.Inject(optional = true)
@Named("IMAGE_WAIT_PERIOD")
long waitPeriod = 1;
private long waitPeriod = 1;
private PredicateWithResult<ZoneAndId, Image> imageReadyPredicate;
@Inject
public NovaImageExtension(NovaClient novaClient, Function<ImageInZone, Image> imageInZoneToImage,
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService userThreads) {
public NovaImageExtension(NovaClient novaClient,
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService userThreads,
PredicateWithResult<ZoneAndId, Image> imageReadyPredicate) {
this.novaClient = checkNotNull(novaClient);
this.imageInZoneToImage = imageInZoneToImage;
this.executor = userThreads;
this.imageReadyPredicate = imageReadyPredicate;
}
@Override
@ -93,48 +92,21 @@ public class NovaImageExtension implements ImageExtension {
checkState(template instanceof CloneImageTemplate,
" openstack-nova only supports creating images through cloning.");
CloneImageTemplate cloneTemplate = (CloneImageTemplate) template;
final ZoneAndId zoneAndId = ZoneAndId.fromSlashEncoded(cloneTemplate.getSourceNodeId());
ZoneAndId sourceImageZoneAndId = ZoneAndId.fromSlashEncoded(cloneTemplate.getSourceNodeId());
String newImageId = novaClient.getServerClientForZone(sourceImageZoneAndId.getZone()).createImageFromServer(
cloneTemplate.getName(), sourceImageZoneAndId.getId());
final ZoneAndId targetImageZoneAndId = ZoneAndId.fromZoneAndId(sourceImageZoneAndId.getZone(), newImageId);
final String newImageId = novaClient.getServerClientForZone(zoneAndId.getZone()).createImageFromServer(
cloneTemplate.getName(), zoneAndId.getId());
logger.info(">> Registered new Image %s, waiting for it to become available.", newImageId);
return Futures.makeListenable(executor.submit(new Callable<Image>() {
@Override
public Image call() throws Exception {
return Retryables.retryGettingResultOrFailing(new PredicateWithResult<String, Image>() {
org.jclouds.openstack.nova.v1_1.domain.Image result;
RuntimeException lastFailure;
@Override
public boolean apply(String input) {
result = checkNotNull(findImage(ZoneAndId.fromZoneAndId(zoneAndId.getZone(), newImageId)));
switch (result.getStatus()) {
case ACTIVE:
logger.info("<< Image %s is available for use.", newImageId);
return true;
case UNKNOWN:
case SAVING:
logger.debug("<< Image %s is not available yet.", newImageId);
return false;
default:
lastFailure = new IllegalStateException("Image was not created: " + newImageId);
throw lastFailure;
}
}
@Override
public Image getResult() {
return imageInZoneToImage.apply(new ImageInZone(result, zoneAndId.getZone()));
}
@Override
public Throwable getLastFailure() {
return lastFailure;
}
}, newImageId, maxWait, waitPeriod, TimeUnit.SECONDS,
"Image was not created within the time limit, Giving up! [Limit: " + maxWait + " secs.]");
return Retryables.retryGettingResultOrFailing(imageReadyPredicate, targetImageZoneAndId, maxWait,
waitPeriod, TimeUnit.SECONDS, "Image was not created within the time limit, Giving up! [Limit: "
+ maxWait + " secs.]");
}
}), executor);
}
@ -150,7 +122,7 @@ public class NovaImageExtension implements ImageExtension {
return true;
}
private org.jclouds.openstack.nova.v1_1.domain.Image findImage(final ZoneAndId zoneAndId) {
public static org.jclouds.openstack.nova.v1_1.domain.Image findImage(NovaClient novaClient, final ZoneAndId zoneAndId) {
return Iterables.tryFind(novaClient.getImageClientForZone(zoneAndId.getZone()).listImagesInDetail(),
new Predicate<org.jclouds.openstack.nova.v1_1.domain.Image>() {
@Override

View File

@ -58,6 +58,7 @@ import org.jclouds.openstack.nova.v1_1.compute.loaders.CreateUniqueKeyPair;
import org.jclouds.openstack.nova.v1_1.compute.loaders.FindSecurityGroupOrCreate;
import org.jclouds.openstack.nova.v1_1.compute.loaders.LoadFloatingIpsForInstance;
import org.jclouds.openstack.nova.v1_1.compute.options.NovaTemplateOptions;
import org.jclouds.openstack.nova.v1_1.compute.predicates.GetImageWhenImageInZoneHasActiveStatusPredicateWithResult;
import org.jclouds.openstack.nova.v1_1.compute.strategy.ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet;
import org.jclouds.openstack.nova.v1_1.domain.KeyPair;
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.FlavorInZone;
@ -68,6 +69,7 @@ import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ZoneAndId;
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ZoneAndName;
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ZoneSecurityGroupNameAndPorts;
import org.jclouds.openstack.nova.v1_1.predicates.FindSecurityGroupWithNameAndReturnTrue;
import org.jclouds.predicates.PredicateWithResult;
import org.jclouds.predicates.RetryablePredicate;
import com.google.common.base.Function;
@ -141,6 +143,9 @@ public class NovaComputeServiceContextModule extends
bind(new TypeLiteral<ImageExtension>() {
}).to(NovaImageExtension.class);
bind(new TypeLiteral<PredicateWithResult<ZoneAndId, Image>>() {
}).to(GetImageWhenImageInZoneHasActiveStatusPredicateWithResult.class);
}
@Override

View File

@ -0,0 +1,89 @@
/**
* 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.openstack.nova.v1_1.compute.predicates;
import static com.google.common.base.Preconditions.checkNotNull;
import javax.annotation.Resource;
import javax.inject.Inject;
import javax.inject.Named;
import org.jclouds.compute.domain.Image;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.logging.Logger;
import org.jclouds.openstack.nova.v1_1.NovaClient;
import org.jclouds.openstack.nova.v1_1.compute.NovaImageExtension;
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ImageInZone;
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ZoneAndId;
import org.jclouds.predicates.PredicateWithResult;
import com.google.common.base.Function;
/**
* @author David Alves
*/
public final class GetImageWhenImageInZoneHasActiveStatusPredicateWithResult implements
PredicateWithResult<ZoneAndId, Image> {
@Resource
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL;
private org.jclouds.openstack.nova.v1_1.domain.Image result;
private ZoneAndId resultZoneAndId;
private RuntimeException lastFailure;
private Function<ImageInZone, Image> imageInZoneToImage;
private NovaClient client;
@Inject
public GetImageWhenImageInZoneHasActiveStatusPredicateWithResult(Function<ImageInZone, Image> imageInZoneToImage,
NovaClient client) {
this.imageInZoneToImage = imageInZoneToImage;
this.client = client;
}
@Override
public boolean apply(ZoneAndId input) {
result = checkNotNull(NovaImageExtension.findImage(client,
ZoneAndId.fromZoneAndId(input.getZone(), input.getId())));
resultZoneAndId = input;
switch (result.getStatus()) {
case ACTIVE:
logger.info("<< Image %s is available for use.", input.getId());
return true;
case SAVING:
logger.debug("<< Image %s is not available yet.", input.getId());
return false;
default:
lastFailure = new IllegalStateException("Image was not created: " + input.getId());
throw lastFailure;
}
}
@Override
public Image getResult() {
return imageInZoneToImage.apply(new ImageInZone(result, resultZoneAndId.getZone()));
}
@Override
public Throwable getLastFailure() {
return lastFailure;
}
}

View File

@ -0,0 +1,70 @@
package org.jclouds.openstack.nova.v1_1.compute.predicates;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
import java.util.Map;
import org.jclouds.compute.ComputeServiceContext;
import org.jclouds.compute.domain.Image;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ZoneAndId;
import org.jclouds.openstack.nova.v1_1.internal.BaseNovaComputeServiceContextExpectTest;
import org.jclouds.predicates.PredicateWithResult;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMap;
import com.google.inject.Injector;
@Test(groups = "unit", testName = "GetImageWhenImageInZoneHasActiveStatucPredicateWithResultExpectTest")
public class GetImageWhenImageInZoneHasActiveStatusPredicateWithResultExpectTest extends
BaseNovaComputeServiceContextExpectTest<Injector> {
private final HttpResponse listImagesDetailImageExtensionResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResource("/image_list_detail_imageextension.json")).build();
private Map<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder()
.put(keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess)
.put(listImagesDetail, listImagesDetailImageExtensionResponse).build();
public void testReturnsFalseOnImageStatusSavingAndTrueOnActive() {
Injector injector = requestsSendResponses(requestResponseMap);
PredicateWithResult<ZoneAndId, Image> predicate = injector
.getInstance(GetImageWhenImageInZoneHasActiveStatusPredicateWithResult.class);
ZoneAndId zoneAdnId0 = ZoneAndId.fromZoneAndId("az-1.region-a.geo-1", "13");
ZoneAndId zoneAdnId1 = ZoneAndId.fromZoneAndId("az-1.region-a.geo-1", "12");
assertTrue(!predicate.apply(zoneAdnId1));
assertTrue(predicate.apply(zoneAdnId0));
assertEquals("natty-server-cloudimg-amd64", predicate.getResult().getName());
}
public void testFailsOnOtherStatuses() {
Injector injector = requestsSendResponses(requestResponseMap);
PredicateWithResult<ZoneAndId, Image> predicate = injector
.getInstance(GetImageWhenImageInZoneHasActiveStatusPredicateWithResult.class);
ZoneAndId zoneAdnId0 = ZoneAndId.fromZoneAndId("az-1.region-a.geo-1", "15");
ZoneAndId zoneAdnId1 = ZoneAndId.fromZoneAndId("az-1.region-a.geo-1", "14");
ZoneAndId zoneAdnId2 = ZoneAndId.fromZoneAndId("az-1.region-a.geo-1", "11");
ZoneAndId zoneAdnId3 = ZoneAndId.fromZoneAndId("az-1.region-a.geo-1", "10");
assertTrue(illegalStateExceptionThrown(predicate, zoneAdnId0));
assertTrue(illegalStateExceptionThrown(predicate, zoneAdnId1));
assertTrue(illegalStateExceptionThrown(predicate, zoneAdnId2));
assertTrue(illegalStateExceptionThrown(predicate, zoneAdnId3));
}
private boolean illegalStateExceptionThrown(PredicateWithResult<ZoneAndId, Image> predicate, ZoneAndId zoneAndId) {
try {
predicate.apply(zoneAndId);
} catch (IllegalStateException e) {
return true;
}
return false;
}
@Override
public Injector apply(ComputeServiceContext input) {
return input.utils().injector();
}
}

View File

@ -0,0 +1,121 @@
{
"images": [{
"status": "UNRECOGNIZED",
"updated": "2012-02-02T19:11:00Z",
"name": "oneiric-server-cloudimg-amd64",
"links": [{
"href": "https://nova-api.trystack.org:9774/v1.1/37/images/15",
"rel": "self"
}, {
"href": "https://nova-api.trystack.org:9774/37/images/15",
"rel": "bookmark"
}],
"created": "2012-02-02T19:10:52Z",
"progress": 100,
"id": "15",
"metadata": {
"kernel_id": "14",
"min_disk": 0,
"min_ram": 0,
"owner": "1"
}
}, {
"status": "UNKNOWN",
"updated": "2012-02-02T19:10:51Z",
"name": "oneiric-server-cloudimg-amd64-kernel",
"links": [{
"href": "https://nova-api.trystack.org:9774/v1.1/37/images/14",
"rel": "self"
}, {
"href": "https://nova-api.trystack.org:9774/37/images/14",
"rel": "bookmark"
}],
"created": "2012-02-02T19:10:50Z",
"progress": 100,
"id": "14",
"metadata": {
"min_disk": 0,
"owner": "1",
"min_ram": 0
}
}, {
"status": "ACTIVE",
"updated": "2012-02-02T19:10:41Z",
"name": "natty-server-cloudimg-amd64",
"links": [{
"href": "https://nova-api.trystack.org:9774/v1.1/37/images/13",
"rel": "self"
}, {
"href": "https://nova-api.trystack.org:9774/37/images/13",
"rel": "bookmark"
}],
"created": "2012-02-02T19:10:33Z",
"progress": 100,
"id": "13",
"metadata": {
"kernel_id": "12",
"min_disk": 0,
"min_ram": 0,
"owner": "1"
}
}, {
"status": "SAVING",
"updated": "2012-02-02T19:10:33Z",
"name": "natty-server-cloudimg-amd64-kernel",
"links": [{
"href": "https://nova-api.trystack.org:9774/v1.1/37/images/12",
"rel": "self"
}, {
"href": "https://nova-api.trystack.org:9774/37/images/12",
"rel": "bookmark"
}],
"created": "2012-02-02T19:10:32Z",
"progress": 100,
"id": "12",
"metadata": {
"min_disk": 0,
"owner": "1",
"min_ram": 0
}
}, {
"status": "ERROR",
"updated": "2012-02-02T19:10:41Z",
"name": "natty-server-cloudimg-amd64",
"links": [{
"href": "https://nova-api.trystack.org:9774/v1.1/37/images/11",
"rel": "self"
}, {
"href": "https://nova-api.trystack.org:9774/37/images/11",
"rel": "bookmark"
}],
"created": "2012-02-02T19:10:33Z",
"progress": 100,
"id": "11",
"metadata": {
"kernel_id": "12",
"min_disk": 0,
"min_ram": 0,
"owner": "1"
}
}, {
"status": "ERROR",
"updated": "2012-02-02T19:10:41Z",
"name": "natty-server-cloudimg-amd64",
"links": [{
"href": "https://nova-api.trystack.org:9774/v1.1/37/images/10",
"rel": "self"
}, {
"href": "https://nova-api.trystack.org:9774/37/images/10",
"rel": "bookmark"
}],
"created": "2012-02-02T19:10:33Z",
"progress": 100,
"id": "10",
"metadata": {
"kernel_id": "12",
"min_disk": 0,
"min_ram": 0,
"owner": "1"
}
}]
}

View File

@ -22,10 +22,10 @@ import com.google.common.annotations.Beta;
import com.google.common.base.Predicate;
@Beta
public interface PredicateWithResult<Input,Result> extends Predicate<Input> {
public interface PredicateWithResult<Input, Result> extends Predicate<Input> {
Result getResult();
Throwable getLastFailure();
}