Repository: cyberark/cyberark-conjur-cli (language: python)

Issue:
Release automation - Run integration tests on RHEL 8 (Milestone 2)
We would like to automate our release pipeline to speed up our release process for agents that are available to us RHEL 8

### Milestone 2
- [ ] Use two agents RHEL 8 and deploy two containers 1. to pack the CLI and run the integration tests 2. Conjur server


Discussion / hints:

Fix patch:
diff --git a/CentOS-Linux-AppStream.repo b/CentOS-Linux-AppStream.repo
new file mode 100644
index 00000000..681401fe
--- /dev/null
+++ b/CentOS-Linux-AppStream.repo
@@ -0,0 +1,11 @@
+# CentOS-Linux-AppStream.repo
+# This file enables installation of gnome-keyring and dbus-x11 on
+# RHEL8 test container since RHEL base image does not contain the repos of these RPMs
+#
+
+[appstream]
+name=CentOS Linux $releasever - AppStream
+mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=AppStream&infra=$infra
+#baseurl=http://mirror.centos.org/$contentdir/$releasever/AppStream/$basearch/os/
+gpgcheck=0
+enabled=1
\ No newline at end of file
diff --git a/Jenkinsfile b/Jenkinsfile
index ef0dd40b..cc340c6b 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -49,6 +49,18 @@ pipeline {
         }
       }
     }
