Selenoid Parallel CI testing

Selenoid is handy tool to manage test runner with minimum efforts.
As it uses own dependent containers by itself, need to maintain single Selenoid run per build agent.

docker-compose.yml

services:   
e2e-test-runner:     
networks:       
selenoid: null     
depends_on:       
- selenoid     
build:       
context: .       
dockerfile: unittest/Dockerfile
volumes:       
- $PWD/unittest/build/reports:/app/unittest/build/reports       
- /var/run/docker.sock:/var/run/docker.sock 

selenoid:   
image: aerokube/selenoid:latest-release   
networks:     
selenoid: null   
volumes:     
- "/tmp/selenoid/video:/opt/selenoid/video:delegated"     
- "/tmp/selenoid/logs:/opt/selenoid/logs:delegated"     
- "$PWD/end-to-end/selenoid:/etc/selenoid:delegated"     
- "/var/run/docker.sock:/var/run/docker.sock"   
expose:     
- "4444"  
environment:     
- OVERRIDE_VIDEO_OUTPUT_DIR=/tmp/selenoid/video   
command: ["-conf", "browsers.json", "-video-output-dir", "/opt/selenoid/video", "-log-output-dir", "/opt/selenoid/logs","-save-all-logs","-disable-privileged", "-container-network", "selenoid"] 

CI Pipeline (BK)

 
agents:   
aws:
instance-id: "$BUILDKITE_AGENT_META_DATA_AWS_INSTANCE_ID"

first-step.sh

#!/bin/bash
echo --- :docker: Prep Selenoid docker network create selenoid || true 
docker pull selenoid/video-recorder:latest-release 
docker pull selenoid/vnc_chrome:79.0 
mkdir -p /tmp/selenoid/video 
mkdir -p /tmp/selenoid/logs

last-step.sh

 
#!/bin/bash
echo +++ :arrow_up: Upload Selenoid Artifacts 
buildkite-agent artifact upload "/tmp/selenoid/**/**" 
echo +++ :dusty_stick: Cleaning Up 
rm -rf /tmp/selenoid/logs 
rm -rf /tmp/selenoid/video 
docker-compose -f docker-compose.yml down

Salesforce CLI with Docker in AWS

Auth was the tricky part as the normal auth requires a browser session rather than taking it from the CLI prompt.

Step 1. Create Dockerfile

FROM node:9.9.0-alpine
RUN npm install sfdx-cli --global
RUN sfdx --version
RUN sfdx plugins --core

Step 2. Generate auth URL from your laptop and push into SSM
sfdx force:auth:web:login -r https://test.salesforce.com -a <alias>
sfdx force:org:display -u <alias> --verbose

Step 3. Push the auth URL into SSM
Note: Auth URL looks like – force://……salesforce.com
aws ssm put-parameter --name /SF/dev --value <auth_URL_here> --description 'some description' --type SecureString --key-id <KMS_key_id_here> --region <region_here> --overwrite

To list all the keys in the path:
aws ssm get-parameters-by-path --path /SF/ --query 'Parameters[*].Name'

Step 4. Run auth within the docker container
aws ssm get-parameters --name <name_here> --with-decryption --output text --query 'Parameters[*].Value' --region ap-southeast-2 > auth.txt"
sfdx force:auth:sfdxurl:store -f auth.txt -a <alias_here>

you probably don’t need to worry about auth.txt if you’re using docker –rm or similar.

Pooq Proxy

This is my current setup for watching Pooq in Australia for anyone interested.
With local proxy, below 4k streaming (UHD) seems alright with NBN via AussieBB.
TPG might have better throughput as they have PPC-1 submarine cable.

Architecture Diagram

Prerequisite
1. Nginx installed host in Korea – AWS LightSail costs USD 3.5/month~
2. DNS dmasquerading – this can be any dns server running from your router or NAS
3. Pooq subscription – Can be paid via Korean credit card or Apple store credit

Dnsmasq Config

address=/live-su.cdn.pooq.co.kr/vod-su.cdn.pooq.co.kr/_put current IP address of the host here to exclude them from the below_
address=/.cdn.pooq.co.kr/_your Nginx host IP here_

