diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/SDCApiMetadata.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/SDCApiMetadata.java index 7bf3be99f5..e9679e8afe 100644 --- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/SDCApiMetadata.java +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/SDCApiMetadata.java @@ -25,6 +25,7 @@ import org.jclouds.apis.ApiMetadata; import org.jclouds.compute.ComputeServiceContext; import org.jclouds.joyent.sdc.v6_5.compute.config.SDCComputeServiceContextModule; import org.jclouds.joyent.sdc.v6_5.config.DatacentersAreZonesModule; +import org.jclouds.joyent.sdc.v6_5.config.SDCProperties; import org.jclouds.joyent.sdc.v6_5.config.SDCRestClientModule; import org.jclouds.rest.RestContext; import org.jclouds.rest.internal.BaseRestApiMetadata; @@ -62,6 +63,7 @@ public class SDCApiMetadata extends BaseRestApiMetadata { public static Properties defaultProperties() { Properties properties = BaseRestApiMetadata.defaultProperties(); + properties.setProperty(SDCProperties.AUTOGENERATE_KEYS, "true"); return properties; } diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/SDCComputeService.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/SDCComputeService.java new file mode 100644 index 0000000000..e6e4cc919f --- /dev/null +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/SDCComputeService.java @@ -0,0 +1,153 @@ +/** + * 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.joyent.sdc.v6_5.compute; + +import static com.google.common.base.Preconditions.checkNotNull; +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.Map; +import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.atomic.AtomicReference; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.jclouds.Constants; +import org.jclouds.collect.Memoized; +import org.jclouds.compute.ComputeServiceContext; +import org.jclouds.compute.callables.RunScriptOnNode; +import org.jclouds.compute.domain.Hardware; +import org.jclouds.compute.domain.Image; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.domain.TemplateBuilder; +import org.jclouds.compute.extensions.ImageExtension; +import org.jclouds.compute.functions.GroupNamingConvention; +import org.jclouds.compute.internal.BaseComputeService; +import org.jclouds.compute.internal.PersistNodeCredentials; +import org.jclouds.compute.options.TemplateOptions; +import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts; +import org.jclouds.compute.strategy.CreateNodesInGroupThenAddToSet; +import org.jclouds.compute.strategy.DestroyNodeStrategy; +import org.jclouds.compute.strategy.GetImageStrategy; +import org.jclouds.compute.strategy.GetNodeMetadataStrategy; +import org.jclouds.compute.strategy.InitializeRunScriptOnNodeOrPlaceInBadMap; +import org.jclouds.compute.strategy.ListNodesStrategy; +import org.jclouds.compute.strategy.RebootNodeStrategy; +import org.jclouds.compute.strategy.ResumeNodeStrategy; +import org.jclouds.compute.strategy.SuspendNodeStrategy; +import org.jclouds.domain.Credentials; +import org.jclouds.domain.Location; +import org.jclouds.joyent.sdc.v6_5.SDCClient; +import org.jclouds.joyent.sdc.v6_5.compute.internal.KeyAndPrivateKey; +import org.jclouds.joyent.sdc.v6_5.compute.options.SDCTemplateOptions; +import org.jclouds.joyent.sdc.v6_5.domain.Key; +import org.jclouds.joyent.sdc.v6_5.domain.datacenterscoped.DatacenterAndName; +import org.jclouds.joyent.sdc.v6_5.features.KeyClient; +import org.jclouds.joyent.sdc.v6_5.predicates.KeyPredicates; +import org.jclouds.scriptbuilder.functions.InitAdminAccess; + +import com.google.common.base.Function; +import com.google.common.base.Optional; +import com.google.common.base.Predicate; +import com.google.common.base.Supplier; +import com.google.common.cache.LoadingCache; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.common.collect.Multimap; + +/** + * @author Adrian Cole + */ +@Singleton +public class SDCComputeService extends BaseComputeService { + protected final SDCClient novaClient; + protected final LoadingCache keyCache; + protected final Function, Multimap> orphanedGroupsByDatacenterId; + protected final GroupNamingConvention.Factory namingConvention; + + @Inject + protected SDCComputeService(ComputeServiceContext context, Map credentialStore, + @Memoized Supplier> images, @Memoized Supplier> sizes, + @Memoized Supplier> locations, ListNodesStrategy listNodesStrategy, + GetImageStrategy getImageStrategy, GetNodeMetadataStrategy getNodeMetadataStrategy, + CreateNodesInGroupThenAddToSet runNodesAndAddToSetStrategy, RebootNodeStrategy rebootNodeStrategy, + DestroyNodeStrategy destroyNodeStrategy, ResumeNodeStrategy startNodeStrategy, + SuspendNodeStrategy stopNodeStrategy, Provider templateBuilderProvider, + Provider templateOptionsProvider, + @Named(TIMEOUT_NODE_RUNNING) Predicate> nodeRunning, + @Named(TIMEOUT_NODE_TERMINATED) Predicate> nodeTerminated, + @Named(TIMEOUT_NODE_SUSPENDED) Predicate> nodeSuspended, + InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, + RunScriptOnNode.Factory runScriptOnNodeFactory, InitAdminAccess initAdminAccess, + PersistNodeCredentials persistNodeCredentials, Timeouts timeouts, + @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, SDCClient novaClient, + LoadingCache keyCache, + Function, Multimap> orphanedGroupsByDatacenterId, + GroupNamingConvention.Factory namingConvention, Optional imageExtension) { + super(context, credentialStore, images, sizes, locations, listNodesStrategy, getImageStrategy, + getNodeMetadataStrategy, runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy, + startNodeStrategy, stopNodeStrategy, templateBuilderProvider, templateOptionsProvider, nodeRunning, + nodeTerminated, nodeSuspended, initScriptRunnerFactory, initAdminAccess, runScriptOnNodeFactory, + persistNodeCredentials, timeouts, executor, imageExtension); + this.novaClient = checkNotNull(novaClient, "novaClient"); + this.keyCache = checkNotNull(keyCache, "keyCache"); + this.orphanedGroupsByDatacenterId = checkNotNull(orphanedGroupsByDatacenterId, "orphanedGroupsByDatacenterId"); + this.namingConvention = checkNotNull(namingConvention, "namingConvention"); + } + + @Override + protected void cleanUpIncidentalResourcesOfDeadNodes(Set deadNodes) { + Multimap zoneToZoneAndGroupNames = orphanedGroupsByDatacenterId.apply(deadNodes); + for (String datacenterId : zoneToZoneAndGroupNames.keySet()) { + cleanupOrphanedKeysInZone(ImmutableSet.copyOf(zoneToZoneAndGroupNames.get(datacenterId)), datacenterId); + } + } + + private void cleanupOrphanedKeysInZone(Set groups, String datacenterId) { + KeyClient keyClient = novaClient.getKeyClient(); + for (String group : groups) { + for (Key key : Iterables.filter(keyClient.list(), + KeyPredicates.nameMatches(namingConvention.create().containsGroup(group)))) { + DatacenterAndName datacenterAndName = DatacenterAndName.fromDatacenterAndName(datacenterId, key.getName()); + logger.debug(">> deleting key(%s)", datacenterAndName); + keyClient.delete(key.getName()); + // TODO: test this clear happens + keyCache.invalidate(datacenterAndName); + logger.debug("<< deleted key(%s)", datacenterAndName); + } + + keyCache.invalidate(DatacenterAndName.fromDatacenterAndName(datacenterId, namingConvention.create() + .sharedNameForGroup(group))); + } + } + + /** + * returns template options, except of type {@link SDCTemplateOptions}. + */ + @Override + public SDCTemplateOptions templateOptions() { + return SDCTemplateOptions.class.cast(super.templateOptions()); + } + +} diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/SDCComputeServiceAdapter.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/SDCComputeServiceAdapter.java index b9eac76e64..97ce63fe77 100644 --- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/SDCComputeServiceAdapter.java +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/SDCComputeServiceAdapter.java @@ -92,9 +92,11 @@ public class SDCComputeServiceAdapter implements logger.trace("<< machine(%s)", machine.getId()); MachineInDatacenter machineInDatacenter = new MachineInDatacenter(machine, datacenterId); - // TODO: credentials or password - // if (!privateKey.isPresent()) - // credentialsBuilder.password(lightweightMachine.getAdminPass()); + + //TODO machineInDatacenter.metadata for password + if (template.getOptions().getLoginPrivateKey() != null){ + credentialsBuilder.privateKey(template.getOptions().getLoginPrivateKey()); + } return new NodeAndInitialCredentials(machineInDatacenter, machineInDatacenter.slashEncode(), credentialsBuilder.build()); } diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/config/SDCComputeServiceContextModule.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/config/SDCComputeServiceContextModule.java index 37dd2d4922..b8c5a291be 100644 --- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/config/SDCComputeServiceContextModule.java +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/config/SDCComputeServiceContextModule.java @@ -18,27 +18,40 @@ */ package org.jclouds.joyent.sdc.v6_5.compute.config; +import static org.jclouds.joyent.sdc.v6_5.config.SDCProperties.AUTOGENERATE_KEYS; + +import java.security.SecureRandom; import java.util.Map; import java.util.Set; import javax.inject.Singleton; import org.jclouds.collect.Memoized; +import org.jclouds.compute.ComputeService; 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.OperatingSystem; +import org.jclouds.compute.options.TemplateOptions; +import org.jclouds.compute.strategy.impl.CreateNodesWithGroupEncodedIntoNameThenAddToSet; import org.jclouds.domain.Location; import org.jclouds.functions.IdentityFunction; +import org.jclouds.joyent.sdc.v6_5.compute.SDCComputeService; import org.jclouds.joyent.sdc.v6_5.compute.SDCComputeServiceAdapter; import org.jclouds.joyent.sdc.v6_5.compute.functions.DatasetInDatacenterToImage; import org.jclouds.joyent.sdc.v6_5.compute.functions.DatasetToOperatingSystem; import org.jclouds.joyent.sdc.v6_5.compute.functions.MachineInDatacenterToNodeMetadata; +import org.jclouds.joyent.sdc.v6_5.compute.functions.OrphanedGroupsByDatacenterId; import org.jclouds.joyent.sdc.v6_5.compute.functions.PackageInDatacenterToHardware; +import org.jclouds.joyent.sdc.v6_5.compute.internal.KeyAndPrivateKey; +import org.jclouds.joyent.sdc.v6_5.compute.loaders.CreateUniqueKey; +import org.jclouds.joyent.sdc.v6_5.compute.options.SDCTemplateOptions; +import org.jclouds.joyent.sdc.v6_5.compute.strategy.ApplySDCTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet; import org.jclouds.joyent.sdc.v6_5.domain.Dataset; import org.jclouds.joyent.sdc.v6_5.domain.Machine; +import org.jclouds.joyent.sdc.v6_5.domain.datacenterscoped.DatacenterAndName; import org.jclouds.joyent.sdc.v6_5.domain.datacenterscoped.DatasetInDatacenter; import org.jclouds.joyent.sdc.v6_5.domain.datacenterscoped.MachineInDatacenter; import org.jclouds.joyent.sdc.v6_5.domain.datacenterscoped.PackageInDatacenter; @@ -48,10 +61,16 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.google.inject.Injector; import com.google.inject.Provides; import com.google.inject.TypeLiteral; +import com.google.inject.name.Names; /** * Module for building a compute service context for SDC @@ -82,9 +101,35 @@ public class SDCComputeServiceContextModule extends // we aren't converting location from a provider-specific type bind(new TypeLiteral>() { - }).to((Class) IdentityFunction.class); + }).to(Class.class.cast(IdentityFunction.class)); + + // how to figure out if a group in a datacenter is no longer in use + bind(new TypeLiteral, Multimap>>() { + }).to(OrphanedGroupsByDatacenterId.class); + + bind(ComputeService.class).to(SDCComputeService.class); + bind(TemplateOptions.class).to(SDCTemplateOptions.class); + + bind(CreateNodesWithGroupEncodedIntoNameThenAddToSet.class).to( + ApplySDCTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet.class); + + bind(new TypeLiteral>() { + }).to(CreateUniqueKey.class); + } + + @Override + protected TemplateOptions provideTemplateOptions(Injector injector, TemplateOptions options) { + return options.as(SDCTemplateOptions.class) + .generateKey(injector.getInstance( + com.google.inject.Key.get(boolean.class, Names.named(AUTOGENERATE_KEYS)))); } + @Provides + @Singleton + protected LoadingCache keyMap( + CacheLoader in) { + return CacheBuilder.newBuilder().build(in); + } @Provides @Singleton protected Supplier> createLocationIndexedById( @@ -105,6 +150,12 @@ public class SDCComputeServiceContextModule extends }, locations); } + + @Provides + @Singleton + protected SecureRandom provideSecureRandom() { + return new SecureRandom(); + } @VisibleForTesting public static final Map toPortableNodeStatus = ImmutableMap diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/functions/OrphanedGroupsByDatacenterId.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/functions/OrphanedGroupsByDatacenterId.java new file mode 100644 index 0000000000..5e0bbfc984 --- /dev/null +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/functions/OrphanedGroupsByDatacenterId.java @@ -0,0 +1,76 @@ +/** + * 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.joyent.sdc.v6_5.compute.functions; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.filter; +import static com.google.common.collect.Iterables.transform; + +import java.util.Set; + +import javax.inject.Inject; + +import org.jclouds.compute.ComputeService; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.predicates.NodePredicates; +import org.jclouds.joyent.sdc.v6_5.compute.predicates.AllNodesInGroupTerminated; +import org.jclouds.joyent.sdc.v6_5.domain.datacenterscoped.DatacenterAndName; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Multimap; +import com.google.common.collect.Multimaps; + +/** + * + * @author Adrian Cole + */ +public class OrphanedGroupsByDatacenterId implements Function, Multimap> { + private final Predicate allNodesInGroupTerminated; + + @Inject + protected OrphanedGroupsByDatacenterId(ComputeService computeService) { + this(new AllNodesInGroupTerminated(checkNotNull(computeService, "computeService"))); + } + + @VisibleForTesting + OrphanedGroupsByDatacenterId(Predicate allNodesInGroupTerminated) { + this.allNodesInGroupTerminated = checkNotNull(allNodesInGroupTerminated, "allNodesInGroupTerminated"); + } + + public Multimap apply(Set deadNodes) { + Iterable nodesWithGroup = filter(deadNodes, NodePredicates.hasGroup()); + Set datacenterAndGroupNames = ImmutableSet.copyOf(filter(transform(nodesWithGroup, + new Function() { + + @Override + public DatacenterAndName apply(NodeMetadata input) { + String datacenterId = input.getLocation().getId(); + return DatacenterAndName.fromDatacenterAndName(datacenterId, input.getGroup()); + } + + }), allNodesInGroupTerminated)); + Multimap datacenterToDatacenterAndGroupNames = Multimaps.transformValues(Multimaps.index(datacenterAndGroupNames, + DatacenterAndName.DATACENTER_FUNCTION), DatacenterAndName.NAME_FUNCTION); + return datacenterToDatacenterAndGroupNames; + } + +} \ No newline at end of file diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/internal/KeyAndPrivateKey.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/internal/KeyAndPrivateKey.java new file mode 100644 index 0000000000..04c75a52c4 --- /dev/null +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/internal/KeyAndPrivateKey.java @@ -0,0 +1,74 @@ +/** + * 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.joyent.sdc.v6_5.compute.internal; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.jclouds.joyent.sdc.v6_5.domain.Key; + +import com.google.common.base.Objects; + +/** + * @author Adrian Cole + */ +public class KeyAndPrivateKey { + + public static KeyAndPrivateKey fromKeyAndPrivateKey(Key key, String privateKey) { + return new KeyAndPrivateKey(key, privateKey); + } + + protected final Key key; + protected final String privateKey; + + protected KeyAndPrivateKey(Key key, String privateKey) { + this.key = checkNotNull(key, "key"); + this.privateKey = checkNotNull(privateKey, "privateKey"); + } + + @Override + public int hashCode() { + return Objects.hashCode(key, privateKey); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + KeyAndPrivateKey other = (KeyAndPrivateKey) obj; + return Objects.equal(key, other.key) && Objects.equal(privateKey, other.privateKey); + } + + public Key getKey() { + return key; + } + + public String getPrivateKey() { + return privateKey; + } + + @Override + public String toString() { + return "[key=" + key + ", privateKey=" + privateKey + "]"; + } + +} diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/loaders/CreateUniqueKey.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/loaders/CreateUniqueKey.java new file mode 100644 index 0000000000..ca192664ff --- /dev/null +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/loaders/CreateUniqueKey.java @@ -0,0 +1,90 @@ +/** + * 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.joyent.sdc.v6_5.compute.loaders; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.security.SecureRandom; +import java.util.Map; + +import javax.annotation.Resource; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.jclouds.compute.functions.GroupNamingConvention; +import org.jclouds.compute.reference.ComputeServiceConstants; +import org.jclouds.crypto.Crypto; +import org.jclouds.crypto.SshKeys; +import org.jclouds.joyent.sdc.v6_5.SDCClient; +import org.jclouds.joyent.sdc.v6_5.compute.internal.KeyAndPrivateKey; +import org.jclouds.joyent.sdc.v6_5.domain.Key; +import org.jclouds.joyent.sdc.v6_5.domain.datacenterscoped.DatacenterAndName; +import org.jclouds.logging.Logger; + +import com.google.common.cache.CacheLoader; +import com.google.inject.Inject; + +/** + * @author Adrian Cole + */ +@Singleton +public class CreateUniqueKey extends CacheLoader { + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; + protected final SDCClient sdcClient; + protected final GroupNamingConvention.Factory namingConvention; + protected final Crypto crypto; + protected final Provider secureRandom; + + @Inject + public CreateUniqueKey(SDCClient sdcClient, GroupNamingConvention.Factory namingConvention, Crypto crypto, Provider secureRandom) { + this.sdcClient = checkNotNull(sdcClient, "sdcClient"); + this.namingConvention = checkNotNull(namingConvention, "namingConvention"); + this.crypto = checkNotNull(crypto, "crypto"); + this.secureRandom = checkNotNull(secureRandom, "secureRandom"); + } + + @Override + public KeyAndPrivateKey load(DatacenterAndName datacenterAndName) { + String datacenterId = checkNotNull(datacenterAndName, "datacenterAndName").getDatacenter(); + String prefix = datacenterAndName.getName(); + + Map keyPair = SshKeys.generate(crypto.rsaKeyPairGenerator(), secureRandom.get()); + String publicKey = keyPair.get("public"); + String privateKey = keyPair.get("private"); + + logger.debug(">> creating key datacenter(%s) prefix(%s)", datacenterId, prefix); + + Key key = null; + while (key == null) { + String name = namingConvention.createWithoutPrefix().uniqueNameForGroup(prefix); + try { + key = sdcClient.getKeyClient().create(Key.builder().name(name).key(publicKey).build()); + } catch (IllegalStateException e) { + logger.trace("error creating keypair named %s, %s", name, e.getMessage()); + } + } + + logger.debug("<< created key(%s)", key.getName()); + return KeyAndPrivateKey.fromKeyAndPrivateKey(key, privateKey); + } + +} diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/options/SDCTemplateOptions.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/options/SDCTemplateOptions.java new file mode 100644 index 0000000000..d4c6e134b4 --- /dev/null +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/options/SDCTemplateOptions.java @@ -0,0 +1,334 @@ +/** + * 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.joyent.sdc.v6_5.compute.options; + +import static com.google.common.base.Objects.equal; + +import java.util.Map; + +import org.jclouds.compute.options.TemplateOptions; +import org.jclouds.domain.LoginCredentials; +import org.jclouds.scriptbuilder.domain.Statement; + +import com.google.common.base.Objects; +import com.google.common.base.Objects.ToStringHelper; + +/** + * Contains options supported in the {@code ComputeService#runNode} operation on the + * "joyent-sdc" provider.

Usage

The recommended way to instantiate a + * SDCTemplateOptions object is to statically import SDCTemplateOptions.* and invoke a static + * creation method followed by an instance mutator (if needed): + *

+ * + * import static org.jclouds.aws.ec2.compute.options.SDCTemplateOptions.Builder.*; + *

+ * ComputeService client = // get connection + * templateBuilder.options(inboundPorts(22, 80, 8080, 443)); + * Set set = client.createNodesInGroup(tag, 2, templateBuilder.build()); + * + * + * @author Adrian Cole + */ +public class SDCTemplateOptions extends TemplateOptions implements Cloneable { + @Override + public SDCTemplateOptions clone() { + SDCTemplateOptions options = new SDCTemplateOptions(); + copyTo(options); + return options; + } + + @Override + public void copyTo(TemplateOptions to) { + super.copyTo(to); + if (to instanceof SDCTemplateOptions) { + SDCTemplateOptions eTo = SDCTemplateOptions.class.cast(to); + eTo.generateKey(shouldGenerateKey()); + } + } + + protected boolean generateKey = false; + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + SDCTemplateOptions that = SDCTemplateOptions.class.cast(o); + return super.equals(that) && equal(this.generateKey, that.generateKey); + } + + @Override + public int hashCode() { + return Objects.hashCode(super.hashCode(), generateKey); + } + + @Override + public ToStringHelper string() { + return super.string().add("generateKey", generateKey); + } + + /** + * @see #shouldGenerateKey() + */ + public SDCTemplateOptions generateKey(boolean enable) { + this.generateKey = enable; + return this; + } + + /** + * + * @return true if auto generation of keys is enabled + */ + public boolean shouldGenerateKey() { + return generateKey; + } + + public static class Builder { + + /** + * @see SDCTemplateOptions#shouldGenerateKey() + */ + public static SDCTemplateOptions generateKey(boolean enable) { + return new SDCTemplateOptions().generateKey(enable); + } + + // methods that only facilitate returning the correct object type + + /** + * @see TemplateOptions#inboundPorts + */ + public static SDCTemplateOptions inboundPorts(int... ports) { + SDCTemplateOptions options = new SDCTemplateOptions(); + return SDCTemplateOptions.class.cast(options.inboundPorts(ports)); + } + + /** + * @see TemplateOptions#port + */ + public static SDCTemplateOptions blockOnPort(int port, int seconds) { + SDCTemplateOptions options = new SDCTemplateOptions(); + return SDCTemplateOptions.class.cast(options.blockOnPort(port, seconds)); + } + + /** + * @see TemplateOptions#installPrivateKey + */ + public static SDCTemplateOptions installPrivateKey(String rsaKey) { + SDCTemplateOptions options = new SDCTemplateOptions(); + return SDCTemplateOptions.class.cast(options.installPrivateKey(rsaKey)); + } + + /** + * @see TemplateOptions#authorizePublicKey + */ + public static SDCTemplateOptions authorizePublicKey(String rsaKey) { + SDCTemplateOptions options = new SDCTemplateOptions(); + return SDCTemplateOptions.class.cast(options.authorizePublicKey(rsaKey)); + } + + /** + * @see TemplateOptions#userMetadata + */ + public static SDCTemplateOptions userMetadata(Map userMetadata) { + SDCTemplateOptions options = new SDCTemplateOptions(); + return SDCTemplateOptions.class.cast(options.userMetadata(userMetadata)); + } + + /** + * @see TemplateOptions#overrideLoginUser + */ + public static SDCTemplateOptions overrideLoginUser(String user) { + SDCTemplateOptions options = new SDCTemplateOptions(); + return options.overrideLoginUser(user); + } + + /** + * @see TemplateOptions#overrideLoginPassword + */ + public static SDCTemplateOptions overrideLoginPassword(String password) { + SDCTemplateOptions options = new SDCTemplateOptions(); + return options.overrideLoginPassword(password); + } + + /** + * @see TemplateOptions#overrideLoginPrivateKey + */ + public static SDCTemplateOptions overrideLoginPrivateKey(String privateKey) { + SDCTemplateOptions options = new SDCTemplateOptions(); + return options.overrideLoginPrivateKey(privateKey); + } + + /** + * @see TemplateOptions#overrideAuthenticateSudo + */ + public static SDCTemplateOptions overrideAuthenticateSudo(boolean authenticateSudo) { + SDCTemplateOptions options = new SDCTemplateOptions(); + return options.overrideAuthenticateSudo(authenticateSudo); + } + + /** + * @see TemplateOptions#overrideLoginCredentials + */ + public static SDCTemplateOptions overrideLoginCredentials(LoginCredentials credentials) { + SDCTemplateOptions options = new SDCTemplateOptions(); + return options.overrideLoginCredentials(credentials); + } + + /** + * @see TemplateOptions#blockUntilRunning + */ + public static SDCTemplateOptions blockUntilRunning(boolean blockUntilRunning) { + SDCTemplateOptions options = new SDCTemplateOptions(); + return options.blockUntilRunning(blockUntilRunning); + } + + } + + // methods that only facilitate returning the correct object type + + /** + * {@inheritDoc} + */ + @Override + public SDCTemplateOptions blockOnPort(int port, int seconds) { + return SDCTemplateOptions.class.cast(super.blockOnPort(port, seconds)); + } + + /** + * {@inheritDoc} + */ + @Override + public SDCTemplateOptions inboundPorts(int... ports) { + return SDCTemplateOptions.class.cast(super.inboundPorts(ports)); + } + + /** + * {@inheritDoc} + */ + @Override + public SDCTemplateOptions authorizePublicKey(String publicKey) { + return SDCTemplateOptions.class.cast(super.authorizePublicKey(publicKey)); + } + + /** + * {@inheritDoc} + */ + @Override + public SDCTemplateOptions installPrivateKey(String privateKey) { + return SDCTemplateOptions.class.cast(super.installPrivateKey(privateKey)); + } + + /** + * {@inheritDoc} + */ + @Override + public SDCTemplateOptions blockUntilRunning(boolean blockUntilRunning) { + return SDCTemplateOptions.class.cast(super.blockUntilRunning(blockUntilRunning)); + } + + /** + * {@inheritDoc} + */ + @Override + public SDCTemplateOptions dontAuthorizePublicKey() { + return SDCTemplateOptions.class.cast(super.dontAuthorizePublicKey()); + } + + /** + * {@inheritDoc} + */ + @Override + public SDCTemplateOptions nameTask(String name) { + return SDCTemplateOptions.class.cast(super.nameTask(name)); + } + + /** + * {@inheritDoc} + */ + @Override + public SDCTemplateOptions runAsRoot(boolean runAsRoot) { + return SDCTemplateOptions.class.cast(super.runAsRoot(runAsRoot)); + } + + /** + * {@inheritDoc} + */ + @Override + public SDCTemplateOptions runScript(Statement script) { + return SDCTemplateOptions.class.cast(super.runScript(script)); + } + + /** + * {@inheritDoc} + */ + @Override + public SDCTemplateOptions overrideLoginCredentials(LoginCredentials overridingCredentials) { + return SDCTemplateOptions.class.cast(super.overrideLoginCredentials(overridingCredentials)); + } + + /** + * {@inheritDoc} + */ + @Override + public SDCTemplateOptions overrideLoginPassword(String password) { + return SDCTemplateOptions.class.cast(super.overrideLoginPassword(password)); + } + + /** + * {@inheritDoc} + */ + @Override + public SDCTemplateOptions overrideLoginPrivateKey(String privateKey) { + return SDCTemplateOptions.class.cast(super.overrideLoginPrivateKey(privateKey)); + } + + /** + * {@inheritDoc} + */ + @Override + public SDCTemplateOptions overrideLoginUser(String loginUser) { + return SDCTemplateOptions.class.cast(super.overrideLoginUser(loginUser)); + } + + /** + * {@inheritDoc} + */ + @Override + public SDCTemplateOptions overrideAuthenticateSudo(boolean authenticateSudo) { + return SDCTemplateOptions.class.cast(super.overrideAuthenticateSudo(authenticateSudo)); + } + + /** + * {@inheritDoc} + */ + @Override + public SDCTemplateOptions userMetadata(Map userMetadata) { + return SDCTemplateOptions.class.cast(super.userMetadata(userMetadata)); + } + + /** + * {@inheritDoc} + */ + @Override + public SDCTemplateOptions userMetadata(String key, String value) { + return SDCTemplateOptions.class.cast(super.userMetadata(key, value)); + } + +} diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/predicates/AllNodesInGroupTerminated.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/predicates/AllNodesInGroupTerminated.java new file mode 100644 index 0000000000..ee20fddc35 --- /dev/null +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/predicates/AllNodesInGroupTerminated.java @@ -0,0 +1,52 @@ +/** + * 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.joyent.sdc.v6_5.compute.predicates; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Predicates.and; +import static com.google.common.collect.Iterables.all; +import static org.jclouds.compute.predicates.NodePredicates.TERMINATED; +import static org.jclouds.compute.predicates.NodePredicates.inGroup; +import static org.jclouds.compute.predicates.NodePredicates.locationId; + +import javax.inject.Inject; + +import org.jclouds.compute.ComputeService; +import org.jclouds.joyent.sdc.v6_5.domain.datacenterscoped.DatacenterAndName; + +import com.google.common.base.Predicate; + +/** + * @author Adrian Cole + */ +public class AllNodesInGroupTerminated implements Predicate { + private final ComputeService computeService; + + + //TODO: TESTME + @Inject + public AllNodesInGroupTerminated(ComputeService computeService) { + this.computeService = checkNotNull(computeService, "computeService"); + } + + @Override + public boolean apply(DatacenterAndName input) { + return all(computeService.listNodesDetailsMatching(locationId(input.getDatacenter())), and(inGroup(input.getName()), TERMINATED)); + } +} \ No newline at end of file diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/strategy/ApplySDCTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/strategy/ApplySDCTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet.java new file mode 100644 index 0000000000..c81bebfa0b --- /dev/null +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/strategy/ApplySDCTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet.java @@ -0,0 +1,110 @@ +/** + * 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.joyent.sdc.v6_5.compute.strategy; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.jclouds.Constants; +import org.jclouds.compute.config.CustomizationResponse; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.domain.Template; +import org.jclouds.compute.domain.TemplateBuilder; +import org.jclouds.compute.functions.GroupNamingConvention; +import org.jclouds.compute.strategy.CreateNodeWithGroupEncodedIntoName; +import org.jclouds.compute.strategy.CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap; +import org.jclouds.compute.strategy.ListNodesStrategy; +import org.jclouds.compute.strategy.impl.CreateNodesWithGroupEncodedIntoNameThenAddToSet; +import org.jclouds.joyent.sdc.v6_5.compute.internal.KeyAndPrivateKey; +import org.jclouds.joyent.sdc.v6_5.compute.options.SDCTemplateOptions; +import org.jclouds.joyent.sdc.v6_5.domain.datacenterscoped.DatacenterAndName; + +import com.google.common.cache.LoadingCache; +import com.google.common.collect.Multimap; + +/** + * + * @author Adrian Cole + */ +@Singleton +public class ApplySDCTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet extends + CreateNodesWithGroupEncodedIntoNameThenAddToSet { + + private final LoadingCache keyCache; + private final Provider templateBuilderProvider; + + @Inject + protected ApplySDCTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet( + CreateNodeWithGroupEncodedIntoName addNodeWithTagStrategy, + ListNodesStrategy listNodesStrategy, + GroupNamingConvention.Factory namingConvention, + CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap.Factory customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory, + @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, + Provider templateBuilderProvider, + LoadingCache keyCache) { + super(addNodeWithTagStrategy, listNodesStrategy, namingConvention, executor, + customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory); + this.templateBuilderProvider = checkNotNull(templateBuilderProvider, "templateBuilderProvider"); + this.keyCache = checkNotNull(keyCache, "keyCache"); + } + + @Override + public Map> execute(String group, int count, Template template, Set goodNodes, + Map badNodes, Multimap customizationResponses) { + + Template mutableTemplate; + // ensure we don't mutate the input template, fromTemplate ignores imageId + // so + // build directly from imageId if we have it + if (template.getImage() != null && template.getImage().getId() != null) { + mutableTemplate = templateBuilderProvider.get().imageId(template.getImage().getId()).fromTemplate(template) + .build(); + // otherwise build from generic parameters + } else { + mutableTemplate = templateBuilderProvider.get().fromTemplate(template).build(); + } + + SDCTemplateOptions templateOptions = SDCTemplateOptions.class.cast(mutableTemplate.getOptions()); + + assert template.getOptions().equals(templateOptions) : "options didn't clone properly"; + + String datacenter = mutableTemplate.getLocation().getId(); + + if (templateOptions.shouldGenerateKey()) { + KeyAndPrivateKey keyPair = keyCache.getUnchecked(DatacenterAndName.fromDatacenterAndName(datacenter, namingConvention.create() + .sharedNameForGroup(group))); + // in order to delete the key later + keyCache.asMap().put(DatacenterAndName.fromDatacenterAndName(datacenter, keyPair.getKey().getName()), keyPair); + templateOptions.overrideLoginPrivateKey(keyPair.getPrivateKey()); + } + checkArgument(templateOptions.getRunScript() == null || templateOptions.getLoginPrivateKey() != null, + "when specifying runScript, you must either set overrideLoginPrivateKey, or generateKey(true)"); + return super.execute(group, count, mutableTemplate, goodNodes, badNodes, customizationResponses); + } +} \ No newline at end of file diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/config/SDCProperties.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/config/SDCProperties.java index 71eaaea09a..e408831c84 100644 --- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/config/SDCProperties.java +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/config/SDCProperties.java @@ -25,4 +25,10 @@ package org.jclouds.joyent.sdc.v6_5.config; */ public class SDCProperties { + /** + * Whenever a node is created, automatically generate keys for groups, as needed, also + * delete the key(s) when the last node in the group is destroyed. + */ + public static final String AUTOGENERATE_KEYS = "jclouds.joyent-sdc.autogenerate-keys"; + } diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Machine.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Machine.java index b0b77f50d3..5bbe326a44 100644 --- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Machine.java +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Machine.java @@ -70,6 +70,10 @@ public class Machine implements Comparable { public static Builder builder() { return new Builder(); } + + public Builder toBuilder() { + return new Builder().fromMachine(this); + } public static class Builder { private String id; diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/datacenterscoped/DatacenterAndName.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/datacenterscoped/DatacenterAndName.java index e6eba70278..158c3846c7 100644 --- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/datacenterscoped/DatacenterAndName.java +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/datacenterscoped/DatacenterAndName.java @@ -44,7 +44,7 @@ public class DatacenterAndName { }; - public final static Function ZONE_FUNCTION = new Function(){ + public final static Function DATACENTER_FUNCTION = new Function(){ @Override public String apply(DatacenterAndName input) { diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/functions/internal/SDCTypeAdapters.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/functions/internal/SDCTypeAdapters.java index 03a2424cdd..78c6066d73 100644 --- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/functions/internal/SDCTypeAdapters.java +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/functions/internal/SDCTypeAdapters.java @@ -28,7 +28,7 @@ import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; /** - * @author Adam Lowe + * @author Adrian Cole */ public class SDCTypeAdapters { diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/predicates/KeyPredicates.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/predicates/KeyPredicates.java new file mode 100644 index 0000000000..c5963b117c --- /dev/null +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/predicates/KeyPredicates.java @@ -0,0 +1,58 @@ +/* + * 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.joyent.sdc.v6_5.predicates; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.jclouds.joyent.sdc.v6_5.domain.Key; + +import com.google.common.base.Predicate; + +/** + * Predicates handy when working with Keys + * + * @author Adrian Cole + */ + +public class KeyPredicates { + + + /** + * matches name of the given key pair + * + * @param name + * @return predicate that matches name + */ + public static Predicate nameMatches(final Predicate name) { + checkNotNull(name, "name must be defined"); + + return new Predicate() { + @Override + public boolean apply(Key ext) { + return name.apply(ext.getName()); + } + + @Override + public String toString() { + return "nameMatches(" + name + ")"; + } + }; + } + +} diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/functions/DatasetInDatacenterToImageTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/functions/DatasetInDatacenterToImageTest.java index fe117f44ea..c517630939 100644 --- a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/functions/DatasetInDatacenterToImageTest.java +++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/functions/DatasetInDatacenterToImageTest.java @@ -46,8 +46,8 @@ import com.google.common.collect.ImmutableMap; @Test(testName = "DatasetInDatacenterToHardwareTest") public class DatasetInDatacenterToImageTest { - Location provider = new LocationBuilder().scope(LocationScope.PROVIDER).id("openstack-sdc") - .description("openstack-sdc").build(); + Location provider = new LocationBuilder().scope(LocationScope.PROVIDER).id("joyent-sdc") + .description("joyent-sdc").build(); Location zone = new LocationBuilder().id("us-sw-1").description("us-sw-1").scope(LocationScope.ZONE) .parent(provider).build(); Supplier> locationIndex = Suppliers.> ofInstance(ImmutableMap diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/functions/MachineInDatacenterToNodeMetadataTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/functions/MachineInDatacenterToNodeMetadataTest.java index 89c07b44bd..2ff1ac0a33 100644 --- a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/functions/MachineInDatacenterToNodeMetadataTest.java +++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/functions/MachineInDatacenterToNodeMetadataTest.java @@ -57,8 +57,8 @@ import com.google.inject.Guice; @Test(testName = "MachineInDatacenterToNodeMetadataTest") public class MachineInDatacenterToNodeMetadataTest { - Location provider = new LocationBuilder().scope(LocationScope.PROVIDER).id("openstack-sdc") - .description("openstack-sdc").build(); + Location provider = new LocationBuilder().scope(LocationScope.PROVIDER).id("joyent-sdc") + .description("joyent-sdc").build(); Location zone = new LocationBuilder().id("us-sw-1").description("us-sw-1").scope(LocationScope.ZONE) .parent(provider).build(); Supplier> locationIndex = Suppliers.> ofInstance(ImmutableMap diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/functions/OrphanedGroupsByDatacenterIdTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/functions/OrphanedGroupsByDatacenterIdTest.java new file mode 100644 index 0000000000..0d617756da --- /dev/null +++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/functions/OrphanedGroupsByDatacenterIdTest.java @@ -0,0 +1,100 @@ +/** + * 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.joyent.sdc.v6_5.compute.functions; + +import static org.testng.Assert.assertEquals; + +import java.util.Map; +import java.util.Set; + +import org.jclouds.compute.domain.Hardware; +import org.jclouds.compute.domain.Image; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.functions.GroupNamingConvention; +import org.jclouds.domain.Location; +import org.jclouds.domain.LocationBuilder; +import org.jclouds.domain.LocationScope; +import org.jclouds.joyent.sdc.v6_5.compute.config.SDCComputeServiceContextModule; +import org.jclouds.joyent.sdc.v6_5.domain.Machine.State; +import org.jclouds.joyent.sdc.v6_5.domain.datacenterscoped.DatacenterAndName; +import org.jclouds.joyent.sdc.v6_5.domain.datacenterscoped.MachineInDatacenter; +import org.jclouds.joyent.sdc.v6_5.parse.ParseMachineTest; +import org.testng.annotations.Test; + +import com.google.common.base.Predicates; +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.ImmutableSet; +import com.google.inject.Guice; + +/** + * + * + * @author Adrian Cole + */ +@Test(testName = "OrphanedGroupsByDatacenterIdTest") +public class OrphanedGroupsByDatacenterIdTest { + + Location provider = new LocationBuilder().scope(LocationScope.PROVIDER).id("joyent-sdc").description( + "joyent-sdc").build(); + Location datacenter = new LocationBuilder().id("us-east-1").description("us-east-1").scope( + LocationScope.ZONE).parent(provider).build(); + Supplier> locationIndex = Suppliers.> ofInstance(ImmutableMap + . of("us-east-1", datacenter)); + + GroupNamingConvention.Factory namingConvention = Guice.createInjector().getInstance(GroupNamingConvention.Factory.class); + + MachineInDatacenter machine1 = new MachineInDatacenter(new ParseMachineTest().expected().toBuilder().name("test-fe2").state(State.DELETED).build(), "us-east-1"); + MachineInDatacenter machine2 = new MachineInDatacenter(new ParseMachineTest().expected().toBuilder().name("sample-fe1").state(State.DELETED).build(), "us-east-1"); + + @Test + public void testWhenComputeServiceSaysAllNodesAreDeadBothGroupsAreReturned() { + + + MachineInDatacenterToNodeMetadata converter = new MachineInDatacenterToNodeMetadata( + SDCComputeServiceContextModule.toPortableNodeStatus, locationIndex, Suppliers + .> ofInstance(ImmutableSet. of()), Suppliers + .> ofInstance(ImmutableSet. of()), namingConvention); + + Set set = ImmutableSet.of(converter.apply(machine2), converter.apply(machine1)); + + assertEquals(new OrphanedGroupsByDatacenterId(Predicates. alwaysTrue()).apply(set), ImmutableMultimap + . builder().putAll("us-east-1", "sample", "test").build()); + } + + @Test + public void testWhenComputeServiceSaysAllNodesAreDeadNoGroupsAreReturned() { + + MachineInDatacenter machine1 = new MachineInDatacenter(new ParseMachineTest().expected(), "us-east-1"); + MachineInDatacenter machine2 = new MachineInDatacenter(new ParseMachineTest().expected(), "us-east-1"); + + MachineInDatacenterToNodeMetadata converter = new MachineInDatacenterToNodeMetadata( + SDCComputeServiceContextModule.toPortableNodeStatus, locationIndex, Suppliers + .> ofInstance(ImmutableSet. of()), Suppliers + .> ofInstance(ImmutableSet. of()), namingConvention); + + Set set = ImmutableSet.of(converter.apply(machine2), converter.apply(machine1)); + + assertEquals(new OrphanedGroupsByDatacenterId(Predicates. alwaysFalse()).apply(set), ImmutableMultimap + . of()); + + } +} diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/functions/PackageInDatacenterToHardwareTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/functions/PackageInDatacenterToHardwareTest.java index 1390929caf..8a88530063 100644 --- a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/functions/PackageInDatacenterToHardwareTest.java +++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/functions/PackageInDatacenterToHardwareTest.java @@ -43,8 +43,8 @@ import com.google.common.collect.ImmutableMap; */ @Test(testName = "PackageInDatacenterToHardwareTest") public class PackageInDatacenterToHardwareTest { - Location provider = new LocationBuilder().scope(LocationScope.PROVIDER).id("openstack-sdc").description( - "openstack-sdc").build(); + Location provider = new LocationBuilder().scope(LocationScope.PROVIDER).id("joyent-sdc").description( + "joyent-sdc").build(); Location zone = new LocationBuilder().id("us-sw-1").description("us-sw-1").scope( LocationScope.ZONE).parent(provider).build(); Supplier> locationIndex = Suppliers.> ofInstance(ImmutableMap diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/loaders/CreateUniqueKeyTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/loaders/CreateUniqueKeyTest.java new file mode 100644 index 0000000000..9e2a4a22e6 --- /dev/null +++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/loaders/CreateUniqueKeyTest.java @@ -0,0 +1,118 @@ +/** + * 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.joyent.sdc.v6_5.compute.loaders; + +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; +import static org.jclouds.crypto.PemsTest.PRIVATE_KEY; +import static org.jclouds.crypto.PemsTest.PUBLIC_KEY; +import static org.testng.Assert.assertEquals; + +import java.io.IOException; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.InvalidKeySpecException; + +import org.jclouds.compute.functions.GroupNamingConvention; +import org.jclouds.compute.functions.GroupNamingConvention.Factory; +import org.jclouds.crypto.Crypto; +import org.jclouds.crypto.Pems; +import org.jclouds.crypto.SshKeys; +import org.jclouds.io.Payloads; +import org.jclouds.joyent.sdc.v6_5.SDCClient; +import org.jclouds.joyent.sdc.v6_5.compute.internal.KeyAndPrivateKey; +import org.jclouds.joyent.sdc.v6_5.domain.Key; +import org.jclouds.joyent.sdc.v6_5.domain.datacenterscoped.DatacenterAndName; +import org.jclouds.joyent.sdc.v6_5.features.KeyClient; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.TypeLiteral; +import com.google.inject.util.Providers; + +/** + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "CreateUniqueKeyTest") +public class CreateUniqueKeyTest { + private Factory namingConvention; + private KeyPair keyPair; + private String openSshKey; + + @BeforeClass + public void setup() throws InvalidKeySpecException, NoSuchAlgorithmException, IOException { + namingConvention = Guice.createInjector(new AbstractModule() { + + @Override + protected void configure() { + bind(new TypeLiteral>() { + }).toInstance(Suppliers.ofInstance("foo")); + } + }).getInstance(GroupNamingConvention.Factory.class); + KeyFactory keyfactory = KeyFactory.getInstance("RSA"); + PrivateKey privateKey = keyfactory.generatePrivate(Pems.privateKeySpec(Payloads.newStringPayload(PRIVATE_KEY))); + + PublicKey publicKey = keyfactory + .generatePublic(Pems.publicKeySpec(Payloads.newStringPayload(PUBLIC_KEY))); + + keyPair = new KeyPair(publicKey, privateKey); + openSshKey = SshKeys.encodeAsOpenSSH(RSAPublicKey.class.cast(publicKey)); + } + + @Test + public void testApply() { + SDCClient sdcClient = createMock(SDCClient.class); + KeyClient keyClient = createMock(KeyClient.class); + Crypto crypto = createMock(Crypto.class); + KeyPairGenerator rsaKeyPairGenerator = createMock(KeyPairGenerator.class); + SecureRandom secureRandom = createMock(SecureRandom.class); + + Key key = Key.builder().name("group-foo").key(openSshKey).build(); + + expect(crypto.rsaKeyPairGenerator()).andReturn(rsaKeyPairGenerator); + rsaKeyPairGenerator.initialize(2048, secureRandom); + expect(rsaKeyPairGenerator.genKeyPair()).andReturn(keyPair); + + expect(sdcClient.getKeyClient()).andReturn(keyClient); + + expect(keyClient.create(key)).andReturn(key); + + replay(sdcClient, keyClient, crypto, rsaKeyPairGenerator, secureRandom); + + CreateUniqueKey parser = new CreateUniqueKey(sdcClient, namingConvention, crypto, Providers.of(secureRandom)); + + assertEquals(parser.load(DatacenterAndName.fromDatacenterAndName("datacenter", "group")), + KeyAndPrivateKey.fromKeyAndPrivateKey(key, PRIVATE_KEY)); + + verify(sdcClient, keyClient, crypto, rsaKeyPairGenerator, secureRandom); + } + +} diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/options/SDCTemplateOptionsTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/options/SDCTemplateOptionsTest.java new file mode 100644 index 0000000000..bf75a904d7 --- /dev/null +++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/options/SDCTemplateOptionsTest.java @@ -0,0 +1,181 @@ +/** + * 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.joyent.sdc.v6_5.compute.options; + +import static org.jclouds.joyent.sdc.v6_5.compute.options.SDCTemplateOptions.Builder.authorizePublicKey; +import static org.jclouds.joyent.sdc.v6_5.compute.options.SDCTemplateOptions.Builder.blockOnPort; +import static org.jclouds.joyent.sdc.v6_5.compute.options.SDCTemplateOptions.Builder.generateKey; +import static org.jclouds.joyent.sdc.v6_5.compute.options.SDCTemplateOptions.Builder.inboundPorts; +import static org.jclouds.joyent.sdc.v6_5.compute.options.SDCTemplateOptions.Builder.installPrivateKey; +import static org.testng.Assert.assertEquals; + +import java.io.IOException; + +import org.jclouds.compute.options.TemplateOptions; +import org.testng.annotations.Test; + +/** + * Tests possible uses of SDCTemplateOptions and SDCTemplateOptions.Builder.* + * + * @author Adrian Cole + */ +@Test(testName = "SDCTemplateOptionsTest") +public class SDCTemplateOptionsTest { + + public void testAs() { + TemplateOptions options = new SDCTemplateOptions(); + assertEquals(options.as(SDCTemplateOptions.class), options); + } + + @Test + public void testGenerateKeyDefault() { + SDCTemplateOptions options = new SDCTemplateOptions(); + assert !options.shouldGenerateKey(); + } + + @Test + public void testGenerateKey() { + SDCTemplateOptions options = new SDCTemplateOptions().generateKey(true); + assert options.shouldGenerateKey(); + } + + @Test + public void testGenerateKeyStatic() { + SDCTemplateOptions options = generateKey(true); + assert options.shouldGenerateKey(); + } + + // superclass tests + @Test(expectedExceptions = IllegalArgumentException.class) + public void testinstallPrivateKeyBadFormat() { + SDCTemplateOptions options = new SDCTemplateOptions(); + options.installPrivateKey("whompy"); + } + + @Test + public void testinstallPrivateKey() throws IOException { + SDCTemplateOptions options = new SDCTemplateOptions(); + options.installPrivateKey("-----BEGIN RSA PRIVATE KEY-----"); + assertEquals(options.getPrivateKey(), "-----BEGIN RSA PRIVATE KEY-----"); + } + + @Test + public void testNullinstallPrivateKey() { + SDCTemplateOptions options = new SDCTemplateOptions(); + assertEquals(options.getPrivateKey(), null); + } + + @Test + public void testinstallPrivateKeyStatic() throws IOException { + SDCTemplateOptions options = installPrivateKey("-----BEGIN RSA PRIVATE KEY-----"); + assertEquals(options.getPrivateKey(), "-----BEGIN RSA PRIVATE KEY-----"); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testinstallPrivateKeyNPE() { + installPrivateKey(null); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testauthorizePublicKeyBadFormat() { + SDCTemplateOptions options = new SDCTemplateOptions(); + options.authorizePublicKey("whompy"); + } + + @Test + public void testauthorizePublicKey() throws IOException { + SDCTemplateOptions options = new SDCTemplateOptions(); + options.authorizePublicKey("ssh-rsa"); + assertEquals(options.getPublicKey(), "ssh-rsa"); + } + + @Test + public void testNullauthorizePublicKey() { + SDCTemplateOptions options = new SDCTemplateOptions(); + assertEquals(options.getPublicKey(), null); + } + + @Test + public void testauthorizePublicKeyStatic() throws IOException { + SDCTemplateOptions options = authorizePublicKey("ssh-rsa"); + assertEquals(options.getPublicKey(), "ssh-rsa"); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testauthorizePublicKeyNPE() { + authorizePublicKey(null); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testblockOnPortBadFormat() { + SDCTemplateOptions options = new SDCTemplateOptions(); + options.blockOnPort(-1, -1); + } + + @Test + public void testblockOnPort() { + SDCTemplateOptions options = new SDCTemplateOptions(); + options.blockOnPort(22, 30); + assertEquals(options.getPort(), 22); + assertEquals(options.getSeconds(), 30); + + } + + @Test + public void testNullblockOnPort() { + SDCTemplateOptions options = new SDCTemplateOptions(); + assertEquals(options.getPort(), -1); + assertEquals(options.getSeconds(), -1); + } + + @Test + public void testblockOnPortStatic() { + SDCTemplateOptions options = blockOnPort(22, 30); + assertEquals(options.getPort(), 22); + assertEquals(options.getSeconds(), 30); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testinboundPortsBadFormat() { + SDCTemplateOptions options = new SDCTemplateOptions(); + options.inboundPorts(-1, -1); + } + + @Test + public void testinboundPorts() { + SDCTemplateOptions options = new SDCTemplateOptions(); + options.inboundPorts(22, 30); + assertEquals(options.getInboundPorts()[0], 22); + assertEquals(options.getInboundPorts()[1], 30); + + } + + @Test + public void testDefaultOpen22() { + SDCTemplateOptions options = new SDCTemplateOptions(); + assertEquals(options.getInboundPorts()[0], 22); + } + + @Test + public void testinboundPortsStatic() { + SDCTemplateOptions options = inboundPorts(22, 30); + assertEquals(options.getInboundPorts()[0], 22); + assertEquals(options.getInboundPorts()[1], 30); + } +} diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/MachineClientLiveTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/MachineClientLiveTest.java index ffbd01ae17..25477e150b 100644 --- a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/MachineClientLiveTest.java +++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/MachineClientLiveTest.java @@ -78,7 +78,7 @@ public class MachineClientLiveTest extends BaseSDCClientLiveTest { } } - private Map keypair; + private Map key; private String fingerprint; private RetryablePredicate socketTester; private Predicate machineRunning; @@ -91,9 +91,9 @@ public class MachineClientLiveTest extends BaseSDCClientLiveTest { @Override public void setupContext() { super.setupContext(); - keypair = SshKeys.generate(); - fingerprint = SshKeys.fingerprintPublicKey(keypair.get("public")); - sdcContext.getApi().getKeyClient().create(Key.builder().name(fingerprint).key(keypair.get("public")).build()); + key = SshKeys.generate(); + fingerprint = SshKeys.fingerprintPublicKey(key.get("public")); + sdcContext.getApi().getKeyClient().create(Key.builder().name(fingerprint).key(key.get("public")).build()); client = sdcContext.getApi().getMachineClientForDatacenter( Iterables.get(sdcContext.getApi().getConfiguredDatacenters(), 0)); socketTester = new RetryablePredicate(new InetSocketAddressConnect(), 180, 1, 1, TimeUnit.SECONDS); @@ -123,8 +123,8 @@ public class MachineClientLiveTest extends BaseSDCClientLiveTest { assertEquals(newMachine.getMetadata().get("foo").toString(), "bar"); assertTrue( - newMachine.getMetadata().get(MetadataKeys.ROOT_AUTHORIZED_KEYS.key()).indexOf(keypair.get("public")) != -1, - newMachine + "; key: " + keypair.get("public")); + newMachine.getMetadata().get(MetadataKeys.ROOT_AUTHORIZED_KEYS.key()).indexOf(key.get("public")) != -1, + newMachine + "; key: " + key.get("public")); assertTrue(machineRunning.apply(newMachine), newMachine.toString()); machine = client.get(newMachine.getId()); @@ -142,7 +142,7 @@ public class MachineClientLiveTest extends BaseSDCClientLiveTest { HostAndPort socket = HostAndPort.fromParts(publicAddress, 22); assertTrue(socketTester.apply(socket), socket.toString()); SshClient client = context.utils().injector().getInstance(SshClient.Factory.class) - .create(socket, LoginCredentials.builder().user("root").privateKey(keypair.get("private")).build()); + .create(socket, LoginCredentials.builder().user("root").privateKey(key.get("private")).build()); try { client.connect(); ExecResponse exec = client.exec("echo hello");