diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/compute/EC2ComputeService.java b/aws/core/src/main/java/org/jclouds/aws/ec2/compute/EC2ComputeService.java index 99f2b8f83b..35a2be6fbf 100644 --- a/aws/core/src/main/java/org/jclouds/aws/ec2/compute/EC2ComputeService.java +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/compute/EC2ComputeService.java @@ -30,9 +30,9 @@ import org.jclouds.Constants; import org.jclouds.aws.domain.Region; import org.jclouds.aws.ec2.EC2Client; import org.jclouds.aws.ec2.compute.config.EC2ComputeServiceContextModule.GetRegionFromNodeOrDefault; -import org.jclouds.aws.ec2.compute.domain.KeyPairCredentials; import org.jclouds.aws.ec2.compute.domain.PortsRegionTag; import org.jclouds.aws.ec2.compute.domain.RegionTag; +import org.jclouds.aws.ec2.domain.KeyPair; import org.jclouds.aws.ec2.domain.RunningInstance; import org.jclouds.compute.ComputeServiceContext; import org.jclouds.compute.domain.Image; @@ -59,7 +59,7 @@ import com.google.common.collect.Iterables; public class EC2ComputeService extends BaseComputeService { protected final EC2Client ec2Client; protected final GetRegionFromNodeOrDefault getRegionFromNodeOrDefault; - protected final Map credentialsMap; + protected final Map credentialsMap; protected final Map securityGroupMap; protected final Predicate instanceStateTerminated; @@ -74,8 +74,7 @@ public class EC2ComputeService extends BaseComputeService { Provider templateBuilderProvider, ComputeUtils utils, @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, EC2Client ec2Client, GetRegionFromNodeOrDefault getRegionFromNodeOrDefault, - Map credentialsMap, - Map securityGroupMap, + Map credentialsMap, Map securityGroupMap, @Named("TERMINATED") Predicate instanceStateTerminated) { super(context, images, sizes, locations, listNodesStrategy, getNodeMetadataStrategy, runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy, @@ -98,11 +97,14 @@ public class EC2ComputeService extends BaseComputeService { } private void deleteKeyPair(Region region, String tag) { - if (ec2Client.getKeyPairServices().describeKeyPairsInRegion(region, tag).size() > 0) { - logger.debug(">> deleting keyPair(%s)", tag); - ec2Client.getKeyPairServices().deleteKeyPairInRegion(region, tag); - credentialsMap.remove(new RegionTag(region, tag)); // TODO: test this clear happens - logger.debug("<< deleted keyPair(%s)", tag); + for (KeyPair keyPair : ec2Client.getKeyPairServices().describeKeyPairsInRegion(region)) { + if (keyPair.getKeyName().matches(tag + "-[0-9]+")) { + logger.debug(">> deleting keyPair(%s)", tag); + ec2Client.getKeyPairServices().deleteKeyPairInRegion(region, keyPair.getKeyName()); + credentialsMap.remove(new RegionTag(region, keyPair.getKeyName())); // TODO: test this + // clear happens + logger.debug("<< deleted keyPair(%s)", keyPair.getKeyName()); + } } } diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/compute/config/EC2ComputeServiceContextModule.java b/aws/core/src/main/java/org/jclouds/aws/ec2/compute/config/EC2ComputeServiceContextModule.java index 5d4348f029..db55ad0b88 100755 --- a/aws/core/src/main/java/org/jclouds/aws/ec2/compute/config/EC2ComputeServiceContextModule.java +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/compute/config/EC2ComputeServiceContextModule.java @@ -40,10 +40,9 @@ import org.jclouds.aws.ec2.EC2AsyncClient; import org.jclouds.aws.ec2.EC2Client; import org.jclouds.aws.ec2.compute.EC2ComputeService; import org.jclouds.aws.ec2.compute.domain.EC2Size; -import org.jclouds.aws.ec2.compute.domain.KeyPairCredentials; import org.jclouds.aws.ec2.compute.domain.PortsRegionTag; import org.jclouds.aws.ec2.compute.domain.RegionTag; -import org.jclouds.aws.ec2.compute.functions.CreateKeyPairIfNeeded; +import org.jclouds.aws.ec2.compute.functions.CreateNewKeyPair; import org.jclouds.aws.ec2.compute.functions.CreateSecurityGroupIfNeeded; import org.jclouds.aws.ec2.compute.functions.ImageParser; import org.jclouds.aws.ec2.compute.functions.RunningInstanceToNodeMetadata; @@ -51,6 +50,7 @@ import org.jclouds.aws.ec2.compute.strategy.EC2DestroyNodeStrategy; import org.jclouds.aws.ec2.compute.strategy.EC2RunNodesAndAddToSetStrategy; import org.jclouds.aws.ec2.config.EC2ContextModule; import org.jclouds.aws.ec2.domain.AvailabilityZone; +import org.jclouds.aws.ec2.domain.KeyPair; import org.jclouds.aws.ec2.domain.RunningInstance; import org.jclouds.aws.ec2.services.InstanceClient; import org.jclouds.compute.ComputeService; @@ -214,7 +214,7 @@ public class EC2ComputeServiceContextModule extends EC2ContextModule { @Provides @Singleton - protected final Map credentialsMap(CreateKeyPairIfNeeded in) { + protected final Map credentialsMap(CreateNewKeyPair in) { // doesn't seem to clear when someone issues remove(key) // return new MapMaker().makeComputingMap(in); return Maps.newLinkedHashMap(); diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/compute/domain/EC2Size.java b/aws/core/src/main/java/org/jclouds/aws/ec2/compute/domain/EC2Size.java index b70c0dd055..18c9ab9716 100644 --- a/aws/core/src/main/java/org/jclouds/aws/ec2/compute/domain/EC2Size.java +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/compute/domain/EC2Size.java @@ -1,24 +1,19 @@ /** * - * Copyright (C) 2009 Global Cloud Specialists, Inc. + * Copyright (C) 2009 Cloud Conscious, LLC. * * ==================================================================== - * 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 + * Licensed 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 + * 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. + * 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.aws.ec2.compute.domain; diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/compute/domain/KeyPairCredentials.java b/aws/core/src/main/java/org/jclouds/aws/ec2/compute/domain/KeyPairCredentials.java deleted file mode 100644 index 96103b0198..0000000000 --- a/aws/core/src/main/java/org/jclouds/aws/ec2/compute/domain/KeyPairCredentials.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.jclouds.aws.ec2.compute.domain; - -import org.jclouds.aws.ec2.domain.KeyPair; -import org.jclouds.domain.Credentials; - -public class KeyPairCredentials extends Credentials { - private final KeyPair keyPair; - - public KeyPairCredentials(String account, KeyPair keyPair) { - super(account, keyPair.getKeyMaterial()); - this.keyPair = keyPair; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + ((keyPair == null) ? 0 : keyPair.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (!super.equals(obj)) - return false; - if (getClass() != obj.getClass()) - return false; - KeyPairCredentials other = (KeyPairCredentials) obj; - if (keyPair == null) { - if (other.keyPair != null) - return false; - } else if (!keyPair.equals(other.keyPair)) - return false; - return true; - } - -} \ No newline at end of file diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/compute/domain/PortsRegionTag.java b/aws/core/src/main/java/org/jclouds/aws/ec2/compute/domain/PortsRegionTag.java index 44a096e62e..45ba345d05 100644 --- a/aws/core/src/main/java/org/jclouds/aws/ec2/compute/domain/PortsRegionTag.java +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/compute/domain/PortsRegionTag.java @@ -1,7 +1,30 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed 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.aws.ec2.compute.domain; import org.jclouds.aws.domain.Region; +/** + * + * @author Adrian Cole + */ public class PortsRegionTag extends RegionTag { private final int[] ports; diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/compute/domain/RegionTag.java b/aws/core/src/main/java/org/jclouds/aws/ec2/compute/domain/RegionTag.java index 22b6fdc769..128dd3c1fa 100644 --- a/aws/core/src/main/java/org/jclouds/aws/ec2/compute/domain/RegionTag.java +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/compute/domain/RegionTag.java @@ -1,8 +1,29 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed 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.aws.ec2.compute.domain; import org.jclouds.aws.domain.Region; - +/** + * + * @author Adrian Cole + */ public class RegionTag { protected final Region region; protected final String tag; diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/compute/functions/CreateKeyPairIfNeeded.java b/aws/core/src/main/java/org/jclouds/aws/ec2/compute/functions/CreateKeyPairIfNeeded.java deleted file mode 100644 index 1fd680d4fe..0000000000 --- a/aws/core/src/main/java/org/jclouds/aws/ec2/compute/functions/CreateKeyPairIfNeeded.java +++ /dev/null @@ -1,54 +0,0 @@ -package org.jclouds.aws.ec2.compute.functions; - -import javax.annotation.Resource; -import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; - -import org.jclouds.aws.AWSResponseException; -import org.jclouds.aws.domain.Region; -import org.jclouds.aws.ec2.EC2Client; -import org.jclouds.aws.ec2.compute.domain.KeyPairCredentials; -import org.jclouds.aws.ec2.compute.domain.RegionTag; -import org.jclouds.aws.ec2.domain.KeyPair; -import org.jclouds.compute.reference.ComputeServiceConstants; -import org.jclouds.logging.Logger; - -import com.google.common.base.Function; -import com.google.common.collect.Iterables; - -@Singleton -public class CreateKeyPairIfNeeded implements Function { - @Resource - @Named(ComputeServiceConstants.COMPUTE_LOGGER) - protected Logger logger = Logger.NULL; - protected final EC2Client ec2Client; - - @Inject - public CreateKeyPairIfNeeded(EC2Client ec2Client) { - this.ec2Client = ec2Client; - } - - @Override - public KeyPairCredentials apply(RegionTag from) { - return new KeyPairCredentials("root", createKeyPairInRegion(from.getRegion(), from.getTag())); - } - - private KeyPair createKeyPairInRegion(Region region, String name) { - logger.debug(">> creating keyPair region(%s) name(%s)", region, name); - KeyPair keyPair; - try { - keyPair = ec2Client.getKeyPairServices().createKeyPairInRegion(region, name); - logger.debug("<< created keyPair(%s)", keyPair.getKeyName()); - } catch (AWSResponseException e) { - if (e.getError().getCode().equals("InvalidKeyPair.Duplicate")) { - keyPair = Iterables.getLast(ec2Client.getKeyPairServices().describeKeyPairsInRegion( - region, name)); - logger.debug("<< reused keyPair(%s)", keyPair.getKeyName()); - } else { - throw e; - } - } - return keyPair; - } -} diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/compute/functions/CreateNewKeyPair.java b/aws/core/src/main/java/org/jclouds/aws/ec2/compute/functions/CreateNewKeyPair.java new file mode 100644 index 0000000000..3972beea07 --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/compute/functions/CreateNewKeyPair.java @@ -0,0 +1,76 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed 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.aws.ec2.compute.functions; + +import java.security.SecureRandom; + +import javax.annotation.Resource; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.jclouds.aws.AWSResponseException; +import org.jclouds.aws.domain.Region; +import org.jclouds.aws.ec2.EC2Client; +import org.jclouds.aws.ec2.compute.domain.RegionTag; +import org.jclouds.aws.ec2.domain.KeyPair; +import org.jclouds.compute.reference.ComputeServiceConstants; +import org.jclouds.logging.Logger; + +import com.google.common.base.Function; + +/** + * + * @author Adrian Cole + */ +@Singleton +public class CreateNewKeyPair implements Function { + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; + protected final EC2Client ec2Client; + + @Inject + public CreateNewKeyPair(EC2Client ec2Client) { + this.ec2Client = ec2Client; + } + + @Override + public KeyPair apply(RegionTag from) { + return createNewKeyPairInRegion(from.getRegion(), from.getTag()); + } + + private KeyPair createNewKeyPairInRegion(Region region, String tag) { + logger.debug(">> creating keyPair region(%s) tag(%s)", region, tag); + KeyPair keyPair = null; + while (keyPair == null) { + try { + keyPair = ec2Client.getKeyPairServices().createKeyPairInRegion(region, + tag + "-" + new SecureRandom().nextInt(100)); + logger.debug("<< created keyPair(%s)", keyPair.getKeyName()); + } catch (AWSResponseException e) { + if (!e.getError().getCode().equals("InvalidKeyPair.Duplicate")) { + throw e; + } + } + } + return keyPair; + } +} diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/compute/functions/ImageParser.java b/aws/core/src/main/java/org/jclouds/aws/ec2/compute/functions/ImageParser.java index 9e1f952fe5..020855c287 100644 --- a/aws/core/src/main/java/org/jclouds/aws/ec2/compute/functions/ImageParser.java +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/compute/functions/ImageParser.java @@ -57,14 +57,14 @@ public class ImageParser implements Function + * + * ==================================================================== + * Licensed 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.aws.ec2.compute.functions; +import static com.google.common.base.Preconditions.checkNotNull; + import java.net.InetAddress; import java.net.URI; import java.util.Map; @@ -8,23 +28,28 @@ import java.util.Set; import javax.inject.Inject; import javax.inject.Singleton; -import org.jclouds.aws.ec2.compute.domain.KeyPairCredentials; import org.jclouds.aws.ec2.compute.domain.RegionTag; import org.jclouds.aws.ec2.domain.Image; import org.jclouds.aws.ec2.domain.InstanceState; +import org.jclouds.aws.ec2.domain.KeyPair; import org.jclouds.aws.ec2.domain.RunningInstance; import org.jclouds.aws.ec2.options.DescribeImagesOptions; import org.jclouds.aws.ec2.services.AMIClient; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.NodeState; import org.jclouds.compute.domain.internal.NodeMetadataImpl; +import org.jclouds.compute.strategy.PopulateDefaultLoginCredentialsForImageStrategy; import org.jclouds.domain.Credentials; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; +/** + * @author Adrian Cole + */ @Singleton public class RunningInstanceToNodeMetadata implements Function { private static final Map instanceToNodeState = ImmutableMap @@ -33,47 +58,56 @@ public class RunningInstanceToNodeMetadata implements Function credentialsMap; - private final ImageParser imageParser; + private final Map credentialsMap; + private final PopulateDefaultLoginCredentialsForImageStrategy credentialProvider; @Inject - public RunningInstanceToNodeMetadata(AMIClient amiClient, - Map credentialsMap, - ImageParser imageParser) { - this.amiClient = amiClient; - this.credentialsMap = credentialsMap; - this.imageParser = imageParser; + RunningInstanceToNodeMetadata(AMIClient amiClient, Map credentialsMap, + PopulateDefaultLoginCredentialsForImageStrategy credentialProvider) { + this.amiClient = checkNotNull(amiClient, "amiClient"); + this.credentialsMap = checkNotNull(credentialsMap, "credentialsMap"); + this.credentialProvider = checkNotNull(credentialProvider, "credentialProvider"); } @Override - public NodeMetadata apply(RunningInstance from) { - String id = from.getId(); + public NodeMetadata apply(RunningInstance instance) { + String id = checkNotNull(instance, "instance").getId(); String name = null; // user doesn't determine a node name; URI uri = null; // no uri to get rest access to host info Map userMetadata = ImmutableMap. of(); - String tag = from.getKeyName(); - NodeState state = instanceToNodeState.get(from.getInstanceState()); - Set publicAddresses = nullSafeSet(from.getIpAddress()); - Set privateAddresses = nullSafeSet(from.getPrivateIpAddress()); - Credentials credentials = credentialsMap.containsKey(new RegionTag(from.getRegion(), tag)) ? credentialsMap - .get(new RegionTag(from.getRegion(), tag)) - : null; - Image image = Iterables.getOnlyElement(amiClient.describeImagesInRegion(from.getRegion(), - DescribeImagesOptions.Builder.imageIds(from.getImageId()))); + String tag = instance.getKeyName().replaceAll("-[0-9]+", ""); + NodeState state = instanceToNodeState.get(instance.getInstanceState()); - // canonical/alestic images use the ubuntu user to login - // TODO: add this as a property of image - if (credentials != null && image.getImageOwnerId().matches("063491364108|099720109477")) - credentials = new Credentials("ubuntu", credentials.key); + Set publicAddresses = nullSafeSet(instance.getIpAddress()); + Set privateAddresses = nullSafeSet(instance.getPrivateIpAddress()); - if(credentials == null) credentials = imageParser.apply(image).getDefaultCredentials(); + Credentials credentials = new Credentials(getLoginAccountFor(instance), getPrivateKeyOrNull( + instance, tag)); + + String locationId = instance.getAvailabilityZone().toString(); - String locationId = from.getAvailabilityZone().toString(); Map extra = ImmutableMap. of(); + return new NodeMetadataImpl(id, name, locationId, uri, userMetadata, tag, state, publicAddresses, privateAddresses, extra, credentials); } + @VisibleForTesting + String getPrivateKeyOrNull(RunningInstance instance, String tag) { + KeyPair keyPair = credentialsMap.get(new RegionTag(instance.getRegion(), instance + .getKeyName())); + String privateKey = keyPair != null ? keyPair.getKeyMaterial() : null; + return privateKey; + } + + @VisibleForTesting + String getLoginAccountFor(RunningInstance from) { + Image image = Iterables.getOnlyElement(amiClient.describeImagesInRegion(from.getRegion(), + DescribeImagesOptions.Builder.imageIds(from.getImageId()))); + return checkNotNull(credentialProvider.execute(image), "login from image: " + + from.getImageId()).account; + } + Set nullSafeSet(InetAddress in) { if (in == null) { return ImmutableSet. of(); diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/compute/strategy/EC2RunNodesAndAddToSetStrategy.java b/aws/core/src/main/java/org/jclouds/aws/ec2/compute/strategy/EC2RunNodesAndAddToSetStrategy.java index 2fa4abbfff..e6ab741f12 100644 --- a/aws/core/src/main/java/org/jclouds/aws/ec2/compute/strategy/EC2RunNodesAndAddToSetStrategy.java +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/compute/strategy/EC2RunNodesAndAddToSetStrategy.java @@ -37,13 +37,13 @@ import org.jclouds.Constants; import org.jclouds.aws.domain.Region; import org.jclouds.aws.ec2.EC2Client; import org.jclouds.aws.ec2.compute.domain.EC2Size; -import org.jclouds.aws.ec2.compute.domain.KeyPairCredentials; import org.jclouds.aws.ec2.compute.domain.PortsRegionTag; import org.jclouds.aws.ec2.compute.domain.RegionTag; -import org.jclouds.aws.ec2.compute.functions.CreateKeyPairIfNeeded; +import org.jclouds.aws.ec2.compute.functions.CreateNewKeyPair; import org.jclouds.aws.ec2.compute.functions.CreateSecurityGroupIfNeeded; import org.jclouds.aws.ec2.compute.functions.RunningInstanceToNodeMetadata; import org.jclouds.aws.ec2.domain.AvailabilityZone; +import org.jclouds.aws.ec2.domain.KeyPair; import org.jclouds.aws.ec2.domain.Reservation; import org.jclouds.aws.ec2.domain.RunningInstance; import org.jclouds.aws.ec2.options.RunInstancesOptions; @@ -84,9 +84,9 @@ public class EC2RunNodesAndAddToSetStrategy implements RunNodesAndAddToSetStrate }; protected final ComputeService computeService; protected final EC2Client ec2Client; - protected final Map credentialsMap; + protected final Map credentialsMap; protected final Map securityGroupMap; - protected final CreateKeyPairIfNeeded createKeyPairIfNeeded; + protected final CreateNewKeyPair createNewKeyPair; protected final CreateSecurityGroupIfNeeded createSecurityGroupIfNeeded; protected final Predicate instanceStateRunning; protected final RunningInstanceToNodeMetadata runningInstanceToNodeMetadata; @@ -94,9 +94,8 @@ public class EC2RunNodesAndAddToSetStrategy implements RunNodesAndAddToSetStrate @Inject protected EC2RunNodesAndAddToSetStrategy(ComputeService computeService, EC2Client ec2Client, - Map credentialsMap, - Map securityGroupMap, - CreateKeyPairIfNeeded createKeyPairIfNeeded, + Map credentialsMap, Map securityGroupMap, + CreateNewKeyPair createKeyPairIfNeeded, CreateSecurityGroupIfNeeded createSecurityGroupIfNeeded, @Named("RUNNING") Predicate instanceStateRunning, RunningInstanceToNodeMetadata runningInstanceToNodeMetadata, ComputeUtils utils, @@ -105,7 +104,7 @@ public class EC2RunNodesAndAddToSetStrategy implements RunNodesAndAddToSetStrate this.ec2Client = ec2Client; this.credentialsMap = credentialsMap; this.securityGroupMap = securityGroupMap; - this.createKeyPairIfNeeded = createKeyPairIfNeeded; + this.createNewKeyPair = createKeyPairIfNeeded; this.createSecurityGroupIfNeeded = createSecurityGroupIfNeeded; this.instanceStateRunning = instanceStateRunning; this.runningInstanceToNodeMetadata = runningInstanceToNodeMetadata; @@ -136,9 +135,11 @@ public class EC2RunNodesAndAddToSetStrategy implements RunNodesAndAddToSetStrate // another thread // deletes a key RegionTag regionTag = new RegionTag(region, tag); - if (!credentialsMap.containsKey(regionTag)) { - credentialsMap.put(regionTag, createKeyPairIfNeeded.apply(regionTag)); - } + + KeyPair keyPair = createNewKeyPair.apply(regionTag); + + credentialsMap.put(new RegionTag(region, keyPair.getKeyName()), keyPair); + TemplateOptions options = template.getOptions(); PortsRegionTag portsRegionTag = new PortsRegionTag(region, tag, options.getInboundPorts()); if (!securityGroupMap.containsKey(portsRegionTag)) { @@ -150,7 +151,7 @@ public class EC2RunNodesAndAddToSetStrategy implements RunNodesAndAddToSetStrate ">> running %d instance region(%s) zone(%s) ami(%s) type(%s) keyPair(%s) securityGroup(%s)", count, region, zone, template.getImage().getId(), ec2Size.getInstanceType(), tag, tag); - RunInstancesOptions instanceOptions = withKeyName(tag)// key + RunInstancesOptions instanceOptions = withKeyName(keyPair.getKeyName())// key .asType(ec2Size.getInstanceType())// instance size .withSecurityGroup(tag)// group I created above .withAdditionalInfo(tag); diff --git a/aws/core/src/test/java/org/jclouds/aws/ec2/compute/EC2ComputeServiceLiveTest.java b/aws/core/src/test/java/org/jclouds/aws/ec2/compute/EC2ComputeServiceLiveTest.java index 45da461951..57676a17be 100644 --- a/aws/core/src/test/java/org/jclouds/aws/ec2/compute/EC2ComputeServiceLiveTest.java +++ b/aws/core/src/test/java/org/jclouds/aws/ec2/compute/EC2ComputeServiceLiveTest.java @@ -21,16 +21,15 @@ package org.jclouds.aws.ec2.compute; import static org.testng.Assert.assertEquals; import org.jclouds.compute.BaseComputeServiceLiveTest; -import org.jclouds.compute.domain.*; -import org.jclouds.domain.Credentials; +import org.jclouds.compute.domain.Architecture; +import org.jclouds.compute.domain.OsFamily; +import org.jclouds.compute.domain.Template; import org.jclouds.ssh.jsch.config.JschSshClientModule; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import com.google.common.collect.Iterables; -import java.util.Map; - /** * * @author Adrian Cole @@ -63,17 +62,6 @@ public class EC2ComputeServiceLiveTest extends BaseComputeServiceLiveTest { Architecture.X86_32).imageId("ami-bb709dd2").build(); } - @Test - public void testScriptExecution() throws Exception { - Template simpleTemplate = client.templateBuilder().smallest().build(); - client.runNodesWithTag("ec2", 1, simpleTemplate); - Map map = client.getNodesWithTag("ec2"); - map.values().iterator().next(); - Credentials creds = new Credentials("ubuntu", keyPair.get("public")); - client.runScriptOnNodesWithTag("ec2", "mkdir ~/ahha; sleep 3".getBytes()); - client.destroyNodesWithTag("ec2"); - } - @Override protected JschSshClientModule getSshModule() { return new JschSshClientModule(); diff --git a/aws/core/src/test/java/org/jclouds/aws/ec2/functions/ImageParserTest.java b/aws/core/src/test/java/org/jclouds/aws/ec2/compute/functions/ImageParserTest.java similarity index 95% rename from aws/core/src/test/java/org/jclouds/aws/ec2/functions/ImageParserTest.java rename to aws/core/src/test/java/org/jclouds/aws/ec2/compute/functions/ImageParserTest.java index bb3e2d9025..e1ae31ba80 100644 --- a/aws/core/src/test/java/org/jclouds/aws/ec2/functions/ImageParserTest.java +++ b/aws/core/src/test/java/org/jclouds/aws/ec2/compute/functions/ImageParserTest.java @@ -16,7 +16,7 @@ * limitations under the License. * ==================================================================== */ -package org.jclouds.aws.ec2.functions; +package org.jclouds.aws.ec2.compute.functions; import static org.easymock.EasyMock.expect; import static org.easymock.classextension.EasyMock.createMock; @@ -27,7 +27,6 @@ import java.io.InputStream; import java.util.Set; import org.jclouds.aws.domain.Region; -import org.jclouds.aws.ec2.compute.functions.ImageParser; import org.jclouds.aws.ec2.compute.strategy.EC2PopulateDefaultLoginCredentialsForImageStrategy; import org.jclouds.aws.ec2.domain.Image; import org.jclouds.aws.ec2.xml.DescribeImagesResponseHandler; @@ -52,8 +51,7 @@ public class ImageParserTest extends BaseHandlerTest { Set result = parseImages(is); assertEquals(result.size(), 6); - ImageParser parser = new ImageParser(); - parser.setAuthenticator(new EC2PopulateDefaultLoginCredentialsForImageStrategy()); + ImageParser parser = new ImageParser(new EC2PopulateDefaultLoginCredentialsForImageStrategy()); org.jclouds.compute.domain.Image ubuntuHardy = parser.apply(Iterables.get(result, 0)); assertEquals(ubuntuHardy.getArchitecture(), org.jclouds.compute.domain.Architecture.X86_32); @@ -116,7 +114,7 @@ public class ImageParserTest extends BaseHandlerTest { assertEquals(alesticHardy.getUserMetadata(), ImmutableMap. of("owner", "063491364108")); assertEquals(alesticHardy.getVersion(), "20080905"); - + // should skip kernel assert parser.apply(Iterables.get(result, 5)) == null; } @@ -126,8 +124,7 @@ public class ImageParserTest extends BaseHandlerTest { Set result = parseImages(is); - ImageParser parser = new ImageParser(); - parser.setAuthenticator(new EC2PopulateDefaultLoginCredentialsForImageStrategy()); + ImageParser parser = new ImageParser(new EC2PopulateDefaultLoginCredentialsForImageStrategy()); org.jclouds.compute.domain.Image image = parser.apply(Iterables.get(result, 0)); diff --git a/compute/src/test/java/org/jclouds/compute/BaseComputeServiceLiveTest.java b/compute/src/test/java/org/jclouds/compute/BaseComputeServiceLiveTest.java index 55b5c719e7..1b91be358e 100644 --- a/compute/src/test/java/org/jclouds/compute/BaseComputeServiceLiveTest.java +++ b/compute/src/test/java/org/jclouds/compute/BaseComputeServiceLiveTest.java @@ -44,6 +44,8 @@ import org.jclouds.compute.domain.OsFamily; import org.jclouds.compute.domain.Size; import org.jclouds.compute.domain.Template; import org.jclouds.compute.domain.TemplateBuilder; +import org.jclouds.compute.options.RunScriptOptions; +import org.jclouds.domain.Credentials; import org.jclouds.domain.Location; import org.jclouds.http.HttpResponseException; import org.jclouds.logging.log4j.config.Log4JLoggingModule; @@ -60,6 +62,7 @@ import org.testng.annotations.Test; import com.google.common.base.Charsets; import com.google.common.base.Predicate; +import com.google.common.base.Throwables; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; @@ -109,20 +112,26 @@ public abstract class BaseComputeServiceLiveTest { String secret = Files.toString(new File(secretKeyFile), Charsets.UTF_8); assert secret.startsWith("-----BEGIN RSA PRIVATE KEY-----") : "invalid key:\n" + secret; - context = new ComputeServiceContextFactory().createContext(service, user, password, - ImmutableSet.of(new Log4JLoggingModule(), getSshModule())); + initializeContextAndClient(); Injector injector = Guice.createInjector(getSshModule()); sshFactory = injector.getInstance(SshClient.Factory.class); SocketOpen socketOpen = injector.getInstance(SocketOpen.class); socketTester = new RetryablePredicate(socketOpen, 60, 1, TimeUnit.SECONDS); injector.injectMembers(socketOpen); // add logger - client = context.getComputeService(); // keyPair = sshFactory.generateRSAKeyPair("", ""); keyPair = ImmutableMap. of("private", secret, "public", Files.toString( new File(secretKeyFile + ".pub"), Charsets.UTF_8)); } + private void initializeContextAndClient() throws IOException { + if (context != null) + context.close(); + context = new ComputeServiceContextFactory().createContext(service, user, password, + ImmutableSet.of(new Log4JLoggingModule(), getSshModule())); + client = context.getComputeService(); + } + private void checkSecretKeyFile(String secretKeyFile) throws FileNotFoundException { Utils.checkNotEmpty(secretKeyFile, "System property: [jclouds.test.ssh.keyfile] set to an empty string"); @@ -140,7 +149,7 @@ public abstract class BaseComputeServiceLiveTest { } @Test(dependsOnMethods = "testTemplateMatch") - public void testCreate() throws Exception { + public void testCreateTwoNodesWithRunScript() throws Exception { try { client.destroyNodesWithTag(tag); } catch (HttpResponseException e) { @@ -155,20 +164,56 @@ public abstract class BaseComputeServiceLiveTest { buildScript(template.getImage().getOsFamily()).getBytes()); nodes = Sets.newTreeSet(client.runNodesWithTag(tag, 2, template).values()); assertEquals(nodes.size(), 2); - checkNodes(); + checkNodes(nodes, tag); NodeMetadata node1 = nodes.first(); NodeMetadata node2 = nodes.last(); // credentials aren't always the same // assertEquals(node1.getCredentials(), node2.getCredentials()); assert !node1.getId().equals(node2.getId()); - // run one more - nodes.addAll(client.runNodesWithTag(tag, 1, template).values()); - assertEquals(nodes.size(), 3); - checkNodes(); } - private void checkNodes() throws IOException { + @Test(dependsOnMethods = "testCreateTwoNodesWithRunScript") + public void testCreateAnotherNodeWithANewContextToEnsureSharedMemIsntRequired() throws Exception { + initializeContextAndClient(); + + checkNodes(client.runNodesWithTag(tag, 1, template).values(), tag); + } + + @Test + public void testScriptExecutionAfterBootWithBasicTemplate() throws Exception { + String tag = this.tag + "script"; + Template simpleTemplate = buildTemplate(client.templateBuilder()); + simpleTemplate.getOptions().blockOnPort(22, 60); + try { + Map nodes = client.runNodesWithTag(tag, 1, simpleTemplate); + Credentials good = nodes.values().iterator().next().getCredentials(); + assert good.account != null; + + try { + runScriptWithCreds(tag, simpleTemplate.getImage().getOsFamily(), new Credentials( + good.account, "romeo")); + assert false : "shouldn't pass with a bad password"; + } catch (SshException e) { + assert Throwables.getRootCause(e).getMessage().contains("Auth fail") : e; + } + + runScriptWithCreds(tag, simpleTemplate.getImage().getOsFamily(), good); + + checkNodes(nodes.values(), tag); + + } finally { + client.destroyNodesWithTag(tag); + } + } + + private Map runScriptWithCreds(String tag, OsFamily osFamily, + Credentials creds) { + return client.runScriptOnNodesWithTag(tag, buildScript(osFamily).getBytes(), + RunScriptOptions.Builder.overrideCredentialsWith(creds)); + } + + private void checkNodes(Iterable nodes, String tag) throws IOException { for (NodeMetadata node : nodes) { assertNotNull(node.getId()); assertNotNull(node.getTag()); @@ -191,7 +236,6 @@ public abstract class BaseComputeServiceLiveTest { protected String buildScript(OsFamily osFamily) { switch (osFamily) { - case JEOS: case UBUNTU: return new StringBuilder()// .append("echo nameserver 208.67.222.222 >> /etc/resolv.conf\n")// @@ -221,7 +265,7 @@ public abstract class BaseComputeServiceLiveTest { } } - @Test(enabled = true, dependsOnMethods = "testCreate") + @Test(enabled = true, dependsOnMethods = "testCreateAnotherNodeWithANewContextToEnsureSharedMemIsntRequired") public void testGet() throws Exception { Set metadataSet = Sets.newHashSet(client.getNodesWithTag(tag) .values()); @@ -291,7 +335,7 @@ public abstract class BaseComputeServiceLiveTest { private void sshPing(NodeMetadata node) throws IOException { for (int i = 0; i < 5; i++) {// retry loop TODO replace with predicate. try { - doCheckKey(node); + doCheckJavaIsInstalledViaSsh(node); return; } catch (SshException e) { try { @@ -303,7 +347,7 @@ public abstract class BaseComputeServiceLiveTest { } } - protected void doCheckKey(NodeMetadata node) throws IOException { + protected void doCheckJavaIsInstalledViaSsh(NodeMetadata node) throws IOException { InetSocketAddress socket = new InetSocketAddress(Iterables.get(node.getPublicAddresses(), 0), 22); socketTester.apply(socket); // TODO add transitionTo option that accepts a socket conection diff --git a/gogrid/src/test/java/org/jclouds/gogrid/GoGridComputeServiceLiveTest.java b/gogrid/src/test/java/org/jclouds/gogrid/GoGridComputeServiceLiveTest.java index a454bf08b7..a1cacad4e8 100644 --- a/gogrid/src/test/java/org/jclouds/gogrid/GoGridComputeServiceLiveTest.java +++ b/gogrid/src/test/java/org/jclouds/gogrid/GoGridComputeServiceLiveTest.java @@ -19,90 +19,46 @@ package org.jclouds.gogrid; import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; - -import java.util.Map; import org.jclouds.compute.BaseComputeServiceLiveTest; -import org.jclouds.compute.ComputeService; import org.jclouds.compute.ComputeServiceContextFactory; import org.jclouds.compute.domain.Architecture; -import org.jclouds.compute.domain.ComputeMetadata; -import org.jclouds.compute.domain.NodeMetadata; -import org.jclouds.compute.domain.NodeState; import org.jclouds.compute.domain.OsFamily; import org.jclouds.compute.domain.Template; -import org.jclouds.compute.options.RunScriptOptions; -import org.jclouds.domain.Credentials; import org.jclouds.rest.RestContext; import org.jclouds.ssh.jsch.config.JschSshClientModule; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; -import com.google.common.base.Predicate; -import com.google.common.collect.Iterables; - /** * @author Oleksiy Yarmula */ @Test(groups = "live", enabled = true, sequential = true, testName = "gogrid.GoGridComputeServiceLiveTest") public class GoGridComputeServiceLiveTest extends BaseComputeServiceLiveTest { - @BeforeClass - @Override - public void setServiceDefaults() { - service = "gogrid"; - } + @BeforeClass + @Override + public void setServiceDefaults() { + service = "gogrid"; + } - @Test - public void testTemplateBuilder() { - Template defaultTemplate = client.templateBuilder().build(); - assertEquals(defaultTemplate.getImage().getArchitecture(), Architecture.X86_64); - assertEquals(defaultTemplate.getImage().getOsFamily(), OsFamily.CENTOS); - assertEquals(defaultTemplate.getLocation().getId(), "SANFRANCISCO"); - assertEquals(defaultTemplate.getSize().getCores(), 1.0d); - } + @Test + public void testTemplateBuilder() { + Template defaultTemplate = client.templateBuilder().build(); + assertEquals(defaultTemplate.getImage().getArchitecture(), Architecture.X86_64); + assertEquals(defaultTemplate.getImage().getOsFamily(), OsFamily.CENTOS); + assertEquals(defaultTemplate.getLocation().getId(), "SANFRANCISCO"); + assertEquals(defaultTemplate.getSize().getCores(), 1.0d); + } - @Override - protected JschSshClientModule getSshModule() { - return new JschSshClientModule(); - } + @Override + protected JschSshClientModule getSshModule() { + return new JschSshClientModule(); + } - public void testAssignability() throws Exception { - @SuppressWarnings("unused") - RestContext goGridContext = new ComputeServiceContextFactory() - .createContext(service, user, password).getProviderSpecificContext(); - } - - @Test(enabled = true) - public void endToEndComputeServiceTest() { - ComputeService service = context.getComputeService(); - Template t = service.templateBuilder().minRam(1024).imageId("1532").build(); - - assertEquals(t.getImage().getId(), "1532"); - service.runNodesWithTag(this.service, 3, t); - - Map nodes = service.getNodes(); - - ComputeMetadata node = Iterables.find(nodes.values(), new Predicate() { - @Override - public boolean apply(ComputeMetadata computeMetadata) { - return computeMetadata.getName().startsWith(GoGridComputeServiceLiveTest.this.service); - } - }); - - NodeMetadata nodeMetadata = service.getNodeMetadata(node); - assertEquals(nodeMetadata.getPublicAddresses().size(), 1, - "There must be 1 public address for the node"); - assertTrue(nodeMetadata.getName().startsWith(this.service)); - service.rebootNode(nodeMetadata); // blocks until finished - - assertEquals(service.getNodeMetadata(nodeMetadata).getState(), NodeState.RUNNING); - - client.runScriptOnNodesWithTag("gogrid", - "mkdir ~/ahha; sleep 3".getBytes(), - new RunScriptOptions.Builder().overrideCredentials(new Credentials("root", null)).build()); - - service.destroyNodesWithTag("gogrid"); - } + public void testAssignability() throws Exception { + @SuppressWarnings("unused") + RestContext goGridContext = new ComputeServiceContextFactory() + .createContext(service, user, password).getProviderSpecificContext(); + } } diff --git a/vcloud/hostingdotcom/src/test/java/org/jclouds/vcloud/hostingdotcom/compute/HostingDotComVCloudComputeServiceLiveTest.java b/vcloud/hostingdotcom/src/test/java/org/jclouds/vcloud/hostingdotcom/compute/HostingDotComVCloudComputeServiceLiveTest.java index 97e98d0262..df02dcf0e2 100644 --- a/vcloud/hostingdotcom/src/test/java/org/jclouds/vcloud/hostingdotcom/compute/HostingDotComVCloudComputeServiceLiveTest.java +++ b/vcloud/hostingdotcom/src/test/java/org/jclouds/vcloud/hostingdotcom/compute/HostingDotComVCloudComputeServiceLiveTest.java @@ -53,8 +53,8 @@ public class HostingDotComVCloudComputeServiceLiveTest extends VCloudComputeServ // Takes too long @Override @Test(enabled = false) - public void testCreate() throws Exception { - super.testCreate(); + public void testCreateTwoNodesWithRunScript() throws Exception { + super.testCreateTwoNodesWithRunScript(); } @Override