From 40ddd8dc378b7711a04bd3ce22c112f7b72b6b53 Mon Sep 17 00:00:00 2001 From: Florian Braun <5863788+FloThinksPi@users.noreply.github.com> Date: Wed, 23 Jul 2025 14:05:46 +0200 Subject: [PATCH 1/2] Fix devcontainer --- .devcontainer/README.md | 97 +++++++++++++ .devcontainer/devcontainer.json | 3 +- .devcontainer/docker-compose.override.yml | 3 +- .devcontainer/images/bbs/Dockerfile | 2 +- .devcontainer/images/devcontainer/Dockerfile | 132 +++++++++++++++++- .devcontainer/images/devcontainer/setup.sh | 59 -------- .devcontainer/scripts/codespaces_init.sh | 17 --- .devcontainer/scripts/codespaces_start.sh | 123 +++++++++++++++- .../scripts/setupDevelopmentEnvironment.sh | 115 --------------- .devcontainer/scripts/setupIDEs.sh | 10 -- docker-compose.yml | 26 +--- 11 files changed, 350 insertions(+), 237 deletions(-) create mode 100644 .devcontainer/README.md delete mode 100755 .devcontainer/images/devcontainer/setup.sh delete mode 100755 .devcontainer/scripts/codespaces_init.sh delete mode 100755 .devcontainer/scripts/setupDevelopmentEnvironment.sh delete mode 100755 .devcontainer/scripts/setupIDEs.sh diff --git a/.devcontainer/README.md b/.devcontainer/README.md new file mode 100644 index 00000000000..1169668ec4d --- /dev/null +++ b/.devcontainer/README.md @@ -0,0 +1,97 @@ +# Cloud Controller Development Environment + +This document outlines how to set up and use the development environment for the Cloud Controller. + +## Getting Started + +1. **Prerequisites**: Ensure you have Docker and Visual Studio Code with the "Remote - Containers" extension installed. +2. **Launch the Environment**: + * Open the `cloud_controller_ng` project folder in VS Code. + * When prompted, click **"Reopen in Container"**. This will build the Docker images and start the development container. Also you can use the command palette (`Ctrl+Shift+P` or `Cmd+Shift+P` on macOS) and select **"Dev Containers: Reopen in Container"** or go to the remote explorer and select **"Reopen in Container"**. + * The initial build may take some time as it installs all dependencies. + +## Interacting with the Environment + +Once the container is running, the startup script (`.devcontainer/scripts/codespaces_start.sh`) will automatically: + +* Set up and seed the PostgreSQL and MariaDB databases. +* Configure the Cloud Controller application. +* Start all necessary services (UAA, Minio, Nginx, etc.). + +## VS Code Launch Configurations + +The `.vscode/launch.json` file contains several configurations for running and debugging different components of the Cloud Controller. You can access these from the "Run and Debug" panel in VS Code. + +### Main Application + +* **`[Postgres] CloudController`**: Runs the main Cloud Controller API using PostgreSQL as the database. +* **`[Mariadb] CloudController`**: Runs the main Cloud Controller API using MariaDB as the database. + +> Note only one can be run at a time. Ensure the other is stopped before starting a new one. + +### Workers & Scheduler + +* **`[Postgres/Mariadb] CC Worker`**: Starts the generic background job worker against the respective database. +* **`[Postgres/Mariadb] CC Local Worker`**: Starts the local background job worker against the respective database. +* **`[Postgres/Mariadb] CC Scheduler`**: Starts the clock process for scheduling recurring tasks against the respective database. + +> Note only one can be run at a time either against Postgres or Mysql. Ensure the other is stopped before starting a new one. + +### Testing + +* **`[Postgres/Mariadb] Unittests`**: Runs the complete RSpec test suite against the specified database. + +### Cloud Foundry CLI + +The environment comes pre-installed with the `cf8-cli` and `uaac`. You can interact with the local Cloud Foundry deployment from the VS Code terminal. + +**API Endpoint**: `http://localhost:80` + +**Admin Credentials**: + +* **Username**: `ccadmin` +* **Password**: `secret` + +**Example Login Workflow**: + +1. **Target the API**: + + ```bash + cf api http://localhost:80 --skip-ssl-validation + ``` + +2. **Log In**: + + ```bash + cf auth ccadmin secret + ``` + +### PostgreSQL and MariaDB Access + +You can connect directly to the databases running in the environment from the VS Code terminal. + +#### PostgreSQL + +* **Host**: `localhost` +* **Port**: `5432` +* **User**: `postgres` +* **Password**: `supersecret` + +**Example Connection**: + +```bash +PGPASSWORD=supersecret psql -h localhost -U postgres -d ccdb +``` + +#### MariaDB (MySQL) + +* **Host**: `127.0.0.1` +* **Port**: `3306` +* **User**: `root` +* **Password**: `supersecret` + +**Example Connection**: + +```bash +mysql -h 127.0.0.1 -u root -psupersecret ccdb +``` diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 4fbbe631148..a861fda6dfb 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -20,8 +20,7 @@ "nginx" ], "workspaceFolder": "/workspace", - "initializeCommand": ".devcontainer/scripts/codespaces_init.sh", - "onCreateCommand": ".devcontainer/scripts/codespaces_start.sh", + "onCreateCommand": "bash .devcontainer/scripts/codespaces_start.sh", "customizations": { // Configure properties specific to VS Code. "vscode": { diff --git a/.devcontainer/docker-compose.override.yml b/.devcontainer/docker-compose.override.yml index a9d2ab587a9..5d718b2ba7f 100644 --- a/.devcontainer/docker-compose.override.yml +++ b/.devcontainer/docker-compose.override.yml @@ -1,4 +1,3 @@ -version: "3.3" services: # Dev Container @@ -8,7 +7,6 @@ services: context: ./ dockerfile: .devcontainer/images/devcontainer/Dockerfile restart: unless-stopped - command: /bin/sh -c "while sleep 1000; do :; done" volumes: - .:/workspace:cached - /var/run/docker.sock:/var/run/docker.sock @@ -17,3 +15,4 @@ services: - SYS_PTRACE security_opt: - seccomp:unconfined + privileged: true diff --git a/.devcontainer/images/bbs/Dockerfile b/.devcontainer/images/bbs/Dockerfile index 41ff1964f07..13116d64edb 100644 --- a/.devcontainer/images/bbs/Dockerfile +++ b/.devcontainer/images/bbs/Dockerfile @@ -1,6 +1,6 @@ FROM golang:1 -RUN apt update && apt install -y jq && git clone --recurse-submodules -b $(curl -s https://api.github.com/repos/cloudfoundry/diego-release/releases/latest | jq -r '.tag_name') https://github.com/cloudfoundry/diego-release && cd ./diego-release/src/code.cloudfoundry.org && \ +RUN apt update && git clone --recurse-submodules -b v2.118.0 https://github.com/cloudfoundry/diego-release && cd ./diego-release/src/code.cloudfoundry.org && \ CG_ENABLED=0 go install code.cloudfoundry.org/locket/cmd/locket && \ CG_ENABLED=0 go install code.cloudfoundry.org/bbs/cmd/bbs VOLUME /bbs diff --git a/.devcontainer/images/devcontainer/Dockerfile b/.devcontainer/images/devcontainer/Dockerfile index 13c83c04fae..00b9c6dbf20 100644 --- a/.devcontainer/images/devcontainer/Dockerfile +++ b/.devcontainer/images/devcontainer/Dockerfile @@ -1,7 +1,129 @@ -FROM mcr.microsoft.com/vscode/devcontainers/ruby:3.2 +FROM mcr.microsoft.com/devcontainers/base:ubuntu-24.04 + + +SHELL ["/bin/bash", "-o", "pipefail", "-c"] + +# Set environment variables for Ruby development +ENV BUNDLE_PATH=/home/vscode/.bundle +ENV GEM_HOME=/home/vscode/.gem +ENV GEM_PATH=/home/vscode/.gem +ENV RAILS_ENV=development +ENV RACK_ENV=development + +# Install system dependencies for Ruby development +RUN wget -q -O - https://packages.cloudfoundry.org/debian/cli.cloudfoundry.org.key | gpg --dearmor -o /usr/share/keyrings/cli.cloudfoundry.org.gpg && \ + echo "deb [signed-by=/usr/share/keyrings/cli.cloudfoundry.org.gpg] https://packages.cloudfoundry.org/debian stable main" > /etc/apt/sources.list.d/cloudfoundry-cli.list && \ + apt-get update && \ + apt-get install -y \ + # CF CLI + cf8-cli \ + # Build tools + build-essential \ + autoconf \ + bison \ + patch \ + # Ruby dependencies + libssl-dev \ + libreadline-dev \ + zlib1g-dev \ + libncurses5-dev \ + libffi-dev \ + libgdbm-dev \ + libyaml-dev \ + libsqlite3-dev \ + sqlite3 \ + libgmp-dev \ + libgdbm6 \ + libdb-dev \ + uuid-dev \ + # Database clients + postgresql-client \ + postgresql-client-common \ + mysql-client \ + redis-tools \ + # Database development headers + libpq-dev \ + default-libmysqlclient-dev \ + # Development tools + curl \ + wget \ + git \ + vim \ + nano \ + htop \ + tree \ + jq \ + unzip \ + zip \ + # Network tools + netcat-openbsd \ + # XML/HTML processing + libxml2-dev \ + libxslt1-dev \ + # GPG for RVM + gnupg2 \ + # Additional utilities + tmux \ + screen \ + less \ + man-db \ + && rm -rf /var/lib/apt/lists/* + +# Install yq v4 for the correct architecture +RUN YQ_ARCH=$(uname -m) && \ + case $YQ_ARCH in \ + x86_64) YQ_ARCH="amd64";; \ + aarch64) YQ_ARCH="arm64";; \ + esac && \ + wget "https://github.com/mikefarah/yq/releases/download/v4.47.1/yq_linux_${YQ_ARCH}" -O /usr/local/bin/yq && \ + chmod +x /usr/local/bin/yq + -COPY --from=docker:dind /usr/local/bin/docker /usr/local/bin/ -COPY .devcontainer/images/devcontainer/setup.sh /tmp/setup.sh -COPY .ruby-version /tmp/.ruby-version USER vscode -RUN /tmp/setup.sh && sudo rm /tmp/setup.sh /tmp/.ruby-version + +# Create necessary directories and set permissions +RUN mkdir -p $BUNDLE_PATH $GEM_HOME && chown -R vscode:vscode $BUNDLE_PATH $GEM_HOME + +# Install GPG keys for RVM +RUN gpg2 --keyserver keyserver.ubuntu.com --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB + +# Install RVM +RUN curl -sSL https://get.rvm.io | bash -s stable --path /home/vscode/.rvm + +# Copy Ruby version file for reference +COPY .ruby-version /tmp/ruby-version + +# Install Ruby using RVM with binary packages when available, fallback to parallel source compilation +RUN /bin/bash -l -c "source /home/vscode/.rvm/scripts/rvm && \ + export rvm_make_opts=\"-j$(nproc)\" && \ + export RUBY_CONFIGURE_OPTS=\"--disable-install-doc\" && \ + rvm install $(cat /tmp/ruby-version) && \ + rvm use $(cat /tmp/ruby-version) --default && \ + gem install bundler" + +# Configure Bundler and install cf-uaac as vscode user +RUN /bin/bash -l -c "source /home/vscode/.rvm/scripts/rvm && \ + bundle config set --global path \"$BUNDLE_PATH\" && \ + bundle config set --global jobs $(nproc) && \ + bundle config set --global specific_platform true && \ + gem install cf-uaac" + +# Configure Git +RUN git config --global core.fileMode false + +# Source RVM and configure shell +RUN echo 'export BUNDLE_PATH="$BUNDLE_PATH"' >> /home/vscode/.bashrc \ + && echo 'export GEM_HOME="$GEM_HOME"' >> /home/vscode/.bashrc \ + && echo 'export GEM_PATH="$GEM_PATH"' >> /home/vscode/.bashrc \ + && echo 'source /home/vscode/.rvm/scripts/rvm' >> /home/vscode/.bashrc + +# Configure Git (useful defaults) +RUN git config --global init.defaultBranch main \ + && git config --global pull.rebase false \ + && git config --global core.autocrlf input + +# Set working directory +WORKDIR /workspace +USER root +# Sleep forever to keep the container running +CMD ["tail", "-f", "/dev/null"] diff --git a/.devcontainer/images/devcontainer/setup.sh b/.devcontainer/images/devcontainer/setup.sh deleted file mode 100755 index 64ac33c39ba..00000000000 --- a/.devcontainer/images/devcontainer/setup.sh +++ /dev/null @@ -1,59 +0,0 @@ -#!/bin/bash -set -Eeuo pipefail -# shellcheck disable=SC2064 -trap "pkill -P $$" EXIT - -setupAptPackages () { - # CF CLI is not available for aarch64 :( - if [[ $(uname -m) == aarch64 ]]; then - PACKAGES="postgresql-client postgresql-client-common mariadb-client ruby-dev" - else - PACKAGES="cf8-cli postgresql-client postgresql-client-common mariadb-client ruby-dev" - fi - - wget -q -O - https://packages.cloudfoundry.org/debian/cli.cloudfoundry.org.key | sudo apt-key add - - echo "deb https://packages.cloudfoundry.org/debian stable main" | sudo tee /etc/apt/sources.list.d/cloudfoundry-cli.list - sudo apt-get update - export DEBIAN_FRONTEND="noninteractive" && echo 'debconf debconf/frontend select Noninteractive' | sudo debconf-set-selections - sudo apt-get install -o Dpkg::Options::="--force-overwrite" $PACKAGES -y -} - -setupRuby () { - rbenv install $(cat /tmp/.ruby-version) - rbenv global $(cat /tmp/.ruby-version) -} - -setupRubyGems () { - gem install cf-uaac -} - -setupCredhubCli () { - set -x - wget "$(curl -s https://api.github.com/repos/cloudfoundry/credhub-cli/releases/latest | - jq -r '.assets[] | select(.name|match("credhub-linux.*")) | .browser_download_url')" -O /tmp/credhub.tar.gz - cd /tmp - sudo tar -xzf /tmp/credhub.tar.gz && sudo rm -f /tmp/credhub.tar.gz && sudo mv /tmp/credhub /usr/bin -} - -setupYqCli () { - sudo wget "$(curl -s https://api.github.com/repos/mikefarah/yq/releases/latest | - jq -r '.assets[] | select(.name|match("linux_amd64$")) | .browser_download_url')" -O /usr/bin/yq - sudo chmod +x /usr/bin/yq -} - -echo """ -export COMPOSE_DOCKER_CLI_BUILD=1 -export DOCKER_BUILDKIT=1 -""" > ~/.bashrc - -setupAptPackages -setupRuby -setupRubyGems -setupCredhubCli -setupYqCli - -# Setup User Permissions -sudo groupadd docker -sudo usermod -aG docker "vscode" - -trap "" EXIT \ No newline at end of file diff --git a/.devcontainer/scripts/codespaces_init.sh b/.devcontainer/scripts/codespaces_init.sh deleted file mode 100755 index c9aa8f38683..00000000000 --- a/.devcontainer/scripts/codespaces_init.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -set -Eeuxo pipefail -set -o allexport -# shellcheck disable=SC2064 -trap "pkill -P $$" EXIT - -# Setup volume mounts -mkdir -p tmp - -# Speed up docker builds by prebuilding with buildx -docker-compose pull || tee tmp/fail & -docker buildx bake -f docker-compose.yml -f .devcontainer/docker-compose.override.yml || tee tmp/fail & - -wait $(jobs -p) -test -f tmp/fail && rm tmp/fail && exit 1 - -trap "" EXIT \ No newline at end of file diff --git a/.devcontainer/scripts/codespaces_start.sh b/.devcontainer/scripts/codespaces_start.sh index 6934883c137..41b8fda779a 100755 --- a/.devcontainer/scripts/codespaces_start.sh +++ b/.devcontainer/scripts/codespaces_start.sh @@ -1,12 +1,123 @@ #!/bin/bash set -Eeuxo pipefail -# shellcheck disable=SC2064 -trap "pkill -P $$" EXIT # Setup IDEs -./.devcontainer/scripts/setupIDEs.sh +cp -a -f .devcontainer/configs/vscode/.vscode . +cp -a -f .devcontainer/configs/intellij/.idea . -# Setup DBs and CC Config File -./.devcontainer/scripts/setupDevelopmentEnvironment.sh +# Database Information +POSTGRES_CONNECTION_PREFIX="postgres://postgres:supersecret@localhost:5432" +MYSQL_CONNECTION_PREFIX="mysql2://root:supersecret@127.0.0.1:3306" -trap "" EXIT \ No newline at end of file +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + +setupPostgres () { + echo "Waiting for postgres..." + while ! pg_isready -h localhost -p 5432 -q -U postgres; do + sleep 1 + done + echo "Postgres is up." + + # Parallel Test DBs + DB="postgres" POSTGRES_CONNECTION_PREFIX="postgres://postgres:supersecret@localhost:5432" bundle exec rake db:pick db:parallel:recreate + # Sequential Test DBs + export PGPASSWORD=supersecret + psql -U postgres -h localhost -tc "SELECT 1 FROM pg_database WHERE datname = 'cc_test'" | grep -q 1 || psql -U postgres -h localhost -c "CREATE DATABASE cc_test" + psql -U postgres -h localhost -tc "SELECT 1 FROM pg_database WHERE datname = 'diego'" | grep -q 1 || psql -U postgres -h localhost -c "CREATE DATABASE diego" + psql -U postgres -h localhost -tc "SELECT 1 FROM pg_database WHERE datname = 'locket'" | grep -q 1 || psql -U postgres -h localhost -c "CREATE DATABASE locket" + # Main DB + DB="postgres" DB_CONNECTION_STRING="postgres://postgres:supersecret@localhost:5432/ccdb" bundle exec rake db:recreate db:migrate db:seed +} + +setupMariadb () { + echo "Waiting for mysql..." + while ! mysqladmin ping -h 127.0.0.1 -u root -psupersecret --silent; do + sleep 1 + done + echo "MySQL is up." + + # Parallel Test DBs + DB="mysql" MYSQL_CONNECTION_PREFIX="mysql2://root:supersecret@127.0.0.1:3306" bundle exec rake db:pick db:parallel:recreate + # Sequential Test DBs + mysql -h 127.0.0.1 -u root -psupersecret -e "CREATE DATABASE IF NOT EXISTS cc_test; CREATE DATABASE IF NOT EXISTS diego; CREATE DATABASE IF NOT EXISTS locket;" + # Main DB + DB="mysql" DB_CONNECTION_STRING="mysql2://root:supersecret@127.0.0.1:3306/ccdb" bundle exec rake db:recreate db:migrate db:seed +} + +# Install packages +bundle config set --local with 'debug' +bundle install -j"$(nproc)" + +# Setup Containers +setupPostgres & +POSTGRES_PID=$! +setupMariadb & +MARIADB_PID=$! + +wait $POSTGRES_PID +wait $MARIADB_PID + +# CC config +mkdir -p tmp +cp -a config/cloud_controller.yml tmp/cloud_controller.yml + +yq -i e '.external_domain="localhost"' tmp/cloud_controller.yml +yq -i e '.system_domain="localhost"' tmp/cloud_controller.yml + +yq -i e '.login.url="http://localhost:8080"' tmp/cloud_controller.yml +yq -i e '.login.enabled=true' tmp/cloud_controller.yml + +yq -i e '.nginx.use_nginx=true' tmp/cloud_controller.yml +yq -i e '.nginx.instance_socket=""' tmp/cloud_controller.yml + +yq -i e '.logging.file="tmp/cloud_controller.log"' tmp/cloud_controller.yml +yq -i e '.telemetry_log_path="tmp/cloud_controller_telemetry.log"' tmp/cloud_controller.yml +TMPDIR=$(pwd)/tmp yq -i e '.directories.tmpdir=env(TMPDIR)' tmp/cloud_controller.yml; +yq -i e '.directories.diagnostics="tmp"' tmp/cloud_controller.yml +yq -i e '.security_event_logging.enabled=true' tmp/cloud_controller.yml +yq -i e '.security_event_logging.file="tmp/cef.log"' tmp/cloud_controller.yml + +yq -i e '.uaa.url="http://localhost:8080"' tmp/cloud_controller.yml +yq -i e '.uaa.internal_url="http://localhost:8080"' tmp/cloud_controller.yml +yq -i e '.uaa.resource_id="cloud_controller"' tmp/cloud_controller.yml +yq -i e 'del(.uaa.symmetric_secret)' tmp/cloud_controller.yml + +yq -i e '.resource_pool.fog_connection.provider="AWS"' tmp/cloud_controller.yml +yq -i e '.resource_pool.fog_connection.endpoint="http://localhost:9001"' tmp/cloud_controller.yml +yq -i e '.resource_pool.fog_connection.aws_access_key_id="minioadmin"' tmp/cloud_controller.yml +yq -i e '.resource_pool.fog_connection.aws_secret_access_key="minioadmin"' tmp/cloud_controller.yml +yq -i e '.resource_pool.fog_connection.aws_signature_version=2' tmp/cloud_controller.yml +yq -i e '.resource_pool.fog_connection.path_style=true' tmp/cloud_controller.yml + +yq -i e '.packages.fog_connection.provider="AWS"' tmp/cloud_controller.yml +yq -i e '.packages.fog_connection.endpoint="http://localhost:9001"' tmp/cloud_controller.yml +yq -i e '.packages.fog_connection.aws_access_key_id="minioadmin"' tmp/cloud_controller.yml +yq -i e '.packages.fog_connection.aws_secret_access_key="minioadmin"' tmp/cloud_controller.yml +yq -i e '.packages.fog_connection.aws_signature_version=2' tmp/cloud_controller.yml +yq -i e '.packages.fog_connection.path_style=true' tmp/cloud_controller.yml + +yq -i e '.droplets.fog_connection.provider="AWS"' tmp/cloud_controller.yml +yq -i e '.droplets.fog_connection.endpoint="http://localhost:9001"' tmp/cloud_controller.yml +yq -i e '.droplets.fog_connection.aws_access_key_id="minioadmin"' tmp/cloud_controller.yml +yq -i e '.droplets.fog_connection.aws_secret_access_key="minioadmin"' tmp/cloud_controller.yml +yq -i e '.droplets.fog_connection.aws_signature_version=2' tmp/cloud_controller.yml +yq -i e '.droplets.fog_connection.path_style=true' tmp/cloud_controller.yml + +yq -i e '.buildpacks.fog_connection.provider="AWS"' tmp/cloud_controller.yml +yq -i e '.buildpacks.fog_connection.endpoint="http://localhost:9001"' tmp/cloud_controller.yml +yq -i e '.buildpacks.fog_connection.aws_access_key_id="minioadmin"' tmp/cloud_controller.yml +yq -i e '.buildpacks.fog_connection.aws_secret_access_key="minioadmin"' tmp/cloud_controller.yml +yq -i e '.buildpacks.fog_connection.aws_signature_version=2' tmp/cloud_controller.yml +yq -i e '.buildpacks.fog_connection.path_style=true' tmp/cloud_controller.yml + +yq -i e '.cloud_controller_username_lookup_client_name="login"' tmp/cloud_controller.yml +yq -i e '.cloud_controller_username_lookup_client_secret="loginsecret"' tmp/cloud_controller.yml + +yq -i e '.diego.bbs.url="http://127.0.0.1:1234"' tmp/cloud_controller.yml +yq -i e '.diego.bbs.key_file="spec/fixtures/certs/bbs_client.key"' tmp/cloud_controller.yml +yq -i e '.diego.bbs.cert_file="spec/fixtures/certs/bbs_client.crt"' tmp/cloud_controller.yml +yq -i e '.diego.bbs.ca_file="spec/fixtures/certs/bbs_ca.crt"' tmp/cloud_controller.yml + +yq -i e '.packages.max_package_size=2147483648' tmp/cloud_controller.yml + +# Exit if any error happened diff --git a/.devcontainer/scripts/setupDevelopmentEnvironment.sh b/.devcontainer/scripts/setupDevelopmentEnvironment.sh deleted file mode 100755 index d4522b8ec5e..00000000000 --- a/.devcontainer/scripts/setupDevelopmentEnvironment.sh +++ /dev/null @@ -1,115 +0,0 @@ -#!/bin/bash -set -Eeuo pipefail -# shellcheck disable=SC2064 -trap "pkill -P $$" EXIT - -# Database Information -POSTGRES_CONNECTION_PREFIX="postgres://postgres:supersecret@localhost:5432" -MYSQL_CONNECTION_PREFIX="mysql2://root:supersecret@127.0.0.1:3306" - -SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) - -setupPostgres () { - export DB="postgres" - # Parallel Test DBs - export POSTGRES_CONNECTION_PREFIX - bundle exec rake db:pick db:parallel:recreate - # Sequential Test DBs - export PGPASSWORD=supersecret - psql -U postgres -h localhost -tc "SELECT 1 FROM pg_database WHERE datname = 'cc_test'" | grep -q 1 || psql -U postgres -h localhost -c "CREATE DATABASE cc_test" - psql -U postgres -h localhost -tc "SELECT 1 FROM pg_database WHERE datname = 'diego'" | grep -q 1 || psql -U postgres -h localhost -c "CREATE DATABASE diego" - psql -U postgres -h localhost -tc "SELECT 1 FROM pg_database WHERE datname = 'locket'" | grep -q 1 || psql -U postgres -h localhost -c "CREATE DATABASE locket" - # Main DB - export DB_CONNECTION_STRING="${POSTGRES_CONNECTION_PREFIX}/ccdb" - bundle exec rake db:recreate db:migrate db:seed -} - -setupMariadb () { - export DB="mysql" - # Parallel Test DBs - export MYSQL_CONNECTION_PREFIX - bundle exec rake db:pick db:parallel:recreate - # Sequential Test DBs - mysql -h 127.0.0.1 -u root -psupersecret -e "CREATE DATABASE IF NOT EXISTS cc_test; CREATE DATABASE IF NOT EXISTS diego; CREATE DATABASE IF NOT EXISTS locket;" - # Main DB - export DB_CONNECTION_STRING="${MYSQL_CONNECTION_PREFIX}/ccdb" - bundle exec rake db:recreate db:migrate db:seed -} - -# Install packages -bundle config set --local with 'debug' -bundle install - -# Setup Containers -setupPostgres || tee tmp/fail & -setupMariadb || tee tmp/fail & - -# CC config -mkdir -p tmp -cp -a config/cloud_controller.yml tmp/cloud_controller.yml - -yq -i e '.external_domain="localhost"' tmp/cloud_controller.yml -yq -i e '.system_domain="localhost"' tmp/cloud_controller.yml - -yq -i e '.login.url="http://localhost:8080"' tmp/cloud_controller.yml -yq -i e '.login.enabled=true' tmp/cloud_controller.yml - -yq -i e '.nginx.use_nginx=true' tmp/cloud_controller.yml -yq -i e '.nginx.instance_socket=""' tmp/cloud_controller.yml - -yq -i e '.logging.file="tmp/cloud_controller.log"' tmp/cloud_controller.yml -yq -i e '.telemetry_log_path="tmp/cloud_controller_telemetry.log"' tmp/cloud_controller.yml -TMPDIR=$(pwd)/tmp yq -i e '.directories.tmpdir=env(TMPDIR)' tmp/cloud_controller.yml; -yq -i e '.directories.diagnostics="tmp"' tmp/cloud_controller.yml -yq -i e '.security_event_logging.enabled=true' tmp/cloud_controller.yml -yq -i e '.security_event_logging.file="tmp/cef.log"' tmp/cloud_controller.yml - -yq -i e '.uaa.url="http://localhost:8080"' tmp/cloud_controller.yml -yq -i e '.uaa.internal_url="http://localhost:8080"' tmp/cloud_controller.yml -yq -i e '.uaa.resource_id="cloud_controller"' tmp/cloud_controller.yml -yq -i e 'del(.uaa.symmetric_secret)' tmp/cloud_controller.yml - -yq -i e '.resource_pool.fog_connection.provider="AWS"' tmp/cloud_controller.yml -yq -i e '.resource_pool.fog_connection.endpoint="http://localhost:9001"' tmp/cloud_controller.yml -yq -i e '.resource_pool.fog_connection.aws_access_key_id="minioadmin"' tmp/cloud_controller.yml -yq -i e '.resource_pool.fog_connection.aws_secret_access_key="minioadmin"' tmp/cloud_controller.yml -yq -i e '.resource_pool.fog_connection.aws_signature_version=2' tmp/cloud_controller.yml -yq -i e '.resource_pool.fog_connection.path_style=true' tmp/cloud_controller.yml - -yq -i e '.packages.fog_connection.provider="AWS"' tmp/cloud_controller.yml -yq -i e '.packages.fog_connection.endpoint="http://localhost:9001"' tmp/cloud_controller.yml -yq -i e '.packages.fog_connection.aws_access_key_id="minioadmin"' tmp/cloud_controller.yml -yq -i e '.packages.fog_connection.aws_secret_access_key="minioadmin"' tmp/cloud_controller.yml -yq -i e '.packages.fog_connection.aws_signature_version=2' tmp/cloud_controller.yml -yq -i e '.packages.fog_connection.path_style=true' tmp/cloud_controller.yml - -yq -i e '.droplets.fog_connection.provider="AWS"' tmp/cloud_controller.yml -yq -i e '.droplets.fog_connection.endpoint="http://localhost:9001"' tmp/cloud_controller.yml -yq -i e '.droplets.fog_connection.aws_access_key_id="minioadmin"' tmp/cloud_controller.yml -yq -i e '.droplets.fog_connection.aws_secret_access_key="minioadmin"' tmp/cloud_controller.yml -yq -i e '.droplets.fog_connection.aws_signature_version=2' tmp/cloud_controller.yml -yq -i e '.droplets.fog_connection.path_style=true' tmp/cloud_controller.yml - -yq -i e '.buildpacks.fog_connection.provider="AWS"' tmp/cloud_controller.yml -yq -i e '.buildpacks.fog_connection.endpoint="http://localhost:9001"' tmp/cloud_controller.yml -yq -i e '.buildpacks.fog_connection.aws_access_key_id="minioadmin"' tmp/cloud_controller.yml -yq -i e '.buildpacks.fog_connection.aws_secret_access_key="minioadmin"' tmp/cloud_controller.yml -yq -i e '.buildpacks.fog_connection.aws_signature_version=2' tmp/cloud_controller.yml -yq -i e '.buildpacks.fog_connection.path_style=true' tmp/cloud_controller.yml - -yq -i e '.cloud_controller_username_lookup_client_name="login"' tmp/cloud_controller.yml -yq -i e '.cloud_controller_username_lookup_client_secret="loginsecret"' tmp/cloud_controller.yml - -yq -i e '.diego.bbs.url="http://127.0.0.1:1234"' tmp/cloud_controller.yml -yq -i e '.diego.bbs.key_file="spec/fixtures/certs/bbs_client.key"' tmp/cloud_controller.yml -yq -i e '.diego.bbs.cert_file="spec/fixtures/certs/bbs_client.crt"' tmp/cloud_controller.yml -yq -i e '.diego.bbs.ca_file="spec/fixtures/certs/bbs_ca.crt"' tmp/cloud_controller.yml - -yq -i e '.packages.max_package_size=2147483648' tmp/cloud_controller.yml - -# Wait for background jobs and exit 1 if any error happened -# shellcheck disable=SC2046 -wait $(jobs -p) -test -f tmp/fail && rm tmp/fail && exit 1 - -trap "" EXIT \ No newline at end of file diff --git a/.devcontainer/scripts/setupIDEs.sh b/.devcontainer/scripts/setupIDEs.sh deleted file mode 100755 index 19b9a2391b8..00000000000 --- a/.devcontainer/scripts/setupIDEs.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -set -Eeuo pipefail -# shellcheck disable=SC2064 -trap "pkill -P $$" EXIT - -# Setup IDEs -cp -a -f .devcontainer/configs/vscode/.vscode . -cp -a -f .devcontainer/configs/intellij/.idea . - -trap "" EXIT \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 3e13466f356..bddb9d4df70 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,3 @@ -version: "3.3" services: # Postgres @@ -7,10 +6,11 @@ services: image: postgres:16-alpine environment: POSTGRES_PASSWORD: supersecret - command: # logs every statement sent to the server. Slow, but helpful. Fills up disk Quickly ! + command: + # logs every statement sent to the server. Slow, but helpful. Fills up disk Quickly ! - "postgres" - - "-c" - - "log_statement=all" + # - "-c" + # - "log_statement=all" ports: - "127.0.0.1:5432:5432" networks: @@ -100,13 +100,7 @@ services: environment: MC_HOST_custom: http://localhost:9001 healthcheck: - test: - [ - "CMD", - "mc", - "ready", - "custom" - ] + test: [ "CMD", "mc", "ready", "custom" ] interval: 30s timeout: 20s retries: 3 @@ -124,13 +118,7 @@ services: - "127.0.0.1:9292:9292" - "127.0.0.1:9393:80" healthcheck: - test: - [ - "CMD", - "curl", - "-f", - "http://localhost:9292/v2/catalog" - ] + test: [ "CMD", "curl", "-f", "http://localhost:9292/v2/catalog" ] interval: 30s timeout: 20s retries: 3 @@ -152,12 +140,10 @@ services: - cc-net volumes: - .devcontainer/images/nginx/conf:/usr/local/nginx/conf:ro - - ./tmp:/tmp cap_add: - NET_BIND_SERVICE extra_hosts: - "host.docker.internal:host-gateway" restart: unless-stopped - networks: cc-net: From 64310f0caecb7ab1493903c9fb08764b2044193d Mon Sep 17 00:00:00 2001 From: Florian Braun <5863788+FloThinksPi@users.noreply.github.com> Date: Fri, 1 Aug 2025 15:18:43 +0200 Subject: [PATCH 2/2] Make DBs run on ramdisk and optimize rspec parallelism Since in a dev scenario we dont care about data loss we can speed up the tests majorly by running the DBs on a ramdisk. Also since the DB consumes CPU during the test its not good to start rspec executes equal to the number of CPU cores since the sume of load excedes that by a lot. So we leave 20% spare for the DB to have less CPU preemtive scheduling and all in all finish the rspecs faster. An environment variable was introduced PARALLEL_TEST_PROCESSORS_MULTIPLE that can be set to any floating point value and multiplies the CPU core count times PARALLEL_TEST_PROCESSORS_MULTIPLE. It defaults to 0.8 and rounds the CPU count up to the next integer so it cannot be less than 1 ever. Apply DB performance optimizations also for github actions --- .devcontainer/devcontainer.json | 2 +- .../{codespaces_start.sh => startup.sh} | 8 ++--- .github/workflows/unit_tests.yml | 27 +++++++++++++-- .../unit_tests_backwards_compatibility.yml | 28 +++++++++++++-- devenv.sh | 16 ++++----- docker-compose.yml | 34 +++++++++++++++++-- lib/tasks/spec.rake | 5 ++- 7 files changed, 98 insertions(+), 22 deletions(-) rename .devcontainer/scripts/{codespaces_start.sh => startup.sh} (95%) mode change 100755 => 100644 diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index a861fda6dfb..01df7627c67 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -20,7 +20,7 @@ "nginx" ], "workspaceFolder": "/workspace", - "onCreateCommand": "bash .devcontainer/scripts/codespaces_start.sh", + "onCreateCommand": "bash .devcontainer/scripts/startup.sh", "customizations": { // Configure properties specific to VS Code. "vscode": { diff --git a/.devcontainer/scripts/codespaces_start.sh b/.devcontainer/scripts/startup.sh old mode 100755 new mode 100644 similarity index 95% rename from .devcontainer/scripts/codespaces_start.sh rename to .devcontainer/scripts/startup.sh index 41b8fda779a..def264b1606 --- a/.devcontainer/scripts/codespaces_start.sh +++ b/.devcontainer/scripts/startup.sh @@ -19,6 +19,7 @@ setupPostgres () { echo "Postgres is up." # Parallel Test DBs + export PARALLEL_TEST_PROCESSORS=`ruby -e 'require "etc"; puts (Etc.nprocessors * (ENV["PARALLEL_TEST_PROCESSORS_MULTIPLE"] || 0.8).to_f).ceil'` DB="postgres" POSTGRES_CONNECTION_PREFIX="postgres://postgres:supersecret@localhost:5432" bundle exec rake db:pick db:parallel:recreate # Sequential Test DBs export PGPASSWORD=supersecret @@ -37,6 +38,7 @@ setupMariadb () { echo "MySQL is up." # Parallel Test DBs + export PARALLEL_TEST_PROCESSORS=`ruby -e 'require "etc"; puts (Etc.nprocessors * (ENV["PARALLEL_TEST_PROCESSORS_MULTIPLE"] || 0.8).to_f).ceil'` DB="mysql" MYSQL_CONNECTION_PREFIX="mysql2://root:supersecret@127.0.0.1:3306" bundle exec rake db:pick db:parallel:recreate # Sequential Test DBs mysql -h 127.0.0.1 -u root -psupersecret -e "CREATE DATABASE IF NOT EXISTS cc_test; CREATE DATABASE IF NOT EXISTS diego; CREATE DATABASE IF NOT EXISTS locket;" @@ -54,9 +56,6 @@ POSTGRES_PID=$! setupMariadb & MARIADB_PID=$! -wait $POSTGRES_PID -wait $MARIADB_PID - # CC config mkdir -p tmp cp -a config/cloud_controller.yml tmp/cloud_controller.yml @@ -120,4 +119,5 @@ yq -i e '.diego.bbs.ca_file="spec/fixtures/certs/bbs_ca.crt"' tmp/cloud_controll yq -i e '.packages.max_package_size=2147483648' tmp/cloud_controller.yml -# Exit if any error happened +wait $POSTGRES_PID +wait $MARIADB_PID diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index ba493331473..046b5b91b66 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -54,12 +54,24 @@ jobs: --health-interval 10s --health-timeout 5s --health-retries 5 + --tmpfs /var/lib/postgresql/data + --shm-size=256m ports: - 5432:5432 steps: - uses: hmarr/debug-action@v3 - uses: actions/checkout@v4 - uses: ./.github/workflows/composite/setup + - name: Configure PostgreSQL for performance + run: | + sudo apt-get update && sudo apt-get install -y postgresql-client + PGPASSWORD=rootpassword psql -h localhost -U postgres -c "ALTER SYSTEM SET fsync = off;" + PGPASSWORD=rootpassword psql -h localhost -U postgres -c "ALTER SYSTEM SET synchronous_commit = off;" + PGPASSWORD=rootpassword psql -h localhost -U postgres -c "ALTER SYSTEM SET full_page_writes = off;" + PGPASSWORD=rootpassword psql -h localhost -U postgres -c "ALTER SYSTEM SET checkpoint_completion_target = 0.9;" + PGPASSWORD=rootpassword psql -h localhost -U postgres -c "ALTER SYSTEM SET wal_buffers = '16MB';" + PGPASSWORD=rootpassword psql -h localhost -U postgres -c "ALTER SYSTEM SET shared_buffers = '256MB';" + PGPASSWORD=rootpassword psql -h localhost -U postgres -c "SELECT pg_reload_conf();" - name: Run tests run: DB=postgres POSTGRES_CONNECTION_PREFIX="postgres://postgres:rootpassword@localhost:5432" bundle exec rake spec - uses: ravsamhq/notify-slack-action@v2 @@ -83,13 +95,24 @@ jobs: env: MYSQL_DATABASE: cc_test MYSQL_ROOT_PASSWORD: password - options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 + options: >- + --health-cmd="mysqladmin ping" + --health-interval=10s + --health-timeout=5s + --health-retries=3 + --tmpfs /var/lib/mysql + --tmpfs /tmp ports: - 3306:3306 steps: - uses: hmarr/debug-action@v3 - uses: actions/checkout@v4 - uses: ./.github/workflows/composite/setup + - name: Configure MySQL for performance + run: | + sudo apt-get update && sudo apt-get install -y mysql-client + mysql -h 127.0.0.1 -u root -ppassword -e "SET GLOBAL innodb_flush_log_at_trx_commit = 0;" + mysql -h 127.0.0.1 -u root -ppassword -e "SET GLOBAL innodb_buffer_pool_size = 268435456;" - name: Run tests run: DB=mysql MYSQL_CONNECTION_PREFIX="mysql2://root:password@127.0.0.1:3306" bundle exec rake spec - uses: ravsamhq/notify-slack-action@v2 @@ -98,4 +121,4 @@ jobs: status: ${{ job.status }} notify_when: 'failure' # default is 'success,failure,warnings' env: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} # required \ No newline at end of file + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} # required diff --git a/.github/workflows/unit_tests_backwards_compatibility.yml b/.github/workflows/unit_tests_backwards_compatibility.yml index 61840c56837..1083443a725 100644 --- a/.github/workflows/unit_tests_backwards_compatibility.yml +++ b/.github/workflows/unit_tests_backwards_compatibility.yml @@ -4,7 +4,6 @@ concurrency: cancel-in-progress: true on: workflow_dispatch: - description: "This action tests backwards compatibility when db migrations are introduced. It tests database schema at new code(old_cc_ref) with unittests running old code(new_cc_ref) " inputs: old_cc_ref: description: 'Old Version of CC_NG that the backwards compatibility should be checked against' @@ -44,6 +43,8 @@ jobs: --health-interval 10s --health-timeout 5s --health-retries 5 + --tmpfs /var/lib/postgresql/data + --shm-size=256m ports: - 5432:5432 steps: @@ -63,6 +64,16 @@ jobs: )}} - name: Setup Environment uses: ./.github/workflows/composite/setup + - name: Configure PostgreSQL for performance + run: | + sudo apt-get update && sudo apt-get install -y postgresql-client + PGPASSWORD=rootpassword psql -h localhost -U postgres -c "ALTER SYSTEM SET fsync = off;" + PGPASSWORD=rootpassword psql -h localhost -U postgres -c "ALTER SYSTEM SET synchronous_commit = off;" + PGPASSWORD=rootpassword psql -h localhost -U postgres -c "ALTER SYSTEM SET full_page_writes = off;" + PGPASSWORD=rootpassword psql -h localhost -U postgres -c "ALTER SYSTEM SET checkpoint_completion_target = 0.9;" + PGPASSWORD=rootpassword psql -h localhost -U postgres -c "ALTER SYSTEM SET wal_buffers = '16MB';" + PGPASSWORD=rootpassword psql -h localhost -U postgres -c "ALTER SYSTEM SET shared_buffers = '256MB';" + PGPASSWORD=rootpassword psql -h localhost -U postgres -c "SELECT pg_reload_conf();" - name: Migrate Database run: DB=postgres POSTGRES_CONNECTION_PREFIX="postgres://postgres:rootpassword@localhost:5432" bundle exec rake db:parallel:recreate db:parallel:migrate - name: Checkout code to run the unit tests with @@ -92,7 +103,13 @@ jobs: env: MYSQL_DATABASE: cc_test MYSQL_ROOT_PASSWORD: password - options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 + options: >- + --health-cmd="mysqladmin ping" + --health-interval=10s + --health-timeout=5s + --health-retries=3 + --tmpfs /var/lib/mysql + --tmpfs /tmp ports: - 3306:3306 steps: @@ -112,6 +129,11 @@ jobs: )}} - name: Setup Environment uses: ./.github/workflows/composite/setup + - name: Configure MySQL for performance + run: | + sudo apt-get update && sudo apt-get install -y mysql-client + mysql -h 127.0.0.1 -u root -ppassword -e "SET GLOBAL innodb_flush_log_at_trx_commit = 0;" + mysql -h 127.0.0.1 -u root -ppassword -e "SET GLOBAL innodb_buffer_pool_size = 268435456;" - name: Migrate Database run: DB=mysql MYSQL_CONNECTION_PREFIX="mysql2://root:password@127.0.0.1:3306" bundle exec rake db:parallel:recreate db:parallel:migrate - name: Checkout code to run the unit tests with @@ -125,4 +147,4 @@ jobs: - name: Setup Environment uses: ./.github/workflows/composite/setup - name: Run tests - run: DB=mysql MYSQL_CONNECTION_PREFIX="mysql2://root:password@127.0.0.1:3306" bundle exec rake spec:without_migrate \ No newline at end of file + run: DB=mysql MYSQL_CONNECTION_PREFIX="mysql2://root:password@127.0.0.1:3306" bundle exec rake spec:without_migrate diff --git a/devenv.sh b/devenv.sh index c9ea501bcb5..6a7d17af681 100755 --- a/devenv.sh +++ b/devenv.sh @@ -18,27 +18,27 @@ help_command() { # Create a clean development environment create_command(){ - docker-compose -p "" down + docker compose -p "" down docker buildx bake -f docker-compose.yml & - docker-compose -p "" pull & + docker compose -p "" pull & wait $(jobs -p) - docker-compose -p "" up -d --build - ./.devcontainer/scripts/setupDevelopmentEnvironment.sh + docker compose -p "" up -d --build + ./.devcontainer/scripts/startup.sh } # Start containers start_command(){ - docker-compose -p "" start + docker compose -p "" start } # Stop containers stop_command(){ - docker-compose -p "" stop + docker compose -p "" stop } # Remove containers destroy_command(){ - docker-compose -p "" down + docker compose -p "" down } # Call Setup IDEs Script @@ -72,7 +72,7 @@ fi # Check Prerequisites export should_exit=0 # Check Path Exists -for p in docker docker-compose ruby bundle mysql psql yq; do +for p in docker ruby bundle mysql psql yq; do if ! command -v "${p}" >/dev/null 2>&1; then echo "Error: Dependency \"$p\" is not installed" && export should_exit=1 fi diff --git a/docker-compose.yml b/docker-compose.yml index bddb9d4df70..5c41b980614 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,10 +7,26 @@ services: environment: POSTGRES_PASSWORD: supersecret command: - # logs every statement sent to the server. Slow, but helpful. Fills up disk Quickly ! - "postgres" - # - "-c" - # - "log_statement=all" + - "-c" + - "fsync=off" + - "-c" + - "synchronous_commit=off" + - "-c" + - "full_page_writes=off" + - "-c" + - "log_statement=all" + - "-c" + - "checkpoint_segments=32" + - "-c" + - "checkpoint_completion_target=0.9" + - "-c" + - "wal_buffers=16MB" + - "-c" + - "shared_buffers=256MB" + tmpfs: + - /var/lib/postgresql/data + shm_size: 256m ports: - "127.0.0.1:5432:5432" networks: @@ -28,6 +44,18 @@ services: image: mysql:8.2 environment: MYSQL_ROOT_PASSWORD: supersecret + command: + - "--innodb-flush-method=nosync" + - "--innodb-flush-log-at-trx-commit=0" + - "--innodb-doublewrite=0" + - "--innodb-buffer-pool-size=256M" + - "--innodb-log-buffer-size=16M" + - "--innodb-log-file-size=64M" + - "--skip-innodb-checksums" + - "--skip-sync-frm" + tmpfs: + - /var/lib/mysql + - /tmp ports: - "127.0.0.1:3306:3306" healthcheck: diff --git a/lib/tasks/spec.rake b/lib/tasks/spec.rake index 06b8e5923c3..16426c2908a 100644 --- a/lib/tasks/spec.rake +++ b/lib/tasks/spec.rake @@ -2,7 +2,9 @@ desc 'Runs all specs' task spec: 'spec:all' namespace :spec do - task all: ['db:pick', 'db:parallel:recreate'] do + task all: ['db:pick'] do + ENV['PARALLEL_TEST_PROCESSORS'] = (Etc.nprocessors * (ENV['PARALLEL_TEST_PROCESSORS_MULTIPLE'] || 0.8).to_f).ceil.to_s + Rake::Task['db:parallel:recreate'].invoke if ARGV[1] run_specs(ARGV[1]) else @@ -44,6 +46,7 @@ namespace :spec do def run_specs_parallel(path, env_vars='') command = <<~CMD #{env_vars} bundle exec parallel_rspec \ + -n `ruby -e 'require "etc"; puts (Etc.nprocessors * (ENV["PARALLEL_TEST_PROCESSORS_MULTIPLE"] || 0.8).to_f).ceil'` \ --test-options '--order rand' \ --single spec/integration/ \ --single spec/acceptance/ \