+
+    stage('RHEL8 Integration tests') {
+      steps {
+        sh './bin/test_integration_rhel8'
+      }
+
+      post {
+        always {
+          junit 'output/**/*.xml'
+        }
+      }
+    }
     // Only publish if the HEAD is tagged with the same version as in __version__.py
     stage('Publish') {
       parallel {
diff --git a/docker-compose.yml b/docker-compose.yml
index 79889ef6..bf2db401 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -78,3 +78,36 @@ services:
       - conjur-https
     volumes:
       - ${PWD}:/opt/conjur-api-python3
+
+  conjur-appliance:
+    image: registry2.itci.conjur.net/conjur-appliance:5.0-stable
+    ports:
+      - "444:443"
+    expose:
+      - "443"
+      - "444"
+      - "1999"
+      - "5432"
+    security_opt:
+        - "seccomp:unconfined"
+
+  test-rhel8:
+    privileged: true
+    build:
+      context: .
+      dockerfile: Dockerfile.test.rhel8
+    command: ['sleep', '999d']
+    environment:
+      CONJUR_AUTHN_API_KEY: Null
+      PYTHON_KEYRING_BACKEND: Null
+      DEBUG: Null
+      TEST_ENV: "True"
+      SERVER_MODE: Null
+      LOGIN: Null
+      ADMIN_PASSWORD: Null
+      TEST_HOSTNAME: Null
+      ACCOUNT: Null
+    depends_on:
+      - conjur-appliance
+    volumes:
+      - ${PWD}:/opt/conjur-api-python3


Test patch:
diff --git a/Dockerfile.test b/Dockerfile.test
index e2b0b67b..f01b2b04 100644
--- a/Dockerfile.test
+++ b/Dockerfile.test
@@ -23,6 +23,6 @@ RUN pip3 install -r requirements.txt
 
 COPY . $INSTALL_DIR
 
-COPY ./test/configure_dbus.sh /configure_dbus.sh
+COPY ./test/configure_test_executor.sh /configure_test_executor.sh
 
-ENTRYPOINT ["./test/configure_dbus.sh"]
+ENTRYPOINT ["./test/configure_test_executor.sh"]
diff --git a/Dockerfile.test.rhel8 b/Dockerfile.test.rhel8
new file mode 100644
index 00000000..2e63d4f8
--- /dev/null
+++ b/Dockerfile.test.rhel8
@@ -0,0 +1,38 @@
+FROM registry.access.redhat.com/ubi8/ubi:8.3
+
+ENV INSTALL_DIR=/opt/conjur-api-python3
+
+# Copy below repo file to enable installation of gnome-keyring and dbus-x11
+COPY ./CentOS-Linux-AppStream.repo /etc/yum.repos.d/CentOS-Linux-AppStream.repo
+
+RUN yum --disableplugin=subscription-manager -y \
+                                install -y  bash \
+                                            binutils \
+                                            yum-utils \
+                                            gcc gcc-c++ make \
+                                            curl \
+                                            git \
+                                            jq \
+                                            libffi-devel \
+                                            python3 \
+                                            python3-devel \
+                                            python3-pip \
+                                            gnome-keyring \
+                                            dbus-x11 \
+                                            procps \
+         && yum --disableplugin=subscription-manager clean all
+
+RUN mkdir -p $INSTALL_DIR
+WORKDIR $INSTALL_DIR
+
+COPY ./requirements.txt $INSTALL_DIR/
+RUN pip3 install -r requirements.txt
+
+COPY ./bin/build_integrations_tests_runner /build_integrations_tests_runner
+
+COPY . $INSTALL_DIR
+
+COPY ./test/configure_test_executor.sh /configure_test_executor.sh
+
+ENTRYPOINT ["./test/configure_test_executor.sh"]
+
diff --git a/bin/build_integrations_tests_runner b/bin/build_integrations_tests_runner
new file mode 100755
index 00000000..f846dc40
--- /dev/null
+++ b/bin/build_integrations_tests_runner
@@ -0,0 +1,15 @@
+#!/bin/bash -e
+
+function _build_test_integration_exec() {
+  echo "Dropping into virtual environment..."
+  python3 -m venv venv
+  source venv/bin/activate
+
+  echo "Installing requirements for tests..."
+  pip3 install -r requirements.txt
+
+  pyinstaller -F /opt/conjur-api-python3/test/util/test_runners/integrations_tests_runner.py
+  deactivate
+}
+
+_build_test_integration_exec
diff --git a/bin/test_integration b/bin/test_integration
index 20e34e7c..9bebccb2 100755
--- a/bin/test_integration
+++ b/bin/test_integration
@@ -17,7 +17,7 @@ trap 'cleanup' EXIT
 cleanup
 
 echo "Building API container..."
-docker-compose build
+docker-compose build test
 
 echo "Generating certificate..."
 docker-compose up openssl
@@ -32,6 +32,7 @@ export CONJUR_AUTHN_API_KEY=$admin_api_key
 export DEBUG=$DEBUG
 conjur_host_port=$(docker-compose port conjur 80)
 conjur_port="${conjur_host_port##*:}"
+export TEST_HOSTNAME=conjur-https
 
 # Docker-compose will pick this file up when starting containers
 # so we will have these variables in the container
@@ -48,7 +49,7 @@ rm -rf $CURRENT_DIR/output/*
 # Hint: '--privileged' is added here otherwise we will get operation
 # not permitted when attempting to do anything related to dbus
 if [[ "$DEBUG" == "true" ]]; then
-  docker-compose exec --privileged test /bin/sh -c " /dbus.sh" $@
+  docker-compose exec --privileged test /bin/sh -c " /tests_executor.sh" $@
   exit
 fi
 
@@ -56,4 +57,4 @@ docker-compose run \
   --rm \
   --no-deps \
   test \
-  /bin/sh -c " /dbus.sh" $@
+  /bin/sh -c " /tests_executor.sh" $@
diff --git a/bin/test_integration_rhel8 b/bin/test_integration_rhel8
new file mode 100755
index 00000000..62e80e99
--- /dev/null
+++ b/bin/test_integration_rhel8
@@ -0,0 +1,89 @@
+#!/bin/bash -ex
+
+cleanup() {
+  echo "Cleaning up..."
+  echo "Cleaning up... logs in cleanup.log"
+  docker-compose rm --stop --force
+}
+
+DEBUG="false"
+if [[ "$1" = "--debug" || "$1" = "-d" ]]; then
+  DEBUG="true"
+  shift
+fi
+
+trap 'echo "ERROR: Test script encountered an error!"; docker-compose logs &> cleanup.log; cleanup' ERR
+trap 'cleanup' EXIT
+cleanup
+
+echo "Building API container..."
+docker-compose build test-rhel8
+
+echo "Generating certificate..."
+docker-compose up openssl
+
+echo "Starting Conjur..."
+docker-compose up -d conjur-appliance
+
+function _random_password() {
+  { </dev/urandom LC_ALL=C grep -ao '[A-Za-z0-9]' \
+        | head -n$((RANDOM % 12 + 9))
+    echo "@1"   # add a special char and digit.
+  } \
+    | shuf \
+    | tr -d '\n'
+}
+
+echo "Configuring Conjur..."
+export ADMIN_PASSWORD=$(_random_password)
+export DEBUG=$DEBUG
+export CONJUR_AUTHN_API_KEY=$ADMIN_PASSWORD
+export SERVER_MODE="appliance"
+export ACCOUNT="dev"
+export LOGIN="admin"
+export TEST_HOSTNAME=conjur-appliance
+
+# Docker-compose will pick this file up when starting containers
+# so we will have these variables in the container
+cat <<ENV > .env
+CONJUR_AUTHN_API_KEY=$ADMIN_PASSWORD
+DEBUG=$DEBUG
+SERVER_MODE=$SERVER_MODE
+TEST_HOSTNAME=$TEST_HOSTNAME
+ACCOUNT=$ACCOUNT
+LOGIN=$LOGIN
+ADMIN_PASSWORD=$ADMIN_PASSWORD
+ENV
+
+function _configure_conjur() {
+  _cmd="evoke configure master"
+  _cmd="$_cmd --accept-eula"
+  _cmd="$_cmd --hostname $TEST_HOSTNAME"
+  _cmd="$_cmd --master-altnames $TEST_HOSTNAME"
+  _cmd="$_cmd --admin-password $ADMIN_PASSWORD"
+  _cmd="$_cmd dev"
+
+  docker-compose exec -T conjur-appliance \
+    bash -c "$_cmd"
+}
+
+echo "Configure Conjur..."
+_configure_conjur
+
+echo "Starting test env..."
+docker-compose up -d test-rhel8
+
+rm -rf "$CURRENT_DIR"/output/*
+
+# Hint: '--privileged' is added here otherwise we will get operation
+# not permitted when attempting to do anything related to dbus
+if [[ "$DEBUG" == "true" ]]; then
+  docker-compose exec --privileged test-rhel8 /bin/sh -c " /tests_executor.sh" "$@"
+  exit
+fi
+
+docker-compose run \
+  --rm \
+  --no-deps \
+  test-rhel8 \
+  /bin/sh -c " /tests_executor.sh" "$@"
diff --git a/test/configure_dbus.sh b/test/configure_dbus.sh
deleted file mode 100755
index d5a1488e..00000000
--- a/test/configure_dbus.sh
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/bash
-
-echo "Configuring D-bus environment"
-echo 'eval "$(dbus-launch --sh-syntax)"' > /dbus.sh
-# Create D-bus keyring directories
-mkdir -p ~/.cache
-mkdir -p ~/.local/share/keyrings
-# Unlock D-bus with empty password
-echo 'ps -ef | grep dbus | grep -v grep' >> /dbus.sh
-echo 'eval "$(echo | gnome-keyring-daemon --unlock)"' >> /dbus.sh
-# Export D-bus variables to working shell to support keyring
-echo "export DBUS_SESSION_BUS_ADDRESS" >> /dbus.sh
-echo "export GNOME_KEYRING_CONTROL" >> /dbus.sh
-echo "export SSH_AUTH_SOCK" >> /dbus.sh
-
-if [[ "$DEBUG" == "true" ]]; then
-  echo "bash" >> /dbus.sh
-else
-  echo "bash -c \"nose2 -v -X --config integration_test.cfg -A 'integration'\"" >> /dbus.sh
-fi
-
-chmod 755 /dbus.sh
-
-# shellcheck disable=SC2068
-exec $@
diff --git a/test/configure_test_executor.sh b/test/configure_test_executor.sh
new file mode 100755
index 00000000..ca8bc107
--- /dev/null
+++ b/test/configure_test_executor.sh
@@ -0,0 +1,62 @@
+#!/bin/bash
+
+###########################################################
+# Docker image entry point script                         #
+# Generates the script that configures DBUS and runs the  #
+# tests according the SERVER_MODE (appliance/oss)         #
+###########################################################
+
+script_file=/tests_executor.sh
+
+mkdir -p ~/.cache
+mkdir -p ~/.local/share/keyrings
+
+# Write the tests runner script that configures DBUS and runs
+# the tests.
+cat > $script_file <<'endmsg'
+#!/bin/bash -ex
+
+eval "$(dbus-launch --sh-syntax)"
+ps -ef | grep dbus | grep -v grep
+eval "$(echo | gnome-keyring-daemon --unlock)"
+export DBUS_SESSION_BUS_ADDRESS
+export GNOME_KEYRING_CONTROL
+export SSH_AUTH_SOCK
+
+endmsg
+
+# When the tests are running against appliance the
+# tests are running using test integration executable.
+# The function builds the test integration executable command
+function _tests_runner_cmd() {
+  local _cmd="./dist/integrations_tests_runner"
+    _cmd="$_cmd --url https://$TEST_HOSTNAME"
+    _cmd="$_cmd --account $ACCOUNT"
+    _cmd="$_cmd --login $LOGIN"
+    _cmd="$_cmd --password $ADMIN_PASSWORD"
+    _cmd="$_cmd --files-folder /opt/conjur-api-python3/test"
+  echo "$_cmd"
+}
+
+# Appends the 1st argumnet to the script file
+function append_to_file() {
+  echo "$1" >> $script_file
+}
+
+if [ "$DEBUG" == "true" ]; then
+  echo "bash" >> $script_file
+elif [ "$SERVER_MODE" == "appliance" ]; then
+    echo "Running testing against $SERVER_MODE. Building the test integration executable..."
+    # Add tests runner executable command
+    source /build_integrations_tests_runner
+    append_to_file "$(_tests_runner_cmd)"
+else
+  # Server mode is Conjur OSS
+  # Add nose2 command
+  append_to_file "bash -c \"nose2 -v -X --config integration_test.cfg -A 'integration'\""
+fi
+
+chmod 755 $script_file
+
+# exec "$@" is typically used to make the entrypoint a pass through that then runs the docker command
+exec $@
diff --git a/test/util/test_runners/integration_test_case.py b/test/util/test_runners/integration_test_case.py
index 9f25b807..cd758db1 100644
--- a/test/util/test_runners/integration_test_case.py
+++ b/test/util/test_runners/integration_test_case.py
@@ -75,6 +75,7 @@ def invoke_cli_as_code(test_runner, *args, exit_code=0):
         with redirect_stdout(capture_stream):
             with patch.object(sys, 'argv', ["cli"] + cli_args):
                 Cli().run()
+
     test_runner.assertEqual(sys_exit.exception.code, exit_code,
                             f"ERROR: CLI returned an unexpected error status code: '{cli_args}'. Output: {capture_stream.getvalue()}")
     return capture_stream.getvalue()
diff --git a/test/util/test_runners/integrations_tests_runner.py b/test/util/test_runners/integrations_tests_runner.py
index f3025862..3c7c59ef 100644
--- a/test/util/test_runners/integrations_tests_runner.py
+++ b/test/util/test_runners/integrations_tests_runner.py
@@ -37,7 +37,7 @@
 # run the integration tests as a process. Otherwise this dependency will not be available
 # once the test module is packed
 import uuid
-
+import ssl
 # Internals
 from test.test_integration_policy import CliIntegrationPolicy
 from test.test_integration_variable import CliIntegrationTestVariable