JCLOUDS-946: Properly scope images to the locations where they are available

This commit is contained in:
Ignasi Barrera 2015-06-28 23:51:08 +02:00
parent 057be8df99
commit 26210fe098
13 changed files with 255 additions and 126 deletions

View File

@ -36,7 +36,7 @@
<test.digitalocean2.api-version>2</test.digitalocean2.api-version>
<test.digitalocean2.identity>FIXME</test.digitalocean2.identity>
<test.digitalocean2.credential>FIXME</test.digitalocean2.credential>
<test.digitalocean2.template>imageId=ubuntu-14-04-x64</test.digitalocean2.template>
<test.digitalocean2.template>osFamily=UBUNTU,os64Bit=true</test.digitalocean2.template>
</properties>
<dependencies>

View File

@ -65,7 +65,7 @@ public class DigitalOcean2ApiMetadata extends BaseHttpApiMetadata<DigitalOcean2A
properties.put(AUDIENCE, "https://cloud.digitalocean.com/v1/oauth/token");
properties.put(CREDENTIAL_TYPE, BEARER_TOKEN_CREDENTIALS.toString());
properties.put(PROPERTY_SESSION_INTERVAL, 3600);
properties.put(TEMPLATE, "imageId=ubuntu-14-04-x64");
properties.put(TEMPLATE, "osFamily=UBUNTU,os64Bit=true");
properties.put(POLL_INITIAL_PERIOD, 5000);
properties.put(POLL_MAX_PERIOD, 20000);
return properties;

View File

