diff --git a/Gemfile b/Gemfile index 330c68f777..49afa3b282 100644 --- a/Gemfile +++ b/Gemfile @@ -90,6 +90,7 @@ group :development, :test do gem 'faye-websocket' gem 'net-ssh' gem 'parallel' + gem 'parallel_tests' gem 'pry-byebug' gem 'pry-rails' gem 'rails-controller-testing' diff --git a/Gemfile.lock b/Gemfile.lock index 4eab22f583..b7dafe5d06 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -309,6 +309,8 @@ GEM validate_url webfinger (>= 1.0.1) parallel (1.21.0) + parallel_tests (4.2.0) + parallel parser (3.0.3.2) ast (~> 2.4.1) pg (1.2.3) @@ -536,6 +538,7 @@ DEPENDENCIES nokogiri (>= 1.8.2) openid_connect parallel + parallel_tests pg pry-byebug pry-rails diff --git a/Jenkinsfile b/Jenkinsfile index cef3cd441e..e441466409 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -77,6 +77,10 @@ if (params.MODE == "PROMOTE") { return } +// Break the total number of tests into a subset of tests. +// This will give 3 nested lists of tests to run, which is +// distributed over 3 jenkins agents. +def NESTED_ARRAY_OF_TESTS_TO_RUN = collateTests() pipeline { agent { label 'executor-v2' } @@ -277,98 +281,282 @@ pipeline { when { expression { params.NIGHTLY } } + agent { label 'executor-v2-rhel-ee' } environment { CUCUMBER_FILTER_TAGS = "${params.CUCUMBER_FILTER_TAGS}" } stages { - stage('EE FIPS agent tests') { - agent { label 'executor-v2-rhel-ee' } + stage("RSpec - EE FIPS agent tests") { steps { sh(script: 'cat /etc/os-release', label: 'RHEL version') sh(script: 'docker --version', label: 'Docker version') + addNewImagesToAgent() unstash 'version_info' // Catch errors so remaining steps always run. catchError { // Run outside parallel block to avoid external pressure - script { - stage("RSpec - EE FIPS agent tests") { - sh "ci/test rspec" + sh "ci/test rspec" + } + } + } + + stage('EE FIPS parallel') { + parallel { + stage('EE FIPS agent tests') { + when { + expression { + testShouldRunOnAgent( + params.RUN_ONLY, + runSpecificTestOnAgent(params.RUN_ONLY, NESTED_ARRAY_OF_TESTS_TO_RUN[0]) + ) } } - runConjurTests(params.RUN_ONLY) + steps { + addNewImagesToAgent() + unstash 'version_info' + runConjurTests( + params.RUN_ONLY, + NESTED_ARRAY_OF_TESTS_TO_RUN[0] + ) + stash( + name: 'testResultEE', + includes: ''' + cucumber/*/*.*, + container_logs/*/*, + spec/reports/*.xml, + spec/reports-audit/*.xml, + gems/conjur-rack/spec/reports/*.xml, + cucumber/*/features/reports/**/*.xml + ''' + ) + } } + // Run a subset of tests on a second agent to prevent oversubscribing the hardware + stage('EE FIPS agent2 tests') { + when { + expression { + testShouldRunOnAgent( + params.RUN_ONLY, + runSpecificTestOnAgent(params.RUN_ONLY, NESTED_ARRAY_OF_TESTS_TO_RUN[1]) + ) + } + } + agent { label 'executor-v2-rhel-ee' } - stash( - name: 'testResultEE', - includes: ''' - cucumber/*/*.*, - container_logs/*/*, - spec/reports/*.xml, - spec/reports-audit/*.xml, - gems/conjur-rack/spec/reports/*.xml, - cucumber/*/features/reports/**/*.xml - ''' - ) - } + environment { + CUCUMBER_FILTER_TAGS = "${params.CUCUMBER_FILTER_TAGS}" + } - post { - always { - dir('ee-test'){ - unstash 'testResultEE' + steps { + addNewImagesToAgent() + unstash 'version_info' + runConjurTests( + params.RUN_ONLY, + NESTED_ARRAY_OF_TESTS_TO_RUN[1] + ) + stash( + name: 'testResultEE2', + includes: ''' + cucumber/*/*.*, + container_logs/*/*, + spec/reports/*.xml, + spec/reports-audit/*.xml, + cucumber/*/features/reports/**/*.xml + ''' + ) + } + } + // Run a subset of tests on a second agent to prevent oversubscribing the hardware + stage('EE FIPS agent3 tests') { + when { + expression { + testShouldRunOnAgent( + params.RUN_ONLY, + runSpecificTestOnAgent(params.RUN_ONLY, NESTED_ARRAY_OF_TESTS_TO_RUN[2]) + ) + } } - archiveArtifacts( - artifacts: "ee-test/cucumber/*/*.*", - fingerprint: false, - allowEmptyArchive: true - ) + agent { label 'executor-v2-rhel-ee' } - archiveArtifacts( - artifacts: "ee-test/container_logs/*/*", - fingerprint: false, - allowEmptyArchive: true - ) + environment { + CUCUMBER_FILTER_TAGS = "${params.CUCUMBER_FILTER_TAGS}" + } - publishHTML( - reportDir: 'ee-test/cucumber', - reportFiles: ''' - api/cucumber_results.html, - authenticators_config/cucumber_results.html, - authenticators_azure/cucumber_results.html, - authenticators_ldap/cucumber_results.html, - authenticators_oidc/cucumber_results.html, - authenticators_jwt/cucumber_results.html, - authenticators_status/cucumber_results.html - policy/cucumber_results.html, - rotators/cucumber_results.html - ''', - reportName: 'EE Integration reports', - reportTitles: '', - allowMissing: false, - alwaysLinkToLastBuild: true, - keepAll: true - ) + steps { + addNewImagesToAgent() + unstash 'version_info' + runConjurTests( + params.RUN_ONLY, + NESTED_ARRAY_OF_TESTS_TO_RUN[2] + ) + stash( + name: 'testResultEE3', + includes: ''' + cucumber/*/*.*, + container_logs/*/*, + spec/reports/*.xml, + spec/reports-audit/*.xml, + cucumber/*/features/reports/**/*.xml + ''' + ) + } } } } } + post { + always { + script { + if (testShouldRunOnAgent(params.RUN_ONLY, runSpecificTestOnAgent(params.RUN_ONLY, NESTED_ARRAY_OF_TESTS_TO_RUN[0]))) { + dir('ee-test'){ + unstash 'testResultEE' + } + } + if (testShouldRunOnAgent(params.RUN_ONLY, runSpecificTestOnAgent(params.RUN_ONLY, NESTED_ARRAY_OF_TESTS_TO_RUN[1]))) { + dir('ee-test'){ + unstash 'testResultEE2' + } + } + if (testShouldRunOnAgent(params.RUN_ONLY, runSpecificTestOnAgent(params.RUN_ONLY, NESTED_ARRAY_OF_TESTS_TO_RUN[2]))) { + dir('ee-test'){ + unstash 'testResultEE3' + } + } + } + + archiveArtifacts( + artifacts: "ee-test/cucumber/*/*.*", + fingerprint: false, + allowEmptyArchive: true + ) + + archiveArtifacts( + artifacts: "ee-test/container_logs/*/*", + fingerprint: false, + allowEmptyArchive: true + ) + + publishHTML( + reportDir: 'ee-test/cucumber', + reportFiles: ''' + api/cucumber_results.html, + authenticators_config/cucumber_results.html, + authenticators_azure/cucumber_results.html, + authenticators_ldap/cucumber_results.html, + authenticators_oidc/cucumber_results.html, + authenticators_jwt/cucumber_results.html, + authenticators_status/cucumber_results.html + policy/cucumber_results.html, + rotators/cucumber_results.html + ''', + reportName: 'EE Integration reports', + reportTitles: '', + allowMissing: false, + alwaysLinkToLastBuild: true, + keepAll: true + ) + } + } } stage('Run environment tests in parallel') { parallel { stage('Standard agent tests') { + when { + expression { + testShouldRunOnAgent( + params.RUN_ONLY, + runSpecificTestOnAgent(params.RUN_ONLY, NESTED_ARRAY_OF_TESTS_TO_RUN[0]) + ) + } + } + environment { CUCUMBER_FILTER_TAGS = "${params.CUCUMBER_FILTER_TAGS}" } steps { - sh(script: 'cat /etc/os-release', label: 'RHEL version') + sh(script: 'cat /etc/os-release', label: 'Ubuntu version') sh(script: 'docker --version', label: 'Docker version') - runConjurTests(params.RUN_ONLY) + runConjurTests( + params.RUN_ONLY, + NESTED_ARRAY_OF_TESTS_TO_RUN[0] + ) + } + } + + // Run a subset of tests on a second agent to prevent oversubscribing the hardware + stage('Standard agent2 tests') { + when { + expression { + testShouldRunOnAgent( + params.RUN_ONLY, + runSpecificTestOnAgent(params.RUN_ONLY, NESTED_ARRAY_OF_TESTS_TO_RUN[1]) + ) + } + } + + agent { label 'executor-v2' } + environment { + CUCUMBER_FILTER_TAGS = "${params.CUCUMBER_FILTER_TAGS}" + } + + steps { + addNewImagesToAgent() + unstash 'version_info' + runConjurTests(params.RUN_ONLY, NESTED_ARRAY_OF_TESTS_TO_RUN[1]) + stash( + name: 'standardTestResult2', + includes: ''' + cucumber/*/*.*, + container_logs/*/*, + spec/reports/*.xml, + spec/reports-audit/*.xml, + cucumber/*/features/reports/**/*.xml + ''' + ) + } + } + + // Run a subset of tests on a second agent to prevent oversubscribing the hardware + stage('Standard agent3 tests') { + when { + expression { + testShouldRunOnAgent( + params.RUN_ONLY, + runSpecificTestOnAgent(params.RUN_ONLY, NESTED_ARRAY_OF_TESTS_TO_RUN[2]) + ) + } + } + + agent { label 'executor-v2' } + environment { + CUCUMBER_FILTER_TAGS = "${params.CUCUMBER_FILTER_TAGS}" + } + + steps { + addNewImagesToAgent() + unstash 'version_info' + runConjurTests( + params.RUN_ONLY, + NESTED_ARRAY_OF_TESTS_TO_RUN[2] + ) + stash( + name: 'standardTestResult3', + includes: ''' + cucumber/*/*.*, + container_logs/*/*, + spec/reports/*.xml, + spec/reports-audit/*.xml, + cucumber/*/features/reports/**/*.xml, + ci/test_suites/*/output/* + ''' + ) } } @@ -396,6 +584,7 @@ pipeline { } steps { + addNewImagesToAgent() unstash 'version_info' // Grant access to this Jenkins agent's IP to AWS security groups // This is required for access to the internal docker registry @@ -626,6 +815,14 @@ pipeline { always { script { + if (testShouldRunOnAgent(params.RUN_ONLY, runSpecificTestOnAgent(params.RUN_ONLY, NESTED_ARRAY_OF_TESTS_TO_RUN[1]))) { + unstash 'standardTestResult2' + } + + if (testShouldRunOnAgent(params.RUN_ONLY, runSpecificTestOnAgent(params.RUN_ONLY, NESTED_ARRAY_OF_TESTS_TO_RUN[2]))) { + unstash 'standardTestResult3' + } + // Only unstash azure if it ran. if (testShouldRun(params.RUN_ONLY, "azure_authenticator")) { unstash 'testResultAzure' @@ -751,12 +948,24 @@ pipeline { // TODO: Do we want to move any of these functions to a separate file? +def addNewImagesToAgent() { + // Pull and retag existing images onto new Jenkins agent + sh """ + docker pull registry.tld/conjur:${tagWithSHA()} + docker pull registry.tld/conjur-ubi:${tagWithSHA()} + docker pull registry.tld/conjur-test:${tagWithSHA()} + docker tag registry.tld/conjur:${tagWithSHA()} conjur:${tagWithSHA()} + docker tag registry.tld/conjur-ubi:${tagWithSHA()} conjur-ubi:${tagWithSHA()} + docker tag registry.tld/conjur-test:${tagWithSHA()} conjur-test:${tagWithSHA()} + """ +} + // Possible minor optimization: Could memoize this. Need to verify it's not // shared across builds. def tagWithSHA() { sh( returnStdout: true, - script: 'echo $(git rev-parse --short=8 HEAD)' + script: 'echo -n $(git rev-parse --short=8 HEAD)' ) } @@ -772,11 +981,37 @@ def testShouldRun(run_only_str, test) { return run_only_str == '' || run_only_str.split().contains(test) } -// "run_only_str" is a space-separated string specifying the subset of tests to -// run. If it's empty, all tests are run. -def runConjurTests(run_only_str) { +def testShouldRunOnAgent(run_only_str, agent_specific_tests) { + return run_only_str == '' || ! agent_specific_tests.isEmpty() +} + +def runSpecificTestOnAgent(run_only_str, agent_specific_tests) { + // runSpecificTestOnAgent allows a subset of tests to be ran + // on an agent, determined by the agent's assigned subset of + // tests it normally runs. + + // Args: + // run_only_str: a space seperated string of test names + // agent_specific_tests: an array of tests that the agent + // is assigned to run + + // Returns: + // An array of test names to run + def run_only_tests = [] + def find_tests = run_only_str.split() + + find_tests.each { run_only_test -> + agent_specific_tests.find { agent_test -> + if (agent_test.contains(run_only_test)) { + run_only_tests.add(run_only_test) + } + } + } + return run_only_tests +} - all_tests = [ +def conjurTests() { + return [ "authenticators_config": [ "Authenticators Config - ${env.STAGE_NAME}": { sh 'ci/test authenticators_config' @@ -787,16 +1022,16 @@ def runConjurTests(run_only_str) { sh 'ci/test authenticators_status' } ], - "authenticators_k8s": [ - "K8s Authenticator - ${env.STAGE_NAME}": { - sh 'ci/test authenticators_k8s' - } - ], "authenticators_ldap": [ "LDAP Authenticator - ${env.STAGE_NAME}": { sh 'ci/test authenticators_ldap' } ], + "api": [ + "API - ${env.STAGE_NAME}": { + sh 'ci/test api' + } + ], "authenticators_oidc": [ "OIDC Authenticator - ${env.STAGE_NAME}": { sh 'summon -f ./ci/test_suites/authenticators_oidc/secrets.yml -e ci ci/test authenticators_oidc' @@ -812,16 +1047,16 @@ def runConjurTests(run_only_str) { sh 'ci/test policy' } ], - "api": [ - "API - ${env.STAGE_NAME}": { - sh 'ci/test api' - } - ], "rotators": [ "Rotators - ${env.STAGE_NAME}": { sh 'ci/test rotators' } ], + "authenticators_k8s": [ + "K8s Authenticator - ${env.STAGE_NAME}": { + sh 'ci/test authenticators_k8s' + } + ], "rspec_audit": [ "Audit - ${env.STAGE_NAME}": { sh 'ci/test rspec_audit' @@ -838,13 +1073,26 @@ def runConjurTests(run_only_str) { } ] ] +} + +def runConjurTests(run_only_str, cuke_test_names) { + // runConjurTests will build a parallel Jenkins block of code + // that will run the specified cucumber test stages. - // Filter for the tests we want run, if requested. - parallel_tests = all_tests - tests = run_only_str.split() + // Args: + // cuke_test_names an array of test names to run. - if (tests.size() > 0) { - parallel_tests = all_tests.subMap(tests) + // Returns: + // A Jenkins block of parallel code. + + def all_tests = conjurTests() + def run_only_tests = runSpecificTestOnAgent(run_only_str, cuke_test_names) + def parallel_tests = all_tests + + if (run_only_tests.isEmpty()) { + parallel_tests = all_tests.subMap(cuke_test_names) + } else { + parallel_tests = all_tests.subMap(run_only_tests) } // Create the parallel pipeline. @@ -852,6 +1100,7 @@ def runConjurTests(run_only_str) { // Since + merges two maps together, sum() combines the individual values of // parallel_tests into one giant map whose keys are the stage names and // whose values are the blocks to be run. + script { parallel( parallel_tests.values().sum() @@ -859,6 +1108,40 @@ def runConjurTests(run_only_str) { } } +def collateTests(jobs_per_agent=4) { + // collateTests will find the names of cucumber tests that should run + // and create a nested list of tests to be ran across mutliple Jenkins + // agents. + + // Args: + // jobs_per_agent: The nested list of tests names will be no more than + // the specified integer. + + // Returns: a nested list of test names. + + def all_tests = conjurTests() + def all_test_names = [] + + all_tests.each{ k, _ -> + all_test_names.add(k) + } + + def parallel_tests = [] + // Create a subset of tests that can be ran by each Jenkins agent + int partitionCount = all_test_names.size() / jobs_per_agent + + partitionCount.times { partitionNumber -> + def start = partitionNumber * jobs_per_agent + def end = start + jobs_per_agent - 1 + parallel_tests.add(all_test_names[start..end]) + } + + if (all_tests.size() % jobs_per_agent) { + parallel_tests.add(all_test_names[partitionCount * jobs_per_agent..-1]) + } + return parallel_tests +} + def defaultCucumberFilterTags(env) { if(env.BRANCH_NAME == 'master' || env.TAG_NAME?.trim()) { // If this is a master or tag build, we want to run all of the tests. So diff --git a/ci/docker-compose.yml b/ci/docker-compose.yml index 38f4c67de1..47754bafda 100644 --- a/ci/docker-compose.yml +++ b/ci/docker-compose.yml @@ -14,6 +14,20 @@ services: # PostgreSQL documentation about "trust" POSTGRES_HOST_AUTH_METHOD: trust + pg2: + image: postgres:10.16 + environment: + # To avoid the following error: + # + # Error: Database is uninitialized and superuser password is not + # specified. You must specify POSTGRES_PASSWORD for the superuser. Use + # "-e POSTGRES_PASSWORD=password" to set it in "docker run". + # + # You may also use POSTGRES_HOST_AUTH_METHOD=trust to allow all + # connections without a password. This is *not* recommended. See + # PostgreSQL documentation about "trust" + POSTGRES_HOST_AUTH_METHOD: trust + audit: image: postgres:10.16 environment: @@ -34,6 +48,20 @@ services: # PostgreSQL documentation about "trust" POSTGRES_HOST_AUTH_METHOD: trust + testdb2: + image: postgres:10.16 + environment: + # To avoid the following error: + # + # Error: Database is uninitialized and superuser password is not + # specified. You must specify POSTGRES_PASSWORD for the superuser. Use + # "-e POSTGRES_PASSWORD=password" to set it in "docker run". + # + # You may also use POSTGRES_HOST_AUTH_METHOD=trust to allow all + # connections without a password. This is *not* recommended. See + # PostgreSQL documentation about "trust" + POSTGRES_HOST_AUTH_METHOD: trust + conjur: image: "conjur-test:${TAG}" environment: @@ -67,14 +95,45 @@ services: - ldap-server - keycloak + conjur2: + image: "conjur-test:${TAG}" + environment: + DATABASE_URL: postgres://postgres@pg2/postgres + CONJUR_ADMIN_PASSWORD: ADmin123!!!! + CONJUR_ACCOUNT: cucumber + CONJUR_DATA_KEY: + RAILS_ENV: + REQUIRE_SIMPLECOV: "true" + CONJUR_LOG_LEVEL: debug + CONJUR_AUTHENTICATORS: authn-ldap/test,authn-ldap/secure,authn-oidc/keycloak,authn-oidc,authn-k8s/test,authn-azure/prod,authn-gcp,authn-jwt/raw,authn-jwt/keycloak,authn-oidc/keycloak2,authn-oidc/okta-2 + LDAP_URI: ldap://ldap-server:389 + LDAP_BASE: dc=conjur,dc=net + LDAP_FILTER: '(uid=%s)' + LDAP_BINDDN: cn=admin,dc=conjur,dc=net + LDAP_BINDPW: ldapsecret + WEB_CONCURRENCY: 0 + RAILS_MAX_THREADS: 10 + command: server + volumes: + # TODO: authenticators_oidc/test has a dep on this + - authn-local2:/run/authn-local + - ./oauth/keycloak:/oauth/keycloak/scripts + - ./ldap-certs:/ldap-certs:ro + - log-volume:/opt/conjur-server/log + - ../coverage:/opt/conjur-server/coverage + expose: + - "80" + links: + - pg2 + - ldap-server + - keycloak + cucumber: image: conjur-test:$TAG entrypoint: bash working_dir: /src/conjur-server environment: - CONJUR_APPLIANCE_URL: http://conjur CONJUR_ACCOUNT: cucumber - DATABASE_URL: postgres://postgres@pg/postgres AUDIT_DATABASE_URL: RAILS_ENV: test CONJUR_LOG_LEVEL: debug @@ -88,14 +147,18 @@ services: volumes: - ..:/src/conjur-server - authn-local:/run/authn-local + - authn-local2:/run/authn-local - ./ldap-certs:/ldap-certs:ro - log-volume:/src/conjur-server/log - jwks-volume:/var/jwks - ./oauth/keycloak:/oauth/keycloak/scripts links: - conjur + - conjur2 - pg + - pg2 - testdb + - testdb2 - keycloak ldap-server: @@ -168,5 +231,6 @@ services: volumes: authn-local: + authn-local2: log-volume: - jwks-volume: \ No newline at end of file + jwks-volume: diff --git a/ci/oauth/keycloak/keycloak_functions.sh b/ci/oauth/keycloak/keycloak_functions.sh index c01216f1d8..dada055816 100644 --- a/ci/oauth/keycloak/keycloak_functions.sh +++ b/ci/oauth/keycloak/keycloak_functions.sh @@ -2,6 +2,10 @@ KEYCLOAK_SERVICE_NAME="keycloak" +# This is executed by the main "ci/test" script after cd-ing into "ci". +# shellcheck disable=SC1091 +source "./shared.sh" + # Note: the single arg is a nameref, which this function sets to an array # containing items of the form "KEY=VAL". function _hydrate_keycloak_env_args() { @@ -84,6 +88,12 @@ function fetch_keycloak_certificate() { # there's a dep on the docker-compose.yml volumes. # Fetch SSL cert to communicate with keycloak (OIDC provider). echo "Initialize keycloak certificate in conjur server" - docker-compose exec -T \ - conjur /oauth/keycloak/scripts/fetch_certificate + + local parallel_services + read -ra parallel_services <<< "$(get_parallel_services 'conjur')" + + for parallel_service in "${parallel_services[@]}"; do + docker-compose exec -T \ + "${parallel_service}" /oauth/keycloak/scripts/fetch_certificate + done } diff --git a/ci/shared.sh b/ci/shared.sh index 5de62b47bc..537c831613 100644 --- a/ci/shared.sh +++ b/ci/shared.sh @@ -2,6 +2,38 @@ export REPORT_ROOT=/src/conjur-server +# Sets the number of parallel processes for cucumber tests +# Due to naming conventions for parallel_cucumber this begins at 1 NOT 0 +PARALLEL_PROCESSES=2 + +get_parallel_services() { + # get_parallel_services converts docker service names + # to the appropriate naming conventions expected for + # parallel cucumber tests. + + # Args: + # $1: A string of space delimited docker service name(s) + + # Returns: + # An array of docker service names matching the expected + # parallel cucumber naming convention. + local services + local parallel_services + read -ra services <<< "$1" + + for service in "${services[@]}"; do + for (( i=1; i<=PARALLEL_PROCESSES; i++ )); do + if (( i == 1 )) ; then + parallel_services+=("$service") + else + parallel_services+=("$service${i}") + fi + done + done + + echo "${parallel_services[@]}" +} + # Note: This function is long but purposefully not split up. None of its parts # are re-used, and the split-up version is harder to follow and duplicates # argument processing. @@ -30,12 +62,25 @@ _run_cucumber_tests() { echo "Start all services..." - docker-compose up --no-deps --no-recreate -d pg conjur "${services[@]}" - docker-compose exec -T conjur conjurctl wait --retries 180 + local parallel_services + read -ra parallel_services <<< "$(get_parallel_services 'conjur pg')" + + if (( ${#services[@]} )); then + docker-compose up --no-deps --no-recreate -d "${parallel_services[@]}" "${services[@]}" + else + docker-compose up --no-deps --no-recreate -d "${parallel_services[@]}" + fi + + read -ra parallel_services <<< "$(get_parallel_services 'conjur')" + for parallel_service in "${parallel_services[@]}"; do + docker-compose exec -T "$parallel_service" conjurctl wait --retries 180 + done echo "Create cucumber account..." - docker-compose exec -T conjur conjurctl account create cucumber + for parallel_service in "${parallel_services[@]}"; do + docker-compose exec -T "$parallel_service" conjurctl account create cucumber + done # Stage 2: Prepare cucumber environment args # ----------------------------------------------------------- @@ -56,16 +101,31 @@ _run_cucumber_tests() { env_var_flags+=(-e "$item") done + # Generate api key ENV variables based on the + # number of parallel processes. + for (( i=1; i <= ${#parallel_services[@]}; ++i )); do + index=$(( i - 1 )) + if (( i == 1 )) ; then + api_keys+=("CONJUR_AUTHN_API_KEY=$(_get_api_key "${parallel_services[$index]}")") + else + api_keys+=("CONJUR_AUTHN_API_KEY${i}=$(_get_api_key "${parallel_services[$index]}")") + fi + done + # Add the cucumber env vars that we always want to send. # Note: These are args for docker-compose run, and as such the right hand # sides of the = do NOT require escaped quotes. docker-compose takes the # entire arg, splits on the =, and uses the rhs as the value, env_var_flags+=( - -e "CONJUR_AUTHN_API_KEY=$(_get_api_key)" -e "CUCUMBER_NETWORK=$(_find_cucumber_network)" -e "CUCUMBER_FILTER_TAGS=$CUCUMBER_FILTER_TAGS" ) + # Add parallel process api_keys to the env_var_flags + for api_key in "${api_keys[@]}"; do + env_var_flags+=(-e "$api_key") + done + # If there's no tty (e.g. we're running as a Jenkins job), pass -T to # docker-compose. run_flags=(--no-deps --rm) @@ -84,16 +144,23 @@ _run_cucumber_tests() { # Stage 3: Run Cucumber # ----------------------------------------------------------- + echo "ENV_ARG_FN: ${env_arg_fn}" >&2 + echo "RUN_FLAGS: ${run_flags[*]}" >&2 + echo "ENV_VAR_FLAGS: ${env_var_flags[*]}" >&2 + echo "CUCUMBER TAGS: ${cucumber_tags_arg}" >&2 + echo "CUCUMBER PROFILE: ${profile}" >&2 + + + # Have to add tags in profile for parallel to run properly + # ${cucumber_tags_arg} should overwrite the profile tags in a way for @smoke to work correctly docker-compose run "${run_flags[@]}" "${env_var_flags[@]}" \ cucumber -ec "\ /oauth/keycloak/scripts/fetch_certificate && - bundle exec cucumber \ - --strict \ - ${cucumber_tags_arg} \ - -p \"$profile\" \ + bundle exec parallel_cucumber . -n ${PARALLEL_PROCESSES} \ + -o '--strict --profile \"${profile}\" ${cucumber_tags_arg} \ --format json --out \"cucumber/$profile/cucumber_results.json\" \ --format html --out \"cucumber/$profile/cucumber_results.html\" \ - --format junit --out \"cucumber/$profile/features/reports\"" + --format junit --out \"cucumber/$profile/features/reports\"'" # Stage 4: Coverage results # ----------------------------------------------------------- @@ -102,21 +169,26 @@ _run_cucumber_tests() { # killed before ruby, the report doesn't get written. So here we kill the # process to write the report. The container is kept alive using an infinite # sleep in the at_exit hook (see .simplecov). - docker-compose exec -T conjur bash -c "pkill -f 'puma 5'" + for parallel_service in "${parallel_services[@]}"; do + docker-compose exec -T "$parallel_service" bash -c "pkill -f 'puma 5'" + done } _get_api_key() { - docker-compose exec -T conjur conjurctl \ + local service=$1 + + docker-compose exec -T "${service}" conjurctl \ role retrieve-key cucumber:user:admin | tr -d '\r' } _find_cucumber_network() { local net - net=$( - docker inspect "$(docker-compose ps -q conjur)" \ - --format '{{.HostConfig.NetworkMode}}' - ) + # Docker compose conjur/pg services use the same + # network for 1 or more instances so only conjur is passed + # and not other parallel services. + conjur_id=$(docker-compose ps -q conjur) + net=$(docker inspect "${conjur_id}" --format '{{.HostConfig.NetworkMode}}') docker network inspect "$net" \ --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}' @@ -180,5 +252,6 @@ start_ldap_server() { echo 'LDAP server failed to start in time' exit 1 fi + echo "Done." } diff --git a/ci/test b/ci/test index cc99051be8..0ba14cbabb 100755 --- a/ci/test +++ b/ci/test @@ -86,9 +86,8 @@ docker_diagnostics() { # its _value_ in the regex. # # Docker Note: container name is always the last field. Hence $NF gets it. - readarray -t cont_names < <( - docker ps --all | awk "/${COMPOSE_PROJECT_NAME}/{print \$NF}" - ) + declare -a cont_names + while IFS=$'\n' read -r line; do cont_names+=("$line"); done < <(docker ps --all | awk "/${COMPOSE_PROJECT_NAME}/{print \$NF}") # Store container logs for archiving. echo "Writing Container logs to" \ diff --git a/ci/test_suites/authenticators_jwt/test b/ci/test_suites/authenticators_jwt/test index e3611a7b2b..db1a27a949 100755 --- a/ci/test_suites/authenticators_jwt/test +++ b/ci/test_suites/authenticators_jwt/test @@ -8,7 +8,9 @@ source "./shared.sh" source "./oauth/keycloak/keycloak_functions.sh" function main() { - docker-compose up --no-deps -d pg conjur jwks jwks_py keycloak + local parallel_services + read -ra parallel_services <<< "$(get_parallel_services 'conjur pg')" + docker-compose up --no-deps -d "${parallel_services[@]}" jwks jwks_py keycloak wait_for_keycloak_server create_keycloak_users diff --git a/ci/test_suites/authenticators_oidc/test b/ci/test_suites/authenticators_oidc/test index b2508a5143..bea0f8bc48 100755 --- a/ci/test_suites/authenticators_oidc/test +++ b/ci/test_suites/authenticators_oidc/test @@ -36,7 +36,9 @@ function _hydrate_all_env_args() { } function main() { - docker-compose up --no-deps -d pg conjur keycloak + local parallel_services + read -ra parallel_services <<< "$(get_parallel_services 'conjur pg')" + docker-compose up --no-deps -d "${parallel_services[@]}" keycloak # We also run an ldap-server container for testing the OIDC & LDAP combined # use-case. We can't run this use-case in a separate Jenkins step because diff --git a/ci/test_suites/rotators/test b/ci/test_suites/rotators/test index 20b2634b81..badf62fa37 100755 --- a/ci/test_suites/rotators/test +++ b/ci/test_suites/rotators/test @@ -5,5 +5,14 @@ set -e # shellcheck disable=SC1091 source "./shared.sh" -additional_services='testdb' -_run_cucumber_tests rotators "$additional_services" +read -ra parallel_services <<< "$(get_parallel_services 'testdb')" +additional_services='' +for service in "${parallel_services[@]}"; do + if [[ "$additional_services" == '' ]]; then + additional_services+="${service}" + else + additional_services+=" ${service}" + fi +done + +_run_cucumber_tests rotators "${additional_services}" diff --git a/config/environments/development.rb b/config/environments/development.rb index c7a29d77cd..d3b9a48acb 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -13,7 +13,9 @@ # Whitelist conjur hostname for tests # For more information, refer to: # https://guides.rubyonrails.org/configuring.html#actiondispatch-hostauthorization - config.hosts << "conjur" + + # Accept multiple hosts for parallel tests + config.hosts << /^conjur[0-9]*$/ # eager_load needed to make authentication work without the hacky # loading code... diff --git a/config/environments/test.rb b/config/environments/test.rb index 26ea1a7b1d..db8a6d08dd 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -3,6 +3,17 @@ require 'logger/formatter/conjur_formatter' require 'test/audit_sink' +parallel_cuke_vars = {} +parallel_cuke_vars['CONJUR_APPLIANCE_URL'] = "http://conjur#{ENV['TEST_ENV_NUMBER']}" +parallel_cuke_vars['DATABASE_URL'] = "postgres://postgres@pg#{ENV['TEST_ENV_NUMBER']}/postgres" +parallel_cuke_vars['CONJUR_AUTHN_API_KEY'] = ENV["CONJUR_AUTHN_API_KEY#{ENV['TEST_ENV_NUMBER']}"] + +parallel_cuke_vars.each do |key, value| + if ENV[key].nil? || ENV[key].empty? + ENV[key] = value + end +end + Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. diff --git a/cucumber.yml b/cucumber.yml index 67b3d17cf5..f3c4378b5e 100644 --- a/cucumber.yml +++ b/cucumber.yml @@ -1,9 +1,11 @@ policy: > + --tags @policy --format pretty -r cucumber/policy cucumber/policy api: > + --tags @api --format pretty -r cucumber/api/features/support/logs_helpers.rb -r cucumber/api/features/step_definitions/logs_steps.rb @@ -20,6 +22,7 @@ api: > # directory is above it (e.g authenticators_azure is above authenticators_common) # then we will not be able to load the methods defined in authenticators_common authenticators_config: > + --tags @authenticators_config --format pretty -r cucumber/api/features/support/step_def_transforms.rb -r cucumber/api/features/support/rest_helpers.rb @@ -34,6 +37,7 @@ authenticators_config: > cucumber/authenticators_config authenticators_status: > + --tags @authenticators_status --format pretty -r cucumber/api/features/support/rest_helpers.rb -r cucumber/api/features/support/step_def_transforms.rb @@ -50,6 +54,7 @@ authenticators_status: > cucumber/authenticators_status authenticators_ldap: > + --tags @authenticators_ldap --format pretty -r cucumber/api/features/support/step_def_transforms.rb -r cucumber/api/features/support/rest_helpers.rb @@ -66,6 +71,7 @@ authenticators_ldap: > cucumber/authenticators_ldap authenticators_oidc: > + --tags @authenticators_oidc --format pretty -r cucumber/api/features/support/step_def_transforms.rb -r cucumber/api/features/support/rest_helpers.rb @@ -87,6 +93,7 @@ authenticators_oidc: > cucumber/authenticators_oidc authenticators_gcp: > + --tags @authenticators_gcp --format pretty -r cucumber/api/features/support/step_def_transforms.rb -r cucumber/api/features/support/rest_helpers.rb @@ -105,6 +112,7 @@ authenticators_gcp: > cucumber/authenticators_gcp authenticators_azure: > + --tags @authenticators_azure --format pretty -r cucumber/api/features/support/step_def_transforms.rb -r cucumber/api/features/support/rest_helpers.rb @@ -123,8 +131,8 @@ authenticators_azure: > cucumber/authenticators_azure authenticators_jwt: > + --tags "not @skip and @authenticators_jwt" --format pretty - --tags "not @skip" -r cucumber/api/features/step_definitions/user_steps.rb -r cucumber/api/features/step_definitions/request_steps.rb -r cucumber/api/features/support/step_def_transforms.rb @@ -148,8 +156,8 @@ authenticators_jwt: > # profiles need to be thought through better and refactored most likely. # rotators: > + --tags 'not @manual and @rotators' --format pretty - -t 'not @manual' -r cucumber/authenticators/features/support/hooks.rb -r cucumber/api/features/support/step_def_transforms.rb -r cucumber/api/features/support/rest_helpers.rb @@ -161,8 +169,8 @@ rotators: > cucumber/rotators manual-rotators: > - --format pretty --tags @manual + --format pretty -r cucumber/rotators/features/support -r cucumber/rotators/features/step_definitions cucumber/rotators diff --git a/cucumber/_authenticators_common/features/support/authenticator_helpers.rb b/cucumber/_authenticators_common/features/support/authenticator_helpers.rb index e460bffa12..986c633bcc 100644 --- a/cucumber/_authenticators_common/features/support/authenticator_helpers.rb +++ b/cucumber/_authenticators_common/features/support/authenticator_helpers.rb @@ -104,7 +104,7 @@ def execute(method, path, payload = {}, options = {}) end def conjur_hostname - ENV.fetch('CONJUR_APPLIANCE_URL', 'http://conjur') + ENV.fetch('CONJUR_APPLIANCE_URL', "http://conjur#{ENV['TEST_ENV_NUMBER']}") end private diff --git a/cucumber/_authenticators_common/features/support/env.rb b/cucumber/_authenticators_common/features/support/env.rb index ccaeaf8735..35f864e9f1 100644 --- a/cucumber/_authenticators_common/features/support/env.rb +++ b/cucumber/_authenticators_common/features/support/env.rb @@ -1,5 +1,16 @@ # frozen_string_literal: true +parallel_cuke_vars = {} +parallel_cuke_vars['CONJUR_APPLIANCE_URL'] = "http://conjur#{ENV['TEST_ENV_NUMBER']}" +parallel_cuke_vars['DATABASE_URL'] = "postgres://postgres@pg#{ENV['TEST_ENV_NUMBER']}/postgres" +parallel_cuke_vars['CONJUR_AUTHN_API_KEY'] = ENV["CONJUR_AUTHN_API_KEY#{ENV['TEST_ENV_NUMBER']}"] + +parallel_cuke_vars.each do |key, value| + if ENV[key].nil? || ENV[key].empty? + ENV[key] = value + end +end + $LOAD_PATH.unshift(Dir.pwd) require 'config/environment' @@ -9,5 +20,5 @@ require 'conjur-api' require 'json_spec/cucumber' -Conjur.configuration.appliance_url = ENV['CONJUR_APPLIANCE_URL'] || 'http://conjur' +Conjur.configuration.appliance_url = ENV['CONJUR_APPLIANCE_URL'] || "http://conjur#{ENV['TEST_ENV_NUMBER']}" Conjur.configuration.account = ENV['CONJUR_ACCOUNT'] || 'cucumber' diff --git a/cucumber/_authenticators_common/features/support/hooks.rb b/cucumber/_authenticators_common/features/support/hooks.rb index bc93541569..08d641f6fc 100644 --- a/cucumber/_authenticators_common/features/support/hooks.rb +++ b/cucumber/_authenticators_common/features/support/hooks.rb @@ -9,6 +9,15 @@ # Prior to this hook, our tests had hidden coupling. This ensures each test is # run independently. Before do + parallel_cuke_vars = {} + parallel_cuke_vars['CONJUR_APPLIANCE_URL'] = "http://conjur#{ENV['TEST_ENV_NUMBER']}" + parallel_cuke_vars['DATABASE_URL'] = "postgres://postgres@pg#{ENV['TEST_ENV_NUMBER']}/postgres" + parallel_cuke_vars['CONJUR_AUTHN_API_KEY'] = ENV["CONJUR_AUTHN_API_KEY#{ENV['TEST_ENV_NUMBER']}"] + + parallel_cuke_vars.each do |key, value| + ENV[key] = value + end + @user_index = 0 @host_index = 0 diff --git a/cucumber/api/features/support/env.rb b/cucumber/api/features/support/env.rb index edabb5a5db..5d47c8df2d 100644 --- a/cucumber/api/features/support/env.rb +++ b/cucumber/api/features/support/env.rb @@ -4,6 +4,17 @@ ENV['RAILS_ENV'] ||= 'test' ENV['CONJUR_LOG_LEVEL'] ||= 'debug' +parallel_cuke_vars = {} +parallel_cuke_vars['CONJUR_APPLIANCE_URL'] = "http://conjur#{ENV['TEST_ENV_NUMBER']}" +parallel_cuke_vars['DATABASE_URL'] = "postgres://postgres@pg#{ENV['TEST_ENV_NUMBER']}/postgres" +parallel_cuke_vars['CONJUR_AUTHN_API_KEY'] = ENV["CONJUR_AUTHN_API_KEY#{ENV['TEST_ENV_NUMBER']}"] + +parallel_cuke_vars.each do |key, value| + if ENV[key].nil? || ENV[key].empty? + ENV[key] = value + end +end + # so that we can require relative to the project root $LOAD_PATH.unshift(File.expand_path('../../../..', __dir__)) require 'config/environment' diff --git a/cucumber/api/features/support/hooks.rb b/cucumber/api/features/support/hooks.rb index 4e47f2f2ef..90c785d26c 100644 --- a/cucumber/api/features/support/hooks.rb +++ b/cucumber/api/features/support/hooks.rb @@ -10,6 +10,15 @@ # Prior to this hook, our tests had hidden coupling. This ensures each test is # run independently. Before do + parallel_cuke_vars = {} + parallel_cuke_vars['CONJUR_APPLIANCE_URL'] = "http://conjur#{ENV['TEST_ENV_NUMBER']}" + parallel_cuke_vars['DATABASE_URL'] = "postgres://postgres@pg#{ENV['TEST_ENV_NUMBER']}/postgres" + parallel_cuke_vars['CONJUR_AUTHN_API_KEY'] = ENV["CONJUR_AUTHN_API_KEY#{ENV['TEST_ENV_NUMBER']}"] + + parallel_cuke_vars.each do |key, value| + ENV[key] = value + end + @user_index = 0 @host_index = 0 diff --git a/cucumber/authenticators/features/support/hooks.rb b/cucumber/authenticators/features/support/hooks.rb index 6c164a773d..46c551b9ee 100644 --- a/cucumber/authenticators/features/support/hooks.rb +++ b/cucumber/authenticators/features/support/hooks.rb @@ -5,6 +5,15 @@ # Prior to this hook, our tests had hidden coupling. This ensures each test is # run independently. Before do + parallel_cuke_vars = {} + parallel_cuke_vars['CONJUR_APPLIANCE_URL'] = "http://conjur#{ENV['TEST_ENV_NUMBER']}" + parallel_cuke_vars['DATABASE_URL'] = "postgres://postgres@pg#{ENV['TEST_ENV_NUMBER']}/postgres" + parallel_cuke_vars['CONJUR_AUTHN_API_KEY'] = ENV["CONJUR_AUTHN_API_KEY#{ENV['TEST_ENV_NUMBER']}"] + + parallel_cuke_vars.each do |key, value| + ENV[key] = value + end + @user_index = 0 Role.truncate(cascade: true) diff --git a/cucumber/authenticators_jwt/features/authn_jwt_token_schema.feature b/cucumber/authenticators_jwt/features/authn_jwt_token_schema.feature index a8c523707f..cb422b2bd4 100644 --- a/cucumber/authenticators_jwt/features/authn_jwt_token_schema.feature +++ b/cucumber/authenticators_jwt/features/authn_jwt_token_schema.feature @@ -875,32 +875,32 @@ Feature: JWT Authenticator - Token Schema - !variable conjur/authn-jwt/raw/enforced-claims - !host - id: myapp + id: myapp-01 annotations: authn-jwt/raw/conjur.org/enforced-property: valid - !grant role: !group conjur/authn-jwt/raw/hosts - member: !host myapp + member: !host myapp-01 """ And I successfully set authn-jwt "enforced-claims" variable to value "conjur.org/enforced-property" And I successfully set authn-jwt "token-app-property" variable to value "conjur.org/host-property" And I add the secret value "test-secret" to the resource "cucumber:variable:test-variable" - And I permit host "myapp" to "execute" it + And I permit host "myapp-01" to "execute" it And I am using file "authn-jwt-token-schema" and alg "RS256" for remotely issue token: """ { "conjur.org/enforced-property":"valid", - "conjur.org/host-property":"myapp" + "conjur.org/host-property":"myapp-01" } """ And I save my place in the log file When I authenticate via authn-jwt with the JWT token - Then host "myapp" has been authorized by Conjur + Then host "myapp-01" has been authorized by Conjur And I successfully GET "/secrets/cucumber/variable/test-variable" with authorized user And The following appears in the log after my savepoint: """ - cucumber:host:myapp successfully authenticated with authenticator authn-jwt service cucumber:webservice:conjur/authn-jwt/raw + cucumber:host:myapp-01 successfully authenticated with authenticator authn-jwt service cucumber:webservice:conjur/authn-jwt/raw """ @sanity diff --git a/cucumber/authenticators_k8s/features/support/env.rb b/cucumber/authenticators_k8s/features/support/env.rb index ee654726b9..4bcebd1693 100644 --- a/cucumber/authenticators_k8s/features/support/env.rb +++ b/cucumber/authenticators_k8s/features/support/env.rb @@ -2,6 +2,17 @@ require 'rack/test' require 'json_spec/cucumber' +parallel_cuke_vars = {} +parallel_cuke_vars['CONJUR_APPLIANCE_URL'] = "http://nginx#{ENV['TEST_ENV_NUMBER']}" +parallel_cuke_vars['DATABASE_URL'] = "postgres://postgres@postgres#{ENV['TEST_ENV_NUMBER']}:5432/postgres" +parallel_cuke_vars['CONJUR_AUTHN_API_KEY'] = ENV["CONJUR_AUTHN_API_KEY#{ENV['TEST_ENV_NUMBER']}"] + +parallel_cuke_vars.each do |key, value| + if ENV[key].nil? || ENV[key].empty? + ENV[key] = value + end +end + def app Rails.application end diff --git a/cucumber/authenticators_k8s/features/support/hooks.rb b/cucumber/authenticators_k8s/features/support/hooks.rb index c4fa9b56bd..b8df4fbcda 100644 --- a/cucumber/authenticators_k8s/features/support/hooks.rb +++ b/cucumber/authenticators_k8s/features/support/hooks.rb @@ -7,6 +7,15 @@ end Before do + parallel_cuke_vars = {} + parallel_cuke_vars['CONJUR_APPLIANCE_URL'] = "https://nginx#{ENV['TEST_ENV_NUMBER']}" + parallel_cuke_vars['DATABASE_URL'] = "postgres://postgres@postgres#{ENV['TEST_ENV_NUMBER']}:5432/postgres" + parallel_cuke_vars['CONJUR_AUTHN_API_KEY'] = ENV["CONJUR_AUTHN_API_KEY#{ENV['TEST_ENV_NUMBER']}"] + + parallel_cuke_vars.each do |key, value| + ENV[key] = value + end + # Erase the certificate and cert injection logs from each container. kube_client.get_pods(namespace: namespace).select{|p| p.metadata.namespace == namespace}.each do |pod| next unless (ready_status = pod.status.conditions.find { |c| c.type == "Ready" }) diff --git a/cucumber/policy/features/support/client.rb b/cucumber/policy/features/support/client.rb index 3f4af64e13..c8e0b76d80 100644 --- a/cucumber/policy/features/support/client.rb +++ b/cucumber/policy/features/support/client.rb @@ -13,7 +13,7 @@ class Client ADMIN_PASSWORD = 'SEcret12!!!!' ACCOUNT = ENV['CONJUR_ACCOUNT'] || 'cucumber' - APPLIANCE_URL = ENV['CONJUR_APPLIANCE_URL'] || 'http://conjur' + APPLIANCE_URL = ENV['CONJUR_APPLIANCE_URL'] || "http://conjur#{ENV['TEST_ENV_NUMBER']}" class User def initialize(user_type, id) diff --git a/cucumber/policy/features/support/env.rb b/cucumber/policy/features/support/env.rb index 0a9bf2c17d..179752d970 100644 --- a/cucumber/policy/features/support/env.rb +++ b/cucumber/policy/features/support/env.rb @@ -5,7 +5,18 @@ require 'conjur-api' require 'rest-client' -Conjur.configuration.appliance_url = ENV['CONJUR_APPLIANCE_URL'] || 'http://conjur' +parallel_cuke_vars = {} +parallel_cuke_vars['CONJUR_APPLIANCE_URL'] = "http://conjur#{ENV['TEST_ENV_NUMBER']}" +parallel_cuke_vars['DATABASE_URL'] = "postgres://postgres@pg#{ENV['TEST_ENV_NUMBER']}/postgres" +parallel_cuke_vars['CONJUR_AUTHN_API_KEY'] = ENV["CONJUR_AUTHN_API_KEY#{ENV['TEST_ENV_NUMBER']}"] + +parallel_cuke_vars.each do |key, value| + if ENV[key].nil? || ENV[key].empty? + ENV[key] = value + end +end + +Conjur.configuration.appliance_url = ENV['CONJUR_APPLIANCE_URL'] || "http://conjur#{ENV['TEST_ENV_NUMBER']}" Conjur.configuration.account = ENV['CONJUR_ACCOUNT'] || 'cucumber' # This is needed to run the cucumber --profile policy successfully diff --git a/cucumber/policy/features/support/hooks.rb b/cucumber/policy/features/support/hooks.rb index e8742d5d59..2f61a26020 100644 --- a/cucumber/policy/features/support/hooks.rb +++ b/cucumber/policy/features/support/hooks.rb @@ -18,6 +18,15 @@ # Prior to this hook, our tests had hidden coupling. This ensures each test is # run independently. Before do + parallel_cuke_vars = {} + parallel_cuke_vars['CONJUR_APPLIANCE_URL'] = "http://conjur#{ENV['TEST_ENV_NUMBER']}" + parallel_cuke_vars['DATABASE_URL'] = "postgres://postgres@pg#{ENV['TEST_ENV_NUMBER']}/postgres" + parallel_cuke_vars['CONJUR_AUTHN_API_KEY'] = ENV["CONJUR_AUTHN_API_KEY#{ENV['TEST_ENV_NUMBER']}"] + + parallel_cuke_vars.each do |key, value| + ENV[key] = value + end + @user_index = 0 Role.truncate(cascade: true) diff --git a/cucumber/rotators/features/step_definitions/rotator_steps.rb b/cucumber/rotators/features/step_definitions/rotator_steps.rb index 18ce8c7089..576e9ff89d 100644 --- a/cucumber/rotators/features/step_definitions/rotator_steps.rb +++ b/cucumber/rotators/features/step_definitions/rotator_steps.rb @@ -69,6 +69,9 @@ end Given(/^I add the value "(.*)" to variable "(.+)"$/) do |val, id| + if val == "testdb" + val = "#{val}#{ENV['TEST_ENV_NUMBER']}" + end @client.add_secret(id: id, value: val) end diff --git a/cucumber/rotators/features/support/env.rb b/cucumber/rotators/features/support/env.rb index 2cdb385df7..74ec8c5016 100644 --- a/cucumber/rotators/features/support/env.rb +++ b/cucumber/rotators/features/support/env.rb @@ -1,5 +1,16 @@ # frozen_string_literal: true +parallel_cuke_vars = {} +parallel_cuke_vars['CONJUR_APPLIANCE_URL'] = "http://conjur#{ENV['TEST_ENV_NUMBER']}" +parallel_cuke_vars['DATABASE_URL'] = "postgres://postgres@pg#{ENV['TEST_ENV_NUMBER']}/postgres" +parallel_cuke_vars['CONJUR_AUTHN_API_KEY'] = ENV["CONJUR_AUTHN_API_KEY#{ENV['TEST_ENV_NUMBER']}"] + +parallel_cuke_vars.each do |key, value| + if ENV[key].nil? || ENV[key].empty? + ENV[key] = value + end +end + # so that we can require relative to the project root $LOAD_PATH.unshift(Dir.pwd) require 'config/environment' @@ -7,5 +18,5 @@ ENV['RAILS_ENV'] ||= 'test' ENV['CONJUR_LOG_LEVEL'] ||= 'debug' -Conjur.configuration.appliance_url = ENV['CONJUR_APPLIANCE_URL'] || 'http://conjur' +Conjur.configuration.appliance_url = ENV['CONJUR_APPLIANCE_URL'] || "http://conjur#{ENV['TEST_ENV_NUMBER']}" Conjur.configuration.account = ENV['CONJUR_ACCOUNT'] || 'cucumber' diff --git a/cucumber/rotators/features/support/rotator_helpers.rb b/cucumber/rotators/features/support/rotator_helpers.rb index 832961acca..184cb7b67c 100644 --- a/cucumber/rotators/features/support/rotator_helpers.rb +++ b/cucumber/rotators/features/support/rotator_helpers.rb @@ -11,7 +11,7 @@ module RotatorHelpers # Utility for the postgres rotator def run_sql_in_testdb(sql, user="postgres", pw="postgres_secret") - system("PGPASSWORD=#{pw} psql -h testdb -U #{user} -c \"#{sql}\"") + system("PGPASSWORD=#{pw} psql -h testdb#{ENV['TEST_ENV_NUMBER']} -U #{user} -c \"#{sql}\"") end def variable(id) @@ -75,7 +75,7 @@ def current_value # is hardcoded here. This shouldn't be problematic as there's likely no # need to make it dynamic. def pg_login_result(user, pw) - system("PGPASSWORD=#{pw} psql -c \"\\q\" -h testdb -U #{user}") + system("PGPASSWORD=#{pw} psql -c \"\\q\" -h testdb#{ENV['TEST_ENV_NUMBER']} -U #{user}") end end diff --git a/engines/conjur_audit/config/routes.rb b/engines/conjur_audit/config/routes.rb index 76ba647776..70e6a08710 100644 --- a/engines/conjur_audit/config/routes.rb +++ b/engines/conjur_audit/config/routes.rb @@ -1,5 +1,16 @@ # frozen_string_literal: true +parallel_cuke_vars = {} +parallel_cuke_vars['CONJUR_APPLIANCE_URL'] = "http://conjur#{ENV['TEST_ENV_NUMBER']}" +parallel_cuke_vars['DATABASE_URL'] = "postgres://postgres@pg#{ENV['TEST_ENV_NUMBER']}/postgres" +parallel_cuke_vars['CONJUR_AUTHN_API_KEY'] = ENV["CONJUR_AUTHN_API_KEY#{ENV['TEST_ENV_NUMBER']}"] + +parallel_cuke_vars.each do |key, value| + if ENV[key].nil? || ENV[key].empty? + ENV[key] = value + end +end + ConjurAudit::Engine.routes.draw do scope format: false do root 'messages#index' diff --git a/engines/conjur_audit/spec/dummy/config/environments/test.rb b/engines/conjur_audit/spec/dummy/config/environments/test.rb index 08941f0fff..d94bba7023 100644 --- a/engines/conjur_audit/spec/dummy/config/environments/test.rb +++ b/engines/conjur_audit/spec/dummy/config/environments/test.rb @@ -1,5 +1,16 @@ # frozen_string_literal: true +parallel_cuke_vars = {} +parallel_cuke_vars['CONJUR_APPLIANCE_URL'] = "http://conjur#{ENV['TEST_ENV_NUMBER']}" +parallel_cuke_vars['DATABASE_URL'] = "postgres://postgres@pg#{ENV['TEST_ENV_NUMBER']}/postgres" +parallel_cuke_vars['CONJUR_AUTHN_API_KEY'] = ENV["CONJUR_AUTHN_API_KEY#{ENV['TEST_ENV_NUMBER']}"] + +parallel_cuke_vars.each do |key, value| + if ENV[key].nil? || ENV[key].empty? + ENV[key] = value + end +end + Rails.application.configure do config.cache_classes = true config.eager_load = false