You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by na...@apache.org on 2017/06/15 10:30:31 UTC

jclouds git commit: Improve duplicate ssh key check in Packet

Repository: jclouds
Updated Branches:
  refs/heads/master eaf10f10a -> b77ea0695


Improve duplicate ssh key check in Packet


Project: http://git-wip-us.apache.org/repos/asf/jclouds/repo
Commit: http://git-wip-us.apache.org/repos/asf/jclouds/commit/b77ea069
Tree: http://git-wip-us.apache.org/repos/asf/jclouds/tree/b77ea069
Diff: http://git-wip-us.apache.org/repos/asf/jclouds/diff/b77ea069

Branch: refs/heads/master
Commit: b77ea0695007a408c6775b034ddb0223966cca82
Parents: eaf10f1
Author: Ignasi Barrera <na...@apache.org>
Authored: Thu Jun 15 12:30:17 2017 +0200
Committer: Ignasi Barrera <na...@apache.org>
Committed: Thu Jun 15 12:30:17 2017 +0200

----------------------------------------------------------------------
 .../strategy/CreateSshKeysThenCreateNodes.java  | 289 ++++++++++---------
 1 file changed, 154 insertions(+), 135 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds/blob/b77ea069/providers/packet/src/main/java/org/jclouds/packet/compute/strategy/CreateSshKeysThenCreateNodes.java
----------------------------------------------------------------------
diff --git a/providers/packet/src/main/java/org/jclouds/packet/compute/strategy/CreateSshKeysThenCreateNodes.java b/providers/packet/src/main/java/org/jclouds/packet/compute/strategy/CreateSshKeysThenCreateNodes.java
index 148079f..09f0e90 100644
--- a/providers/packet/src/main/java/org/jclouds/packet/compute/strategy/CreateSshKeysThenCreateNodes.java
+++ b/providers/packet/src/main/java/org/jclouds/packet/compute/strategy/CreateSshKeysThenCreateNodes.java
@@ -48,6 +48,7 @@ import org.jclouds.packet.domain.SshKey;
 import org.jclouds.ssh.SshKeyPairGenerator;
 import org.jclouds.ssh.SshKeys;
 
+import com.google.common.base.Predicate;
 import com.google.common.base.Splitter;
 import com.google.common.base.Strings;
 import com.google.common.collect.Multimap;
