Syed Sayem blog

Running Selenium grid on Kubernetes cluster using Helm package manager

In this post, I’ll show you how to get Selenium Grid up and running on Kubernetes cluster using Helm package manager.

kubernetes

Selenium Grid

Selenium Grid makes automation execution jobs much easier. Using Selenium Grid, one can run multiple tests on multiple machines in parallel, which reduces execution times from days to hours. However, setting up our own Selenium Grid means we have to configure browsers across multiple machines, virtual or physical, and making sure each machine is running the Selenium Server correctly. This also means maintaining the Grid and making updates would be a time-consuming. This is where Kubernetes comes in and saves the day!

Kubernetes

Kubernetes is an open-source system for automating deployment, scaling, and management of containerized applications.

Helm

Helm is a package manager for Kubernetes applications, responsible for installing and managing Kubernetes applications. Helm packages all of the different Kubernetes resources (such as deployments, services, and ingress) into a chart, which may be hosted in a repository. Users can pull down charts and install them on any number of Kubernetes clusters. This is similar to Homebrew and apt-get package manager. We will use Helm to deploy selenium-grid on Kubernetes Cluster.

What We’ll Accomplish
  1. Set up a Kubernetes cluster using Docker for Mac
  2. Deploy Selenium-Grid to the cluster
  3. Running selenium test and viewing your Selenium tests running on Kubernetes cluster
Prerequisites
  • kubectl - macOS users can use brew install kubectl to install
  • Docker for Mac - Docker with Kubernetes is currently only available on Docker for Mac in the latest Edge version
  • Virtualbox - macOS users can use brew cask install virtualbox to install
  • Helm - macOS users can use brew install kubernetes-helm to install

 

Enabling the local Kubernetes cluster

Click the Docker icon in the status bar, go to “Preferences”, and on the “Kubernetes”-tab, check “Enable Kubernetes”. This will start a single node Kubernetes cluster for you and install the kubectl command line utility. This might take a while, but the dialog will let you know once the Kubernetes cluster is ready.

enable-Kubernetes

Next step, you’ll need to increase Docker’s available memory to 4GB or more.

Click the Docker icon in the status bar, go to “Preferences”, and on the “Advanced”-tab, change memoery to 4GB or more.

increase-memeory

 

Selenium Grid installation

To install the chart with the release name selenium-grid:

helm install --name selenium-grid stable/selenium \
--set chromeDebug.enabled=true \
--set firefoxDebug.enabled=true

The command deploys Selenium Grid on the Kubernetes cluster in the default configuration with chromeDebug and firefoxDebug enabled.

Note: You should see the following output after executing helm install ...

NAME:   selenium-grid
LAST DEPLOYED: Fri May 11 23:09:29 2018
NAMESPACE: default
STATUS: DEPLOYED

RESOURCES:
==> v1/Service
NAME                        TYPE          CLUSTER-IP      EXTERNAL-IP  PORT(S)         AGE
selenium-grid-selenium-hub  LoadBalancer  10.110.225.161  localhost    4444:30665/TCP  0s

==> v1beta1/Deployment
NAME                                  DESIRED  CURRENT  UP-TO-DATE  AVAILABLE  AGE
selenium-grid-selenium-chrome-debug   1        1        1           0          0s
selenium-grid-selenium-firefox-debug  1        1        1           0          0s
selenium-grid-selenium-hub            1        1        1           0          0s

==> v1/Pod(related)
NAME                                                   READY  STATUS             RESTARTS  AGE
selenium-grid-selenium-chrome-debug-6d8898d7c4-7psdh   0/1    ContainerCreating  0         0s
selenium-grid-selenium-firefox-debug-84f6f484d8-wh66d  0/1    ContainerCreating  0         0s
selenium-grid-selenium-hub-f7f874cd-p5lrd              0/1    ContainerCreating  0         0s

Selenium hub will automatically start-up using port 4444 by default. You can view the status of the hub by opening a browser window and navigating to: http://localhost:4444/grid/console

gird-console

 

Run Selenium Test

Let’s run a quick Selenium test to validate our setup.

Clone this github repo:
git clone https://github.com/sayems/selenium.grid.git
Go into the github repo directory
$ cd selenium.grid
Run tests

