Giter Site home page Giter Site logo

just-ask's Introduction

Build Status

JProfiler Special thanks to Java Profiler for their encouragement on Open Source projects.

just-ask

As the name suggests, this library is eventually going to end up becoming a simple prototype that can be enhanced to represent an "On-demand Grid" (which explains the reason behind the name just-ask ).

Why an On-Demand Grid ?

A static Grid (i.e., a hub and a fixed number of nodes) is a good start for setting up a remote execution infrastructure. But once the usage of the hub starts going up, problems start creeping up. Nodes go stale and start causing false failures for the tests and require constant maintenance (restart).

Some of it can be solved by embedding a "self healing" mechanism into the hub, but when it comes to scaling the Grid infrastructure this also does not help a lot.

just-ask is an on-demand grid, wherein there are no fixed nodes attached to the grid. As and when tests make hit the hub, a node is spun off, the test is routed to the node and after usage the node is cleaned up. The on-demand node can be a docker container that hosts a selenium node.

Pre-requisites

just-ask requires :

  • JDK 8.
  • A Selenium Grid of version 3.14.0 or higher.
  • If you would like to leverage docker based on demand solution
    • Access to default Docker unix socket
      • you can refer here
    • Or Docker Remote API enabled
      • For windows you can refer here,
      • For UNIX refer here and
      • For OSX refer here

How to use

In order to consume just-ask for your straight forward on-demand grid needs, following instructions need to be followed:

  • First download the uber jar from here. Make sure you download the uber jar i.e., the jar name that ends with jar-with-dependencies. The latest released v
  • Download the latest Selenium standalone jar from here.
  • Now create a configuration JSON file as shown below :
{
  "dockerRestApiUri": "http://192.168.43.130:2375",
  "localhost": "0.0.0.0",
  "dockerImagePort": "4444",
  "volume": "/dev/shm:/dev/shm",
  "maxSession": 5,
  "mapping": [
    {
      "browser": "chrome",
      "target": "selenium/standalone-chrome:3.14.0",
      "implementation": "com.rationaleemotions.server.DockerBasedSeleniumServer"
    },
    {
      "browser": "firefox",
      "target": "selenium/standalone-firefox:3.14.0",
      "implementation": "com.rationaleemotions.server.JvmBasedSeleniumServer"
    }
  ],
  "environment": {
     "SCREEN_WIDTH": 1280,
     "SCREEN_HEIGHT": 720
  }
}
  • Start the On-demand Grid using the below command (Here the JVM argument -Dconfig.file is used to specify the location of the JSON configuration file that we created above.)
java -Dconfig.file=config.json -cp selenium-server-standalone-3.14.0.jar:just-ask-<VERSION>-jar-with-dependencies.jar \
org.openqa.grid.selenium.GridLauncherV3 -role hub -servlets com.rationaleemotions.servlets.EnrollServlet
  • Now wire in the Ghost Proxy into this on-demand hub by loading the URL : http://localhost:4444/grid/admin/EnrollServlet

  • That's about it. The On-demand Grid is now ready for use.

  • To run tests against the On-demand Grid you just instantiate a RemoteWebDriver instance and then work with it.

    Here's a sample that shows using chrome browser:

URL url = new URL("http://localhost:4444/wd/hub");
RemoteWebDriver driver = new RemoteWebDriver(url, DesiredCapabilities.chrome());

Understanding the JSON configuration file.

