mirror of https://github.com/apache/jclouds.git
added expect test
This commit is contained in:
parent
082158ac3f
commit
176647110a
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}]
|
||||
}
|
|
@ -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();
|
||||
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue