Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ahampton updates #16

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
*.dmg
*.gz
*.iso
*.jar
*.rar
*.tar
*.zip
Expand Down
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ A Selenium Grid plugin for integrating your project with AWS (Amazon Web Service
#### Details
New nodes will be started as needed depending on the browser requested:

* Chrome - New c3.large instances will be started capable of running 6 threads (tests) per instance. If your test run requests 30 threads, this will result in 5 nodes being started up, where as 31 are requested, this will result in 6 nodes started up. Note that these can also run firefox tests as well
* Firefox - New t2.micro instances will be started capable of running 1 thread (test) per instance. Selenium window focus issues were the reason behind running 1 thread per virtual machine. These instances can run chrome tests as well.
* Chrome - New c4.large instances will be started capable of running 5 threads (tests) per instance. If your test run requests 30 threads, this will result in 6 nodes being started up, where as 31 are requested, this will result in 7 nodes started up. Note that these can also run firefox tests as well
* Firefox - New t2.small instances will be started capable of running 1 thread (test) per instance. Selenium window focus issues were the reason behind running 1 thread per virtual machine. These instances can run chrome tests as well.
* Internet Explorer - IE is currently not supported. There are plans to add IE support in an upcoming release.

AWS bills by rounding up to the next hour, so if you leave a machine on for 5 minutes, you get billed for the full hour (60 minutes). Based on this logic, SeleniumGridScaler's automatic termination logic will terminate nodes at the end of the current billing cycle, taking into account current test load. So if there is enough test load, say 12 tests running and only 12 threads are online (2 chrome instances each capable of running 6 for a total of 12), all of the nodes will stay on. If the current time crosses into the next billing cycle (e.g. they're on for 1 hour and 5 minutes), SeleniumGridScaler will not attempt to terminate them until the end of that next billing cycle (will attempt to terminate at 1 hour and 55 minutes instead of prematurely terminating paid for resources).

## Requirements
A Java 7 or later runtime is required in order to run this application
A Java 8 or later runtime is required in order to run this application

## Installation/Startup
Download the automation-grid jar file. To start up the hub process, start the jar with the java jar command like so. Note, you must register the Scaler servlet (AutomationTestRunServlet) with GridLauncher.
Expand Down Expand Up @@ -58,6 +58,7 @@ In your test run, you must send a GET HTTP request to the servlet, with the requ
* browser - This is the browser you want to run in. Currently supported values, are chrome, firefox, and internetexplorer
* threadCount - Number of threads you want to run for your tests
* uuid - Unique test run identifier
* os - (Optional) This represents Selenium's Platform that you want to run in (can be Platform.ANY as well). Linux/Unix is currently the only supported platform. If you don't specify a platform, the logic will default to Platform.ANY and linux nodes will be started.

If you wanted to do a test run with 10 threads in chrome with a test run UUID of 'testRun1', the HTTP request would look something like this

Expand Down Expand Up @@ -137,6 +138,8 @@ Possible values to override/implement:
Please see AWS documentation for more information on how to use/setup these things
* tags (optional) - If you want to add any [tags](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Using_Tags.html) to your instances as they're started, prefix your comma separated key/value pairs with 'tag' (e.g. 'tagDepartment=Department,QA' would add a tag with a key of 'Department' and a value of 'QA')

## Current Issues
Currently Windows is not supported. There was an AMI I built out but forgot to make public before changing jobs. So, if you want to run in Windows, you'll basically need to mimic the startup/configuration logic done in ~/grid/grid_start_node.sh on the linux AMI included in aws.properties.default

## Contributing

Expand Down
Binary file added automation-grid.jar
Binary file not shown.
77 changes: 66 additions & 11 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<developer>
<name>Matthew Hardin</name>
<email>[email protected]</email>
<organization>RetailMeNot, inc</organization>
<organization>Netflix</organization>
<organizationUrl>https://github.com/mhardin</organizationUrl>
</developer>
</developers>
Expand All @@ -26,7 +26,25 @@
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-server</artifactId>
<version>2.42.2</version>
<version>2.52.0</version>
<exclusions>
<exclusion>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</exclusion>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
<exclusion>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-htmlunit-driver</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
Expand All @@ -43,12 +61,12 @@
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.1</version>
<version>3.4</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.4</version>
<version>1.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
Expand All @@ -60,6 +78,12 @@
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.0.9</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
Expand All @@ -77,6 +101,11 @@
<version>1.4.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
</dependencies>

