This reference for version: 1.0.0

Moon is an enterprise version of Selenoid using Kubernetes to launch browsers.

1. Getting Started

1.1. Quick Start Guide

  1. Prerequisites:

    • Running Kubernetes cluster

    • kubectl client installed and pointing to the cluster

  2. Examples of used YAML and JSON files can be found in Example Configuration Files section.

  3. Configure your namespace service account to support Aerokube private registry - replace REGISTRY_USERNAME, REGISTRY_PASSWORD and REGISTRY_EMAIL placeholders with your values:

    $ kubectl create secret docker-registry registry.aerokube.com --docker-server=registry.aerokube.com --docker-username=REGISTRY_USERNAME --docker-password=REGISTRY-PASSWORD --docker-email=REGISTRY-EMAIL
    $ kubectl patch serviceaccount default -p '{"imagePullSecrets": [{"name": "registry.aerokube.com"}]}'

See this Kubernetes documentation section for more details. . Copy test quota to quota directory and initialize browsers configuration:

$ mkdir -p quota
$ touch quota/browsers.json # Add contents to file
$ kubectl create configmap quota --from-file=quota
  1. Start one or more Moon replicas:

    $ kubectl create -f moon.yaml
  2. Start one or more Moon API replicas:

    $ kubectl create -f moon-api.yaml
  3. Determine IP addresses or hostnames for moon and moon-api services. When testing in Minikube this can be done with the following commands:

    $ minikube service moon --url
    http://192.168.99.100:30979
    $ minikube service moon-api --url
    http://192.168.99.100:41567
  4. Run your tests against moon service like you do with regular Selenium:

    http://192.168.99.100:30979/wd/hub
  5. Check that moon-api returns statistics:

    $ http://192.168.99.100:41567/status

    A successful request should return a JSON with browser usage statistics.

1.2. Cluster Architecture

Moon Cluster Architecture

architecture

Moon cluster consists of several important components:

  1. Kubernetes configuration map to store browser quota information and various runtime settings.

  2. One or more moon application containers. Their main purpose is to start and stop browser containers. These replicas are usually exposed as Kubernetes service available on standard Selenium port 4444. You should run all the tests against this service.

  3. One or more moon-api application containers. This API collects and returns various data about running browsers. moon-api is usually exposed as Kubernetes service available on HTTP port (e.g. 80 or 8080).

  4. Running browser containers. Moon is using exactly the same containers as Selenoid.

Basic browser startup functionality is completely stateless and does not require any external database to be running.

2. Configuration

2.1. Configuring Available Browsers

  • Moon is using exactly the same JSON format as Selenoid. An example file can be found in Example Configuration Files section. Because everything in Kubernetes is being run in containers - you cannot specify path to webdriver binary, only container with browser. To be available across the cluster all configuration data is stored in ConfigMap.

  • Moon supports multiple users, so you need to create one file for each user named <username>.json. For example for user test to work properly you should create test.json file.

  • All JSON files should be stored in the same directory because in that case you can update configuration with just one command.

2.1.1. Updating Browsers List

To add or remove browsers:

  1. Having configuration files stored in directory update Moon configuration:

    $ kubectl update configmap quota --from-file=quota
  2. Gracefully restart Moon service:

    $ kubectl update -f moon.yaml

All running user sessions will continue to work without any interruption.

Appendix A: Example Configuration Files