Before you run docker-compose up, make sure Selenium grid is up & running on Kubernetes cluster, you can run the following command: $ kubectl get pods to see Selenium pods.

$ docker-compose up

It typically takes several minutes for docker setup for the first time, so wait for few minutes …

Debugging with VNC Viewer

It is useful to be able to view your Selenium tests running on Kubernetes cluster. This can help us to debug issues better and have more confidence in our tests. To view running tests on Kubernetes cluster.

  1. Download VNC® Viewer for Google Chrome
  2. Execute the following command to bind ports between Kubernetes cluster to the local machine:
  kubectl port-forward --namespace default \
  $(kubectl get pods --namespace default \
    -l app=selenium-grid-selenium-chrome-debug \
    -o jsonpath='{ .items[0].metadata.name }') 5900
  1. Open VNC® Viewer for Google Chrome
    • Address: 127.0.0.1:5900
    • Password: secret

 

vnc

Deletes a release from Kubernetes

The helm list function will show you a list of all deployed releases.

$ helm list

List of all releases

NAME         	 REVISION	 UPDATED   STATUS  	 CHART     NAMESPACE
selenium-grid	 1       	 Fri May 	 DEPLOYED	 selenium	 default

If you want to delete selenium-grid, you can execute the following command

$ helm delete selenium-grid
release "selenium-grid" deleted

If you want to delete all Helm releases, you can pipe the output of helm ls –short to xargs, and run helm delete for each release name.

$ helm ls --short | xargs -L1 helm delete

Configuration

The following table lists the configurable parameters of the Selenium chart and their default values.

