You've come here to learn about Selenium Grid! This is what our Smoca jobs remotely execute on.
To see our grid, visit:
http://grid.us-west2.justin.tv/grid/console

# About Selenium Grid

Selenium Grid allows us to
* Scale across several machines (parallelization)
* Bring up different environments and browsers

A Hub instance of Selenium Grid will talk to Nodes on different boxes. Hub distributes the tests across the nodes, and communicates between them.

## What does its infrastructure look like?

Our grid is brought to you by AWS EC2, Docker, and Jenkins.

It runs in AWS EC2 instances within Docker Containers. Jenkins still triggers the job, which points to the established Grid.

One docker image for each browser. This makes things reproducable, and we have the ability to quickly spin up and down hubs and nodes.

The docker images for the Hub and Browser Nodes (Firefox & Chrome) are provided through [SeleniumHQ/docker-selenium](https://github.com/SeleniumHQ/docker-selenium).

**Diagram Images**
* [General Diagram Overview of Jenkins/Hub/Node](https://s3.amazonaws.com/f.cl.ly/items/1c2k0V2O3D3R2D3U3T1A/overview.png)
* [Diagram Overview of the EC2 Instance/Docker/Node](https://s3.amazonaws.com/f.cl.ly/items/3U3v0M3Y473p102G1y2B/ec2_instance_picture.png)

### Browser Support
Currently, we only support Chrome and Firefox on Linux. This is because of the currently available Docker images.

It is possible to spin up the nodes with a Selenium Node jar file.

# EC2 Instance

As explained above, Grid runs within AWS on EC2 Instances.

The current EC2 sizing (highly subject to change)
* Hub: c3.large
* Node: c3.xlarge

## SSH
[Connecting to Nodes](connecting_to_nodes.md)

## Managing Instances

Terraform is used to manage our EC2 instances and Route53 DNS.You can find the QA/Selenium_Hub folder in the [release/terraform repository](https://git-aws.internal.justin.tv/release/terraform).

# Docker

### Standard Docker Commands

| Command Description        |    Command                      |
| -------------------------- | ------------------------------- |
| Starting containers        | `docker start <container_name>` |
| Listing active containers  | `docker ps`                     |
| Listing all containers     | `docker ps -a`                  |
| Removing containers        | `docker rm <container_id>`      |
| Getting Logs               | `docker logs <container_name>`  |
| Stopping All Containers    | `docker stop $(docker ps -a -q)`|
| Deleting All Containers    | `docker rm $(docker ps -a -q)`  |
| Deleting All Images        | `docker rmi $(docker images -q)`|


# Configuring The Machines

While currently being experimented with, we suggest each EC2 instance run a max of one browser docker container.

## Starting The Hub
Run `docker start hub` on the hub machine.

The hub will be available at http://grid.us-west2.justin.tv/grid/console, or in the format of http://PRIVATE_IP:80/grid/console

#### Manual Configuration
For manual configuration, or if the docker container 'hub' does not exist:
`docker run -d --name hub -p 80:4444 -e "GRID_TIMEOUT=240000" -e "GRID_BROWSER_TIMEOUT=290000" selenium/hub`
Note: We are pointing to port 80 so that it is picked up as a standard web service, and supplying a port won't be required on the URL.

To pick out the variables we are using in the run:

|     Variable         |     Description      |
|----------------------|--------------------- |
| GRID_TIMEOUT         | The timeout in seconds before the hub automatically releases a node that hasn't received any requests for more than the specified number of seconds. After this time, the node will be released for another test in the queue. |
| GRID_BROWSER_TIMEOUT | The maximum time a node is willing to hang inside the browser |

The browserTimeout should be higher than the socket lock timeout, grid timeout, and webdriver timeout

## Starting The Nodes

### Via Puppet

Puppet is configured to automatically launch a browser node docker container when the instance comes online. This will automatically link to the hub.

If it fails to do so, you can trigger puppet to launch the containers by ssh'ing into the box's private IP and running:
`sudo puppet agent --test`

By appending `--test`, this tells Puppet to only run once (as opposed to running every 30 minutes).

If the docker container still fails to come online, you can launch it manually.

### Manually

As these nodes are on a different machine than the hub, we need to tell the node where the hub exists.

|     Variable           |                Description                            |
| ---------------------- |-------------------------------------------------------|
| REMOTE_HOST            | The node's internal IP address, starting with http:// |
| HUB_PORT_4444_TCP_ADDR | The hub's internal IP address                         |
| HUB_PORT_4444_TCP_PORT | The hub's port                                        |

The full command to start a node:

```
docker run -d --name DOCKER_NAME_YOUR_CHOICE -p 5555:5555 \
-e REMOTE_HOST=http://NODE_MACHINE_INTERNAL_IP:5555 \
-e HUB_PORT_4444_TCP_ADDR=HUB_INTERNAL_PUBLIC_IP \
-e HUB_PORT_4444_TCP_PORT=80 \
DOCKER_IMAGE:TAG
```

To obtain the internal IP address, AWS provides a URL you can curl for the address.

`curl -s http://169.254.169.254/latest/meta-data/local-ipv4`

Documentation can be found on the [AWS EC2 Instance IP Addressing documentation](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-instance-addressing.html).

An example on starting a Firefox node:
```
docker run -d --name firefox -p 5555:5555 \
-e REMOTE_HOST=http://$(echo `curl -s http://169.254.169.254/latest/meta-data/local-ipv4`):5555 \
-e HUB_PORT_4444_TCP_ADDR=grid.us-west2.justin.tv \
-e HUB_PORT_4444_TCP_PORT=80 \
selenium/node-firefox:2.53.0
```

You could also use the direct private IP: `-e HUB_PORT_4444_TCP_ADDR=835.99.182.8930`

Note: If a container was launched via Puppet, you may find that stopping the running container will just restart it. You can disable this via:
`sudo systemctl disable docker-grid-hub.service && sudo systemctl stop docker-grid-hub.service`

If that doesn't work, ensure the name is right, by searching for docker in `sudo systemctl list-unit-files | grep enabled`

## Port Configuration / Having Two Containers On One Machine

If you have two docker containers running on one instance, you'll need to assign each node a different port.

For example, the first can have port 5555, and the second can have port 5556. However, you'll want to keep the second 5555 port the same.

A command example:
`-p 5556:5555`

An example on starting a second Firefox node:
```
docker run -d --name firefox2 -p 5556:5555 \
-e REMOTE_HOST=http://$(echo `curl -s http://169.254.169.254/latest/meta-data/local-ipv4`):5555 \
-e HUB_PORT_4444_TCP_ADDR=grid.us-west2.justin.tv \
-e HUB_PORT_4444_TCP_PORT=80 \
selenium/node-firefox:2.53.0
```


## VNC Viewer

See [Connecting to Nodes](connecting_to_nodes.md#vnc-into-linux-grid-node).

## Selenium Logs

You can grab the node and hub logs. This may help in debugging things like timeouts.

**sudo may be needed for Docker commands**

1. ssh into the necessary boxes. `ssh dylan@private_ip_of_box`
2. Find the name of the running container. `docker ps`
3. Grab the logs: `docker logs <container_name>`

# Capybara: Pointing to Grid
Capybara has the ability to point Selenium to a remote browser.
Having the following in your spec_helper.rb will point to Grid instead of a local browser.

```
###########################
## Standard configuration #
###########################

RSpec.configure do |config|
  Capybara.run_server = false
  Capybara.default_driver = :selenium
  Capybara.javascript_driver = :selenium
  Capybara.server_port = 9887

########################
## Grid Configuration ##
########################

caps = Selenium::WebDriver::Remote::Capabilities.firefox # Set Firefox Browser
# caps = Selenium::WebDriver::Remote::Capabilities.chrome # Set Chrome
caps.platform = :LINUX # Set OS
# Register Driver To The Grid
  Capybara.register_driver :selenium do |app|
    Capybara::Selenium::Driver.new(app,
      :browser => :remote,
      :url => 'http://PRIVATE_IP:80/wd/hub',
      :desired_capabilities => caps)
    end
  end
```

Local and want to parallelize your tests across grid? Use our parallel_rspec gem to call something like:
`parallel_rspec -n 4 spec`

Grid will handle which node to put it on. In a local context, each process you spin up will simply point to Hub to do the heavy lifting.


In a non local sense, the Jenkins job will have all of this already configured, and parallelization is handled through our Rake file

# Helpful Resources
* [Selenium Grid Project](http://www.seleniumhq.org/projects/grid/)
* [Docker Documentation](https://docs.docker.com/)
* [Docker Selenium. Getting Started Video](https://www.youtube.com/embed/S4OkrnFb-YY?feature=oembed)
* [Distributed Automation Using Selenium Grid / AWS / Autoscaling](https://www.youtube.com/embed/cbIfU1fvGeo?feature=oembed)