The meaning of each of the attributes of the JSON file is as below :

  • dockerRestApiUri - Represents the Docker Rest API URI (could be unix:///var/run/docker.sock or http://192.168.43.130:2375).
  • localhost - Represents the hostname of the machine on which the Docker Daemon is running on (Its safe to leave its value as 0.0.0.0 )
  • dockerImagePort - Represents the port number that is exposed inside the docker container.
  • volume - Represents a volume to be mounted. Can be left empty.
  • maxSession - Represents the maximum number of concurrent sessions that can be supported by the On-demand Hub after which new test session requests will be queued.
  • mapping - Represents a set of key-value pairs wherein browser represents the browser flavor and target represents the name of the docker image that is capable of supporting the respective browser. The target may not be relevant to all implementation values (for e.g., the target is currently relevant ONLY for docker based on-demand nodes.)
  • environment - Represents environmental variable key-value pairs to be passed to docker container

Understanding the relevance of implementation

just-ask currently supports two implementation flavors :

  • com.rationaleemotions.server.DockerBasedSeleniumServer - Indicates that for the browser in question, you would like to leverage the Docker way of spinning off nodes on demand.
  • com.rationaleemotions.server.JvmBasedSeleniumServer - Indicates that for the browser in question, you would like to leverage the JVM way of spinning off nodes on demand (i.e., the on-demand node would be a new JVM process.)

How to customize and use.

In-case you would like to wire in your own Custom Server implementation, following is how it can be done :

just-ask is a Maven artifact. In order to consume it, you merely need to add the following as a dependency in your pom file.

<dependency>
    <groupId>com.rationaleemotions</groupId>
    <artifactId>just-ask</artifactId>
    <version>1.0.5</version>
</dependency>

Now that you have added the above as a maven dependency, you build your own implementation of the Server, by implementing the interface com.rationaleemotions.server.ISeleniumServer.

After that, you can wire in your implementation via the implementation attribute in the JSON configuration file using the JVM argument -Dconfig.file.

Building the code on your own

In order to get started with using this library here are the set of instructions that can be followed :

  • Build the code using mvn clean package
  • Drop the built jar (you will find two jars, so please make sure you pick up the uber jar which would have its name around something like this just-ask-<VERSION>-jar-with-dependencies.jar ) in the directory that contains the selenium server standalone.
  • Start the selenium hub using the command java -cp selenium-server-standalone-3.14.0.jar:just-ask-<VERSION>-jar-with-dependencies.jar org.openqa.grid.selenium.GridLauncherV3 -role hub -servlets com.rationaleemotions.servlets.EnrollServlet
  • Explicitly register the ghost node by loading the URL http://localhost:4444/grid/admin/EnrollServlet in a browser.

Now the On-demand Grid is ready for use.

just-ask's People

Contributors

bharathc02 avatar dineshkuppan avatar gytiss avatar krmahadevan avatar philippe-granet avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

just-ask's Issues

Container shutdown in afterSession

Consider container shutdown in afterSession() instead of afterCommand().
There are a few reasons for that. The main one is that sometimes you can pass correct alwaysMatch capability and session will be created, but it will be cancelled due to some wrong other capabilities, ie platformName sent as empty string. In this scenario container is left running and not closed.
Also, I believe it should be faster to close session in afterSession(), as there would be no need to check every command in afterCommand() method. Tested this change and I see containers closing correctly.
I can open a PR for this change.

Container clean up based on Grid timeout value

Selenium Grid has a timeout value, which defines when should stale sessions be cleaned up.
Currently in just ask, session is released, but container is left hanging.
I see that BaseRemoteProxy has

private void cleanUpSlot(TestSlot slot)

which calls

((TimeoutListener) proxy).beforeRelease(session);

based on SessionTerminationReason.TIMEOUT or SessionTerminationReason.ORPHAN
Thus thinking of this overload in GhosProxy class

@Override
    public void beforeRelease(TestSession session) {
        // release the resources remotely if the remote started a browser.
        if (session.getExternalKey() == null) {
          return;
        }
        boolean ok = session.sendDeleteSessionRequest();
        if (ok && processTestSession(session)) {
        	stopServerForTestSession(session);
		}
        if (!ok) {
          LOG.warning("Error releasing the resources on timeout for session " + session);
        }
      }

Tested and I see timeout value now is respected, thus stale Docker images are being closed.

EnrollServlet call fails after when grid is up

Selenium Stand alone server : 3.7.1
Just-ask : 1.0.3

I've started the selenium grid using the command below,
java -Dconfig.file=config.json -cp selenium-server-standalone-3.7.1.jar:just-ask-1.0.3-jar-with-dependencies.jar org.openqa.grid.selenium.GridLauncherV3 -role hub -servlets com.rationaleemotions.servlets.EnrollServlet

Selenium Grid hub starts successfully.
When wiring in the Ghost Proxy into the hub via URL http://localhost:4444/grid/admin/EnrollServlet, it throws java.lang.IllegalStateException: There was a problem in hooking in the ghost node..
Entire exception grid.txt

Upon reviewing the code, I noticed that Selenium has deprecated the Registry class (link).

Just-ask is using Registry class in GhostProxy and EnrollServlet.

To fix this, both these classes must resort to using the interface GridRegistry instead of Registry

hasCapability method returns true all the time in GhostProxy

This method should not have Override:

    @Override
    public boolean hasCapability(Map<String, Object> requestedCapability) {
        return true;
    }

Due to above, if Grid does not have the capability, session is put in the queue instead of breaking straight away (like in default Grid). Also, if you do not set newSessionWaitTimeout parameter in Grid HUB start cmd, the session will wait indefinitely,
Also getNewSession should include this piece of code from default getNewSession method:

if (!hasCapability(requestedCapability)) {
        	LOG.fine("Node " + this + " has no matching capability");
          return null;
        }

I can open PR request for this if we agree it is needed.

Add ability to mount drives

This would not only be a new feature, but there a times when docker node simply crashes seemingly without any reason, but I found that it is a known issue due tue containers not started with mount option: SeleniumHQ/docker-selenium#392
As a workaround user can send --disable-dev-shm-usage capability to prevent random container crashes

beforeCommand() in GhostProxy should never throw exceptions

The Proxy mechanism is built such that if org.openqa.grid.internal.listeners.CommandListener#beforeCommand implementations throw exceptions then that command would be retried again. This becomes a problem when the beforeCommand() implementation in GhostProxy throws exceptions for new sessions, causing un-necessary session creation attempt to continuously happen.

Connecting physical node to Grid hub throws exception in Grid console

Hello,

It is a great solution when running chrome/firefox using docker. To make this customized Grid more flexible though, an ability to connect physical nodes to the HUB, ie Safari on Mac(phones as well) and IE and Edge for Windows machines is needed.
I connected a physical Win10 machine with IE and Edge browsers. The test passes actually, but Grid Hub console thows exception:

java.lang.IllegalStateException: Missing target mapping. Available mappings are {chrome=MappingInfo{browser='chrome',target='selenium/standalone-chrome:latest', implementation='com.rationaleemotions.server.DockerBasedSeleniumServer'}, firefox=MappingInfo{browser='firefox',target='selenium/standalone-firefox-debug:latest', implementation='com.rationaleemotions.server.DockerBasedSeleniumServer'}}
        at com.rationaleemotions.proxy.GhostProxy.beforeCommand(GhostProxy.java:87)
        at org.openqa.grid.internal.TestSession.forward(TestSession.java:222)
        at org.openqa.grid.web.servlet.handler.RequestHandler.forwardNewSessionRequestAndUpdateRegistry(RequestHandler.java:91)
        at org.openqa.grid.web.servlet.handler.RequestHandler.process(RequestHandler.java:114)
        at org.openqa.grid.web.servlet.DriverServlet.process(DriverServlet.java:85)
        at org.openqa.grid.web.servlet.DriverServlet.doPost(DriverServlet.java:69)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:707)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
        at org.seleniumhq.jetty9.servlet.ServletHolder.handle(ServletHolder.java:865)
        at org.seleniumhq.jetty9.servlet.ServletHandler.doHandle(ServletHandler.java:535)
        at org.seleniumhq.jetty9.server.handler.ScopedHandler.handle(ScopedHandler.java:146)
        at org.seleniumhq.jetty9.security.SecurityHandler.handle(SecurityHandler.java:548)
        at org.seleniumhq.jetty9.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
        at org.seleniumhq.jetty9.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:257)
        at org.seleniumhq.jetty9.server.session.SessionHandler.doHandle(SessionHandler.java:1595)
        at org.seleniumhq.jetty9.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:255)
        at org.seleniumhq.jetty9.server.handler.ContextHandler.doHandle(ContextHandler.java:1340)
        at org.seleniumhq.jetty9.server.handler.ScopedHandler.nextScope(ScopedHandler.java:203)
        at org.seleniumhq.jetty9.servlet.ServletHandler.doScope(ServletHandler.java:473)
        at org.seleniumhq.jetty9.server.session.SessionHandler.doScope(SessionHandler.java:1564)
        at org.seleniumhq.jetty9.server.handler.ScopedHandler.nextScope(ScopedHandler.java:201)
        at org.seleniumhq.jetty9.server.handler.ContextHandler.doScope(ContextHandler.java:1242)
        at org.seleniumhq.jetty9.server.handler.ScopedHandler.handle(ScopedHandler.java:144)
        at org.seleniumhq.jetty9.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
        at org.seleniumhq.jetty9.server.Server.handle(Server.java:503)
        at org.seleniumhq.jetty9.server.HttpChannel.handle(HttpChannel.java:364)
        at org.seleniumhq.jetty9.server.HttpConnection.onFillable(HttpConnection.java:260)
        at org.seleniumhq.jetty9.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:305)
        at org.seleniumhq.jetty9.io.FillInterest.fillable(FillInterest.java:103)
        at org.seleniumhq.jetty9.io.ChannelEndPoint$2.run(ChannelEndPoint.java:118)
        at org.seleniumhq.jetty9.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:333)
        at org.seleniumhq.jetty9.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:310)
        at org.seleniumhq.jetty9.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:168)
        at org.seleniumhq.jetty9.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:126)
        at org.seleniumhq.jetty9.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:366)
        at org.seleniumhq.jetty9.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:765)
        at org.seleniumhq.jetty9.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:683)
        at java.lang.Thread.run(Unknown Source)