<build>
Expand Down Expand Up @@ -124,12 +153,16 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12</version>
<configuration>
<forkMode>perthread</forkMode>
<threadCount>1</threadCount>
</configuration>
</plugin>
<!-- Cobertura is used for generating code coverage reports-->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>cobertura-maven-plugin</artifactId>
<version>2.6</version>
<version>2.7</version>
<configuration>
<instrumentation>
<excludes>
Expand Down Expand Up @@ -174,8 +207,8 @@
<version>2.4</version>
<inherited>true</inherited>
<configuration>
<target>1.7</target>
<source>1.7</source>
<target>1.8</target>
<source>1.8</source>
</configuration>
</plugin>
<!-- Make sure to include sources with our JAR that we're producing -->
Expand All @@ -192,6 +225,25 @@
</execution>
</executions>
</plugin>
<!-- Maven convergence plugin to ensure there are no dependency version conflicts in the project -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.4</version>
<executions>
<execution>
<id>enforce</id>
<configuration>
<rules>
<dependencyConvergence/>
</rules>
</configuration>
<goals>
<goal>enforce</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
Expand All @@ -202,6 +254,9 @@
<goals>
<goal>jar</goal>
</goals>
<configuration>
<additionalparam>-Xdoclint:none</additionalparam>
</configuration>
</execution>
</executions>
</plugin>
Expand All @@ -213,15 +268,15 @@
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>cobertura-maven-plugin</artifactId>
<version>2.5.1</version>
<version>2.7</version>
</plugin>
</plugins>
</reporting>

<scm>
<url>scm:git:[email protected]:RetailMeNot/SeleniumGridScaler.git</url>
<connection>scm:git:[email protected]:RetailMeNot/SeleniumGridScaler.git</connection>
<developerConnection>scm:git:[email protected]:RetailMeNot/SeleniumGridScaler.git</developerConnection>
<url>scm:git:[email protected]:mhardin/SeleniumGridScaler.git</url>
<connection>scm:git:[email protected]:mhardin/SeleniumGridScaler.git</connection>
<developerConnection>scm:git:[email protected]:mhardin/SeleniumGridScaler.git</developerConnection>
<tag>0.9</tag>
</scm>

Expand Down
11 changes: 6 additions & 5 deletions src/main/java/com/rmn/qa/AutomationCapabilityMatcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,16 @@
*/
package com.rmn.qa;

import com.google.common.annotations.VisibleForTesting;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.openqa.grid.internal.utils.DefaultCapabilityMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import com.google.common.annotations.VisibleForTesting;