@@ -65,152 +66,170 @@ import static com.google.common.collect.Iterables.size;
 @Singleton
 public class CreateSshKeysThenCreateNodes extends CreateNodesWithGroupEncodedIntoNameThenAddToSet {
 
-    @Resource
-    @Named(ComputeServiceConstants.COMPUTE_LOGGER)
-    protected Logger logger = Logger.NULL;
-
-    private final PacketApi api;
-    private final SshKeyPairGenerator keyGenerator;
-
-    @Inject
-    protected CreateSshKeysThenCreateNodes(
-            CreateNodeWithGroupEncodedIntoName addNodeWithGroupStrategy,
-            ListNodesStrategy listNodesStrategy,
-            GroupNamingConvention.Factory namingConvention,
-            @Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService userExecutor,
-            CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap.Factory customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory,
-            PacketApi api, SshKeyPairGenerator keyGenerator) {
-        super(addNodeWithGroupStrategy, listNodesStrategy, namingConvention, userExecutor,
-                customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory);
-        this.api = api;
-        this.keyGenerator = keyGenerator;
-    }
-
-    @Override
-    public Map<?, ListenableFuture<Void>> execute(String group, int count, Template template,
-                                                  Set<NodeMetadata> goodNodes, Map<NodeMetadata, Exception> badNodes,
-                                                  Multimap<NodeMetadata, CustomizationResponse> customizationResponses) {
-
-        PacketTemplateOptions options = template.getOptions().as(PacketTemplateOptions.class);
-        Set<String> generatedSshKeyIds = Sets.newHashSet();
-
-        // If no key has been configured, generate a key pair
-        if (Strings.isNullOrEmpty(options.getPublicKey())) {
-            generateKeyPairAndAddKeyToSet(options, generatedSshKeyIds, group);
-        }
-
-        // If there is a script to run in the node, make sure a private key has
-        // been configured so jclouds will be able to access the node
-        if (options.getRunScript() != null && Strings.isNullOrEmpty(options.getLoginPrivateKey())) {
-            logger.warn(">> A runScript has been configured but no SSH key has been provided."
-                    + " Authentication will delegate to the ssh-agent");
-        }
-
-        // If there is a key configured, then make sure there is a key pair for it
-        if (!Strings.isNullOrEmpty(options.getPublicKey())) {
-            createKeyPairForPublicKeyInOptionsAndAddToSet(options, generatedSshKeyIds);
-        }
-
-        Map<?, ListenableFuture<Void>> responses = super.execute(group, count, template, goodNodes, badNodes,
-                customizationResponses);
-
-        // Key pairs in Packet are only required to create the devices. They aren't used anymore so it is better
-        // to delete the auto-generated key pairs at this point where we know exactly which ones have been
-        // auto-generated by jclouds.
-        registerAutoGeneratedKeyPairCleanupCallbacks(responses, generatedSshKeyIds);
-
-        return responses;
-    }
-
-    private void createKeyPairForPublicKeyInOptionsAndAddToSet(PacketTemplateOptions options,
-                                                               Set<String> generatedSshKeyIds) {
-        logger.debug(">> checking if the key pair already exists...");
-
-        PublicKey userKey;
-        Iterable<String> parts = Splitter.on(' ').split(options.getPublicKey());
-        checkArgument(size(parts) >= 2, "bad format, should be: ssh-rsa AAAAB3...");
-        String type = get(parts, 0);
-
-        try {
-            if ("ssh-rsa".equals(type)) {
-                RSAPublicKeySpec spec = SshKeys.publicKeySpecFromOpenSSH(options.getPublicKey());
-                userKey = KeyFactory.getInstance("RSA").generatePublic(spec);
-            } else {
-                throw new IllegalArgumentException("bad format, ssh-rsa is only supported");
-            }
-        } catch (InvalidKeySpecException ex) {
-            throw propagate(ex);
-        } catch (NoSuchAlgorithmException ex) {
-            throw propagate(ex);
-        }
-      String label = computeFingerprint(userKey);
-      SshKey key = api.sshKeyApi().get(label);
-
-      if (key == null) {
-         logger.debug(">> key pair not found. creating a new key pair %s ...", label);
-         SshKey newKey = api.sshKeyApi().create(label, options.getPublicKey());
-         logger.debug(">> key pair created! %s", newKey);
-         generatedSshKeyIds.add(newKey.id());
-      } else {
-         logger.debug(">> key pair found! %s", key);
-         generatedSshKeyIds.add(key.id());
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
+
+   private final PacketApi api;
+   private final SshKeyPairGenerator keyGenerator;
+
+   @Inject
+   protected CreateSshKeysThenCreateNodes(
+         CreateNodeWithGroupEncodedIntoName addNodeWithGroupStrategy,
+         ListNodesStrategy listNodesStrategy,
+         GroupNamingConvention.Factory namingConvention,
+         @Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService userExecutor,
+         CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap.Factory customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory,
+         PacketApi api, SshKeyPairGenerator keyGenerator) {
+      super(addNodeWithGroupStrategy, listNodesStrategy, namingConvention, userExecutor,
+            customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory);
+      this.api = api;
+      this.keyGenerator = keyGenerator;
+   }
+
+   @Override
+   public Map<?, ListenableFuture<Void>> execute(String group, int count, Template template,
+         Set<NodeMetadata> goodNodes, Map<NodeMetadata, Exception> badNodes,
+         Multimap<NodeMetadata, CustomizationResponse> customizationResponses) {
+
+      PacketTemplateOptions options = template.getOptions().as(PacketTemplateOptions.class);
+      Set<String> generatedSshKeyIds = Sets.newHashSet();
+
+      // If no key has been configured, generate a key pair
+      if (Strings.isNullOrEmpty(options.getPublicKey())) {
+         generateKeyPairAndAddKeyToSet(options, generatedSshKeyIds, group);
+      }
+
+      // If there is a script to run in the node, make sure a private key has
+      // been configured so jclouds will be able to access the node
+      if (options.getRunScript() != null && Strings.isNullOrEmpty(options.getLoginPrivateKey())) {
+         logger.warn(">> A runScript has been configured but no SSH key has been provided."
+               + " Authentication will delegate to the ssh-agent");
       }
-    }
 
-    private void generateKeyPairAndAddKeyToSet(PacketTemplateOptions options, Set<String> generatedSshKeyIds, String prefix) {
-        logger.debug(">> creating default keypair for node...");
+      // If there is a key configured, then make sure there is a key pair for it
+      if (!Strings.isNullOrEmpty(options.getPublicKey())) {
+         createKeyPairForPublicKeyInOptionsAndAddToSet(options, generatedSshKeyIds);
+      }
 
-        Map<String, String> defaultKeys = keyGenerator.get();
+      Map<?, ListenableFuture<Void>> responses = super.execute(group, count, template, goodNodes, badNodes,
+            customizationResponses);
 
-        SshKey sshKey = api.sshKeyApi().create(prefix + System.getProperty("user.name"), defaultKeys.get("public"));
-        generatedSshKeyIds.add(sshKey.id());
-        logger.debug(">> keypair created! %s", sshKey);
+      // Key pairs in Packet are only required to create the devices. They
+      // aren't used anymore so it is better
+      // to delete the auto-generated key pairs at this point where we know
+      // exactly which ones have been
+      // auto-generated by jclouds.
+      registerAutoGeneratedKeyPairCleanupCallbacks(responses, generatedSshKeyIds);
 
-        // If a private key has not been explicitly set, configure the generated one
-        if (Strings.isNullOrEmpty(options.getLoginPrivateKey())) {
-            options.overrideLoginPrivateKey(defaultKeys.get("private"));
-        }
-    }
+      return responses;
+   }
 
-    private void registerAutoGeneratedKeyPairCleanupCallbacks(Map<?, ListenableFuture<Void>> responses,
-                                                              final Set<String> generatedSshKeyIds) {
-        // The Futures.allAsList fails immediately if some of the futures fail. The Futures.successfulAsList, however,
-        // returns a list containing the results or 'null' for those futures that failed. We want to wait for all them
-        // (even if they fail), so better use the latter form.
-        ListenableFuture<List<Void>> aggregatedResponses = Futures.successfulAsList(responses.values());
+   private void createKeyPairForPublicKeyInOptionsAndAddToSet(PacketTemplateOptions options,
+         Set<String> generatedSshKeyIds) {
+      logger.debug(">> checking if the key pair already exists...");
 
-        // Key pairs must be cleaned up after all futures completed (even if some failed).
-        Futures.addCallback(aggregatedResponses, new FutureCallback<List<Void>>() {
-            @Override
-            public void onSuccess(List<Void> result) {
-                cleanupAutoGeneratedKeyPairs(generatedSshKeyIds);
-            }
+      PublicKey userKey = readPublicKey(options.getPublicKey());
+      final String fingerprint = computeFingerprint(userKey);
 
+      synchronized (CreateSshKeysThenCreateNodes.class) {
+         boolean keyExists = api.sshKeyApi().list().concat().anyMatch(new Predicate<SshKey>() {
             @Override
-            public void onFailure(Throwable t) {
-                cleanupAutoGeneratedKeyPairs(generatedSshKeyIds);
+            public boolean apply(SshKey input) {
+               return input.fingerprint().equals(fingerprint);
             }
+         });
+
+         if (!keyExists) {
+            logger.debug(">> key pair not found. creating a new key pair %s ...", fingerprint);
+            SshKey newKey = api.sshKeyApi().create(fingerprint, options.getPublicKey());
+            logger.debug(">> key pair created! %s", newKey);
+            generatedSshKeyIds.add(newKey.id());
+         } else {
+            logger.debug(">> key pair found for key %s", fingerprint);
+         }
+      }
+   }
+
+   private static PublicKey readPublicKey(String publicKey) {
+      Iterable<String> parts = Splitter.on(' ').split(publicKey);
+      checkArgument(size(parts) >= 2, "bad format, should be: ssh-rsa AAAAB3...");
+      String type = get(parts, 0);
+
+      try {
+         if ("ssh-rsa".equals(type)) {
+            RSAPublicKeySpec spec = SshKeys.publicKeySpecFromOpenSSH(publicKey);
+            return KeyFactory.getInstance("RSA").generatePublic(spec);
+         } else {
+            throw new IllegalArgumentException("bad format, ssh-rsa is only supported");
+         }
+      } catch (InvalidKeySpecException ex) {
+         throw propagate(ex);
+      } catch (NoSuchAlgorithmException ex) {
+         throw propagate(ex);
+      }
+   }
+
+   private void generateKeyPairAndAddKeyToSet(PacketTemplateOptions options, Set<String> generatedSshKeyIds,
+         String prefix) {
+      logger.debug(">> creating default keypair for node...");
+
+      Map<String, String> defaultKeys = keyGenerator.get();
+
+      SshKey sshKey = api.sshKeyApi().create(namingConvention.create().uniqueNameForGroup(prefix),
+            defaultKeys.get("public"));
+      generatedSshKeyIds.add(sshKey.id());
+      logger.debug(">> keypair created! %s", sshKey);
 
-            private void cleanupAutoGeneratedKeyPairs(Set<String> generatedSshKeyIds) {
-                logger.debug(">> cleaning up auto-generated key pairs...");
-                for (String sshKeyId : generatedSshKeyIds) {
-                    try {
-                        api.sshKeyApi().delete(sshKeyId);
-                    } catch (Exception ex) {
-                        logger.warn(">> could not delete key pair %s: %s", sshKeyId, ex.getMessage());
-                    }
-                }
+      // If a private key has not been explicitly set, configure the generated
+      // one
+      if (Strings.isNullOrEmpty(options.getLoginPrivateKey())) {
+         options.overrideLoginPrivateKey(defaultKeys.get("private"));
+      }
+   }
+
+   private void registerAutoGeneratedKeyPairCleanupCallbacks(Map<?, ListenableFuture<Void>> responses,
+         final Set<String> generatedSshKeyIds) {
+      // The Futures.allAsList fails immediately if some of the futures fail.
+      // The Futures.successfulAsList, however,
+      // returns a list containing the results or 'null' for those futures that
+      // failed. We want to wait for all them
+      // (even if they fail), so better use the latter form.
+      ListenableFuture<List<Void>> aggregatedResponses = Futures.successfulAsList(responses.values());
+
+      // Key pairs must be cleaned up after all futures completed (even if some
+      // failed).
+      Futures.addCallback(aggregatedResponses, new FutureCallback<List<Void>>() {
+         @Override
+         public void onSuccess(List<Void> result) {
+            cleanupAutoGeneratedKeyPairs(generatedSshKeyIds);
+         }
+
+         @Override
+         public void onFailure(Throwable t) {
+            cleanupAutoGeneratedKeyPairs(generatedSshKeyIds);
+         }
+
+         private void cleanupAutoGeneratedKeyPairs(Set<String> generatedSshKeyIds) {
+            logger.debug(">> cleaning up auto-generated key pairs...");
+            for (String sshKeyId : generatedSshKeyIds) {
+               try {
+                  api.sshKeyApi().delete(sshKeyId);
+               } catch (Exception ex) {
+                  logger.warn(">> could not delete key pair %s: %s", sshKeyId, ex.getMessage());
+               }
             }
-        }, userExecutor);
-    }
-
-    private static String computeFingerprint(PublicKey key) {
-        if (key instanceof RSAPublicKey) {
-            RSAPublicKey rsaKey = (RSAPublicKey) key;
-            return SshKeys.fingerprint(rsaKey.getPublicExponent(), rsaKey.getModulus());
-        } else {
-            throw new IllegalArgumentException("Only RSA keys are supported");
-        }
-    }
+         }
+      }, userExecutor);
+   }
+
+   private static String computeFingerprint(PublicKey key) {
+      if (key instanceof RSAPublicKey) {
+         RSAPublicKey rsaKey = (RSAPublicKey) key;
+         return SshKeys.fingerprint(rsaKey.getPublicExponent(), rsaKey.getModulus());
+      } else {
+         throw new IllegalArgumentException("Only RSA keys are supported");
+      }
+   }
 
 }