HIVE-14849: Support google-compute-engine provider on Hive ptest framework 
(Sergio Pena, reviewed by Prasanth Jayachandran)


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

Branch: refs/heads/repl2
Commit: 291f3d503d5a8627f86ef5f7fdd7880d8da4760c
Parents: cf72a73
Author: Sergio Pena <sergio.p...@cloudera.com>
Authored: Wed Sep 28 21:33:00 2016 -0500
Committer: Sergio Pena <sergio.p...@cloudera.com>
Committed: Wed Sep 28 21:33:00 2016 -0500

----------------------------------------------------------------------
 .../ptest2/conf/cloudhost.properties.example    |  37 +++
 testutils/ptest2/pom.xml                        |   5 +
 .../execution/context/CloudComputeService.java  | 224 +++++++++++++++----
 .../context/CloudExecutionContextProvider.java  | 105 +++++++--
 4 files changed, 311 insertions(+), 60 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/hive/blob/291f3d50/testutils/ptest2/conf/cloudhost.properties.example
----------------------------------------------------------------------
diff --git a/testutils/ptest2/conf/cloudhost.properties.example 
b/testutils/ptest2/conf/cloudhost.properties.example
new file mode 100644
index 0000000..c336052
--- /dev/null
+++ b/testutils/ptest2/conf/cloudhost.properties.example
@@ -0,0 +1,37 @@
+#
+# This is just an example of different cloudhost providers
+#
+
+# This context provides configurations for AWS EC2 and GCE (google compute 
engine)
+executionContextProvider = 
org.apache.hive.ptest.execution.context.CloudExecutionContextProvider$Builder
+
+# Option: GCE
+cloudProvider = google-compute-engine
+gceJsonFile = # GCE JSON KEY FILE
+instanceType = 
https://www.googleapis.com/compute/v1/projects/<PROJECT_ID>/zones/us-central1-a/machineTypes/n1-standard-8
+imageId = 
https://www.googleapis.com/compute/v1/projects/<PROJECT_ID>/global/images/hive-ptest-debian-8-20160927
+# keyPair = # UNUSED
+securityGroup = hive-ptest
+
+# Option: AWS
+cloudProvider = aws-ec2
+apiKey =    # AWS ACCESS KEY
+accessKey = # AWS SECRET ACCESS KEY
+instanceType = c3.2xlarge
+imageId = us-west-1/ami-1fa1445b
+keyPair = hive-ptest
+securityGroup = hive-ptest
+
+# Generic options
+workingDirectory = /data/hive-ptest
+profileDirectory = /usr/local/hiveptest/etc/public/
+privateKey = /home/hiveptest/.ssh/hive-ptest-user-key
+dataDir = /data/hive-ptest/data/
+numHosts = 12
+groupName = hive-ptest-slaves
+localDirs = /home/hiveptest/
+user = hiveptest
+numThreads = 2
+maxLogDirectoriesPerProfile = 30
+userMetadata.owner = # USER
+maxHostsPerCreateRequest = 12
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/hive/blob/291f3d50/testutils/ptest2/pom.xml
----------------------------------------------------------------------
diff --git a/testutils/ptest2/pom.xml b/testutils/ptest2/pom.xml
index cea29b6..97981fb 100644
--- a/testutils/ptest2/pom.xml
+++ b/testutils/ptest2/pom.xml
@@ -107,6 +107,11 @@ limitations under the License.
       <version>${jclouds.version}</version>
     </dependency>
     <dependency>
+      <groupId>org.apache.jclouds.labs</groupId>
+      <artifactId>google-compute-engine</artifactId>
+      <version>${jclouds.version}</version>
+    </dependency>
+    <dependency>
       <groupId>org.apache.jclouds.driver</groupId>
       <artifactId>jclouds-sshj</artifactId>
       <version>${jclouds.version}</version>

http://git-wip-us.apache.org/repos/asf/hive/blob/291f3d50/testutils/ptest2/src/main/java/org/apache/hive/ptest/execution/context/CloudComputeService.java
----------------------------------------------------------------------
diff --git 
a/testutils/ptest2/src/main/java/org/apache/hive/ptest/execution/context/CloudComputeService.java
 