/**
* Custom CapabilityMatcher which will not match a node that is marked as Expired/Terminated, which will happen
Expand Down Expand Up @@ -73,7 +74,7 @@ public boolean matches(Map<String, Object> nodeCapability,Map<String, Object> re
// If the run that spun up these hubs is still happening, just perform the default matching behavior
// as that run is the one that requested these nodes.
AutomationDynamicNode node = context.getNode(instanceId);
if(node != null && (node.getStatus() == AutomationDynamicNode.STATUS.EXPIRED || node.getStatus() == AutomationDynamicNode.STATUS.TERMINATED) ) {
if(node != null && (node.getStatus() != AutomationDynamicNode.STATUS.RUNNING) ) {
log.debug(String.format("Node [%s] will not be used to match a request as it is expired/terminated",instanceId));
// If the run that spun these hubs up is not in progress AND this node has been flagged to shutdown,
// do not match this node up to fulfill a test request
Expand Down
1 change: 1 addition & 0 deletions src/main/java/com/rmn/qa/AutomationConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public interface AutomationConstants {
String EXTRA_CAPABILITIES_PROPERTY_NAME="extraCapabilities";
String AWS_ACCESS_KEY="awsAccessKey";
String AWS_PRIVATE_KEY="awsSecretKey";
String AWS_TOKEN="awsToken";
String AWS_DEFAULT_RESOURCE_NAME= "aws.properties.default";
String REAPER_THREAD_CONFIG = "useReaperThread";
}
2 changes: 1 addition & 1 deletion src/main/java/com/rmn/qa/AutomationContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public static AutomationRunContext getContext() {
/**
* Clears out the previous context. Used for unit testing
*/
public static void refreshContext() {
public static synchronized void refreshContext() {
AutomationContext.context = new AutomationRunContext();
}

Expand Down
50 changes: 42 additions & 8 deletions src/main/java/com/rmn/qa/AutomationDynamicNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import java.util.Calendar;
import java.util.Date;

import org.openqa.selenium.Platform;

/**
* Represents a dynamically started node that is used to run tests
*/
Expand All @@ -33,7 +35,8 @@ public enum STATUS { RUNNING,EXPIRED,TERMINATED };
private static final int NODE_INTERVAL_LIFETIME = 55; // 55 minutes

// TODO: Refactor this to be AutomationRunRequest
private final String uuid,instanceId,browser,os;
private final String uuid, instanceId, browser, ipAddress, instanceType;
private final Platform platform;
private Date startDate,endDate;
private final int nodeCapacity;
private STATUS status;
Expand All @@ -43,18 +46,28 @@ public enum STATUS { RUNNING,EXPIRED,TERMINATED };
* @param uuid UUID of the test run that created this node
* @param instanceId Instance ID of the instance this node represents
* @param browser Requested browser of the test run that created this node
* @param os Requested OS of the test run that created this node
* @param platform Requested OS of the test run that created this node
* @param startDate Date that this node was started
* @param nodeCapacity Maximum test capacity that this node can run
*/
public AutomationDynamicNode(String uuid,String instanceId,String browser, String os,Date startDate,int nodeCapacity){
public AutomationDynamicNode(String uuid,String instanceId,String browser, Platform platform, Date startDate, int nodeCapacity){
this(uuid, instanceId, browser, platform, null, startDate, nodeCapacity);
}

public AutomationDynamicNode(String uuid,String instanceId,String browser, Platform platform, String ipAddress, Date startDate, int nodeCapacity){
this(uuid, instanceId, browser, platform, ipAddress, startDate, nodeCapacity, null);
}

public AutomationDynamicNode(String uuid,String instanceId,String browser, Platform platform, String ipAddress, Date startDate, int nodeCapacity, String instanceType){
this.uuid = uuid;
this.instanceId = instanceId;
this.browser = browser;
this.os = os;
this.platform = platform;
this.ipAddress = ipAddress;
this.startDate = startDate;
this.endDate = computeEndDate(startDate);
this.nodeCapacity = nodeCapacity;
this.instanceType = instanceType;
this.status = STATUS.RUNNING;
}

Expand Down Expand Up @@ -106,11 +119,19 @@ public String getBrowser() {
}

/**
* Returns the OS of this node
* Returns the Platform of this node
* @return
*/
public String getOs() {
return os;
public Platform getPlatform() {
return platform;
}

/**
* Returns the private IP address of this node
* @return
*/
public String getIpAddress() {
return ipAddress;
}

/**
Expand Down Expand Up @@ -156,6 +177,14 @@ public int getNodeCapacity() {
return nodeCapacity;
}

/**
* Returns the instance type of this node
* @return
*/
public String getInstanceType() {
return instanceType;
}

/**
* Returns the current status of this node.
* @return
Expand Down Expand Up @@ -199,6 +228,11 @@ public int hashCode() {

@Override
public String toString() {
return String.format("Node - UUID: %s Instance ID: %s Created Date: %s",uuid,instanceId, startDate);
return "AutomationDynamicNode{" +
"uuid='" + uuid + '\'' +
", instanceId='" + instanceId + '\'' +
", startDate=" + startDate +
", ipAddress='" + ipAddress + '\'' +
'}';
}
}
Loading