Example browsers.json file contents
{
  "firefox": {
    "default": "55.0",
    "versions": {
      "55.0": {
        "image": "selenoid/vnc:firefox_55.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "54.0": {
        "image": "selenoid/vnc:firefox_54.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "53.0": {
        "image": "selenoid/vnc:firefox_53.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "52.0": {
        "image": "selenoid/vnc:firefox_52.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "51.0": {
        "image": "selenoid/vnc:firefox_51.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "50.0": {
        "image": "selenoid/vnc:firefox_50.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "49.0": {
        "image": "selenoid/vnc:firefox_49.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "48.0": {
        "image": "selenoid/vnc:firefox_48.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "47.0": {
        "image": "selenoid/vnc:firefox_47.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "46.0": {
        "image": "selenoid/vnc:firefox_46.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "45.0": {
        "image": "selenoid/vnc:firefox_45.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "44.0": {
        "image": "selenoid/vnc:firefox_44.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "43.0": {
        "image": "selenoid/vnc:firefox_43.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "42.0": {
        "image": "selenoid/vnc:firefox_42.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "41.0": {
        "image": "selenoid/vnc:firefox_41.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "40.0": {
        "image": "selenoid/vnc:firefox_40.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "39.0": {
        "image": "selenoid/vnc:firefox_39.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "38.0": {
        "image": "selenoid/vnc:firefox_38.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "37.0": {
        "image": "selenoid/vnc:firefox_37.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "36.0": {
        "image": "selenoid/vnc:firefox_36.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "35.0": {
        "image": "selenoid/vnc:firefox_35.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "34.0": {
        "image": "selenoid/vnc:firefox_34.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "33.0": {
        "image": "selenoid/vnc:firefox_33.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "32.0": {
        "image": "selenoid/vnc:firefox_32.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "31.0": {
        "image": "selenoid/vnc:firefox_31.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "30.0": {
        "image": "selenoid/vnc:firefox_30.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "29.0": {
        "image": "selenoid/vnc:firefox_29.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "28.0": {
        "image": "selenoid/vnc:firefox_28.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "27.0": {
        "image": "selenoid/vnc:firefox_27.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "26.0": {
        "image": "selenoid/vnc:firefox_26.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "25.0": {
        "image": "selenoid/vnc:firefox_25.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "24.0": {
        "image": "selenoid/vnc:firefox_24.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "23.0": {
        "image": "selenoid/vnc:firefox_23.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "22.0": {
        "image": "selenoid/vnc:firefox_22.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "21.0": {
        "image": "selenoid/vnc:firefox_21.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "20.0": {
        "image": "selenoid/vnc:firefox_20.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "19.0": {
        "image": "selenoid/vnc:firefox_19.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "18.0": {
        "image": "selenoid/vnc:firefox_18.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "17.0": {
        "image": "selenoid/vnc:firefox_17.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "16.0": {
        "image": "selenoid/vnc:firefox_16.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "15.0": {
        "image": "selenoid/vnc:firefox_15.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "14.0": {
        "image": "selenoid/vnc:firefox_14.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "13.0": {
        "image": "selenoid/vnc:firefox_13.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "12.0": {
        "image": "selenoid/vnc:firefox_12.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "11.0": {
        "image": "selenoid/vnc:firefox_11.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "10.0": {
        "image": "selenoid/vnc:firefox_10.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "9.0": {
        "image": "selenoid/vnc:firefox_9.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "8.0": {
        "image": "selenoid/vnc:firefox_8.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "7.0": {
        "image": "selenoid/vnc:firefox_7.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "6.0": {
        "image": "selenoid/vnc:firefox_6.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "5.0": {
        "image": "selenoid/vnc:firefox_5.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "4.0": {
        "image": "selenoid/vnc:firefox_4.0",
        "port": "4444",
        "path": "/wd/hub"
      },
      "3.6": {
        "image": "selenoid/vnc:firefox_3.6",
        "port": "4444",
        "path": "/wd/hub"
      }
    }
  },
  "chrome": {
    "default": "60.0",
    "versions": {
      "60.0": {
        "image": "selenoid/vnc:chrome_60.0",
        "port": "4444"
      },
      "59.0": {
        "image": "selenoid/vnc:chrome_59.0",
        "port": "4444"
      },
      "58.0": {
        "image": "selenoid/vnc:chrome_58.0",
        "port": "4444"
      },
      "57.0": {
        "image": "selenoid/vnc:chrome_57.0",
        "port": "4444"
      },
      "56.0": {
        "image": "selenoid/vnc:chrome_56.0",
        "port": "4444"
      },
      "55.0": {
        "image": "selenoid/vnc:chrome_55.0",
        "port": "4444"
      },
      "54.0": {
        "image": "selenoid/vnc:chrome_54.0",
        "port": "4444"
      },
      "53.0": {
        "image": "selenoid/vnc:chrome_53.0",
        "port": "4444"
      },
      "52.0": {
        "image": "selenoid/vnc:chrome_52.0",
        "port": "4444"
      },
      "51.0": {
        "image": "selenoid/vnc:chrome_51.0",
        "port": "4444"
      },
      "50.0": {
        "image": "selenoid/vnc:chrome_50.0",
        "port": "4444"
      },
      "49.0": {
        "image": "selenoid/vnc:chrome_49.0",
        "port": "4444"
      },
      "48.0": {
        "image": "selenoid/vnc:chrome_48.0",
        "port": "4444"
      }
    }
  },
  "opera": {
    "default": "47.0",
    "versions": {
      "47.0": {
        "image": "selenoid/vnc:opera_47.0",
        "port": "4444"
      },
      "46.0": {
        "image": "selenoid/vnc:opera_46.0",
        "port": "4444"
      },
      "45.0": {
        "image": "selenoid/vnc:opera_45.0",
        "port": "4444"
      },
      "44.0": {
        "image": "selenoid/vnc:opera_44.0",
        "port": "4444"
      },
      "43.0": {
        "image": "selenoid/vnc:opera_43.0",
        "port": "4444"
      },
      "42.0": {
        "image": "selenoid/vnc:opera_42.0",
        "port": "4444"
      },
      "41.0": {
        "image": "selenoid/vnc:opera_41.0",
        "port": "4444"
      },
      "40.0": {
        "image": "selenoid/vnc:opera_40.0",
        "port": "4444"
      },
      "39.0": {
        "image": "selenoid/vnc:opera_39.0",
        "port": "4444"
      },
      "38.0": {
        "image": "selenoid/vnc:opera_38.0",
        "port": "4444"
      },
      "37.0": {
        "image": "selenoid/vnc:opera_37.0",
        "port": "4444"
      },
      "36.0": {
        "image": "selenoid/vnc:opera_36.0",
        "port": "4444"
      },
      "35.0": {
        "image": "selenoid/vnc:opera_35.0",
        "port": "4444"
      },
      "34.0": {
        "image": "selenoid/vnc:opera_34.0",
        "port": "4444"
      },
      "33.0": {
        "image": "selenoid/vnc:opera_33.0",
        "port": "4444"
      },
      "12.1": {
        "image": "selenoid/vnc:opera_12.1",
        "port": "4444",
        "path": "/wd/hub"
      }
    }
  }
}
Example moon.yaml file contents
kind: Service
apiVersion: v1
metadata:
  name: moon
spec:
  selector:
    app: moon
  ports:
  - protocol: TCP
    port: 4444
  type: NodePort
---
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: moon
spec:
  replicas: 3
  template:
    metadata:
      labels:
        app: moon
    spec:
      containers:
      - name: moon
        image: registry.aerokube.com/moon/moon:1.0.0
        ports:
        - containerPort: 4444
        volumeMounts:
        - name: quota
          mountPath: /quota
      volumes:
      - name: quota
        configMap:
          name: quota
Example moon-api.yaml file contents
kind: Service
apiVersion: v1
metadata:
  name: moon-api
spec:
  selector:
    app: moon-api
  ports:
  - protocol: TCP
    port: 8080
  type: NodePort
---
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: moon-api
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: moon-api
    spec:
      containers:
      - name: moon-api
        image: registry.aerokube.com/moon/moon-api:1.0.0
        ports:
        - containerPort: 8080
        volumeMounts:
        - name: quota
          mountPath: /quota
      volumes:
      - name: quota
        configMap:
          name: quota