b/testutils/ptest2/src/main/java/org/apache/hive/ptest/execution/context/CloudComputeService.java
index 64ee68e..e26c5ca 100644
--- 
a/testutils/ptest2/src/main/java/org/apache/hive/ptest/execution/context/CloudComputeService.java
+++ 
b/testutils/ptest2/src/main/java/org/apache/hive/ptest/execution/context/CloudComputeService.java
@@ -18,11 +18,13 @@
  */
 package org.apache.hive.ptest.execution.context;
 
-import java.util.Collections;
-import java.util.Properties;
-import java.util.Map;
-import java.util.Set;
+import java.io.File;
+import java.io.IOException;
+import java.util.*;
 
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.io.Files;
 import org.jclouds.Constants;
 import org.jclouds.ContextBuilder;
 import org.jclouds.aws.ec2.compute.AWSEC2TemplateOptions;
@@ -34,7 +36,12 @@ import org.jclouds.compute.domain.ComputeMetadata;
 import org.jclouds.compute.domain.NodeMetadata;
 import org.jclouds.compute.domain.NodeMetadata.Status;
 import org.jclouds.compute.domain.Template;
+import org.jclouds.compute.options.TemplateOptions;
+import org.jclouds.domain.Credentials;
+import org.jclouds.googlecloud.GoogleCredentialsFromJson;
+import 
org.jclouds.googlecomputeengine.compute.options.GoogleComputeEngineTemplateOptions;
 import org.jclouds.logging.log4j.config.Log4JLoggingModule;
+import org.jclouds.sshj.config.SshjSshClientModule;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -43,55 +50,76 @@ import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
 