Parameter Description Default
global.nodeselector Node label to be useed globally for scheduling of all images nil
hub.image The selenium hub image selenium/hub
hub.tag The selenium hub image tag 3.11.0
hub.pullPolicy The pull policy for the hub image IfNotPresent
hub.port The port the hub listens on 4444
hub.javaOpts The java options for the selenium hub JVM, default sets the maximum heap size to 1,000 mb -Xmx1000m
hub.resources The resources for the hub container, defaults to minimum half a cpu and maximum 1,000 mb RAM {"limits":{"cpu":".5", "memory":"1000Mi"}}
hub.serviceType The Service type NodePort
hub.serviceSessionAffinity The session affinity for the hub service None
hub.gridNewSessionWaitTimeout   nil
hub.gridJettyMaxThreads   nil
hub.gridNodePolling   nil
hub.gridCleanUpCycle   nil
hub.gridTimeout   nil
hub.gridBrowserTimeout   nil
hub.gridMaxSession   nil
hub.gridUnregisterIfStillDownAfer   nil
hub.seOpts Command line arguments to pass to hub nil
hub.timeZone The time zone for the container nil
hub.nodeselector Node label to use for scheduling of the hub if set this takes precedence over the global value nil
chrome.enabled Schedule a chrome node pod false
chrome.image The selenium node chrome image selenium/node-chrome
chrome.tag The selenium node chrome tag 3.11.0
chrome.pullPolicy The pull policy for the node chrome image IfNotPresent
chrome.replicas The number of selenium node chrome pods 1
chrome.javaOpts The java options for the selenium node chrome JVM, default sets the maximum heap size to 900 mb -Xmx900m
chrome.volumeMounts Additional volumes to mount, the default provides a larger shared memory [{"mountPath":"/dev/shm", "name":"dshm"}]
chrome.volumes Additional volumes import, the default provides a larger shared memory [{"name":"dshm", "emptyDir":{"medium":"Memory"}}]
chrome.resources The resources for the node chrome container, defaults to minimum half a cpu and maximum 1,000 mb {"limits":{"cpu":".5", "memory":"1000Mi"}}
chrome.screenWidth   nil
chrome.screenHeight   nil
chrome.screenDepth   nil
chrome.display The vnc display nil
chrome.chromeVersion The version of chrome to use nil
chrome.nodeMaxInstances The maximum number of browser instances nil
chrome.nodeMaxSession The maximum number of sessions nil
chrome.nodeRegistryCycle The number of milliseconds to wait, registering a node nil
chrome.nodePort The port to listen on nil
chrome.seOpts Command line arguments to pass to node nil
chrome.timeZone The time zone for the container nil
chrome.nodeselector Node label to use for scheduling of chrome images if set this takes precedence over the global value nil
chromeDebug.enabled Schedule a selenium node chrome debug pod false
chromeDebug.image The selenium node chrome debug image selenium/node-chrome-debug
chromeDebug.tag The selenium node chrome debug tag 3.11.0
chromeDebug.pullPolicy The selenium node chrome debug pull policy IfNotPresent
chromeDebug.replicas The number of selenium node chrome debug pods 1
chromeDebug.javaOpts The java options for a selenium node chrome debug JVM, default sets the max heap size to 900 mb -Xmx900m
chromeDebug.volumeMounts Additional volumes to mount, the default provides a larger shared [{"mountPath":"/dev/shm", "name":"dshm"}]
chromeDebug.volumes Additional volumes import, the default provides a larger shared [{"name":"dshm", "emptyDir":{"medium":"Memory"}}]
chromeDebug.resources The resources for the hub container, defaults to minimum half a cpu and maximum 1,000 mb {"limits":{"cpu":".5", "memory":"1000Mi"}}
chromeDebug.screenWidth   nil
chromeDebug.screenHeight   nil
chromeDebug.screenDepth   nil
chromeDebug.display The vnc display nil
chromeDebug.chromeVersion The version of chrome to use nil
chromeDebug.nodeMaxInstances The maximum number of browser instances nil
chromeDebug.nodeMaxSession The maximum number of sessions nil
chromeDebug.nodeRegistryCycle The number of milliseconds to wait, registering a node nil
chromeDebug.nodePort The port to listen on nil
chromeDebug.seOpts Command line arguments to pass to node nil
chromeDebug.timeZone The time zone for the container nil
chromeDebug.nodeselector Node label to use for scheduling of chromeDebug images if set this takes precedence over the global value nil
firefox.enabled Schedule a selenium node firefox pod false
firefox.image The selenium node firefox image selenium/node-firefox
firefox.tag The selenium node firefox tag 3.11.0
firefox.pullPolicy The selenium node firefox pull policy IfNotPresent
firefox.replicas The number of selenium node firefox pods 1
firefox.javaOpts The java options for a selenium node firefox JVM, default sets the max heap size to 900 mb -Xmx900m
firefox.resources The resources for the hub container, defaults to minimum half a cpu and maximum 1,000 mb {"limits":{"cpu":".5", "memory":"1000Mi"}}
firefox.screenWidth   nil
firefox.screenHeight   nil
firefox.screenDepth   nil
firefox.display The vnc display nil
firefox.firefoxVersion The version of firefox to use nil
firefox.nodeMaxInstances The maximum number of browser instances nil
firefox.nodeMaxSession The maximum number of sessions nil
firefox.nodeRegistryCycle The number of milliseconds to wait, registering a node nil
firefox.nodePort The port to listen on nil
firefox.seOpts Command line arguments to pass to node nil
firefox.timeZone The time zone for the container nil
firefox.nodeselector Node label to use for scheduling of firefox images if set this takes precedence over the global value nil
firefoxDebug.enabled Schedule a selenium node firefox debug pod false
firefoxDebug.image The selenium node firefox debug image selenium/node-firefox-debug
firefoxDebug.tag The selenium node firefox debug tag 3.11.0
firefoxDebug.pullPolicy The selenium node firefox debug pull policy IfNotPresent
firefoxDebug.replicas The numer of selenium node firefox debug pods 1
firefoxDebug.javaOpts The java options for a selenium node firefox debug JVM, default sets the max heap size to 900 mb -Xmx900m
firefoxDebug.resources The resources for the selenium node firefox debug container, defaults to minimum half a cpu and maximum 1,000 mb {"limits":{"cpu":".5", "memory":"1000Mi"}}
firefoxDebug.screenWidth   nil
firefoxDebug.screenHeight   nil
firefoxDebug.screenDepth   nil
firefoxDebug.display The vnc display nil
firefoxDebug.firefoxVersion The version of firefox to use nil
firefoxDebug.nodeMaxInstances The maximum number of browser instances nil
firefoxDebug.nodeMaxSession The maximum number of sessions nil
firefoxDebug.nodeRegistryCycle The number of milliseconds to wait, registering a node nil
firefoxDebug.nodePort The port to listen on nil
firefoxDebug.seOpts Command line arguments to pass to node nil
firefoxDebug.timeZone The time zone for the container nil
firefoxDebug.nodeselector Node label to use for scheduling of firefoxDebug images if set this takes precedence over the global value nil