Thus it would be nice to run tests targeting physical machines without these errors.
Of course, it is a bit unclear what to do if a physical node is connected with Chrome/Firefox capablities. In that case there should be some logic how to forward requests, probably using platformName. Ie ANY, LINUX going to docker.

Allow to pull images from internal repository

Currently the only way to pull images is from public docker HUB. If company is using it's own repository and does not allow to pull images from public HUB repo, this solution cannot be used.
I see that you can create an object with custom server configuration like that:
RegistryAuth auth = RegistryAuth.builder().password("test").serverAddress("test").username("test").build(); and pass it DockerClient pull method.
Somehow those parameters would need to be passed though, maybe via command line arguments as it would contain user password.

The logic to check if the image needs to be downloaded or not is not working fine.

The current logic to check if an image needs to be downloaded into the docker host or not, is flawed because it just checks the size of the images that were retrieved as a result of the filtering. This logic breaks when the user's host has one of the images available and the other is not available.

For e.g., lets say the user's docker host has firefox image available and chrome image is not available. Now a test comes with a desired capability of chrome, the current logic just checks if images for (chrome and firefox) is available and if the retrieved list size is not empty it assumes that there's no download required. This breaks the test.

Enhancements for "com.rationaleemotions.server.DockerHelper"

The following enhancements need to be done for com.rationaleemotions.server.DockerHelper :

  • Ability to spin off containers in privileged mode ( via com.spotify.docker.client.messages.HostConfig.Builder#privileged)
  • Ability to link devices (via com.spotify.docker.client.messages.HostConfig.Builder#devices(java.util.List<com.spotify.docker.client.messages.Device>)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.