diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 66693e0e2b..7ad818f7bc 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -53,5 +53,12 @@ jobs: - name: Checkout ${{ github.sha }} uses: actions/checkout@v2 + - name: Setup Ruby using Bundler + uses: ruby/setup-ruby@v1 + with: + ruby-version: "2.5.5" + bundler-cache: true + bundler: "1.17.3" + - name: Run E2E tests - run: rake test:e2e + run: bundle exec rake test:e2e diff --git a/.gitignore b/.gitignore index ef2c790241..bc00b0d15e 100644 --- a/.gitignore +++ b/.gitignore @@ -97,3 +97,6 @@ tags /packaging/*.pp /packaging/*.if /packaging/tmp + +# Ignore files downloaded for testing +/tests/*.zip diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000000..605d02fadf --- /dev/null +++ b/Gemfile @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } + +gem "rake" + +group :test do + gem "rspec" + gem "watir" +end diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000000..88f124e781 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,38 @@ +GEM + remote: https://rubygems.org/ + specs: + childprocess (3.0.0) + diff-lcs (1.4.4) + rake (13.0.3) + regexp_parser (1.8.2) + rspec (3.10.0) + rspec-core (~> 3.10.0) + rspec-expectations (~> 3.10.0) + rspec-mocks (~> 3.10.0) + rspec-core (3.10.0) + rspec-support (~> 3.10.0) + rspec-expectations (3.10.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.10.0) + rspec-mocks (3.10.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.10.0) + rspec-support (3.10.0) + rubyzip (2.3.0) + selenium-webdriver (3.142.7) + childprocess (>= 0.5, < 4.0) + rubyzip (>= 1.2.2) + watir (6.17.0) + regexp_parser (~> 1.2) + selenium-webdriver (~> 3.6) + +PLATFORMS + ruby + +DEPENDENCIES + rake + rspec + watir + +BUNDLED WITH + 1.17.3 diff --git a/docker/launch-ood b/docker/launch-ood index 4b7b2c2aa5..d617710b88 100755 --- a/docker/launch-ood +++ b/docker/launch-ood @@ -3,4 +3,4 @@ set -e runuser -u ondemand-dex /usr/sbin/ondemand-dex serve /etc/ood/dex/config.yaml & -/opt/rh/httpd24/root/usr/sbin/httpd-scl-wrapper -DFOREGROUND +/usr/sbin/httpd -DFOREGROUND diff --git a/lib/tasks/container_files/Dockerfile.base b/lib/tasks/container_files/Dockerfile.base index b094b8921a..f002a92a84 100644 --- a/lib/tasks/container_files/Dockerfile.base +++ b/lib/tasks/container_files/Dockerfile.base @@ -24,6 +24,7 @@ RUN dnf -y update && \ ondemand-ruby \ ondemand-nodejs \ ondemand-python \ + ondemand-dex \ ondemand-passenger \ ondemand-nginx && \ dnf clean all && rm -rf /var/cache/dnf/* @@ -32,6 +33,7 @@ RUN mkdir -p /opt/ood RUN mkdir -p /var/www/ood/{apps,public,discover} RUN mkdir -p /var/www/ood/apps/{sys,dev,usr} +COPY docker/launch-ood /opt/ood/launch COPY mod_ood_proxy /opt/ood/mod_ood_proxy COPY nginx_stage /opt/ood/nginx_stage COPY ood-portal-generator /opt/ood/ood-portal-generator @@ -49,6 +51,11 @@ RUN source /opt/rh/ondemand/enable && \ RUN mkdir -p /etc/ood/config RUN cp /opt/ood/nginx_stage/share/nginx_stage_example.yml /etc/ood/config/nginx_stage.yml RUN cp /opt/ood/ood-portal-generator/share/ood_portal_example.yml /etc/ood/config/ood_portal.yml +RUN sed -i -r \ + -e 's/^#listen_addr_port:.*/listen_addr_port: 8080/g' \ + -e 's/^#port:.*/port: 8080/g' \ + -e 's/^#servername:.*/servername: localhost/g' \ + /etc/ood/config/ood_portal.yml # make some misc directories & files RUN mkdir -p /var/lib/ondemand-nginx/config/apps/{sys,dev,usr} @@ -66,5 +73,6 @@ RUN echo $VERSION > /opt/ood/VERSION # this one bc centos:8 doesn't generate localhost cert RUN /usr/libexec/httpd-ssl-gencerts -EXPOSE 80 -CMD [ "/usr/sbin/httpd", "-DFOREGROUND" ] +EXPOSE 8080 +EXPOSE 5556 +CMD [ "/opt/ood/launch" ] diff --git a/lib/tasks/container_files/Dockerfile.test b/lib/tasks/container_files/Dockerfile.test index 86aa43afa2..fca220f599 100644 --- a/lib/tasks/container_files/Dockerfile.test +++ b/lib/tasks/container_files/Dockerfile.test @@ -4,13 +4,8 @@ ARG USER=ood ARG UID=1000 ARG GID=1000 -RUN dnf reinstall -y httpd-tools - RUN groupadd -g $GID $USER && \ - useradd -u $UID --create-home --gid $USER $USER && \ - /usr/bin/htpasswd -b -c /etc/httpd/.htpasswd $USER $USER - -COPY lib/tasks/container_files/test/entrypoint.sh /entrypoint.sh + useradd -u $UID --create-home --gid $USER $USER -CMD [ "/entrypoint.sh" ] +CMD [ "/opt/ood/launch" ] diff --git a/lib/tasks/container_files/test/entrypoint.sh b/lib/tasks/container_files/test/entrypoint.sh deleted file mode 100755 index ce36d35a71..0000000000 --- a/lib/tasks/container_files/test/entrypoint.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -set -x - -/opt/ood/ood-portal-generator/sbin/update_ood_portal - -/usr/sbin/httpd -DFOREGROUND \ No newline at end of file diff --git a/lib/tasks/container_files/test/ood_portal.yml b/lib/tasks/container_files/test/ood_portal.yml deleted file mode 100644 index 8e546f7879..0000000000 --- a/lib/tasks/container_files/test/ood_portal.yml +++ /dev/null @@ -1,380 +0,0 @@ ---- -# -# Portal configuration -# - -# The address and port to listen for connections on -# Example: -# listen_addr_port: 443 -# Default: null (don't add any more listen directives) -#listen_addr_port: null - -# The server name used for name-based Virtual Host -# Example: -# servername: 'www.example.com' -# Default: null (don't use name-based Virtual Host) -#servername: null - -# The server name used for rewrites -# Example: -# proxy_server: 'proxy.example.com' -# Default: The value of servername -#proxy_server: null - -# The port specification for the Virtual Host -# Example: -# port: 8080 -#Default: null (use default port 80 or 443 if SSL enabled) -#port: null - -# List of SSL Apache directives -# Example: -# ssl: -# - 'SSLCertificateFile "/etc/pki/tls/certs/www.example.com.crt"' -# - 'SSLCertificateKeyFile "/etc/pki/tls/private/www.example.com.key"' -# Default: null (no SSL support) -#ssl: null - -# Root directory of log files (can be relative ServerRoot) -# Example: -# logroot: '/path/to/my/logs' -# Default: 'logs' (this is relative to ServerRoot) -#logroot: 'logs' - -# Should RewriteEngine be used -# Example: -# use_rewrites: false -# Default: true -#use_rewrites: true - -# Should Maintenance Rewrite rules be added -# Example: -# use_maintenance: false -# Default: true -#use_maintenance: true - -# List of IPs to whitelist when maintenance is enabled -# Example: -# maintenance_ip_whitelist: -# - 192.168.0..* -# - 192.168.1..* -# Default: [] (no IPs whitelisted) -#maintenance_ip_whitelist: [] - -# Root directory of the Lua handler code -# Example: -# lua_root: '/path/to/lua/handlers' -# Default : '/opt/ood/mod_ood_proxy/lib' (default install directory of mod_ood_proxy) -#lua_root: '/opt/ood/mod_ood_proxy/lib' - -# Verbosity of the Lua module logging -# (see https://httpd.apache.org/docs/2.4/mod/core.html#loglevel) -# Example: -# lua_log_level: 'warn' -# Default: 'info' (get verbose logs) -#lua_log_level: 'info' - -# System command used to map authenticated-user to system-user -# Example: -# user_map_cmd: '/opt/ood/ood_auth_map/bin/ood_auth_map.regex --regex=''^(\w+)@example.com$''' -# Default: '/opt/ood/ood_auth_map/bin/ood_auth_map.regex' (this echo's back auth-user) -#user_map_cmd: '/opt/ood/ood_auth_map/bin/ood_auth_map.regex' - -# Use an alternative CGI environment variable instead of REMOTE_USER for -# determining the authenticated-user fed to the mapping script -# Example: -# user_env: 'OIDC_CLAIM_preferred_username' -# Default: null (use REMOTE_USER) -#user_env: null - -# Redirect user to the following URI if fail to map there authenticated-user to -# a system-user -# Example: -# map_fail_uri: '/register' -# Default: null (don't redirect, just display error message) -#map_fail_uri: null - -# System command used to run the `nginx_stage` script with sudo privileges -# Example: -# pun_stage_cmd: 'sudo /path/to/nginx_stage' -# Default: 'sudo /opt/ood/nginx_stage/sbin/nginx_stage' (don't forget sudo) -#pun_stage_cmd: 'sudo /opt/ood/nginx_stage/sbin/nginx_stage' - -# List of Apache authentication directives -# NB: Be sure the appropriate Apache module is installed for this -# Default: (see below, uses OIDC auth with Dex) -auth: - - AuthType Basic - - AuthName "Private" - - AuthUserFile "/etc/httpd/.htpasswd" - - Require valid-user - -# Redirect user to the following URI when accessing root URI -# Example: -# root_uri: '/my_uri' -# # https://www.example.com/ => https://www.example.com/my_uri -# Default: '/pun/sys/dashboard' (default location of the OOD Dashboard app) -#root_uri: '/pun/sys/dashboard' - -# Track server-side analytics with a Google Analytics account and property -# (see https://github.com/OSC/mod_ood_proxy/blob/master/lib/analytics.lua for -# information on how to setup the GA property) -# Example: -# analytics: -# url: 'http://www.google-analytics.com/collect' -# id: 'UA-79331310-4' -# Default: null (do not track) -#analytics: null - -# -# Publicly available assets -# - -# Public sub-uri (available to public with no authentication) -# Example: -# public_uri: '/assets' -# Default: '/public' -#public_uri: '/public' - -# Root directory that serves the public sub-uri (be careful, everything under -# here is open to the public) -# Example: -# public_root: '/path/to/public/assets' -# Default: '/var/www/ood/public' -#public_root: '/var/www/ood/public' - -# -# Logout redirect helper -# - -# Logout sub-uri -# Example -# logout_uri: '/log_me_out' -# NB: If you change this, then modify the Dashboard app with the new sub-uri -# Default: '/logout' (the Dashboard app is by default going to expect this) -#logout_uri: '/logout' - -# Redirect user to the following URI when accessing logout URI -# Example: -# logout_redirect: '/oidc?logout=https%3A%2F%2Fwww.example.com' -# Default: '/pun/sys/dashboard/logout' (the Dashboard app provides a simple -# HTML page explaining logout to the user) -#logout_redirect: '/pun/sys/dashboard/logout' - -# -# Reverse proxy to backend nodes -# - -# Regular expression used for whitelisting allowed hostnames of nodes -# Example: -# host_regex: '[\w.-]+\.example\.com' -# Default: '[^/]+' (allow reverse proxying to all hosts, this allows external -# hosts as well) -#host_regex: '[^/]+' - -# Sub-uri used to reverse proxy to backend web server running on node that -# knows the full URI path -# Example: -# node_uri: '/node' -# Default: null (disable this feature) -#node_uri: null - -# Sub-uri used to reverse proxy to backend web server running on node that -# ONLY uses *relative* URI paths -# Example: -# rnode_uri: '/rnode' -# Default: null (disable this feature) -#rnode_uri: null - -# -# Per-user NGINX Passenger apps -# - -# Sub-uri used to control PUN processes -# Example: -# nginx_uri: '/my_pun_controller' -# Default: '/nginx' -#nginx_uri: '/nginx' - -# Sub-uri used to access the PUN processes -# Example: -# pun_uri: '/my_pun_apps' -# Default: '/pun' -#pun_uri: '/pun' - -# Root directory that contains the PUN Unix sockets that the proxy uses to -# connect to -# Example: -# pun_socket_root: '/path/to/pun/sockets' -# Default: '/var/run/ondemand-nginx' (default location set in nginx_stage) -#pun_socket_root: '/var/run/ondemand-nginx' - -# Number of times the proxy attempts to connect to the PUN Unix socket before -# giving up and displaying an error to the user -# Example: -# pun_max_retries: 25 -# Default: 5 (only try 5 times) -#pun_max_retries: 5 - -# -# Support for OpenID Connect -# - -# Sub-uri used by mod_auth_openidc for authentication -# Example: -# oidc_uri: '/oidc' -# Default: null (disable OpenID Connect support) -#oidc_uri: null - -# Sub-uri user is redirected to if they are not authenticated. This is used to -# *discover* what ID provider the user will login through. -# Example: -# oidc_discover_uri: '/discover' -# Default: null (disable support for discovering OpenID Connect IdP) -#oidc_discover_uri: null - -# Root directory on the filesystem that serves the HTML code used to display -# the discovery page -# Example: -# oidc_discover_root: '/var/www/ood/discover' -# Default: null (disable support for discovering OpenID Connect IdP) -#oidc_discover_root: null - -# -# Support for registering unmapped users -# -# (Not necessary if using regular expressions for mapping users) -# - -# Sub-uri user is redirected to if unable to map authenticated-user to -# system-user -# Example: -# register_uri: '/register' -# Default: null (display error to user if mapping fails) -#register_uri: null - -# Root directory on the filesystem that serves the HTML code used to register -# an unmapped user -# Example: -# register_root: '/var/www/ood/register' -# Default: null (display error to user if mapping fails) -#register_root: null - -# OIDC metadata URL -# Example: -# oidc_provider_metadata_url: https://example.com:5554/.well-known/openid-configuration -# Default: null (value auto-generated if using Dex) -#oidc_provider_metadata_url: null - -# OIDC client ID -# Example: -# oidc_client_id: ondemand.example.com -# Default: null (value auto-generated if using Dex) -#oidc_client_id: null - -# OIDC client secret -# Example: -# oidc_client_secret: 334389048b872a533002b34d73f8c29fd09efc50 -# Default: null (value auto-generated if using Dex) -#oidc_client_secret: null - -# OIDC remote user claim. This is the claim that populates REMOTE_USER -# Example: -# oidc_remote_user_claim: preferred_username -# Default: preferred_username -#oidc_remote_user_claim: preferred_username - -# OIDC scopes -# Example: -# oidc_scope: "openid profile email groups" -# Default: "openid profile email" -#oidc_scope: "openid profile email" - -# OIDC session inactivity timeout -# Example: -# oidc_session_inactivity_timeout: 28800 -# Default: 28800 -#oidc_session_inactivity_timeout: 28800 - -# OIDC session max duration -# Example: -# oidc_session_max_duration: 28800 -# Default: 28800 -#oidc_session_max_duration: 28800 - -# OIDC max number of state cookies and if to automatically clean old cookies -# Example: -# oidc_state_max_number_of_cookies: "10 true" -# Default: "10 true" -#oidc_state_max_number_of_cookies: "10 true" - -# OIDC Enable SameSite cookie -# When ssl is defined this defaults to 'Off' -# When ssl is not defined this defaults to 'On' -# Example: -# oidc_cookie_same_site: 'Off' -# Default: 'On' -#oidc_cookie_same_site: 'On' - -# Additional OIDC settings as key-value pairs -# Example: -# oidc_settings: -# OIDCPassIDTokenAs: serialized -# OIDCPassRefreshToken: On -# Default: {} (empty hash) - -# Dex configurations, values inside the "dex" structure are directly used to configure Dex -# If the value for "dex" key is false or null, Dex support is disabled -# Dex support will auto-enable if ondemand-dex package is installed -#dex: - # Default based on if ssl key for ood-portal-generator is defined -# ssl: false - # Only used if SSL is disabled -# http_port: "5556" - # Only used if SSL is enabled -# https_port: "5554" - # tls_cert and tls_key take OnDemand configured values for ssl and copy keys to /etc/ood/dex maintaining file names -# tls_cert: null -# tls_key: null -# storage_file: /etc/ood/dex/dex.db -# grpc: null -# expiry: null - # Client ID, defaults to servername or FQDN -# client_id: null -# client_name: OnDemand - # Client secret, value auto generated - # A value that is a filesystem path can be used to store secret in a file -# client_secret: /etc/ood/dex/ondemand.secret - # The OnDemand redirectURI is auto-generated, this option allows adding additional URIs -# client_redirect_uris: [] - # Additional Dex OIDC clients to configure -# static_clients: [] - # The following example is to configure OpenLDAP - # Docs: https://github.com/dexidp/dex/blob/master/Documentation/connectors/ldap.md -# connectors: -# - type: ldap -# id: ldap -# name: LDAP -# config: -# host: openldap.my_center.edu:636 -# insecureSkipVerify: false -# bindDN: cn=admin,dc=example,dc=org -# bindPW: admin -# userSearch: -# baseDN: ou=People,dc=example,dc=org -# filter: "(objectClass=posixAccount)" -# username: uid -# idAttr: uid -# emailAttr: mail -# nameAttr: gecos -# preferredUsernameAttr: uid -# groupSearch: -# baseDN: ou=Groups,dc=example,dc=org -# filter: "(objectClass=posixGroup)" -# userMatchers: -# - userAttr: DN -# groupAttr: member -# nameAttr: cn -# frontend: -# theme: ondemand -# dir: /usr/share/ondemand-dex/web diff --git a/lib/tasks/test.rb b/lib/tasks/test.rb index 990d10377f..963e3d32a0 100644 --- a/lib/tasks/test.rb +++ b/lib/tasks/test.rb @@ -39,8 +39,34 @@ sh "shellcheck nginx_stage/sbin/update_nginx_stage" end + begin + require "rspec/core/rake_task" + RSpec::Core::RakeTask.new(:e2e_spec) do |task| + task.pattern = "#{PROJ_DIR.join('spec', 'e2e')}/*_spec.rb" + task.rspec_opts = ['--format documentation'] + end + rescue LoadError + end + + desc "Get chromedriver" + task :chromedriver, [:version] do |t, args| + version = args[:version] || '87.0.4280.88' + uname = `uname -s` + case uname.chomp + when 'Darwin' + file = 'chromedriver_mac64.zip' + when 'Linux' + file = 'chromedriver_linux64.zip' + end + url = "https://chromedriver.storage.googleapis.com/#{version}/#{file}" + sh "curl -o tests/#{file} #{url}" + chdir PROJ_DIR.join("tests") do + sh "unzip -o #{file}" + end + end + def start_test_container - args = [ container_runtime, "run", "--name", test_image_name, "--detach", "--rm", "-p", "8080:80"] + args = [ container_runtime, "run", "--name", test_image_name, "--detach", "--rm", "-p", "8080:8080", "-p", "5556:5556"] args.concat mount_args args.concat rt_specific_flags args.concat ["#{test_image_name}:latest"] @@ -52,13 +78,8 @@ def stop_test_container sh "#{container_runtime} stop #{test_image_name}" end - def e2e_test - sh "sleep 2" - sh "curl -L -vv -f -u ood:ood localhost:8080" - end - def mount_args - ["-v", "#{TASK_DIR}/container_files/test/ood_portal.yml:/etc/ood/config/ood_portal.yml:ro"] + [] end def rt_specific_flags @@ -72,8 +93,12 @@ def rt_specific_flags desc "Run end to end tests" task :e2e => ["package:test_container"] do start_test_container - e2e_test + ENV['PATH'] = PROJ_DIR.join('tests').to_s + ":#{ENV['PATH']}" + Rake::Task['test:e2e_spec'].invoke stop_test_container + rescue SystemExit => e + stop_test_container + raise e rescue => e stop_test_container raise e diff --git a/spec/e2e/browser_spec.rb b/spec/e2e/browser_spec.rb new file mode 100644 index 0000000000..349f4dad12 --- /dev/null +++ b/spec/e2e/browser_spec.rb @@ -0,0 +1,15 @@ +require 'watir' + +describe 'OnDemand browser test' do + let(:browser) { Watir::Browser.new :chrome, headless: true, options: {args: ['--disable-dev-shm-usage']} } + after(:each) { browser.close } + + it 'successfully logs into OnDemand' do + browser.goto "http://localhost:8080" + browser.text_field(id: 'username').set "ood@localhost" + browser.text_field(id: 'password').set "password" + browser.button(id: 'submit-login').click + + expect(browser.title).to eq('Dashboard - Open OnDemand') + end +end diff --git a/tests/.keep b/tests/.keep new file mode 100644 index 0000000000..e69de29bb2