Specify each parameter using the --set key=value[,key=value] argument to helm install. For example,

$ helm install --name my-release \
  --set chrome.enabled=true \
    stable/selenium

Selenium Page Object in Golang

Recently I started learning Golang and I want to share with you how I taught myself to program in Golang and created a selenium page object project in Golang. The first thing I did to teach myself to program in Go was to take the Tour of Go

The tour is a great interactive tutorial that helps you get your feet wet with the language. It works right inside your web browser, so you do not have to install anything to get going right away with learning go.

Next, I started reading “Go Programming Language by Alan Donovan”. This really helped me learn how to write code in go. The book teaches you Go in an incremental fashion using simple exercises that build upon each other. It is one of the best ways to learn Go right now.

Once you have some idea on how to program in Go, its time to start writing small a project like Selenium test.

When writing selenium tests, a common pattern is to use Page Objects. Page Objects help you write cleaner tests by encapsulating information about the elements on your application page. A Page Object can be reused across multiple tests, and if the template of your application changes, you only need to update the Page Object.

Selenium doesn’t have any official language binding for Golang, so we will use one of the selenium third-party library for golang.

To get the third-party selenium library, open the terminal and run the following command:

$ go get github.com/tebeka/selenium

Next, install any WebDrivers you plan to use. For Mac OS X (using Homebrew):

$ brew install phantomjs
$ brew install chromedriver
$ brew install selenium-server-standalone

For Arch Linux (using Pacman/Yaourt):

$ yaourt -S selenium-server-standalone
$ yaourt -S chromedriver

Clone the git repo:

$ go get github.com/sayems/golang.webdriver

Project structure

golang.webdriver/
      +- pages/
            +- page.go
            +- home_page.go
            +- login_page.go
            +- men_page.go
            +- women_page.go
      +- tests/ 
            +- base_test.go
            +- register_test.go
            +- login_test.go
            +- other_test.go
  +- .gitignore
  +- Dockerfile
  +- LICENSE
  +- README.md
  +- docker-compose.yml
  +- pom.xml

home_page.go

package pages

type HomePage struct {
	Page Page
}

var account = "#header ..."
var myAccount = "#header-account ..."

func (s *HomePage) GoToAccountPage() *AccountPage {
	s.Page.FindByCss(account).Click()
	s.Page.FindByCss(myAccount).Click()
	return &AccountPage{Page:s.Page}
}

func (s *HomePage) GoToMenPage() *MenPage {
	return &MenPage{Page:s.Page}
}

func (s *HomePage) GoToWomenPage() *WomenPage {
	return &WomenPage{Page:s.Page}
}

func (s *HomePage) GoToAccessoriesPage() *AccessoriesPage {
	return &AccessoriesPage{Page:s.Page}
}

func (s *HomePage) GoToSalePage() *SalePage {
	return &SalePage{Page:s.Page}
}

home_test.go

package main

import (
	"testing"
	"github.com/sayems/golang.webdriver/selenium/pages"
)

func TestSelenium(t *testing.T) {

	login := pages.HomePage{Page:page}
	login.GoToAccountPage().
		CreateAnAccount().
		Register()

}

You can see the full source code in this repository

Android Basics Nanodegree Certificate

Today, I graduated with an Android Basics Nanodegree by Google. The course was co-created by Google and Udacity. Over the last 2 months, I have learned an incredible amount about developing Android app. Not only did I complete the projects neccessary for graduation, but I also created more projects on the side to learn more skills. Over the course of those 2 months, I built 10 Android apps. While I have learned a ton during this period, there is still so much to learn about Android Development.

first-screenshot