Nginx Config

 server {
    server_name apis.pooq.co.kr ;
    listen 80;

    location / {
      proxy_set_header Host $host;
      proxy_pass http://apis.pooq.co.kr;
    }
  }

 server {
    server_name ~^(.*)\.cdn\.wavve\.com$ ;
    listen 80;

    location / {
      proxy_set_header Host $host;
      proxy_pass http://vod-m02.cdn.wavve.com;
    }
  }

 server {
    server_name ~^(.*)\.cdn\.pooq\.co\.kr$ ;
    listen 80;

    location / {
      proxy_set_header Host $host;
      proxy_pass http://vod-m02.cdn.pooq.co.kr;
    }
  }

  server {
    server_name ~^api\.tving\.com$ ;
    listen 80;

    location / {
      proxy_set_header Host $host;
      proxy_pass http://api.tving.com;
    }
  }

  server {
    server_name pip-vod-xcdn.tving.com ;
    listen 80;

    location / {
      proxy_set_header Host $host;
      proxy_pass http://pip-vod-xcdn.tving.com;
    }
  }

}

stream {
  map $ssl_preread_server_name $selected_upstream {
    api.tving.com tving_api;
    apis.pooq.co.kr pooq_api;
     ~^(.*)\.cdn\.pooq\.co\.kr$ pooq_cdn;
    ~^(.*)\.cdn\.wavve\.com$ wavve_cdn;
  }
  upstream tving_api { server api.tving.com:443; }
  upstream pooq_api { server apis.pooq.co.kr:443; }
  upstream pooq_cdn { server vod-m02.cdn.pooq.co.kr:443; }
  upstream wavve_cdn { server vod-m02.cdn.wavve.com:443; }

  server {
    listen 443;
    proxy_pass $selected_upstream;
    ssl_preread on;
  }
}


Note: need libnginx-mod-stream package installed

(Optional) Pooq Local Proxy
https://github.com/logan-han/pooq-proxy
Need to reroute *.cdn.pooq.co.kr traffic to the proxy using DNS server.
Don’t forget to set config.js file to point the nginx host.

Generate custom x509 certificate in Okta

* Requires API key with admin access, least for the target app

Obtain app name & label using app ID

curl -v -X GET \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "Authorization: SSWS ${api_token}" \
"https://[okta_instance].okta.com/api/v1/apps/[app_id]"

Generate custom certificate and capture ‘kid’ value from response

curl -v -X POST \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "Authorization: SSWS ${api_token}" \
-d '{
}' "https://[okta_instance].okta.com/api/v1/apps/[app_id]/credentials/keys/generate?validityYears=[number]"

Inject custom certificate to app

curl -v -X PUT \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "Authorization: SSWS ${api_token}" \
-d '{
  "name": "[app_name]",
  "label": "[app_label]",
  "signOnMode": "SAML_2_0",
  "credentials": {
    "signing": {
      "kid": "[kid]"
    }
  }
 }
}' "https://[okta_instnace].okta.com/api/v1/apps/[app_id]"

Selenium test with docker-compose

In case using selenium standalone during test.

docker-compose YAML

  version: '3'
    services:
      test:
        depends_on:
          - selenium
        environment:
          - E2ETEST_HOST=test
          - SELENIUM_PORT=4444
          - SELENIUM_HOST=selenium
        build:
          context: .
          dockerfile: Dockerfile
        ports:
          - 80
          - 443
        command: run_test.sh
      selenium:
        image: selenium/standalone-chrome
        ports:
          - 4444

nightwatch config sample

"test_settings": {
    "default": {
        "selenium_port"  : parseInt(process.env.SELENIUM_PORT) || 4444,
        "selenium_host"  : process.env.SELENIUM_HOST,
        "silent": true,
        "desiredCapabilities": {
            "browserName": "chrome",
            "javascriptEnabled": true,
            "acceptSslCerts": true,
            "chromeOptions" : {
                "args" : ["--no-sandbox"]
            }
        }
    }

run_test.sh

docker-compose down
docker-compose up --force-recreate --build --abort-on-container-exit --exit-code-from test test