+import static com.google.common.base.Charsets.UTF_8;
+
 public class CloudComputeService {
   private static final Logger LOG = LoggerFactory
       .getLogger(CloudComputeService.class);
+  private final CloudComputeConfig mConfig;
   private final ComputeServiceContext mComputeServiceContext;
   private final ComputeService mComputeService;
-  private final String mInstanceType;
-  private final String mGroupName;
+  private final Template mTemplate;
   private final String mGroupTag;
-  private final String mImageId;
-  private final String mkeyPair;
-  private final String mSecurityGroup;
-  private final Map<String, String> mUserMetadata;
-  /**
-   * JClouds requests on-demand instances when null
-   */
-  private final Float mMaxBid;
-  public CloudComputeService(String apiKey, String accessKey, String 
instanceType, String groupName,
-      String imageId, String keyPair, String securityGroup, Float maxBid, 
Map<String,String> userMetadata) {
-    mInstanceType = instanceType;
-    mGroupName = groupName;
-    mImageId = imageId;
-    mkeyPair = keyPair;
-    mSecurityGroup = securityGroup;
-    mMaxBid = maxBid;
-    mGroupTag = "group=" + mGroupName;
-    mUserMetadata = userMetadata;
+
+  public CloudComputeService(final CloudComputeConfig config) {
+    mConfig = config;
+
+    mComputeServiceContext = initComputeServiceContext(config.getmProvider(), 
config.getIdentity(), config.getCredential());
+    mComputeService = mComputeServiceContext.getComputeService();
+    mTemplate = 
mComputeService.templateBuilder().hardwareId(config.getInstanceType()).imageId(config.getImageId()).build();
+
+    TemplateOptions options = mTemplate.getOptions();
+
+    // Set generic options
+    options.blockOnPort(22, 60);
+    options.userMetadata(config.getUserMetaData());
+
+    // Set provider options
+    switch (config.getmProvider()) {
+      case AWS:
+        mGroupTag = String.format("group=%s", config.getGroupName());
+
+        options.as(AWSEC2TemplateOptions.class)
+                .keyPair(config.getKeyPairName())
+                .securityGroupIds(config.getSecurityGroup())
+                .spotPrice(config.getMaxBid())
+                .tags(Collections.singletonList(mGroupTag));
+        break;
+      case GCE:
+        mGroupTag = config.getGroupName();
+
+        options.as(GoogleComputeEngineTemplateOptions.class)
+                .tags(Arrays.asList(config.getGroupName(), 
config.getSecurityGroup())); // GCE firewall is set through instance tags
+        break;
+      default:
+        mGroupTag = "";
+    }
+  }
+
+  private ComputeServiceContext 
initComputeServiceContext(CloudComputeConfig.CloudComputeProvider provider, 
String identity, String credential) {
     Properties overrides = new Properties();
+
     overrides.put(ComputeServiceProperties.POLL_INITIAL_PERIOD, 
String.valueOf(60L * 1000L));
     overrides.put(ComputeServiceProperties.POLL_MAX_PERIOD, 
String.valueOf(600L * 1000L));
     overrides.put(Constants.PROPERTY_MAX_RETRIES, String.valueOf(60));
-    mComputeServiceContext = ContextBuilder.newBuilder("aws-ec2")
-        .credentials(apiKey, accessKey)
-        .modules(ImmutableSet.of(new Log4JLoggingModule()))
-        .overrides(overrides)
-        .buildView(ComputeServiceContext.class);
-    mComputeService = mComputeServiceContext.getComputeService();
+
+    return ContextBuilder.newBuilder(provider.getmJcloudsId())
+            .credentials(identity, credential)
+            .modules(ImmutableSet.of(
+                    new SshjSshClientModule(),
+                    new Log4JLoggingModule()
+            ))
+            .overrides(overrides)
+            .buildView(ComputeServiceContext.class);
   }
+
   public Set<NodeMetadata> createNodes(int count)
       throws RunNodesException {
     Set<NodeMetadata> result = Sets.newHashSet();
-    Template template = mComputeService.templateBuilder()
-        .hardwareId(mInstanceType).imageId(mImageId).build();
-    template.getOptions().as(AWSEC2TemplateOptions.class).keyPair(mkeyPair)
-    .securityGroupIds(mSecurityGroup).blockOnPort(22, 60)
-    .spotPrice(mMaxBid).tags(Collections.singletonList(mGroupTag))
-    .userMetadata(mUserMetadata);
-    result.addAll(mComputeService.createNodesInGroup(mGroupName, count, 
template));
+    result.addAll(mComputeService.createNodesInGroup(mConfig.getGroupName(), 
count, mTemplate));
     return result;
   }
+
   static Predicate<ComputeMetadata> createFilterPTestPredicate(final String 
groupName,
       final String groupTag) {
     return new Predicate<ComputeMetadata>() {
@@ -122,7 +150,7 @@ public class CloudComputeService {
   public Set<NodeMetadata> listRunningNodes(){
     Set<NodeMetadata> result = Sets.newHashSet();
     result.addAll(mComputeService.listNodesDetailsMatching(
-        createFilterPTestPredicate(mGroupName, mGroupTag)));
+        createFilterPTestPredicate(mConfig.getGroupName(), mGroupTag)));
     return result;
   }
   public void destroyNode(String nodeId) {
@@ -131,4 +159,124 @@ public class CloudComputeService {
   public void close() {
     mComputeServiceContext.close();
   }
+
+  public static class CloudComputeConfig {
+    public enum CloudComputeProvider {
+      AWS("aws-ec2"),
+      GCE("google-compute-engine");
+
+      private final String mJcloudsId;
+
+      CloudComputeProvider(String jcloudsId) {
+        mJcloudsId = jcloudsId;
+      }
+
+      public String getmJcloudsId() {
+        return mJcloudsId;
+      }
+    };
+
+    private final CloudComputeProvider mProvider;
+
+    private String mIdentity;
+    private String mCredential;
+    private String mInstanceType;
+    private String mImageId;
+    private String mGroupName;
+    private String mSecurityGroup;
+    private String mKeyPairName;
+    private Map<String, String> mUserMetaData;
+
+    /**
+     * JClouds requests on-demand instances when null
+     */
+    private Float mMaxBid;
+
+    public CloudComputeConfig(CloudComputeProvider provider) {
+      mProvider = provider;
+    }
+
+    public void setCredentials(String identity, String credential) {
+      mIdentity = identity;
+      mCredential = credential;
+    }
+
+    public void setInstanceType(String instanceType) {
+      mInstanceType = instanceType;
+    }
+
+    public void setImageId(String imageId) {
+      mImageId = imageId;
+    }
+
+    public void setGroupName(String groupName) {
+      mGroupName = groupName;
+    }
+
+    public void setSecurityGroup(String securityGroup) {
+      mSecurityGroup = securityGroup;
+    }
+
+    public void setMaxBid(Float maxBid) {
+      mMaxBid = maxBid;
+    }
+
+    public void setmKeyPairName(String keyPairName) {
+      mKeyPairName = keyPairName;
+    }
+
+    public void setUserMetaData(Map<String, String> userMetaData) {
+      mUserMetaData = userMetaData;
+    }
+
+    public CloudComputeProvider getmProvider() {
+      return mProvider;
+    }
+
+    public String getIdentity() {
+      return mIdentity;
+    }
+
+    public String getCredential() {
+      return mCredential;
+    }
+
+    public String getInstanceType() {
+      return mInstanceType;
+    }
+
+    public String getImageId() {
+      return mImageId;
+    }
+
+    public String getGroupName() {
+      return mGroupName;
+    }
+
+    public String getSecurityGroup() {
+      return mSecurityGroup;
+    }
+
+    public Float getMaxBid() {
+      return mMaxBid;
+    }
+
+    public String getKeyPairName() {
+      return mKeyPairName;
+    }
+
+    public Map<String, String> getUserMetaData() {
+      if (mUserMetaData == null) {
+        return ImmutableMap.of();
+      }
+
+      return mUserMetaData;
+    }
+  }
+
+  public static Credentials getCredentialsFromJsonKeyFile(String filename) 
throws IOException {
+    String fileContents = Files.toString(new File(filename), UTF_8);
+    Supplier<Credentials> credentialSupplier = new 
GoogleCredentialsFromJson(fileContents);
+    return credentialSupplier.get();
+  }
 }

http://git-wip-us.apache.org/repos/asf/hive/blob/291f3d50/testutils/ptest2/src/main/java/org/apache/hive/ptest/execution/context/CloudExecutionContextProvider.java
----------------------------------------------------------------------
diff --git 
a/testutils/ptest2/src/main/java/org/apache/hive/ptest/execution/context/CloudExecutionContextProvider.java
 
b/testutils/ptest2/src/main/java/org/apache/hive/ptest/execution/context/CloudExecutionContextProvider.java
index 343efde..8b82497 100644
--- 
a/testutils/ptest2/src/main/java/org/apache/hive/ptest/execution/context/CloudExecutionContextProvider.java
+++ 
b/testutils/ptest2/src/main/java/org/apache/hive/ptest/execution/context/CloudExecutionContextProvider.java
@@ -34,12 +34,14 @@ import java.util.concurrent.TimeUnit;
 
 import org.apache.hive.ptest.execution.Constants;
 import org.apache.hive.ptest.execution.Dirs;
+import org.apache.hive.ptest.execution.LocalCommandFactory;
 import org.apache.hive.ptest.execution.conf.Context;
 import org.apache.hive.ptest.execution.conf.Host;
 import org.apache.hive.ptest.execution.ssh.SSHCommand;
 import org.apache.hive.ptest.execution.ssh.SSHCommandExecutor;
 import org.jclouds.compute.RunNodesException;
 import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.domain.Credentials;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -55,16 +57,24 @@ import com.google.common.collect.Sets;
 public class CloudExecutionContextProvider implements ExecutionContextProvider 
{
   private static final Logger LOG = LoggerFactory
       .getLogger(CloudExecutionContextProvider.class);
-  public static final String DATA_DIR = "dataDir";
+  public static final String CLOUD_PROVIDER = "cloudProvider";
+
+  // GCE settings
+  public static final String GCE_JSON_CREDS_FILE = "gceJsonFile";
+  
+  // AWS settings
   public static final String API_KEY = "apiKey";
   public static final String ACCESS_KEY = "accessKey";
+  public static final String KEY_PAIR = "keyPair";
+  public static final String MAX_BID = "maxBid";
+  
+  // Generic settings
+  public static final String DATA_DIR = "dataDir";
   public static final String NUM_HOSTS = "numHosts";
   public static final String MAX_HOSTS_PER_CREATE_REQUEST = 
"maxHostsPerCreateRequest";
   public static final String GROUP_NAME = "groupName";
   public static final String IMAGE_ID = "imageId";
-  public static final String KEY_PAIR = "keyPair";
   public static final String SECURITY_GROUP = "securityGroup";
-  public static final String MAX_BID = "maxBid";
   public static final String SLAVE_LOCAL_DIRECTORIES = "localDirs";
   public static final String USERNAME = "user";
   public static final String INSTANCE_TYPE = "instanceType";
@@ -304,6 +314,7 @@ public class CloudExecutionContextProvider implements 
ExecutionContextProvider {
           LOG.error("Verify command still executing on a host after 10 
minutes");
         }
       } catch (InterruptedException e) {
+        terminateInternal(result);
         throw new CreateHostsFailedException("Interrupted while trying to 
create hosts", e);
       } finally {
         if(!executorService.isShutdown()) {
@@ -418,29 +429,79 @@ public class CloudExecutionContextProvider implements 
ExecutionContextProvider {
       return create(context, workingDirectory);
     }
   }
+  
+  private static CloudComputeService createAwsService(final Context context) {
+    String apiKey = Preconditions.checkNotNull(context.getString(API_KEY), 
API_KEY + " is required");
+    String accessKey = 
Preconditions.checkNotNull(context.getString(ACCESS_KEY), ACCESS_KEY + " is 
required");
+    String imageId = Preconditions.checkNotNull(context.getString(IMAGE_ID), 
IMAGE_ID + " is required");
+    String keyPair = Preconditions.checkNotNull(context.getString(KEY_PAIR), 
KEY_PAIR + " is required");
+    String securityGroup = 
Preconditions.checkNotNull(context.getString(SECURITY_GROUP), SECURITY_GROUP + 
" is required");
+
+    Float maxBid = context.getFloat(MAX_BID);
+    Preconditions.checkArgument(maxBid == null || maxBid > 0, MAX_BID + " must 
be null or greater than zero");
+
+    String instanceType = context.getString(INSTANCE_TYPE, "c1.xlarge");
+    String groupName = context.getString(GROUP_NAME, "hive-ptest-slaves");
+
+    CloudComputeService.CloudComputeConfig config =
+            new 
CloudComputeService.CloudComputeConfig(CloudComputeService.CloudComputeConfig.CloudComputeProvider.AWS);
+
+    config.setCredentials(apiKey, accessKey);
+    config.setInstanceType(instanceType);
+    config.setGroupName(groupName);
+    config.setImageId(imageId);
+    config.setmKeyPairName(keyPair);
+    config.setSecurityGroup(securityGroup);
+    config.setMaxBid(maxBid);
+    config.setUserMetaData(context.getSubProperties(USER_METADATA + "."));
+
+    return new CloudComputeService(config);
+  }
+  
+  private static CloudComputeService createGceService(final Context context) 
throws IOException {
+    String gceJsonFile = 
Preconditions.checkNotNull(context.getString(GCE_JSON_CREDS_FILE), 
GCE_JSON_CREDS_FILE + " is required");
+    String imageId = Preconditions.checkNotNull(context.getString(IMAGE_ID), 
IMAGE_ID + " is required");
+    String securityGroup = 
Preconditions.checkNotNull(context.getString(SECURITY_GROUP), SECURITY_GROUP + 
" is required");
+    String instanceType = 
Preconditions.checkNotNull(context.getString(INSTANCE_TYPE, ""), INSTANCE_TYPE 
+ " is required");
+
+    String groupName = context.getString(GROUP_NAME, "hive-ptest-slaves");
+
+    CloudComputeService.CloudComputeConfig config =
+            new 
CloudComputeService.CloudComputeConfig(CloudComputeService.CloudComputeConfig.CloudComputeProvider.GCE);
+
+    Credentials creds = 
+            CloudComputeService.getCredentialsFromJsonKeyFile(gceJsonFile);
+
+    config.setCredentials(creds.identity, creds.credential);
+    config.setInstanceType(instanceType);
+    config.setGroupName(groupName);
+    config.setImageId(imageId);
+    config.setSecurityGroup(securityGroup);
+    config.setUserMetaData(context.getSubProperties(USER_METADATA + "."));
+
+    return new CloudComputeService(config);
+  }
+
+  private static CloudComputeService createService(final Context context) 
throws IOException {
+    String cloudProvider = context.getString(CLOUD_PROVIDER, "aws-ec2");
+    
+    if (cloudProvider.equalsIgnoreCase("aws-ec2")) {
+      return createAwsService(context);
+    } else if (cloudProvider.equalsIgnoreCase("google-compute-engine")) {
+      return createGceService(context);
+    } else {
+      throw new IllegalArgumentException("Unknown cloud provider name: " + 
cloudProvider);
+    }
+  }
 
   private static CloudExecutionContextProvider create(Context context,
       String workingDirectory) throws IOException {
     String dataDir = Preconditions.checkNotNull(context.getString(DATA_DIR),
         DATA_DIR + " is required");
-    String apiKey = Preconditions.checkNotNull(context.getString(API_KEY),
-        API_KEY + " is required");
-    String accessKey = Preconditions.checkNotNull(
-        context.getString(ACCESS_KEY), ACCESS_KEY + " is required");
     int maxHostsPerCreateRequest = 
context.getInteger(MAX_HOSTS_PER_CREATE_REQUEST, 2);
     Integer numHosts = context.getInteger(NUM_HOSTS, 8);
     Preconditions.checkArgument(numHosts > 0, NUM_HOSTS
         + " must be greater than zero");
-    String groupName = context.getString(GROUP_NAME, "hive-ptest-slaves");
-    String imageId = Preconditions.checkNotNull(context.getString(IMAGE_ID),
-        IMAGE_ID + " is required");
-    String keyPair = Preconditions.checkNotNull(context.getString(KEY_PAIR),
-        KEY_PAIR + " is required");
-    String securityGroup = Preconditions.checkNotNull(
-        context.getString(SECURITY_GROUP), SECURITY_GROUP + " is required");
-    Float maxBid = context.getFloat(MAX_BID);
-    Preconditions.checkArgument(maxBid == null || maxBid > 0, MAX_BID
-        + " must be null or greater than zero");
     String privateKey = Preconditions.checkNotNull(
         context.getString(PRIVATE_KEY), PRIVATE_KEY + " is required");
     String user = context.getString(USERNAME, "hiveptest");
@@ -448,12 +509,12 @@ public class CloudExecutionContextProvider implements 
ExecutionContextProvider {
         .split(context.getString(SLAVE_LOCAL_DIRECTORIES, "/home/hiveptest/")),
         String.class);
     Integer numThreads = context.getInteger(NUM_THREADS, 3);
-    String instanceType = context.getString(INSTANCE_TYPE, "c1.xlarge");
-    CloudComputeService cloudComputeService = new CloudComputeService(apiKey, 
accessKey,
-        instanceType, groupName, imageId, keyPair, securityGroup, maxBid, 
context.getSubProperties(USER_METADATA + "."));
+    
+    CloudComputeService cloudComputeService = createService(context);
     CloudExecutionContextProvider service = new CloudExecutionContextProvider(
-        dataDir, numHosts, cloudComputeService, new SSHCommandExecutor(LOG), 
workingDirectory,
-        privateKey, user, localDirs, numThreads, 60, maxHostsPerCreateRequest);
+        dataDir, numHosts, cloudComputeService,
+        new SSHCommandExecutor(LOG, new LocalCommandFactory(LOG), "-o 
UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"),
+        workingDirectory, privateKey, user, localDirs, numThreads, 60, 
maxHostsPerCreateRequest);
     return service;
   }
 }

Reply via email to