I decided to start the Android Basics Nanodegree at Udacity because I wanted a guided way of learning by industry professionals. It also helped that they were partnered with a lot of big companies and offered code reviews for all of your projects. While there are a lot of online options for bootcamps, Udacity was by far the cheapest and offered an environment that was more tailored to my style. With great reviews for the course, I signed up for the Nanodegree.

Overall, it was wonderful experience taking Android Basics Nanodegree by Google and graduating successfully. I would highly recommend any beginner or intermediate developer to take the Android Basics Nanodegree on Udacity.

Android Nanodegree scholarship

A few days ago I participated in a competition to get scholarship from Udacity, an online learning platform. The competition is runned until December 31st or until there’s 100 people who finished Android Basic Nanodegree program.

first-screenshot

Whoever finished the program from 1st to 100th participant will get full scholarship from Udacity for Android Developer Nanodegree sponsored by Google.

second-screenshot

After struggling for a few days to finished the Nanodegree as soon as I can, I got positioned number 128. Which mean I will not get the scholarship but I am happy that I finish the program.

third-screenshot

Udacity Android Courses

The sections below include all of Udacity’s Android-related courses, including their Android Basics & Android Developer Nanodegree material.

The complete course list is presently lacking a linear curriculum, so this reference aims to provide a clear, easy to follow path based on each course’s prerequisites. The course order in the “Full Curriculum” sections is structured so that inidividuals with no prior programming experience can work through them from start to finish.

All of the courses can be taken for free; although the paid Nanodegree programs do offer additional benefits, including:

  • a Google-accredited certificate of completion
  • code reviews & guidance by Google-certified instructors
  • access to a course support forum
  • guaranteed job eligibility (Nanodegree Plus)

Full Curriculum

Level Group Course Length Projects
Beginner Preparation Java Ⅰ 6 Weeks  
Beginner Preparation Java Ⅱ 4 Months  
Beginner Preparation Git & GitHub 3 Weeks Contribute to a Repository
Beginner Preparation Install Android Studio 1 Day  
Beginner Core App Development Ⅰ 4 Weeks Score Tracker, Fact List, Quiz, Music Player, Business Info
Beginner Core UI Design 4 Weeks Build Your Own App
Beginner Core Multi-Screen Apps 8 Weeks Report Card, Tour Guide
Beginner Core Networking 5 Weeks Book List, News Reader
Beginner Core UX Design 6 Weeks Build a User-Targeted App
Intermediate Core App Development Ⅱ 2 Weeks Popular Movies, Weather, Build Your Own App
Intermediate Core Material Design 4 Weeks Apply Material Design to News Reader
Intermediate Services Sign-In & Profiles 2 Weeks  
Intermediate Services Maps 2 Weeks  
Intermediate Services Location & Context 2 Weeks  
Intermediate Services Analytics 2 Weeks  
Intermediate Services Ads 2 Weeks  
Intermediate Games Game Design Ⅰ 8 Weeks Breakout Clone
Intermediate Games Game Design Ⅱ 8 Weeks Space Shooter
Advanced Core App Development Ⅲ 6 Weeks Stock Tracker
Advanced Core Optimizing App Performance 4 Weeks  
Advanced Core Firebase 8 Weeks  
Advanced Core Gradle 6 Weeks Joke Teller
Advanced Platforms Android TV & Google Cast 1 Week  
Advanced Platforms Android Wear 2 Weeks Create Wear Interface for Weather App
Advanced Platforms Android Auto 1 Week  

 

* Course lengths are based on Udacity’s presumed allocation of 6 hours per week.


Nanodegree Outlines

Android Basics

Skill Level:

  • Entering students should be motivated to learn and be comfortable with basic computer skills like managing files, navigating the Internet and running programs.

Curriculum:

Also, there are currently Habit Tracker & Inventory Tracker projects listed with the message: “Supporting course content coming in August!”.


Android Developer

Skill Level:

  • Entering students are expected to have prior experience building applications (web or mobile)
  • You should have at least 1-2 years of experience in Java or another object-oriented programming language prior to enrolling.

Curriculum:
        App Development Ⅱ
        App Development Ⅲ
        Gradle
        Material Design
        Android Wear