diff --git a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/NovaComputeService.java b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/NovaComputeService.java index 32af8e9fee..3af389417d 100644 --- a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/NovaComputeService.java +++ b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/NovaComputeService.java @@ -54,10 +54,13 @@ import org.jclouds.domain.Credentials; import org.jclouds.domain.Location; import org.jclouds.openstack.nova.v1_1.NovaClient; import org.jclouds.openstack.nova.v1_1.compute.options.NovaTemplateOptions; +import org.jclouds.openstack.nova.v1_1.domain.KeyPair; import org.jclouds.openstack.nova.v1_1.domain.SecurityGroup; import org.jclouds.openstack.nova.v1_1.domain.zonescoped.SecurityGroupInZone; import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ZoneAndName; +import org.jclouds.openstack.nova.v1_1.extensions.KeyPairClient; import org.jclouds.openstack.nova.v1_1.extensions.SecurityGroupClient; +import org.jclouds.openstack.nova.v1_1.predicates.KeyPairPredicates; import org.jclouds.openstack.nova.v1_1.predicates.SecurityGroupPredicates; import org.jclouds.scriptbuilder.functions.InitAdminAccess; @@ -77,6 +80,7 @@ import com.google.common.collect.Multimap; public class NovaComputeService extends BaseComputeService { private final NovaClient novaClient; private final LoadingCache securityGroupMap; + private final LoadingCache keyPairCache; private final Function, Multimap> orphanedGroupsByZoneId; @Inject @@ -96,6 +100,7 @@ public class NovaComputeService extends BaseComputeService { PersistNodeCredentials persistNodeCredentials, Timeouts timeouts, @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, NovaClient novaClient, LoadingCache securityGroupMap, + LoadingCache keyPairCache, Function, Multimap> orphanedGroupsByZoneId) { super(context, credentialStore, images, sizes, locations, listNodesStrategy, getNodeMetadataStrategy, runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy, startNodeStrategy, @@ -104,6 +109,7 @@ public class NovaComputeService extends BaseComputeService { timeouts, executor); this.novaClient = checkNotNull(novaClient, "novaClient"); this.securityGroupMap = checkNotNull(securityGroupMap, "securityGroupMap"); + this.keyPairCache = checkNotNull(keyPairCache, "keyPairCache"); this.orphanedGroupsByZoneId = checkNotNull(orphanedGroupsByZoneId, "orphanedGroupsByZoneId"); } @@ -117,6 +123,7 @@ public class NovaComputeService extends BaseComputeService { protected void cleanOrphanedGroupsInZone(Set groups, String zoneId) { cleanupOrphanedSecurityGroupsInZone(groups, zoneId); + cleanupOrphanedKeyPairsInZone(groups, zoneId); } private void cleanupOrphanedSecurityGroupsInZone(Set groups, String zoneId) { @@ -135,6 +142,24 @@ public class NovaComputeService extends BaseComputeService { } } } + + private void cleanupOrphanedKeyPairsInZone(Set groups, String zoneId) { + Optional keyPairClient = novaClient.getKeyPairExtensionForZone(zoneId); + if (keyPairClient.isPresent()) { + for (String group : groups) { + for (Map wrapper : keyPairClient.get().listKeyPairs()) { + for (KeyPair pair : Iterables.filter(wrapper.values(), KeyPairPredicates.nameStartsWith("jclouds#" + group + "#"))) { + ZoneAndName zoneAndName = ZoneAndName.fromZoneAndName(zoneId, pair.getName()); + logger.debug(">> deleting keypair(%s)", zoneAndName); + keyPairClient.get().deleteKeyPair(pair.getName()); + // TODO: test this clear happens + keyPairCache.invalidate(zoneAndName); + logger.debug("<< deleted keypair(%s)", zoneAndName); + } + } + } + } + } /** * returns template options, except of type {@link NovaTemplateOptions}. diff --git a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/NovaComputeServiceAdapter.java b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/NovaComputeServiceAdapter.java index 0287c63a83..208f48fd1f 100644 --- a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/NovaComputeServiceAdapter.java +++ b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/NovaComputeServiceAdapter.java @@ -26,6 +26,7 @@ import javax.annotation.Resource; import javax.inject.Inject; import javax.inject.Named; +import com.google.common.cache.LoadingCache; import org.jclouds.compute.ComputeServiceAdapter; import org.jclouds.compute.domain.Template; import org.jclouds.compute.reference.ComputeServiceConstants; @@ -39,12 +40,14 @@ import org.jclouds.openstack.nova.v1_1.compute.options.NovaTemplateOptions; import org.jclouds.openstack.nova.v1_1.compute.strategy.ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet; import org.jclouds.openstack.nova.v1_1.domain.Flavor; import org.jclouds.openstack.nova.v1_1.domain.Image; +import org.jclouds.openstack.nova.v1_1.domain.KeyPair; import org.jclouds.openstack.nova.v1_1.domain.RebootType; import org.jclouds.openstack.nova.v1_1.domain.Server; import org.jclouds.openstack.nova.v1_1.domain.zonescoped.FlavorInZone; import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ImageInZone; import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ServerInZone; 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.options.CreateServerOptions; import org.jclouds.openstack.nova.v1_1.predicates.ImagePredicates; @@ -70,14 +73,17 @@ public class NovaComputeServiceAdapter implements protected final NovaClient novaClient; protected final Supplier> zoneIds; protected final RemoveFloatingIpFromNodeAndDeallocate removeFloatingIpFromNodeAndDeallocate; + protected final LoadingCache keyPairCache; @Inject public NovaComputeServiceAdapter(NovaClient novaClient, @Zone Supplier> zoneIds, - RemoveFloatingIpFromNodeAndDeallocate removeFloatingIpFromNodeAndDeallocate) { + RemoveFloatingIpFromNodeAndDeallocate removeFloatingIpFromNodeAndDeallocate, + LoadingCache keyPairCache) { this.novaClient = checkNotNull(novaClient, "novaClient"); this.zoneIds = checkNotNull(zoneIds, "zoneIds"); this.removeFloatingIpFromNodeAndDeallocate = checkNotNull(removeFloatingIpFromNodeAndDeallocate, "removeFloatingIpFromNodeAndDeallocate"); + this.keyPairCache = checkNotNull(keyPairCache, "keyPairCache"); } /** @@ -89,15 +95,26 @@ public class NovaComputeServiceAdapter implements public NodeAndInitialCredentials createNodeWithGroupEncodedIntoName(String group, String name, Template template) { + LoginCredentials.Builder credentialsBuilder = LoginCredentials.builder(); + NovaTemplateOptions templateOptions = template.getOptions().as(NovaTemplateOptions.class); CreateServerOptions options = new CreateServerOptions(); - options.securityGroupNames(template.getOptions().as(NovaTemplateOptions.class).getSecurityGroupNames()); + options.securityGroupNames(templateOptions.getSecurityGroupNames()); + + String keyName = templateOptions.getKeyPairName(); + if (keyName != null) { + options.withKeyName(keyName); + KeyPair keyPair = keyPairCache.getIfPresent(ZoneAndName.fromZoneAndName(template.getLocation().getId(), keyName)); + if (keyPair != null) { + credentialsBuilder.privateKey(keyPair.getPrivateKey()); + } + } String zoneId = template.getLocation().getId(); Server server = novaClient.getServerClientForZone(zoneId).createServer(name, template.getImage().getProviderId(), template.getHardware().getProviderId(), options); ServerInZone serverInZone = new ServerInZone(server, zoneId); - return new NodeAndInitialCredentials(serverInZone, serverInZone.slashEncode(), LoginCredentials - .builder().password(server.getAdminPass()).build()); + return new NodeAndInitialCredentials(serverInZone, serverInZone.slashEncode(), credentialsBuilder + .password(server.getAdminPass()).build()); } @Override diff --git a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/config/NovaComputeServiceContextModule.java b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/config/NovaComputeServiceContextModule.java index 7141ab57d9..b4ac82de48 100644 --- a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/config/NovaComputeServiceContextModule.java +++ b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/config/NovaComputeServiceContextModule.java @@ -18,6 +18,7 @@ */ package org.jclouds.openstack.nova.v1_1.compute.config; +import java.security.SecureRandom; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -42,16 +43,13 @@ import org.jclouds.openstack.nova.v1_1.NovaAsyncClient; import org.jclouds.openstack.nova.v1_1.NovaClient; import org.jclouds.openstack.nova.v1_1.compute.NovaComputeService; import org.jclouds.openstack.nova.v1_1.compute.NovaComputeServiceAdapter; -import org.jclouds.openstack.nova.v1_1.compute.functions.CreateSecurityGroupIfNeeded; -import org.jclouds.openstack.nova.v1_1.compute.functions.FlavorInZoneToHardware; -import org.jclouds.openstack.nova.v1_1.compute.functions.ImageInZoneToImage; -import org.jclouds.openstack.nova.v1_1.compute.functions.NovaImageToOperatingSystem; -import org.jclouds.openstack.nova.v1_1.compute.functions.OrphanedGroupsByZoneId; -import org.jclouds.openstack.nova.v1_1.compute.functions.ServerInZoneToNodeMetadata; +import org.jclouds.openstack.nova.v1_1.compute.functions.*; +import org.jclouds.openstack.nova.v1_1.compute.loaders.FindKeyPairOrCreate; 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.strategy.ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet; +import org.jclouds.openstack.nova.v1_1.domain.KeyPair; import org.jclouds.openstack.nova.v1_1.domain.zonescoped.FlavorInZone; import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ImageInZone; import org.jclouds.openstack.nova.v1_1.domain.zonescoped.SecurityGroupInZone; @@ -131,6 +129,11 @@ public class NovaComputeServiceContextModule bind(CreateNodesWithGroupEncodedIntoNameThenAddToSet.class).to( ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet.class); + bind(new TypeLiteral>() { + }).to(CreateUniqueKeyPair.class); + + bind(new TypeLiteral>() { + }).to(FindKeyPairOrCreate.class); } @Override @@ -166,6 +169,27 @@ public class NovaComputeServiceContextModule return new RetryablePredicate>(in, msDelay, 100l, TimeUnit.MILLISECONDS); } + @Provides + @Singleton + protected LoadingCache keyPairMap( + CacheLoader in) { + return CacheBuilder.newBuilder().build(in); + } + + @Provides + @Singleton + Supplier provideSuffix() { + return new Supplier() { + final SecureRandom random = new SecureRandom(); + + @Override + public String get() { + return random.nextInt(100) + ""; + } + }; + + } + @Provides @Singleton protected Supplier> createLocationIndexedById( diff --git a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/functions/CreateUniqueKeyPair.java b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/functions/CreateUniqueKeyPair.java new file mode 100644 index 0000000000..fab2ed35fa --- /dev/null +++ b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/functions/CreateUniqueKeyPair.java @@ -0,0 +1,82 @@ +/** + * 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.functions; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Function; +import com.google.common.base.Supplier; +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.domain.KeyPair; +import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ZoneAndName; + +import javax.annotation.Resource; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * @author Adam Lowe + */ +@Singleton +public class CreateUniqueKeyPair implements Function { + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; + protected final NovaClient novaClient; + protected final Supplier randomSuffix; + + @Inject + public CreateUniqueKeyPair(NovaClient novaClient, Supplier randomSuffix) { + this.novaClient = checkNotNull(novaClient, "novaClient"); + this.randomSuffix = randomSuffix; + } + + @Override + public KeyPair apply(ZoneAndName zoneAndName) { + checkNotNull(zoneAndName, "zoneAndName"); + return createNewKeyPairInZone(zoneAndName.getZone(), zoneAndName.getName()); + } + + @VisibleForTesting + KeyPair createNewKeyPairInZone(String zone, String group) { + checkNotNull(zone, "zone"); + checkNotNull(group, "group"); + logger.debug(">> creating keyPair zone(%s) group(%s)", zone, group); + + KeyPair keyPair = null; + while (keyPair == null) { + try { + keyPair = novaClient.getKeyPairExtensionForZone(zone).get().createKeyPair(getNextName(group)); + } catch (IllegalStateException e) { + + } + } + + logger.debug("<< created keyPair(%s)", keyPair); + return keyPair; + } + + private String getNextName(String group) { + return String.format("jclouds#%s#%s", group, randomSuffix.get()); + } +} diff --git a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/loaders/FindKeyPairOrCreate.java b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/loaders/FindKeyPairOrCreate.java new file mode 100644 index 0000000000..318e03be7e --- /dev/null +++ b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/loaders/FindKeyPairOrCreate.java @@ -0,0 +1,53 @@ +/** + * 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.loaders; + +import com.google.common.base.Function; +import com.google.common.cache.CacheLoader; +import org.jclouds.openstack.nova.v1_1.domain.KeyPair; +import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ZoneAndName; + +import javax.inject.Inject; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * @author Adam Lowe + */ +public class FindKeyPairOrCreate extends CacheLoader { + protected final Function keypairCreator; + + @Inject + public FindKeyPairOrCreate( + Function keypairCreator) { + this.keypairCreator = checkNotNull(keypairCreator, "keypairCreator"); + } + + @Override + public KeyPair load(ZoneAndName in) { + // not retrieving KeyPairs from the server as the private key isn't returned + return keypairCreator.apply(in); + } + + @Override + public String toString() { + return "returnExistingKeyPairInZoneOrCreateAsNeeded()"; + } + +} diff --git a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/options/NovaTemplateOptions.java b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/options/NovaTemplateOptions.java index 112635fe0d..12e14f52b3 100644 --- a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/options/NovaTemplateOptions.java +++ b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/options/NovaTemplateOptions.java @@ -72,6 +72,7 @@ public class NovaTemplateOptions extends TemplateOptions implements Cloneable { private boolean autoAssignFloatingIp = false; private boolean generateKeyPair = false; private Set securityGroupNames = ImmutableSet.of(); + private String keyPairName; public static final NovaTemplateOptions NONE = new NovaTemplateOptions(); @@ -91,6 +92,14 @@ public class NovaTemplateOptions extends TemplateOptions implements Cloneable { return this; } + /** + * @see #shouldGenerateKeyPair() + */ + public NovaTemplateOptions keyPairName(String keyPairName) { + this.keyPairName = keyPairName; + return this; + } + /** * * @see org.jclouds.openstack.nova.v1_1.options.CreateServerOptions#getSecurityGroupNames @@ -121,6 +130,14 @@ public class NovaTemplateOptions extends TemplateOptions implements Cloneable { return autoAssignFloatingIp; } + /** + * Specifies the keypair used to run instances with + * @return the keypair to be used + */ + public String getKeyPairName() { + return keyPairName; + } + /** *

Note

* @@ -155,7 +172,14 @@ public class NovaTemplateOptions extends TemplateOptions implements Cloneable { public static NovaTemplateOptions generateKeyPair(boolean enable) { return new NovaTemplateOptions().generateKeyPair(enable); } - + + /** + * @see NovaTemplateOptions#getKeyPairName() + */ + public static NovaTemplateOptions keyPairName(String keyPairName) { + return new NovaTemplateOptions().keyPairName(keyPairName); + } + /** * @see org.jclouds.openstack.nova.v1_1.options.CreateServerOptions#getSecurityGroupNames */ diff --git a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/strategy/ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet.java b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/strategy/ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet.java index 8a14d33baa..54debb20c2 100644 --- a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/strategy/ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet.java +++ b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/strategy/ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet.java @@ -47,6 +47,7 @@ import org.jclouds.concurrent.Futures; import org.jclouds.openstack.nova.v1_1.NovaClient; import org.jclouds.openstack.nova.v1_1.compute.functions.AllocateAndAddFloatingIpToNode; import org.jclouds.openstack.nova.v1_1.compute.options.NovaTemplateOptions; +import org.jclouds.openstack.nova.v1_1.domain.KeyPair; import org.jclouds.openstack.nova.v1_1.domain.zonescoped.SecurityGroupInZone; import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ZoneAndName; import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ZoneSecurityGroupNameAndPorts; @@ -66,6 +67,7 @@ public class ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddT private final AllocateAndAddFloatingIpToNode allocateAndAddFloatingIpToNode; private final LoadingCache securityGroupCache; + private final LoadingCache keyPairCache; private final NovaClient novaClient; private final Provider templateBuilderProvider; @@ -78,11 +80,13 @@ public class ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddT @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, Provider templateBuilderProvider, AllocateAndAddFloatingIpToNode allocateAndAddFloatingIpToNode, - LoadingCache securityGroupCache, NovaClient novaClient) { + LoadingCache securityGroupCache, + LoadingCache keyPairCache, NovaClient novaClient) { super(addNodeWithTagStrategy, listNodesStrategy, nodeNamingConvention, executor, customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory); this.templateBuilderProvider = checkNotNull(templateBuilderProvider, "templateBuilderProvider"); this.securityGroupCache = checkNotNull(securityGroupCache, "securityGroupCache"); + this.keyPairCache = checkNotNull(keyPairCache, "keyPairCache"); this.allocateAndAddFloatingIpToNode = checkNotNull(allocateAndAddFloatingIpToNode, "allocateAndAddFloatingIpToNode"); this.novaClient = checkNotNull(novaClient, "novaClient"); @@ -93,9 +97,6 @@ public class ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddT Map badNodes, Multimap customizationResponses) { // ensure we don't mutate the input template Template mutableTemplate = templateBuilderProvider.get().fromTemplate(template).build(); - - // TODO: make NovaTemplateOptions with the following: - // security group, key pair NovaTemplateOptions templateOptions = NovaTemplateOptions.class.cast(mutableTemplate.getOptions()); String zone = mutableTemplate.getLocation().getId(); @@ -110,6 +111,11 @@ public class ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddT checkArgument(novaClient.getKeyPairExtensionForZone(zone).isPresent(), "Key Pairs are required by options, but the extension is not available! options: %s", templateOptions); + if (templateOptions.getKeyPairName() == null) { + KeyPair keyPair = keyPairCache.getUnchecked(ZoneAndName.fromZoneAndName(zone, "jclouds#" + group)); + keyPairCache.asMap().put(ZoneAndName.fromZoneAndName(zone, keyPair.getName()), keyPair); + templateOptions.keyPairName(keyPair.getName()); + } } boolean securityGroupExensionPresent = novaClient.getSecurityGroupExtensionForZone(zone).isPresent(); diff --git a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/predicates/KeyPairPredicates.java b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/predicates/KeyPairPredicates.java new file mode 100644 index 0000000000..e395c2a889 --- /dev/null +++ b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/predicates/KeyPairPredicates.java @@ -0,0 +1,55 @@ +/* + * 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.predicates; + +import com.google.common.base.Predicate; +import org.jclouds.openstack.nova.v1_1.domain.KeyPair; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Predicates handy when working with KeyPairs + * + * @author Adam Lowe + */ + +public class KeyPairPredicates { + + /** + * matches name of the given keypair starts with the specified prefix + * + * @param prefix the prefix you are looking for + * @return the predicate + */ + public static Predicate nameStartsWith(final String prefix) { + checkNotNull(prefix, "name must be defined"); + + return new Predicate() { + @Override + public boolean apply(KeyPair ext) { + return ext.getName() != null && ext.getName().startsWith(prefix); + } + + @Override + public String toString() { + return "nameStartsWith(" + prefix + ")"; + } + }; + } +} diff --git a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/functions/CreateUniqueKeyPairTest.java b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/functions/CreateUniqueKeyPairTest.java new file mode 100644 index 0000000000..83b10060e8 --- /dev/null +++ b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/functions/CreateUniqueKeyPairTest.java @@ -0,0 +1,94 @@ +/** + * 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.functions; + +import com.google.common.base.Optional; +import com.google.common.base.Supplier; +import org.jclouds.openstack.nova.v1_1.NovaClient; +import org.jclouds.openstack.nova.v1_1.domain.KeyPair; +import org.jclouds.openstack.nova.v1_1.extensions.KeyPairClient; +import org.testng.annotations.Test; + +import java.net.UnknownHostException; + +import static org.easymock.EasyMock.expect; +import static org.easymock.classextension.EasyMock.*; +import static org.testng.Assert.assertEquals; + +/** + * @author Adam Lowe + */ +@Test(groups = "unit") +public class CreateUniqueKeyPairTest { + + @Test + public void testApply() throws UnknownHostException { + NovaClient client = createMock(NovaClient.class); + KeyPairClient keyClient = createMock(KeyPairClient.class); + Supplier uniqueIdSupplier = createMock(Supplier.class); + + KeyPair pair = createMock(KeyPair.class); + + expect(client.getKeyPairExtensionForZone("zone")).andReturn(Optional.of(keyClient)).atLeastOnce(); + + expect(uniqueIdSupplier.get()).andReturn("1"); + expect(keyClient.createKeyPair("jclouds#group#1")).andReturn(pair); + + replay(client); + replay(keyClient); + replay(uniqueIdSupplier); + + CreateUniqueKeyPair parser = new CreateUniqueKeyPair(client, uniqueIdSupplier); + + assertEquals(parser.createNewKeyPairInZone("zone", "group"), pair); + + verify(client); + verify(keyClient); + verify(uniqueIdSupplier); + } + + @Test + public void testApplyWithIllegalStateException() throws UnknownHostException { + NovaClient client = createMock(NovaClient.class); + KeyPairClient keyClient = createMock(KeyPairClient.class); + Supplier uniqueIdSupplier = createMock(Supplier.class); + + KeyPair pair = createMock(KeyPair.class); + + expect(client.getKeyPairExtensionForZone("zone")).andReturn(Optional.of(keyClient)).atLeastOnce(); + + expect(uniqueIdSupplier.get()).andReturn("1"); + expect(keyClient.createKeyPair("jclouds#group#1")).andThrow(new IllegalStateException()); + expect(uniqueIdSupplier.get()).andReturn("2"); + expect(keyClient.createKeyPair("jclouds#group#2")).andReturn(pair); + + replay(client); + replay(keyClient); + replay(uniqueIdSupplier); + + CreateUniqueKeyPair parser = new CreateUniqueKeyPair(client, uniqueIdSupplier); + + assertEquals(parser.createNewKeyPairInZone("zone", "group"), pair); + + verify(client); + verify(keyClient); + verify(uniqueIdSupplier); + } + +}