@ -17,13 +17,19 @@
package org.jclouds.digitalocean2.compute;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Predicates.notNull;
import static com.google.common.collect.Iterables.concat;
import static com.google.common.collect.Iterables.contains;
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.collect.Sets.newHashSet;
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_RUNNING;
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_SUSPENDED;
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_TERMINATED;
import java.util.Set;
import javax.annotation.Resource;
import javax.inject.Inject;
import javax.inject.Named;
@ -32,6 +38,7 @@ import org.jclouds.compute.ComputeServiceAdapter;
import org.jclouds.compute.domain.Template;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.digitalocean2.DigitalOcean2Api;
import org.jclouds.digitalocean2.compute.internal.ImageInRegion;
import org.jclouds.digitalocean2.compute.options.DigitalOcean2TemplateOptions;
import org.jclouds.digitalocean2.domain.Action;
import org.jclouds.digitalocean2.domain.Droplet;
@ -43,13 +50,14 @@ import org.jclouds.digitalocean2.domain.options.CreateDropletOptions;
import org.jclouds.domain.LoginCredentials;
import org.jclouds.logging.Logger;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.primitives.Ints;
/**
* Implementation of the Compute Service for the DigitalOcean API.
*/
public class DigitalOcean2ComputeServiceAdapter implements ComputeServiceAdapter<Droplet, Size, Image, Region> {
public class DigitalOcean2ComputeServiceAdapter implements ComputeServiceAdapter<Droplet, Size, ImageInRegion, Region> {
@Resource
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
@ -101,8 +109,30 @@ public class DigitalOcean2ComputeServiceAdapter implements ComputeServiceAdapter
}
@Override
public Iterable<Image> listImages() {
return api.imageApi().list().concat();
public Iterable<ImageInRegion> listImages() {
// Images can claim to be available in a region that is currently marked as "unavailable". We shouldn't return
// the images scoped to those regions.
final Set<String> availableRegionsIds = newHashSet(transform(listLocations(), new Function<Region, String>() {
@Override
public String apply(Region input) {
return input.slug();
}
}));
// Public images re globally available, but non-public ones can only be available in certain regions.
// For these kind of images, return one instance of an ImageInRegion for each region where the image is
// available. This way we can properly scope global and concrete images so they can be properly looked up.
return concat(filter(api.imageApi().list().concat().transform(new Function<Image, Iterable<ImageInRegion>>() {
@Override
public Iterable<ImageInRegion> apply(final Image image) {
return transform(image.regions(), new Function<String, ImageInRegion>() {
@Override
public ImageInRegion apply(String region) {
return availableRegionsIds.contains(region) ? ImageInRegion.create(image, region) : null;
}
});
}
}), notNull()));
}
@Override
@ -142,11 +172,14 @@ public class DigitalOcean2ComputeServiceAdapter implements ComputeServiceAdapter
}
@Override
public Image getImage(String id) {
public ImageInRegion getImage(String id) {
String region = ImageInRegion.extractRegion(id);
String imageId = ImageInRegion.extractImageId(id);
// The id of the image can be an id or a slug. Use the corresponding method of the API depending on what is
// provided. If it can be parsed as a number, use the method to get by ID. Otherwise, get by slug.
Integer imageId = Ints.tryParse(id);
return imageId != null ? api.imageApi().get(imageId) : api.imageApi().get(id);
Integer numericId = Ints.tryParse(imageId);
Image image = numericId == null ? api.imageApi().get(imageId) : api.imageApi().get(numericId);
return ImageInRegion.create(image, region);
}
@Override

View File

@ -28,6 +28,7 @@ import javax.inject.Singleton;
import org.jclouds.compute.ComputeServiceAdapter;
import org.jclouds.compute.config.ComputeServiceAdapterContextModule;
import org.jclouds.compute.domain.Hardware;
import org.jclouds.compute.domain.Image;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.NodeMetadata.Status;
import org.jclouds.compute.extensions.ImageExtension;
@ -41,15 +42,15 @@ import org.jclouds.digitalocean2.compute.DigitalOcean2ComputeServiceAdapter;
import org.jclouds.digitalocean2.compute.extensions.DigitalOcean2ImageExtension;
import org.jclouds.digitalocean2.compute.functions.DropletStatusToStatus;
import org.jclouds.digitalocean2.compute.functions.DropletToNodeMetadata;
import org.jclouds.digitalocean2.compute.functions.ImageToImage;
import org.jclouds.digitalocean2.compute.functions.ImageInRegionToImage;
import org.jclouds.digitalocean2.compute.functions.RegionToLocation;
import org.jclouds.digitalocean2.compute.functions.SizeToHardware;
import org.jclouds.digitalocean2.compute.functions.TemplateOptionsToStatementWithoutPublicKey;
import org.jclouds.digitalocean2.compute.internal.ImageInRegion;
import org.jclouds.digitalocean2.compute.options.DigitalOcean2TemplateOptions;
import org.jclouds.digitalocean2.compute.strategy.CreateKeyPairsThenCreateNodes;
import org.jclouds.digitalocean2.domain.Action;
import org.jclouds.digitalocean2.domain.Droplet;
import org.jclouds.digitalocean2.domain.Image;
import org.jclouds.digitalocean2.domain.Region;
import org.jclouds.digitalocean2.domain.Size;
import org.jclouds.domain.Location;
@ -67,19 +68,19 @@ import com.google.inject.name.Named;
* Configures the compute service classes for the DigitalOcean API.
*/
public class DigitalOcean2ComputeServiceContextModule extends
ComputeServiceAdapterContextModule<Droplet, Size, Image, Region> {
ComputeServiceAdapterContextModule<Droplet, Size, ImageInRegion, Region> {
@Override
protected void configure() {
super.configure();
bind(new TypeLiteral<ComputeServiceAdapter<Droplet, Size, Image, Region>>() {
bind(new TypeLiteral<ComputeServiceAdapter<Droplet, Size, ImageInRegion, Region>>() {
}).to(DigitalOcean2ComputeServiceAdapter.class);
bind(new TypeLiteral<Function<Droplet, NodeMetadata>>() {
}).to(DropletToNodeMetadata.class);
bind(new TypeLiteral<Function<Image, org.jclouds.compute.domain.Image>>() {
}).to(ImageToImage.class);
bind(new TypeLiteral<Function<ImageInRegion, Image>>() {
}).to(ImageInRegionToImage.class);
bind(new TypeLiteral<Function<Region, Location>>() {
}).to(RegionToLocation.class);
bind(new TypeLiteral<Function<Size, Hardware>>() {
@ -87,7 +88,7 @@ public class DigitalOcean2ComputeServiceContextModule extends
bind(new TypeLiteral<Function<Droplet.Status, Status>>() {
}).to(DropletStatusToStatus.class);
install(new LocationsFromComputeServiceAdapterModule<Droplet, Size, Image, Region>() {
install(new LocationsFromComputeServiceAdapterModule<Droplet, Size, ImageInRegion, Region>() {
});
bind(CreateNodesInGroupThenAddToSet.class).to(CreateKeyPairsThenCreateNodes.class);

View File

@ -35,6 +35,7 @@ import org.jclouds.compute.domain.ImageTemplateBuilder;
import org.jclouds.compute.extensions.ImageExtension;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.digitalocean2.DigitalOcean2Api;
import org.jclouds.digitalocean2.compute.internal.ImageInRegion;
import org.jclouds.digitalocean2.domain.Action;
import org.jclouds.digitalocean2.domain.Droplet;
import org.jclouds.digitalocean2.domain.Droplet.Status;
@ -58,12 +59,12 @@ public class DigitalOcean2ImageExtension implements ImageExtension {
private final DigitalOcean2Api api;
private final Predicate<Integer> imageAvailablePredicate;
private final Predicate<Integer> nodeStoppedPredicate;
private final Function<org.jclouds.digitalocean2.domain.Image, Image> imageTransformer;
private final Function<ImageInRegion, Image> imageTransformer;
@Inject DigitalOcean2ImageExtension(DigitalOcean2Api api,
@Named(TIMEOUT_IMAGE_AVAILABLE) Predicate<Integer> imageAvailablePredicate,
@Named(TIMEOUT_NODE_SUSPENDED) Predicate<Integer> nodeStoppedPredicate,
Function<org.jclouds.digitalocean2.domain.Image, Image> imageTransformer) {
Function<ImageInRegion, Image> imageTransformer) {
this.api = api;
this.imageAvailablePredicate = imageAvailablePredicate;
this.nodeStoppedPredicate = nodeStoppedPredicate;
@ -111,7 +112,8 @@ public class DigitalOcean2ImageExtension implements ImageExtension {
}
}).get();
return immediateFuture(imageTransformer.apply(snapshot));
// By default snapshots are only available in the Droplet's region
return immediateFuture(imageTransformer.apply(ImageInRegion.create(snapshot, droplet.region().slug())));
}
@Override

View File

@ -18,10 +18,11 @@ package org.jclouds.digitalocean2.compute.functions;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Iterables.find;
import static com.google.common.collect.Iterables.tryFind;
import static org.jclouds.digitalocean2.compute.internal.ImageInRegion.encodeId;
import java.util.Map;
import java.util.Set;
import javax.annotation.Resource;
import javax.inject.Inject;
import javax.inject.Named;
@ -35,6 +36,7 @@ import org.jclouds.compute.domain.NodeMetadata.Status;
import org.jclouds.compute.domain.NodeMetadataBuilder;
import org.jclouds.compute.functions.GroupNamingConvention;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.digitalocean2.compute.internal.ImageInRegion;
import org.jclouds.digitalocean2.domain.Droplet;
import org.jclouds.digitalocean2.domain.Networks;
import org.jclouds.digitalocean2.domain.Region;
@ -42,6 +44,7 @@ import org.jclouds.domain.Credentials;
import org.jclouds.domain.Location;
import org.jclouds.domain.LoginCredentials;
import org.jclouds.logging.Logger;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
@ -91,7 +94,7 @@ public class DropletToNodeMetadata implements Function<Droplet, NodeMetadata> {
builder.hardware(getHardware(input.sizeSlug()));
builder.location(getLocation(input.region()));
Optional<? extends Image> image = findImage(input.image().id());
Optional<? extends Image> image = findImage(input.image(), input.region().slug());
if (image.isPresent()) {
builder.imageId(image.get().getId());
builder.operatingSystem(image.get().getOperatingSystem());
@ -138,22 +141,8 @@ public class DropletToNodeMetadata implements Function<Droplet, NodeMetadata> {
return builder.build();
}
protected Optional<? extends Image> findImage(Integer id) {
// Try to find the image by ID in the cache. The cache is indexed by slug (for public images) and by id (for
// private ones).
final String imageId = String.valueOf(id);
Optional<? extends Image> image = Optional.fromNullable(images.get().get(imageId));
if (!image.isPresent()) {
// If it is a public image (indexed by slug) but the "int" form of the id was provided, try to find it in the
// whole list of cached images
image = tryFind(images.get().values(), new Predicate<Image>() {
@Override
public boolean apply(Image input) {
return input.getProviderId().equals(imageId);
}
});
}
return image;
protected Optional<? extends Image> findImage(org.jclouds.digitalocean2.domain.Image image, String region) {
return Optional.fromNullable(images.get().get(encodeId(ImageInRegion.create(image, region))));
}
protected Hardware getHardware(final String slug) {

View File

@ -0,0 +1,92 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.digitalocean2.compute.functions;
import static com.google.common.collect.Iterables.find;
import static org.jclouds.compute.domain.OperatingSystem.builder;
import static org.jclouds.digitalocean2.compute.internal.ImageInRegion.encodeId;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.jclouds.collect.Memoized;
import org.jclouds.compute.domain.Image;
import org.jclouds.compute.domain.Image.Status;
import org.jclouds.compute.domain.ImageBuilder;
import org.jclouds.digitalocean2.compute.internal.ImageInRegion;
import org.jclouds.digitalocean2.domain.OperatingSystem;
import org.jclouds.domain.Location;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableMap;
/**
* Transforms an {@link ImageInRegion} to the jclouds portable model.
*/
@Singleton
public class ImageInRegionToImage implements Function<ImageInRegion, Image> {
private final Supplier<Set<? extends Location>> locations;
@Inject ImageInRegionToImage(@Memoized Supplier<Set<? extends Location>> locations) {
this.locations = locations;
}
@Override
public Image apply(final ImageInRegion input) {
String description = input.image().distribution() + " " + input.image().name();
ImageBuilder builder = new ImageBuilder();
// Private images don't have a slug
builder.id(encodeId(input));
builder.providerId(String.valueOf(input.image().id()));
builder.name(input.image().name());
builder.description(description);
builder.status(Status.AVAILABLE);
builder.location(getLocation(input.region()));
OperatingSystem os = OperatingSystem.create(input.image().name(), input.image().distribution());
builder.operatingSystem(builder()
.name(os.distribution().value())
.family(os.distribution().osFamily())
.description(description)
.arch(os.arch())
.version(os.version())
.is64Bit(os.is64bit())
.build());
ImmutableMap.Builder<String, String> metadata = ImmutableMap.builder();
metadata.put("publicImage", String.valueOf(input.image().isPublic()));
builder.userMetadata(metadata.build());
return builder.build();
}
protected Location getLocation(final String region) {
return find(locations.get(), new Predicate<Location>() {
@Override
public boolean apply(Location location) {
return region.equals(location.getId());
}
});
}
}

View File

@ -1,65 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.digitalocean2.compute.functions;
import static org.jclouds.compute.domain.OperatingSystem.builder;
import javax.inject.Singleton;
import org.jclouds.compute.domain.Image.Status;
import org.jclouds.compute.domain.ImageBuilder;
import org.jclouds.digitalocean2.domain.Image;
import org.jclouds.digitalocean2.domain.OperatingSystem;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableMap;
/**
* Transforms an {@link Image} to the jclouds portable model.
*/
@Singleton
public class ImageToImage implements Function<Image, org.jclouds.compute.domain.Image> {
@Override
public org.jclouds.compute.domain.Image apply(final Image input) {
String description = input.distribution() + " " + input.name();
ImageBuilder builder = new ImageBuilder();
// Private images don't have a slug
builder.id(input.slug() != null ? input.slug() : String.valueOf(input.id()));
builder.providerId(String.valueOf(input.id()));
builder.name(input.name());
builder.description(description);
builder.status(Status.AVAILABLE);
OperatingSystem os = OperatingSystem.create(input.name(), input.distribution());
builder.operatingSystem(builder()
.name(os.distribution().value())
.family(os.distribution().osFamily())
.description(description)
.arch(os.arch())
.version(os.version())
.is64Bit(os.is64bit())
.build());
ImmutableMap.Builder<String, String> metadata = ImmutableMap.builder();
metadata.put("publicImage", String.valueOf(input.isPublic()));
builder.userMetadata(metadata.build());
return builder.build();
}
}

View File

@ -0,0 +1,54 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.digitalocean2.compute.internal;
import org.jclouds.digitalocean2.domain.Image;
import com.google.auto.value.AutoValue;
/**
* Scopes an image to a particular region.
*/
@AutoValue
public abstract class ImageInRegion {
public abstract Image image();
public abstract String region();
public static ImageInRegion create(Image image, String region) {
return new AutoValue_ImageInRegion(image, region);
}
public static String encodeId(ImageInRegion imageInRegion) {
// Private images don't have a slug
return String.format("%s/%s", imageInRegion.region(), slugOrId(imageInRegion.image()));
}
public static String extractRegion(String imageId) {
return imageId.substring(0, imageId.indexOf('/'));
}
public static String extractImageId(String imageId) {
return imageId.substring(imageId.indexOf('/') + 1);
}
private static String slugOrId(Image image) {
return image.slug() != null ? image.slug() : String.valueOf(image.id());
}
ImageInRegion() { }
}

View File

@ -1,18 +0,0 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF 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.
#
org.jclouds.digitalocean2.DigitalOcean2ApiMetadata

View File

@ -40,7 +40,7 @@ public class DigitalOcean2TemplateBuilderLiveTest extends BaseTemplateBuilderLiv
@Override
public void testDefaultTemplateBuilder() throws IOException {
Template defaultTemplate = view.getComputeService().templateBuilder().build();
assert defaultTemplate.getImage().getOperatingSystem().getVersion().equals("14.04") : defaultTemplate
assert defaultTemplate.getImage().getOperatingSystem().getVersion().equals("15.04") : defaultTemplate
.getImage().getOperatingSystem().getVersion();
assertEquals(defaultTemplate.getImage().getOperatingSystem().is64Bit(), true);
assertEquals(defaultTemplate.getImage().getOperatingSystem().getFamily(), OsFamily.UBUNTU);

View File

@ -83,7 +83,7 @@ public class DropletToNodeMetadataTest {
region = Region.create("sfo1", "San Francisco 1", ImmutableList.of("2gb"), true, ImmutableList.<String> of());
images = ImmutableSet.of(new ImageBuilder()
.id("ubuntu-1404-x86")
.id("sfo1/ubuntu-1404-x86")
.providerId("1")
.name("mock image")
.status(AVAILABLE)
@ -132,7 +132,7 @@ public class DropletToNodeMetadataTest {
ImmutableList.<Networks.Address> of()), null);
NodeMetadata expected = new NodeMetadataBuilder().ids("1").hardware(getOnlyElement(hardwares))
.imageId("ubuntu-1404-x86").status(RUNNING).location(getOnlyElement(locations)).name("mock-droplet")
.imageId("sfo1/ubuntu-1404-x86").status(RUNNING).location(getOnlyElement(locations)).name("mock-droplet")
.hostname("mock-droplet").group("mock").credentials(credentials)
.publicAddresses(ImmutableSet.of("84.45.69.3")).privateAddresses(ImmutableSet.of("192.168.2.5"))
.providerId("1").backendStatus(ACTIVE.name()).operatingSystem(getOnlyElement(images).getOperatingSystem())

View File

@ -20,25 +20,64 @@ import static org.jclouds.compute.domain.Image.Status.AVAILABLE;
import static org.testng.Assert.assertEquals;
import java.util.Date;
import java.util.Set;
import org.jclouds.compute.domain.ImageBuilder;
import org.jclouds.compute.domain.OperatingSystem;
import org.jclouds.compute.domain.OsFamily;
import org.jclouds.digitalocean2.compute.internal.ImageInRegion;
import org.jclouds.digitalocean2.domain.Image;
import org.jclouds.domain.Location;
import org.jclouds.domain.LocationBuilder;
import org.jclouds.domain.LocationScope;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
@Test(groups = "unit", testName = "ImageToImageTest")
public class ImageToImageTest {
public class ImageInRegionToImageTest {
private Set<Location> locations;
private ImageInRegionToImage function;
@BeforeMethod
public void setup() {
locations = ImmutableSet.of(
new LocationBuilder()
.id("sfo1")
.description("sfo1/San Francisco 1")
.scope(LocationScope.REGION)
.parent(
new LocationBuilder().id("0").description("mock parent location").scope(LocationScope.PROVIDER)
.build()).build(),
new LocationBuilder()
.id("lon1")
.description("lon1/London 1")
.scope(LocationScope.REGION)
.parent(
new LocationBuilder().id("0").description("mock parent location").scope(LocationScope.PROVIDER)
.build()).build());
function = new ImageInRegionToImage(new Supplier<Set<? extends Location>>() {
@Override
public Set<? extends Location> get() {
return locations;
}
});
}
@Test
public void testConvertImage() {
Image image = Image.create(1, "14.04 x64", "distribution", "Ubuntu", "ubuntu-1404-x86", true,
ImmutableList.of("sfo1"), new Date());
ImmutableList.of("sfo1", "lon1"), new Date());
org.jclouds.compute.domain.Image expected = new ImageBuilder()
.id("ubuntu-1404-x86")
.id("lon1/ubuntu-1404-x86") // Location scoped images have the location encoded in the id
.providerId("1")
.name("14.04 x64")
.description("Ubuntu 14.04 x64")
@ -46,12 +85,14 @@ public class ImageToImageTest {
.operatingSystem(
OperatingSystem.builder().name("Ubuntu").description("Ubuntu 14.04 x64").family(OsFamily.UBUNTU)
.version("14.04").arch("x64").is64Bit(true).build())
.location(Iterables.get(locations, 1))
.userMetadata(ImmutableMap.of("publicImage", "true")).build();
org.jclouds.compute.domain.Image result = new ImageToImage().apply(image);
org.jclouds.compute.domain.Image result = function.apply(ImageInRegion.create(image, "lon1"));
assertEquals(result, expected);
assertEquals(result.getDescription(), expected.getDescription());
assertEquals(result.getOperatingSystem(), expected.getOperatingSystem());
assertEquals(result.getStatus(), expected.getStatus());
assertEquals(result.getLocation(), Iterables.get(locations, 1));
}
}