diff --git a/.docker/apache/000-default.conf b/.docker/apache/000-default.conf new file mode 100644 index 00000000..6ab0572a --- /dev/null +++ b/.docker/apache/000-default.conf @@ -0,0 +1,15 @@ + + ServerAdmin webmaster@localhost + DocumentRoot ${APACHE_DOCUMENT_ROOT} + + ErrorLog ${APACHE_LOG_DIR}/error.log + CustomLog ${APACHE_LOG_DIR}/access.log combined + + + Options Indexes FollowSymLinks + AllowOverride All + Require all granted + Order Allow,Deny + Allow from All + + diff --git a/.docker/php/cachet.ini b/.docker/php/cachet.ini new file mode 100644 index 00000000..a6371b51 --- /dev/null +++ b/.docker/php/cachet.ini @@ -0,0 +1,6 @@ +expose_php = off +display_errors = off +max_execution_time = 90 +memory_limit = 512M +upload_max_filesize = 100M +post_max_size = 100M diff --git a/.dockerignore b/.dockerignore index a50b76ae..e94558c6 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,2 @@ -* -!entrypoint.sh -!conf +.github +.env.example diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..a768cd95 --- /dev/null +++ b/.env.example @@ -0,0 +1,65 @@ +APP_NAME=Cachet +APP_ENV=production +APP_KEY= +APP_DEBUG=false +APP_URL=http://localhost + +APP_LOCALE=en +APP_FALLBACK_LOCALE=en +APP_FAKER_LOCALE=en_US + +APP_MAINTENANCE_DRIVER=file +# APP_MAINTENANCE_STORE=database + +PHP_CLI_SERVER_WORKERS=4 + +BCRYPT_ROUNDS=12 + +LOG_CHANNEL=stack +LOG_STACK=single +LOG_DEPRECATIONS_CHANNEL=null +LOG_LEVEL=debug + +DB_CONNECTION=mariadb +DB_HOST=cachet-mariadb +DB_PORT=3306 +DB_DATABASE=cachet +DB_USERNAME=root +DB_PASSWORD="ExamplePassword!2020!" + +SESSION_DRIVER=database +SESSION_LIFETIME=120 +SESSION_ENCRYPT=false +SESSION_PATH=/ +SESSION_DOMAIN=null + +BROADCAST_CONNECTION=log +FILESYSTEM_DISK=local +QUEUE_CONNECTION=database + +CACHE_STORE=database +# CACHE_PREFIX= + +MEMCACHED_HOST=127.0.0.1 + +REDIS_CLIENT=phpredis +REDIS_HOST=127.0.0.1 +REDIS_PASSWORD=null +REDIS_PORT=6379 + +MAIL_MAILER=log +MAIL_SCHEME=null +MAIL_HOST=127.0.0.1 +MAIL_PORT=2525 +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_FROM_ADDRESS="hello@example.com" +MAIL_FROM_NAME="${APP_NAME}" + +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_DEFAULT_REGION=us-east-1 +AWS_BUCKET= +AWS_USE_PATH_STYLE_ENDPOINT=false + +VITE_APP_NAME="${APP_NAME}" \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..debd3c97 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: + - package-ecosystem: "docker" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" diff --git a/.github/workflows/blank.yml b/.github/workflows/blank.yml deleted file mode 100644 index 9f4d3dc2..00000000 --- a/.github/workflows/blank.yml +++ /dev/null @@ -1,37 +0,0 @@ -# This is a basic workflow to help you get started with Actions - -name: CI - -# Controls when the action will run. -on: - # Triggers the workflow on push or pull request events but only for the main branch - push: - branches: [ main ] - pull_request: - branches: [ main ] - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -# A workflow run is made up of one or more jobs that can run sequentially or in parallel -jobs: - # This workflow contains a single job called "build" - build: - # The type of runner that the job will run on - runs-on: ubuntu-latest - - # Steps represent a sequence of tasks that will be executed as part of the job - steps: - - name: Install bats - run: sudo apt-get install bats - - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v2 - - # Runs a set of commands using the runners shell - - name: Run bash automated test suite (bats) - run: | - docker pull curlimages/curl:latest - bats test/test.full.bats - bats test/test.mysql.bats - bats test/test.sqlite.bats diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 00000000..86a850a1 --- /dev/null +++ b/.github/workflows/docker-publish.yml @@ -0,0 +1,89 @@ +name: Docker + +on: + push: + tags: [ 'v*.*.*' ] + branches: [ 'main', 'test', 'feature/*', '3.x' ] + +env: + IMAGE_NAME: ${{ github.repository }} + DOCKER_REPO: ${{ vars.DOCKER_REPO || 'cachethq/docker' }} + + +jobs: + build: + + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + # This is used to complete the identity challenge + # with sigstore/fulcio when running outside of PRs. + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set Versions + uses: actions/github-script@v7 + id: set_version + with: + script: | + const tag = context.ref.substring(10) + const no_v = tag.replace('v', '') + const no_forwardSlash = no_v.replaceAll('/', '') + const dash_index = no_forwardSlash.lastIndexOf('-') + const no_dash = (dash_index > -1) ? no_forwardSlash.substring(0, dash_index) : no_forwardSlash + core.setOutput('tag', tag) + core.setOutput('no-v', no_v) + core.setOutput('no-dash', no_dash) + + - id: lower-repo + name: Repository to lowercase + run: | + echo "repository=${GITHUB_REPOSITORY@L}" >> $GITHUB_OUTPUT + + # Set up QEMU to build multi-platform images + - name: 'Set up QEMU' + uses: docker/setup-qemu-action@v3 + + # Set up BuildKit Docker container builder to be able to build + # multi-platform images and export cache + # https://github.com/docker/setup-buildx-action + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 # v3.0.0 + + # Login against Docker Hub registry + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + # Login against a GitHub Docker registry + # https://github.com/docker/login-action + - name: Log into registry ghcr.io + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + # Build and push Docker image with Buildx (don't push on PR) + # https://github.com/docker/build-push-action + - name: Build and push Docker image + id: build-and-push + uses: docker/build-push-action@v6 + with: + context: . + secrets: | + "github_token=${{ secrets.GITHUB_TOKEN }}" + platforms: linux/amd64,linux/arm64 + push: ${{ github.event_name != 'pull_request' }} + tags: | + ${{ env.DOCKER_REPO }}:${{steps.set_version.outputs.no-dash}} + ghcr.io/${{ steps.lower-repo.outputs.repository }}:${{steps.set_version.outputs.no-dash}} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..cdc4675b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/.env +/.idea diff --git a/Dockerfile b/Dockerfile index 36633d2d..63cf33ab 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,106 +1,62 @@ -FROM nginx:1.17.8-alpine - -EXPOSE 8000 -CMD ["/sbin/entrypoint.sh"] - -ARG cachet_ver -ARG archive_url - -ENV cachet_ver ${cachet_ver:-2.4} -ENV archive_url ${archive_url:-https://github.com/cachethq/Cachet/archive/${cachet_ver}.tar.gz} - -ENV COMPOSER_VERSION 1.9.0 - -RUN apk add --no-cache --update \ - mysql-client \ - php7 \ - php7-apcu \ - php7-bcmath \ - php7-ctype \ - php7-curl \ - php7-dom \ - php7-fileinfo \ - php7-fpm \ - php7-gd \ - php7-iconv \ - php7-intl \ - php7-json \ - php7-mbstring \ - php7-mcrypt \ - php7-mysqlnd \ - php7-opcache \ - php7-openssl \ - php7-pdo \ - php7-pdo_mysql \ - php7-pdo_pgsql \ - php7-pdo_sqlite \ - php7-phar \ - php7-posix \ - php7-redis \ - php7-session \ - php7-simplexml \ - php7-soap \ - php7-sqlite3 \ - php7-tokenizer \ - php7-xml \ - php7-xmlwriter \ - php7-zip \ - php7-zlib \ - postfix \ - postgresql \ - postgresql-client \ - sqlite \ - sudo \ - wget sqlite git curl bash grep \ - supervisor - -# forward request and error logs to docker log collector -RUN ln -sf /dev/stdout /var/log/nginx/access.log && \ - ln -sf /dev/stderr /var/log/nginx/error.log && \ - ln -sf /dev/stdout /var/log/php7/error.log && \ - ln -sf /dev/stderr /var/log/php7/error.log - -RUN adduser -S -s /bin/bash -u 1001 -G root www-data - -RUN echo "www-data ALL=(ALL:ALL) NOPASSWD:SETENV: /usr/sbin/postfix" >> /etc/sudoers +FROM php:8.4-apache +LABEL description="Cachet Docker Image with MariaDB and PHP 8.4" +LABEL version="1.0" +LABEL org.opencontainers.image.description="Cachet Docker Image with MariaDB and PHP 8.4" + +# Set DOCUMENT_ROOT to public directory from Laravel +ENV APACHE_DOCUMENT_ROOT=/var/www/html/public + +# Copy custom apache and php configuration +COPY ./.docker/apache/000-default.conf /etc/apache2/sites-available/000-default.conf +COPY ./.docker/php/cachet.ini /usr/local/etc/php/conf.d/cachet.ini + +# Install composer into container +COPY --from=composer:latest /usr/bin/composer /usr/bin/composer + +# Update repo and packages and install apache & php +RUN apt-get update -y && apt-get upgrade -y +RUN a2enmod rewrite +RUN apt-get install tree nano libzip-dev libwebp-dev libfreetype6-dev libjpeg62-turbo-dev libpng-dev zlib1g-dev libicu-dev libpq-dev -y +RUN apt-get install npm -y + +# Configure PostgreSQL module for PHP +RUN docker-php-ext-configure pgsql -with-pgsql=/usr/local/pgsql + +# Install PHP Modules +RUN docker-php-ext-install pdo_mysql \ + && docker-php-ext-install mysqli \ + && docker-php-ext-install pgsql\ + && docker-php-ext-install pdo_pgsql \ + && docker-php-ext-install zip \ + && docker-php-ext-install exif \ + && docker-php-ext-install gd \ + && docker-php-ext-install bcmath \ + && docker-php-ext-install intl \ + && docker-php-ext-install pcntl + +# Set temporary to user root to copy files and set permissions +USER root -RUN touch /var/run/nginx.pid && \ - chown -R www-data:root /var/run/nginx.pid +# Copy the application files to the Apache document root +RUN git clone -b 3.x https://github.com/cachethq/cachet.git /var/www/html -RUN chown -R www-data:root /etc/php7/php-fpm.d +# Set the correct permissions for the application files +RUN chown -R www-data:www-data /var/www -RUN mkdir -p /var/www/html && \ - mkdir -p /usr/share/nginx/cache && \ - mkdir -p /var/cache/nginx && \ - mkdir -p /var/lib/nginx && \ - chown -R www-data:root /var/www /usr/share/nginx/cache /var/cache/nginx /var/lib/nginx/ +# Set Default User for Apache +USER www-data -# Install composer -RUN wget https://getcomposer.org/installer -O /tmp/composer-setup.php && \ - wget https://composer.github.io/installer.sig -O /tmp/composer-setup.sig && \ - php -r "if (hash('SHA384', file_get_contents('/tmp/composer-setup.php')) !== trim(file_get_contents('/tmp/composer-setup.sig'))) { unlink('/tmp/composer-setup.php'); echo 'Invalid installer' . PHP_EOL; exit(1); }" && \ - php /tmp/composer-setup.php --version=$COMPOSER_VERSION --install-dir=bin && \ - php -r "unlink('/tmp/composer-setup.php');" +# Use GitHub token for Composer because of rate-limitter +RUN --mount=type=secret,id=github_token,env=GITHUB_TOKEN composer config -g github-oauth.github.com $GITHUB_TOKEN -WORKDIR /var/www/html/ -USER 1001 +# Install Composer dependencies and NPM packages +RUN composer install --no-dev -o -RUN wget ${archive_url} && \ - tar xzf ${cachet_ver}.tar.gz --strip-components=1 && \ - chown -R www-data:root /var/www/html && \ - rm -r ${cachet_ver}.tar.gz && \ - php /bin/composer.phar global require "hirak/prestissimo:^0.3" && \ - php /bin/composer.phar install -o && \ - rm -rf bootstrap/cache/* +# RUN composer update cachethq/core +RUN composer update cachethq/core -COPY conf/php-fpm-pool.conf /etc/php7/php-fpm.d/www.conf -COPY conf/supervisord.conf /etc/supervisor/supervisord.conf -COPY conf/nginx.conf /etc/nginx/nginx.conf -COPY conf/nginx-site.conf /etc/nginx/conf.d/default.conf -COPY conf/.env.docker /var/www/html/.env -COPY entrypoint.sh /sbin/entrypoint.sh +# Publish the Cachet assets +RUN php artisan vendor:publish --tag=cachet -USER root -RUN chmod g+rwx /var/run/nginx.pid && \ - chmod -R g+rw /var/www /usr/share/nginx/cache /var/cache/nginx /var/lib/nginx/ /etc/php7/php-fpm.d storage -USER 1001 +# Create symbolic link between storage and public directory +RUN php artisan storage:link \ No newline at end of file diff --git a/Makefile b/Makefile index 6c8198a1..80b7d7fc 100644 --- a/Makefile +++ b/Makefile @@ -1,18 +1,8 @@ -SILENT : -.PHONY : test - -update-dependencies: - docker pull curlimages/curl:latest - docker pull postgres:9.5 - -test: - bats test - compose-build: - docker-compose build + docker compose build compose-up: - docker-compose up + docker compose up -d build: docker build -t cachet/docker . diff --git a/README.md b/README.md index 92c721a6..0a4ac195 100644 --- a/README.md +++ b/README.md @@ -1,121 +1,240 @@ -# Cachet Docker Image +# Docker Cachet -This is the official repository of the [Docker image](https://hub.docker.com/r/cachethq/docker/) for [Cachet](https://github.com/CachetHQ/Cachet). +A Docker setup for [Cachet](https://cachethq.io/) - an open-source status page system built with Laravel. -[Cachet](https://github.com/CachetHQ/Cachet) is a beautiful and powerful open source status page system, a free replacement for services such as StatusPage.io, Status.io and others. +## Overview -For full documentation, visit the [Installing Cachet with Docker](https://docs.cachethq.io/docs/get-started-with-docker) page. +This repository provides a complete Docker setup for running Cachet with the following components: +- **Cachet Application** - The main web application +- **Queue Worker** - Handles background jobs +- **Scheduler Worker** - Manages scheduled tasks +- **MariaDB Database** - Database backend -# Supporting Cachet +## Architecture Rationale +This architecture uses Docker to encapsulate all components of the Cachet application, ensuring easy deployment and management. Each service runs in its own container, allowing for isolation and scalability. The use of Docker Compose simplifies the orchestration of these services, making it easy to start, stop, and manage the entire application stack. +- **Isolation:** Each service runs independently, reducing the risk of one affecting the others. +- **Scalability:** You can scale workers and webservers separately based on workload. +- **Resource Management:** Allocate resources (CPU, memory) per container for better performance. +- **Resilience:** If one container fails (e.g., a worker), others remain unaffected and can restart automatically. +- **Simplified Maintenance:** Easier to update, debug, or restart individual components without downtime for the whole system. -Cachet is a BSD-3-licensed open source project. If you'd like to support future development, check out the [Cachet Patreon](https://patreon.com/jbrooksuk) campaign. +## Prerequisites -# Quickstart +Before you begin, ensure you have the following installed on your system: +- [Docker](https://docs.docker.com/get-docker/) (v20.10 or higher) +- [Docker Compose](https://docs.docker.com/compose/install/) (v2.0 or higher) +- Git (for cloning the repository) -1. Clone this repository +## Quick Start - ```shell - git clone https://github.com/CachetHQ/Docker.git - ``` +### 1. Clone the Repository -2. Edit the docker-compose.yml file to specify your [ENV variables](/conf/.env.docker). +```bash +git clone https://github.com/steffjenl/docker-cachet.git +cd docker-cachet +``` + +### 2. Configure Environment Variables + +Copy the example environment file and customize it: + +```bash +cp .env.example .env +``` + +Edit the `.env` file and update the following required variables: + +```bash +# Application settings +APP_NAME=YourStatusPage +APP_URL=http://localhost:8080 +APP_KEY= # Will be generated automatically + +# Database settings +DB_PASSWORD=your_secure_password_here + +# Mail settings (optional but recommended) +MAIL_MAILER=smtp +MAIL_HOST=your.smtp.host +MAIL_PORT=587 +MAIL_USERNAME=your_email@domain.com +MAIL_PASSWORD=your_email_password +MAIL_FROM_ADDRESS=noreply@yourdomain.com +MAIL_FROM_NAME="${APP_NAME}" +``` + +### 3. Start the Services + +```bash +docker compose up -d +``` + +This will start all services in the background: +- Cachet web application on port 8080 +- MariaDB database +- Queue worker for background jobs +- Scheduler worker for automated tasks + +### 4. Generate Application Key + +After the containers are running, generate the Laravel application key and copy it to your `.env` file: + +```bash +docker exec cachet-app php artisan key:generate --show +``` + +### 5. Run Database Migrations + +Set up the database tables: + +```bash +docker exec cachet-app php artisan migrate --force +``` + +### 6. Create the first user +Create the first user by following the prompts: -3. To build an image containing a specific Cachet release, set the [`cachet_ver` ARG in the docker-compose.yml](/docker-compose.yml) +```bash +docker exec -it cachet-app php artisan cachet:make:user +``` - The *main* branch and *cachethq/docker:latest* Docker automated build are a work in progress / development version of the upstream https://github.com/CachetHQ/Cachet project. As such, *main* or *latest* should not be used in a production environment as it can change at anytime. +### 7. Access Your Status Page - We strongly recommend specifying a stable [Cachet Release](https://github.com/CachetHQ/Cachet/releases) at build time as mentioned above. +Open your web browser and navigate to: +``` +http://localhost:8080 +``` -4. Build and run the image +Follow the setup wizard to complete the installation. - ```shell - docker-compose build - docker-compose up - ``` +## Configuration -5. `cachethq/docker` runs on port 8000 by default. This is exposed on host port 80 when using docker-compose. +### Environment Variables +The `.env` file contains all configuration options. Key variables include: -6. Setup the APP_KEY +| Variable | Description | Default | +|----------|-------------|---------| +| `APP_NAME` | Your status page name | Cachet | +| `APP_URL` | Base URL for your installation | http://localhost | +| `APP_DEBUG` | Enable debug mode (set to false in production) | false | +| `DB_PASSWORD` | Database password | ExamplePassword!2020! | +| `MAIL_MAILER` | Mail driver (smtp, log, etc.) | log | + +### Port Configuration + +By default, the application runs on port 8080. To change this, edit the `docker-compose.yml` file: + +```yaml +ports: + - "YOUR_PORT:80" +``` -Whilst the container is up and running, find the name of the Cachet container via `docker ps`. +### Custom Domain -Run `docker exec -i ID_OF_THE_CONTAINER php artisan key:generate`. +To use a custom domain: -Replace `${APP_KEY:-null}` in `docker-compose.yml` with the newly generated Application key. +1. Update `APP_URL` in your `.env` file +2. Configure your web server/reverse proxy to forward requests to the Docker container +3. Ensure SSL/TLS certificates are properly configured if using HTTPS -__Note:__ make sure you include `base64:` prefix. E.g. `base64:YOUR_UNIQUE_KEY` +## Production Deployment -Restart the Docker containers. +### Security Considerations +1. **Change default passwords**: Update `DB_PASSWORD` in your `.env` file +2. **Use HTTPS**: Configure SSL/TLS certificates +3. **Secure database**: Restrict database access and use strong passwords +4. **Environment variables**: Keep your `.env` file secure and never commit it to version control +5. **Updates**: Regularly update the Docker images -# Docker Hub Automated build +### Reverse Proxy Setup (Nginx Example) -`cachethq/docker` is available as a [Docker Hub Trusted Build](https://hub.docker.com/r/cachethq/docker/). +```nginx +server { + listen 80; + server_name status.yourdomain.com; + + location / { + proxy_pass http://localhost:8080; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} +``` -For a full list of Cachet versions released as Docker images please see the [list of Docker hub tags](https://hub.docker.com/r/cachethq/docker/tags/). +## Maintenance -Please use a [tagged Cachet Docker image release](https://github.com/CachetHQ/Docker/releases) or one of the tagged builds from https://hub.docker.com/r/cachethq/docker/tags/ with `docker pull cachethq/docker:2.3.12`. +### Updating -# Debugging +To update to the latest version: -* The services such as Cachet, supervisord, nginx, and php-fpm log to `stdout` and `stderr`, and are visible in the Docker runtime output. +```bash +docker compose pull +docker compose up -d +``` -* Setting the `DEBUG` Docker environment variable within the `docker-compose.yml` file or at runtime to `true` will enable debugging of the container entrypoint init script. +### Backup -# Testing +#### Database Backup +```bash +docker exec cachet-mariadb mysqldump -u root -p cachet > backup_$(date +%Y%m%d_%H%M%S).sql +``` -Pull requests must pass the [Bash Automated Testing System](https://github.com/sstephenson/bats) tests, which run on [Travis CI](https://travis-ci.org/CachetHQ/Docker) via located in the [test](test) directory. +#### Restore Database +```bash +docker exec -i cachet-mariadb mysql -u root -p cachet < backup_file.sql +``` -Use `make test` to manually run the tests. +### Logs +View application logs: +```bash +docker compose logs -f cachet-app +``` -# Development of Cachet using this docker environment +View all service logs: +```bash +docker compose logs -f +``` -1. Clone the official repo of CachetHQ/Docker: +## Troubleshooting - ```shell - git clone https://github.com/CachetHQ/Docker.git cachet-docker - cd cachet-docker - git tag -l - git checkout $LATEST_TAG - ``` -2. Clone the official repo of CachetHQ/Cachet here and do composer install: +### Common Issues - ```shell - git clone https://github.com/CachetHQ/Cachet.git - ``` +1. **Permission errors**: Ensure Docker has proper permissions +2. **Port conflicts**: Make sure port 8080 isn't already in use +3. **Database connection issues**: Verify database credentials in `.env` -3. Setup the Cachet project: +### Reset Installation -Note: This requires [Composer](https://getcomposer.org/) be installed on your Docker host. +To start fresh: - ```shell -cd Cachet -composer install -cp ../conf/.env.docker ./.env -cd .. +```bash +docker compose down -v +docker compose up -d ``` -4. Edit the docker-compose.yml file to bind mount the repo as a volume: +**Warning**: This will delete all data including your database. + +## Contributing + +1. Fork the repository +2. Create a feature branch +3. Make your changes +4. Submit a pull request + +## Support - ```yaml - cachet: - volumes: - - ./Cachet/:/var/www/html/ - ... - ``` +- [Cachet Documentation](https://docs.cachethq.io/) +- [GitHub Issues](https://github.com/cachethq/Docker/issues) +- [Docker Documentation](https://docs.docker.com/) -5. Build and run the container: +## License - ```shell - docker-compose up - ``` +This project is licensed under the MIT License. See the [Cachet license](https://github.com/cachethq/cachet/blob/3.x/LICENSE) for details. -6. Open new terminal and run the following commands after getting container name via `docker ps`: - ```shell - docker exec -i cachetdocker_cachet_1 php artisan key:generate - docker exec -i cachetdocker_cachet_1 php artisan app:install - ``` diff --git a/conf/.env.docker b/conf/.env.docker deleted file mode 100644 index c3d7a751..00000000 --- a/conf/.env.docker +++ /dev/null @@ -1,49 +0,0 @@ -APP_ENV="{{APP_ENV}}" -APP_DEBUG="{{APP_DEBUG}}" -APP_URL="{{APP_URL}}" -APP_LOG="{{APP_LOG}}" -APP_KEY="{{APP_KEY}}" - -DB_DRIVER="{{DB_DRIVER}}" -DB_HOST="{{DB_HOST}}" -DB_DATABASE="{{DB_DATABASE}}" -DB_USERNAME="{{DB_USERNAME}}" -DB_PASSWORD="{{DB_PASSWORD}}" -DB_PORT="{{DB_PORT}}" -DB_PREFIX="{{DB_PREFIX}}" - -DOCKER=true - -CACHE_DRIVER="{{CACHE_DRIVER}}" - -SESSION_DRIVER="{{SESSION_DRIVER}}" -SESSION_DOMAIN="{{SESSION_DOMAIN}}" -SESSION_SECURE_COOKIE="{{SESSION_SECURE_COOKIE}}" - -QUEUE_DRIVER="{{QUEUE_DRIVER}}" - -CACHET_EMOJI="{{CACHET_EMOJI}}" -CACHET_BEACON="{{CACHET_BEACON}}" -CACHET_AUTO_TWITTER="{{CACHET_AUTO_TWITTER}}" - -MAIL_DRIVER="{{MAIL_DRIVER}}" -MAIL_HOST="{{MAIL_HOST}}" -MAIL_PORT="{{MAIL_PORT}}" -MAIL_USERNAME="{{MAIL_USERNAME}}" -MAIL_PASSWORD="{{MAIL_PASSWORD}}" -MAIL_ADDRESS="{{MAIL_ADDRESS}}" -MAIL_NAME="{{MAIL_NAME}}" -MAIL_ENCRYPTION="{{MAIL_ENCRYPTION}}" - -REDIS_HOST="{{REDIS_HOST}}" -REDIS_DATABASE="{{REDIS_DATABASE}}" -REDIS_PORT="{{REDIS_PORT}}" -REDIS_PASSWORD="{{REDIS_PASSWORD}}" - -GITHUB_TOKEN="{{GITHUB_TOKEN}}" - -NEXMO_KEY="{{NEXMO_KEY}}" -NEXMO_SECRET="{{NEXMO_SECRET}}" -NEXMO_SMS_FROM="{{NEXMO_SMS_FROM}}" - -TRUSTED_PROXIES="{{TRUSTED_PROXIES}}" diff --git a/conf/nginx-site.conf b/conf/nginx-site.conf deleted file mode 100644 index 08971839..00000000 --- a/conf/nginx-site.conf +++ /dev/null @@ -1,43 +0,0 @@ -server { - listen 8000 default; ## Listen for ipv4; this line is default and implied - listen [::]:8000 default; ## Listen for ipv6 - - # Make site accessible from http://localhost/ - server_name localhost; - root /var/www/html/public; - - index index.php; - - charset utf-8; - - location / { - try_files $uri /index.php$is_args$args; - } - # Cache images - location ~* .(jpg|jpeg|png|gif|ico|css|js|ttf|svg)$ { - expires 365d; - } - - # Pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 - location ~ \.php$ { - - fastcgi_pass_header Set-Cookie; - fastcgi_pass_header Cookie; - fastcgi_ignore_headers Cache-Control Expires Set-Cookie; - - fastcgi_cache_bypass 1; - fastcgi_no_cache 1; - - fastcgi_split_path_info ^(.+\.php)(/.+)$; - include fastcgi_params; - fastcgi_pass 127.0.0.1:9000; - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - fastcgi_index index.php; - fastcgi_keep_conn on; - } - - location ~ /\.ht { - deny all; - } - -} diff --git a/conf/nginx.conf b/conf/nginx.conf deleted file mode 100644 index 8b3491e1..00000000 --- a/conf/nginx.conf +++ /dev/null @@ -1,54 +0,0 @@ -worker_processes auto; - -error_log /var/log/nginx/error.log warn; -pid /var/run/nginx.pid; - -events { - use epoll; - worker_connections 1024; - multi_accept on; -} - - -http { - include /etc/nginx/mime.types; - default_type application/octet-stream; - - log_format main '$remote_addr - $remote_user [$time_local] "$request" ' - '$status $body_bytes_sent "$http_referer" ' - '"$http_user_agent" "$http_x_forwarded_for"'; - - access_log /var/log/nginx/access.log main; - - sendfile on; - tcp_nopush on; - tcp_nodelay on; - - keepalive_timeout 65; - - fastcgi_cache_path /usr/share/nginx/cache/fcgi levels=1:2 keys_zone=microcache:10m max_size=1024m inactive=1h; - add_header X-Cache $upstream_cache_status; - - gzip on; - gzip_disable "msie6"; - - gzip_vary on; - gzip_proxied any; - gzip_comp_level 6; - gzip_buffers 16 8k; - gzip_http_version 1.1; - gzip_min_length 256; - gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/vnd.ms-fontobject application/x-font-ttf font/opentype image/svg+xml image/x-icon; - - include /etc/nginx/conf.d/*.conf; - - client_body_temp_path /tmp 1 2; - client_body_buffer_size 256k; - client_body_in_file_only off; - - proxy_temp_path /tmp/proxy 1 2; - fastcgi_temp_path /tmp/fastcgi 1 2; - uwsgi_temp_path /tmp/fastcgi 1 2; - scgi_temp_path /tmp/scgi 1 2; -} -daemon off; diff --git a/conf/php-fpm-pool.conf b/conf/php-fpm-pool.conf deleted file mode 100644 index d8b4cb22..00000000 --- a/conf/php-fpm-pool.conf +++ /dev/null @@ -1,25 +0,0 @@ -[www] -listen = 127.0.0.1:9000 - -request_terminate_timeout = 120s - -pm = ondemand -pm.max_children = {{PHP_MAX_CHILDREN}} -pm.process_idle_timeout = 10s -pm.max_requests = 500 -chdir = / - -env[DB_DRIVER] = $DB_DRIVER -env[DB_HOST] = $DB_HOST -env[DB_DATABASE] = $DB_DATABASE -env[DB_USERNAME] = $DB_USERNAME -env[DB_PASSWORD] = $DB_PASSWORD -env[CACHE_DRIVER] = $CACHE_DRIVER - -catch_workers_output = yes -access.log = /dev/stdout - -[global] -daemonize = no -error_log = /dev/stderr -pid = /tmp/php-fpm.pid diff --git a/conf/supervisord.conf b/conf/supervisord.conf deleted file mode 100644 index acd0bf7a..00000000 --- a/conf/supervisord.conf +++ /dev/null @@ -1,45 +0,0 @@ -[supervisord] -logfile=/dev/null ; (main log file;default $CWD/supervisord.log) -logfile_maxbytes=0 ; (max main logfile bytes b4 rotation;default 50MB) -logfile_backups=0 ; (num of main logfile rotation backups;default 10) -loglevel=info ; (log level;default info; others: debug,warn,trace) -pidfile=/tmp/supervisord.pid ; (supervisord pidfile;default supervisord.pid) -nodaemon=true ; (start in foreground if true;default false) - -; the below section must remain in the config file for RPC -; (supervisorctl/web interface) to work, additional interfaces may be -; added by defining them in separate rpcinterface: sections -[rpcinterface:supervisor] -supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface - -[supervisorctl] -serverurl=unix:///var/run/supervisor.sock ; use a unix:// URL for a unix socket - -[program:nginx] -command=/usr/sbin/nginx -stdout_events_enabled=true -stderr_events_enabled=true -stdout_logfile_maxbytes=0 -stderr_logfile_maxbytes=0 -stdout_logfile=/dev/stdout -stderr_logfile=/dev/stderr - -[program:php-fpm] -command=/usr/sbin/php-fpm7 -c /etc/php7/fpm/pool.d/www.conf -catch_workers_output = Yes -stdout_events_enabled=true -stderr_events_enabled=true -stdout_logfile_maxbytes=0 -stderr_logfile_maxbytes=0 -stdout_logfile=/dev/stdout -stderr_logfile=/dev/stderr - -[program:queue-worker] -command=php artisan queue:work --daemon --delay=2 --sleep=1 --tries=3 -directory=/var/www/html/ -redirect_stderr=true -autostart=true -autorestart=true -stdout_logfile_maxbytes=0 -stderr_logfile_maxbytes=0 -stdout_logfile=/dev/stdout diff --git a/docker-compose.yml b/docker-compose.yml index 7691d2b5..f841f765 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,36 +1,78 @@ -version: "3" - services: - postgres: - image: postgres:12-alpine - volumes: - - /var/lib/postgresql/data + + cachet-app: + image: ghcr.io/cachethq/docker:3.x + container_name: cachet-app + tty: true environment: - - POSTGRES_USER=postgres - - POSTGRES_PASSWORD=postgres - restart: always - cachet: - build: - context: . - args: - - cachet_ver=2.4 + SERVICE_NAME: cachet-app + SERVICE_TAGS: dev + working_dir: /var/www/html ports: - - 80:8000 - links: - - postgres:postgres + - "8080:80" + volumes: + - cachet-storage:/var/www/html/storage + - ./.env:/var/www/html/.env + networks: + - cachet-network + restart: unless-stopped + + cachet-queue-worker: + image: ghcr.io/cachethq/docker:3.x + container_name: cachet-queue-worker + tty: true + environment: + SERVICE_NAME: cachet-queue-worker + SERVICE_TAGS: dev + working_dir: /var/www/html + volumes: + - cachet-storage:/var/www/html/storage + - ./.env:/var/www/html/.env + networks: + - cachet-network + command: php artisan queue:work --queue=default --sleep=3 --tries=3 --max-time=3600 + restart: unless-stopped + + cachet-scheduler-worker: + image: ghcr.io/cachethq/docker:3.x + container_name: cachet-scheduler-worker + tty: true + environment: + SERVICE_NAME: cachet-scheduler-worker + SERVICE_TAGS: dev + working_dir: /var/www/html + volumes: + - cachet-storage:/var/www/html/storage + - ./.env:/var/www/html/.env + networks: + - cachet-network + command: php artisan schedule:work + restart: unless-stopped + + mariadb: + container_name: cachet-mariadb + image: mariadb:lts + restart: unless-stopped environment: - - DB_DRIVER=pgsql - - DB_HOST=postgres - - DB_PORT=5432 - - DB_DATABASE=postgres - - DB_USERNAME=postgres - - DB_PASSWORD=postgres - - DB_PREFIX=chq_ - - APP_KEY=${APP_KEY:-null} - - APP_LOG=errorlog - - APP_ENV=${APP_ENV:-production} - - APP_DEBUG=false - - DEBUG=false - depends_on: - - postgres - restart: on-failure + MARIADB_ROOT_PASSWORD: ${DB_PASSWORD} + healthcheck: + test: [ "CMD", "healthcheck.sh", "--connect", "--innodb_initialized" ] + start_period: 10s + interval: 10s + timeout: 5s + retries: 3 + volumes: + - cachet-mariadb-storage:/var/lib/mysql + networks: + - cachet-network + +volumes: + cachet-storage: + name: cachet-storage + cachet-mariadb-storage: + name: cachet-mariadb-storage + +networks: + cachet-network: + name: cachet-network + driver: bridge diff --git a/entrypoint.sh b/entrypoint.sh deleted file mode 100755 index 0ed4a593..00000000 --- a/entrypoint.sh +++ /dev/null @@ -1,243 +0,0 @@ -#!/bin/bash -set -o errexit -o nounset -o pipefail - -[ "${DEBUG:-false}" == true ] && set -x - -check_database_connection() { - echo "Attempting to connect to database ..." - case "${DB_DRIVER}" in - mysql) - prog="mysqladmin -h ${DB_HOST} -u ${DB_USERNAME} ${DB_PASSWORD:+-p$DB_PASSWORD} -P ${DB_PORT} status" - ;; - pgsql) - prog="/usr/bin/pg_isready" - prog="${prog} -h ${DB_HOST} -p ${DB_PORT} -U ${DB_USERNAME} -d ${DB_DATABASE} -t 1" - ;; - sqlite) - prog="touch /var/www/html/database/database.sqlite" - esac - timeout=60 - while ! ${prog} >/dev/null 2>&1 - do - timeout=$(( timeout - 1 )) - if [[ "$timeout" -eq 0 ]]; then - echo - echo "Could not connect to database server! Aborting..." - exit 1 - fi - echo -n "." - sleep 1 - done - echo -} - -checkdbinitmysql() { - table=sessions - if [[ "$(mysql -N -s -h "${DB_HOST}" -u "${DB_USERNAME}" "${DB_PASSWORD:+-p$DB_PASSWORD}" "${DB_DATABASE}" -P "${DB_PORT}" -e \ - "select count(*) from information_schema.tables where \ - table_schema='${DB_DATABASE}' and table_name='${DB_PREFIX}${table}';")" -eq 1 ]]; then - echo "Table ${DB_PREFIX}${table} exists! ..." - else - echo "Table ${DB_PREFIX}${table} does not exist! ..." - init_db - fi - -} - -checkdbinitpsql() { - table=sessions - export PGPASSWORD=${DB_PASSWORD} - if [[ "$(psql -h "${DB_HOST}" -p "${DB_PORT}" -U "${DB_USERNAME}" -d "${DB_DATABASE}" -c "SELECT to_regclass('${DB_PREFIX}${table}');" | grep -c "${DB_PREFIX}${table}")" -eq 1 ]]; then - echo "Table ${DB_PREFIX}${table} exists! ..." - else - echo "Table ${DB_PREFIX}${table} does not exist! ..." - init_db - fi - -} - -check_configured() { - case "${DB_DRIVER}" in - mysql) - checkdbinitmysql - ;; - pgsql) - checkdbinitpsql - ;; - esac -} - -check_sendmail() { - if [[ "${MAIL_DRIVER:-}" == "sendmail" ]]; then - sudo /usr/sbin/postfix start - fi -} - -initialize_system() { - echo "Initializing Cachet container ..." - - APP_KEY=${APP_KEY:-} - APP_ENV=${APP_ENV:-development} - APP_DEBUG=${APP_DEBUG:-true} - APP_URL=${APP_URL:-http://localhost} - APP_LOG=${APP_LOG:-errorlog} - - DB_DRIVER=${DB_DRIVER:-pgsql} - DB_HOST=${DB_HOST:-postgres} - DB_DATABASE=${DB_DATABASE:-cachet} - DB_PREFIX=${DB_PREFIX:-} - DB_USERNAME=${DB_USERNAME:-postgres} - DB_PASSWORD=${DB_PASSWORD:-postgres} - DB_PORT=${DB_PORT:-} - DB_PREFIX=${DB_PREFIX:-} - - if [[ "${DB_DRIVER}" = "pgsql" ]]; then - DB_PORT=${DB_PORT:-5432} - fi - - if [[ "${DB_DRIVER}" = "mysql" ]]; then - DB_PORT=${DB_PORT:-3306} - fi - - if [[ "${DB_DRIVER}" = "sqlite" ]]; then - DB_DATABASE="" - DB_HOST="" - DB_PORT="" - DB_USERNAME="" - DB_PASSWORD="" - fi - - CACHE_DRIVER=${CACHE_DRIVER:-apc} - - SESSION_DRIVER=${SESSION_DRIVER:-apc} - SESSION_DOMAIN=${SESSION_DOMAIN:-} - SESSION_SECURE_COOKIE=${SESSION_SECURE_COOKIE:-} - - QUEUE_DRIVER=${QUEUE_DRIVER:-database} - CACHET_EMOJI=${CACHET_EMOJI:-false} - CACHET_BEACON=${CACHET_BEACON:-true} - CACHET_AUTO_TWITTER=${CACHET_AUTO_TWITTER:-true} - - MAIL_DRIVER=${MAIL_DRIVER:-smtp} - MAIL_HOST=${MAIL_HOST:-localhost} - MAIL_PORT=${MAIL_PORT:-25} - MAIL_USERNAME=${MAIL_USERNAME:-} - MAIL_PASSWORD=${MAIL_PASSWORD:-} - MAIL_ADDRESS=${MAIL_ADDRESS:-} - MAIL_NAME=${MAIL_NAME:-} - MAIL_ENCRYPTION=${MAIL_ENCRYPTION:-} - - REDIS_HOST=${REDIS_HOST:-} - REDIS_DATABASE=${REDIS_DATABASE:-} - REDIS_PORT=${REDIS_PORT:-} - REDIS_PASSWORD=${REDIS_PASSWORD:-} - - GITHUB_TOKEN=${GITHUB_TOKEN:-} - - NEXMO_KEY=${NEXMO_KEY:-} - NEXMO_SECRET=${NEXMO_SECRET:-} - NEXMO_SMS_FROM=${NEXMO_SMS_FROM:-Cachet} - - PHP_MAX_CHILDREN=${PHP_MAX_CHILDREN:-5} - - TRUSTED_PROXIES=${TRUSTED_PROXIES:-} - - # configure env file - - sed 's,{{APP_ENV}},'"${APP_ENV}"',g' -i /var/www/html/.env - sed 's,{{APP_DEBUG}},'"${APP_DEBUG}"',g' -i /var/www/html/.env - sed 's,{{APP_URL}},'"${APP_URL}"',g' -i /var/www/html/.env - sed 's,{{APP_LOG}},'"${APP_LOG}"',g' -i /var/www/html/.env - - sed 's,{{DB_DRIVER}},'"${DB_DRIVER}"',g' -i /var/www/html/.env - sed 's,{{DB_HOST}},'"${DB_HOST}"',g' -i /var/www/html/.env - sed 's,{{DB_DATABASE}},'"${DB_DATABASE}"',g' -i /var/www/html/.env - sed 's,{{DB_PREFIX}},'"${DB_PREFIX}"',g' -i /var/www/html/.env - sed 's,{{DB_USERNAME}},'"${DB_USERNAME}"',g' -i /var/www/html/.env - sed 's,{{DB_PASSWORD}},'"${DB_PASSWORD}"',g' -i /var/www/html/.env - sed 's,{{DB_PORT}},'"${DB_PORT}"',g' -i /var/www/html/.env - sed 's,{{DB_PREFIX}},'"${DB_PREFIX}"',g' -i /var/www/html/.env - - sed 's,{{CACHE_DRIVER}},'"${CACHE_DRIVER}"',g' -i /var/www/html/.env - - sed 's,{{SESSION_DRIVER}},'"${SESSION_DRIVER}"',g' -i /var/www/html/.env - sed 's,{{SESSION_DOMAIN}},'"${SESSION_DOMAIN}"',g' -i /var/www/html/.env - sed 's,{{SESSION_SECURE_COOKIE}},'"${SESSION_SECURE_COOKIE}"',g' -i /var/www/html/.env - - sed 's,{{QUEUE_DRIVER}},'"${QUEUE_DRIVER}"',g' -i /var/www/html/.env - sed 's,{{CACHET_EMOJI}},'"${CACHET_EMOJI}"',g' -i /var/www/html/.env - sed 's,{{CACHET_BEACON}},'"${CACHET_BEACON}"',g' -i /var/www/html/.env - sed 's,{{CACHET_AUTO_TWITTER}},'"${CACHET_AUTO_TWITTER}"',g' -i /var/www/html/.env - - sed 's,{{MAIL_DRIVER}},'"${MAIL_DRIVER}"',g' -i /var/www/html/.env - sed 's,{{MAIL_HOST}},'"${MAIL_HOST}"',g' -i /var/www/html/.env - sed 's,{{MAIL_PORT}},'"${MAIL_PORT}"',g' -i /var/www/html/.env - sed 's,{{MAIL_USERNAME}},'"${MAIL_USERNAME}"',g' -i /var/www/html/.env - sed 's,{{MAIL_PASSWORD}},'"${MAIL_PASSWORD}"',g' -i /var/www/html/.env - sed 's,{{MAIL_ADDRESS}},'"${MAIL_ADDRESS}"',g' -i /var/www/html/.env - sed 's,{{MAIL_NAME}},'"${MAIL_NAME}"',g' -i /var/www/html/.env - sed 's,{{MAIL_ENCRYPTION}},'"${MAIL_ENCRYPTION}"',g' -i /var/www/html/.env - - sed 's,{{REDIS_HOST}},'"${REDIS_HOST}"',g' -i /var/www/html/.env - sed 's,{{REDIS_DATABASE}},'"${REDIS_DATABASE}"',g' -i /var/www/html/.env - sed 's,{{REDIS_PORT}},'"${REDIS_PORT}"',g' -i /var/www/html/.env - sed 's,{{REDIS_PASSWORD}},'"${REDIS_PASSWORD}"',g' -i /var/www/html/.env - - sed 's,{{GITHUB_TOKEN}},'"${GITHUB_TOKEN}"',g' -i /var/www/html/.env - - sed 's,{{NEXMO_KEY}},'"${NEXMO_KEY}"',g' -i /var/www/html/.env - sed 's,{{NEXMO_SECRET}},'"${NEXMO_SECRET}"',g' -i /var/www/html/.env - sed 's,{{NEXMO_SMS_FROM}},'"${NEXMO_SMS_FROM}"',g' -i /var/www/html/.env - - sed 's,{{PHP_MAX_CHILDREN}},'"${PHP_MAX_CHILDREN}"',g' -i /etc/php7/php-fpm.d/www.conf - - sed 's,{{TRUSTED_PROXIES}},'"${TRUSTED_PROXIES}"',g' -i /var/www/html/.env - - if [[ -z "${APP_KEY}" || "${APP_KEY}" = "null" ]]; then - keygen="$(php artisan key:generate --show)" - APP_KEY=$(echo "${keygen}") - echo "ERROR: Please set the 'APP_KEY=${APP_KEY}' environment variable at runtime or in docker-compose.yml and re-launch" - exit 0 - fi - - sed "s,{{APP_KEY}},$APP_KEY,g" -i /var/www/html/.env - - # remove empty lines - sed '/^.*=""$/d' -i /var/www/html/.env - - rm -rf bootstrap/cache/* -} - -init_db() { - echo "Initializing Cachet database ..." - php artisan cachet:install --no-interaction - check_configured -} - -migrate_db() { - force="" - if [[ "${FORCE_MIGRATION:-false}" == true ]]; then - force="--force" - fi - php artisan migrate ${force} -} - -seed_db() { - php artisan db:seed -} - -start_system() { - initialize_system - check_database_connection - check_configured - migrate_db - seed_db - echo "Starting Cachet! ..." - php artisan config:cache - /usr/bin/supervisord -n -c /etc/supervisor/supervisord.conf -} - -check_sendmail -start_system - -exit 0 diff --git a/openshift/README.md b/openshift/README.md deleted file mode 100644 index 37ff7544..00000000 --- a/openshift/README.md +++ /dev/null @@ -1,23 +0,0 @@ -## Synopsis - -This is a very simple way to deploy CachetHD on to Openshift or Kubernetes. I have only tested this on OpenShift 3.3 at this time 01/29/2017. - -## Motivation - -This was someting I did a work as we needed a dashboard to see that status of the services in our OpenShift. - -## Installation - -To get this all work you need to have a DB you can access. - -Deploy the DC using `oc create -f cachethd-dc.yaml` while logged in to the project you want to deploy to. - -You will need to change the env values to your setup. - -You will then need to setup service and then a route so you can get to Cachet URL. - - -## Contributors - -Andy del Hierro -andelhie@cisco.com \ No newline at end of file diff --git a/openshift/cachethd-dc.yaml b/openshift/cachethd-dc.yaml deleted file mode 100644 index 259af1b4..00000000 --- a/openshift/cachethd-dc.yaml +++ /dev/null @@ -1,135 +0,0 @@ -apiVersion: v1 -kind: DeploymentConfig -metadata: - name: cachet - labels: - app: cachet - annotations: -spec: - strategy: - type: Rolling - rollingParams: - updatePeriodSeconds: 1 - intervalSeconds: 1 - timeoutSeconds: 600 - maxUnavailable: 25% - maxSurge: 25% - resources: - triggers: - - - type: ConfigChange - replicas: 1 - test: false - selector: - app: cachet - deploymentconfig: cachet - template: - metadata: - creationTimestamp: null - labels: - app: cachet - deploymentconfig: cachet - annotations: - spec: - containers: - - - name: cachet - image: cachethq/docker:2.3.10 - ports: - - - containerPort: 80 - protocol: TCP - env: - - - name: APP_ENV - value: nonProd - - - name: APP_DEBUG - value: 'true' - - - name: APP_URL - value: 'https://demo.cachethq.io/api/v1' - - - name: APP_KEY - value: 'base64:KEY' - - - name: DB_DRIVER - value: pgsql #pqsql mysql - - - name: DB_HOST - value: db_hostname - - - name: DB_DATABASE - value: cachethq - - - name: DB_USERNAME - value: cachethq - - - name: DB_PASSWORD - value: mypassword - - - name: DB_PORT - value: 'null' - - - name: DB_PREFIX - value: 'null' - - - name: DOCKER #this is need so when the container restarts it will not have you do setup again. - value: 'true' - - - name: CACHE_DRIVER - value: apc - - - name: SESSION_DRIVER - value: apc - - - name: QUEUE_DRIVER - value: database - - - name: CACHET_EMOJI - value: 'false' - - - name: MAIL_DRIVER - value: smtp - - - name: MAIL_HOST - value: smtp.hostname.com - - - name: MAIL_PORT - value: '25' - - - name: MAIL_USERNAME - value: 'null' - - - name: MAIL_PASSWORD - value: 'null' - - - name: MAIL_ADDRESS - value: 'null' - - - name: MAIL_NAME - value: 'null' - - - name: MAIL_ENCRYPTION - value: 'null' - - - name: REDIS_HOST - value: 'null' - - - name: REDIS_DATABASE - value: 'null' - - - name: REDIS_PORT - value: 'null' - - - name: GITHUB_TOKEN - value: 'null' - resources: - terminationMessagePath: /dev/termination-log - imagePullPolicy: Always - restartPolicy: Always - terminationGracePeriodSeconds: 30 - dnsPolicy: ClusterFirst - securityContext: - runAsUser: 0 -status: diff --git a/release-helper.sh b/release-helper.sh deleted file mode 100755 index 7d4fd08f..00000000 --- a/release-helper.sh +++ /dev/null @@ -1,137 +0,0 @@ -#!/bin/bash -#/ Usage: release-helper.sh [-hdc] -#/ -#/ Creates a temporary branch from which a new release will be tagged and pushed. -#/ -#/ OPTIONS: -#/ -h | --help Show this message. -#/ -d | --delete Delete a tag / release -#/ -c | --check Check current releases on GitHub -#/ Target Cachet Version (ex: v2.3.13) -#/ -set -e - -usage () { - grep "^#/" <"$0" | cut -c4- - exit "${1:-2}" -} - -[ $# -eq 0 ] && usage 1 - -cachet_version= -main_version=2.4 - -do_release () { - -# Make sure we are on clean branch -if [[ ! $(git branch --list cachet-"$cachet_version") ]]; then - echo "Creating new branch cachet-$cachet_version" - git checkout -b cachet-"$cachet_version" -else - echo "Branch cachet-$cachet_version already exists!" - git checkout cachet-"$cachet_version" -fi - -# Generate changelog (requires https://github.com/skywinder/github-changelog-generator) -if hash github_changelog_generator 2>/dev/null; then - github_changelog_generator -u CachetHQ --project Docker --token "$token" --future-release "$cachet_version" -fi - -# Modify Dockerfile, commit, tag, and push -echo "Creating tag for $cachet_version" -gsed s/$main_version/"$cachet_version"/g -i Dockerfile -git commit -am "Cachet $cachet_version release" -git tag -a "$cachet_version" -m "Cachet Release $cachet_version" -git push origin cachet-"$cachet_version" -git push origin "$cachet_version" - -# Create GitHub release -curl -H "Authorization: token $token" -s -H "Content-Type: application/json" -d '{"tag_name":"'"${cachet_version}"'","name":"'"${cachet_version}"'","body":"Cachet Release '"${cachet_version}"'","draft":false,"prerelease":false}' -X POST https://api.github.com/repos/CachetHQ/Docker/releases -} - -check_releases () { -# Get latest releases -CACHET_APP_LATEST_REL=$(curl -H "Authorization: token $token" -s https://api.github.com/repos/cachethq/cachet/releases/latest | jq -r .name) -CACHET_DOCKER_LATEST_REL=$(curl -H "Authorization: token $token" -s https://api.github.com/repos/cachethq/docker/releases/latest | jq -r .name) - -echo "Latest Cachet release: $CACHET_APP_LATEST_REL" -echo "Latest Docker release: $CACHET_DOCKER_LATEST_REL" - -# Compare versions -if [ "$CACHET_APP_LATEST_REL" = "$CACHET_DOCKER_LATEST_REL" ] - then - echo "Releases on GitHub are up to date!" - else - echo "Docker at "$CACHET_DOCKER_LATEST_REL" -- Releasing latest app release $CACHET_APP_LATEST_REL" - cachet_version="$CACHET_APP_LATEST_REL" - do_release -fi - -} - -delete_release () { - if [ -z "$cachet_version" ]; then - echo 1>&2 "error: no version specified." - exit 1 - fi - echo "Removing release $cachet_version" - release_id=$(curl -H "Authorization: token $token" -s -X GET https://api.github.com/repos/CachetHQ/Docker/releases/tags/"$cachet_version" | jq -r .id) - curl -H "Authorization: token $token" -s -X DELETE https://api.github.com/repos/CachetHQ/Docker/releases/"$release_id" || true - git tag -d "$cachet_version" || true - git push origin :"$cachet_version" || true - git branch -d cachet-"$cachet_version" || true -} - -# GitHub API Token -token=${GITHUB_TOKEN} - -if [ -z "$token" ] - then - echo 1>&2 "error: please set GITHUB_TOKEN in your local environment." - exit 1 -fi - -# Parse args. -ARGS=$(getopt --name "$0" --long help,delete,check,update: --options hdcu: -- "$@") || { - usage - exit 2 -} -eval set -- "$ARGS" - -while [ $# -gt 0 ]; do - shift - case "$1" in - -h|--help) - usage - exit 2 - ;; - -d|--delete) - cachet_version=$2 - delete_release - exit 0 - ;; - -c|--check) - check_releases - exit 0 - ;; - -u|--update) - cachet_version=$2 - echo "Updating to Cachet version: $2" - do_release - break - exit 0 - ;; - --) - cachet_version=$2 - do_release - shift - break - ;; - esac - shift -done - -if [ -z "$cachet_version" ]; then - echo 1>&2 "error: no version specified." - exit 1 -fi diff --git a/test/docker-compose-mysql.yml b/test/docker-compose-mysql.yml deleted file mode 100644 index e1e87d8c..00000000 --- a/test/docker-compose-mysql.yml +++ /dev/null @@ -1,32 +0,0 @@ -version: "3" - -services: - mysql: - image: mariadb:10.4 - environment: - - MYSQL_ROOT_PASSWORD=mysql - - MYSQL_USER=mysql - - MYSQL_PASSWORD=mysql - - MYSQL_DATABASE=mysql - - DEBUG=false - cachet: - image: docker_cachet - ports: - - 80:8000 - links: - - mysql:mysql - environment: - - DB_DRIVER=mysql - - DB_HOST=mysql - - DB_DATABASE=mysql - - DB_USERNAME=mysql - - DB_PASSWORD=mysql - - DB_PREFIX=chq_ - - APP_KEY=${APP_KEY:-null} - depends_on: - - mysql - -networks: - default: - external: - name: docker_default diff --git a/test/docker-compose-sqlite.yml b/test/docker-compose-sqlite.yml deleted file mode 100644 index c378e6ec..00000000 --- a/test/docker-compose-sqlite.yml +++ /dev/null @@ -1,21 +0,0 @@ -version: "3" - -services: - cachet: - image: docker_cachet - ports: - - 80:8000 - environment: - - DB_DRIVER=sqlite - - APP_KEY=${APP_KEY:-null} - - DEBUG=false - volumes: - - cachetdb:/var/www/html/database - -networks: - default: - external: - name: docker_default - -volumes: - cachetdb: diff --git a/test/docker_helpers.bash b/test/docker_helpers.bash deleted file mode 100644 index e0c281df..00000000 --- a/test/docker_helpers.bash +++ /dev/null @@ -1,66 +0,0 @@ -## functions to help deal with docker - -# Removes container $1 -function docker_clean { - docker kill $1 &>/dev/null ||: - sleep .25s - docker rm -vf $1 &>/dev/null ||: - sleep .25s -} - -# get the ip of docker container $1 -function docker_ip { - docker inspect --format '{{ .NetworkSettings.Networks.docker_default.IPAddress }}' $1 -} - -# get the id of docker container $1 -function docker_id { - docker inspect --format '{{ .ID }}' $1 -} - -# get the running state of container $1 -# → true/false -# fails if the container does not exist -function docker_running_state { - docker inspect -f {{.State.Running}} $1 -} - -# get the docker container $1 PID -function docker_pid { - docker inspect --format {{.State.Pid}} $1 -} - -# asserts logs from container $1 contains $2 -function docker_assert_log { - local -r container=$1 - shift - run docker logs $container - assert_output -p "$*" -} - -# wait for a container to produce a given text in its log -# $1 container -# $2 timeout in second -# $* text to wait for -function docker_wait_for_log { - local -r container=$1 - local -ir timeout_sec=$2 - shift 2 - retry $(( $timeout_sec * 2 )) .5s docker_assert_log $container "$*" -} - -# Create a docker container named $1 which exposes the docker host unix -# socket over tcp on port 2375. -# -# $1 container name -function docker_tcp { - local container_name="$1" - docker_clean $container_name - docker run -d \ - --label bats-type="socat" \ - --name $container_name \ - --expose 2375 \ - -v /var/run/docker.sock:/var/run/docker.sock \ - rancher/socat-docker - docker run --label bats-type="docker" --link "$container_name:docker" docker:1.10 version -} diff --git a/test/lib/batslib.bash b/test/lib/batslib.bash deleted file mode 100644 index 66f3d48f..00000000 --- a/test/lib/batslib.bash +++ /dev/null @@ -1,594 +0,0 @@ -# -# batslib.bash -# ------------ -# -# The Standard Library is a collection of test helpers intended to -# simplify testing. It contains the following types of test helpers. -# -# - Assertions are functions that perform a test and output relevant -# information on failure to help debugging. They return 1 on failure -# and 0 otherwise. -# -# All output is formatted for readability using the functions of -# `output.bash' and sent to the standard error. -# - - -######################################################################## -# ASSERTIONS -######################################################################## - -# Fail and display a message. When no parameters are specified, the -# message is read from the standard input. Other functions use this to -# report failure. -# -# Globals: -# none -# Arguments: -# $@ - [=STDIN] message -# Returns: -# 1 - always -# Inputs: -# STDIN - [=$@] message -# Outputs: -# STDERR - message -fail() { - (( $# == 0 )) && batslib_err || batslib_err "$@" - return 1 -} - -# Fail and display details if the expression evaluates to false. Details -# include the expression, `$status' and `$output'. -# -# NOTE: The expression must be a simple command. Compound commands, such -# as `[[', can be used only when executed with `bash -c'. -# -# Globals: -# status -# output -# Arguments: -# $1 - expression -# Returns: -# 0 - expression evaluates to TRUE -# 1 - otherwise -# Outputs: -# STDERR - details, on failure -assert() { - if ! "$@"; then - { local -ar single=( - 'expression' "$*" - 'status' "$status" - ) - local -ar may_be_multi=( - 'output' "$output" - ) - local -ir width="$( batslib_get_max_single_line_key_width \ - "${single[@]}" "${may_be_multi[@]}" )" - batslib_print_kv_single "$width" "${single[@]}" - batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}" - } | batslib_decorate 'assertion failed' \ - | fail - fi -} - -# Fail and display details if the expected and actual values do not -# equal. Details include both values. -# -# Globals: -# none -# Arguments: -# $1 - actual value -# $2 - expected value -# Returns: -# 0 - values equal -# 1 - otherwise -# Outputs: -# STDERR - details, on failure -assert_equal() { - if [[ $1 != "$2" ]]; then - batslib_print_kv_single_or_multi 8 \ - 'expected' "$2" \ - 'actual' "$1" \ - | batslib_decorate 'values do not equal' \ - | fail - fi -} - -# Fail and display details if `$status' is not 0. Details include -# `$status' and `$output'. -# -# Globals: -# status -# output -# Arguments: -# none -# Returns: -# 0 - `$status' is 0 -# 1 - otherwise -# Outputs: -# STDERR - details, on failure -assert_success() { - if (( status != 0 )); then - { local -ir width=6 - batslib_print_kv_single "$width" 'status' "$status" - batslib_print_kv_single_or_multi "$width" 'output' "$output" - } | batslib_decorate 'command failed' \ - | fail - fi -} - -# Fail and display details if `$status' is 0. Details include `$output'. -# -# Optionally, when the expected status is specified, fail when it does -# not equal `$status'. In this case, details include the expected and -# actual status, and `$output'. -# -# Globals: -# status -# output -# Arguments: -# $1 - [opt] expected status -# Returns: -# 0 - `$status' is not 0, or -# `$status' equals the expected status -# 1 - otherwise -# Outputs: -# STDERR - details, on failure -assert_failure() { - (( $# > 0 )) && local -r expected="$1" - if (( status == 0 )); then - batslib_print_kv_single_or_multi 6 'output' "$output" \ - | batslib_decorate 'command succeeded, but it was expected to fail' \ - | fail - elif (( $# > 0 )) && (( status != expected )); then - { local -ir width=8 - batslib_print_kv_single "$width" \ - 'expected' "$expected" \ - 'actual' "$status" - batslib_print_kv_single_or_multi "$width" \ - 'output' "$output" - } | batslib_decorate 'command failed as expected, but status differs' \ - | fail - fi -} - -# Fail and display details if the expected does not match the actual -# output or a fragment of it. -# -# By default, the entire output is matched. The assertion fails if the -# expected output does not equal `$output'. Details include both values. -# -# When `-l ' is used, only the -th line is matched. The -# assertion fails if the expected line does not equal -# `${lines[}'. Details include the compared lines and . -# -# When `-l' is used without the argument, the output is searched -# for the expected line. The expected line is matched against each line -# in `${lines[@]}'. If no match is found the assertion fails. Details -# include the expected line and `$output'. -# -# By default, literal matching is performed. Options `-p' and `-r' -# enable partial (i.e. substring) and extended regular expression -# matching, respectively. Specifying an invalid extended regular -# expression with `-r' displays an error. -# -# Options `-p' and `-r' are mutually exclusive. When used -# simultaneously, an error is displayed. -# -# Globals: -# output -# lines -# Options: -# -l - match against the -th element of `${lines[@]}' -# -l - search `${lines[@]}' for the expected line -# -p - partial matching -# -r - extended regular expression matching -# Arguments: -# $1 - expected output -# Returns: -# 0 - expected matches the actual output -# 1 - otherwise -# Outputs: -# STDERR - details, on failure -# error message, on error -assert_output() { - local -i is_match_line=0 - local -i is_match_contained=0 - local -i is_mode_partial=0 - local -i is_mode_regex=0 - - # Handle options. - while (( $# > 0 )); do - case "$1" in - -l) - if (( $# > 2 )) && [[ $2 =~ ^([0-9]|[1-9][0-9]+)$ ]]; then - is_match_line=1 - local -ri idx="$2" - shift - else - is_match_contained=1; - fi - shift - ;; - -p) is_mode_partial=1; shift ;; - -r) is_mode_regex=1; shift ;; - --) break ;; - *) break ;; - esac - done - - if (( is_match_line )) && (( is_match_contained )); then - echo "\`-l' and \`-l ' are mutually exclusive" \ - | batslib_decorate 'ERROR: assert_output' \ - | fail - return $? - fi - - if (( is_mode_partial )) && (( is_mode_regex )); then - echo "\`-p' and \`-r' are mutually exclusive" \ - | batslib_decorate 'ERROR: assert_output' \ - | fail - return $? - fi - - # Arguments. - local -r expected="$1" - - if (( is_mode_regex == 1 )) && [[ '' =~ $expected ]] || (( $? == 2 )); then - echo "Invalid extended regular expression: \`$expected'" \ - | batslib_decorate 'ERROR: assert_output' \ - | fail - return $? - fi - - # Matching. - if (( is_match_contained )); then - # Line contained in output. - if (( is_mode_regex )); then - local -i idx - for (( idx = 0; idx < ${#lines[@]}; ++idx )); do - [[ ${lines[$idx]} =~ $expected ]] && return 0 - done - { local -ar single=( - 'regex' "$expected" - ) - local -ar may_be_multi=( - 'output' "$output" - ) - local -ir width="$( batslib_get_max_single_line_key_width \ - "${single[@]}" "${may_be_multi[@]}" )" - batslib_print_kv_single "$width" "${single[@]}" - batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}" - } | batslib_decorate 'no output line matches regular expression' \ - | fail - elif (( is_mode_partial )); then - local -i idx - for (( idx = 0; idx < ${#lines[@]}; ++idx )); do - [[ ${lines[$idx]} == *"$expected"* ]] && return 0 - done - { local -ar single=( - 'substring' "$expected" - ) - local -ar may_be_multi=( - 'output' "$output" - ) - local -ir width="$( batslib_get_max_single_line_key_width \ - "${single[@]}" "${may_be_multi[@]}" )" - batslib_print_kv_single "$width" "${single[@]}" - batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}" - } | batslib_decorate 'no output line contains substring' \ - | fail - else - local -i idx - for (( idx = 0; idx < ${#lines[@]}; ++idx )); do - [[ ${lines[$idx]} == "$expected" ]] && return 0 - done - { local -ar single=( - 'line' "$expected" - ) - local -ar may_be_multi=( - 'output' "$output" - ) - local -ir width="$( batslib_get_max_single_line_key_width \ - "${single[@]}" "${may_be_multi[@]}" )" - batslib_print_kv_single "$width" "${single[@]}" - batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}" - } | batslib_decorate 'output does not contain line' \ - | fail - fi - elif (( is_match_line )); then - # Specific line. - if (( is_mode_regex )); then - if ! [[ ${lines[$idx]} =~ $expected ]]; then - batslib_print_kv_single 5 \ - 'index' "$idx" \ - 'regex' "$expected" \ - 'line' "${lines[$idx]}" \ - | batslib_decorate 'regular expression does not match line' \ - | fail - fi - elif (( is_mode_partial )); then - if [[ ${lines[$idx]} != *"$expected"* ]]; then - batslib_print_kv_single 9 \ - 'index' "$idx" \ - 'substring' "$expected" \ - 'line' "${lines[$idx]}" \ - | batslib_decorate 'line does not contain substring' \ - | fail - fi - else - if [[ ${lines[$idx]} != "$expected" ]]; then - batslib_print_kv_single 8 \ - 'index' "$idx" \ - 'expected' "$expected" \ - 'actual' "${lines[$idx]}" \ - | batslib_decorate 'line differs' \ - | fail - fi - fi - else - # Entire output. - if (( is_mode_regex )); then - if ! [[ $output =~ $expected ]]; then - batslib_print_kv_single_or_multi 6 \ - 'regex' "$expected" \ - 'output' "$output" \ - | batslib_decorate 'regular expression does not match output' \ - | fail - fi - elif (( is_mode_partial )); then - if [[ $output != *"$expected"* ]]; then - batslib_print_kv_single_or_multi 9 \ - 'substring' "$expected" \ - 'output' "$output" \ - | batslib_decorate 'output does not contain substring' \ - | fail - fi - else - if [[ $output != "$expected" ]]; then - batslib_print_kv_single_or_multi 8 \ - 'expected' "$expected" \ - 'actual' "$output" \ - | batslib_decorate 'output differs' \ - | fail - fi - fi - fi -} - -# Fail and display details if the unexpected matches the actual output -# or a fragment of it. -# -# By default, the entire output is matched. The assertion fails if the -# unexpected output equals `$output'. Details include `$output'. -# -# When `-l ' is used, only the -th line is matched. The -# assertion fails if the unexpected line equals `${lines[}'. -# Details include the compared line and . -# -# When `-l' is used without the argument, the output is searched -# for the unexpected line. The unexpected line is matched against each -# line in `${lines[]}'. If a match is found the assertion fails. -# Details include the unexpected line, the index where it was found and -# `$output' (with the unexpected line highlighted in it if `$output` is -# longer than one line). -# -# By default, literal matching is performed. Options `-p' and `-r' -# enable partial (i.e. substring) and extended regular expression -# matching, respectively. On failure, the substring or the regular -# expression is added to the details (if not already displayed). -# Specifying an invalid extended regular expression with `-r' displays -# an error. -# -# Options `-p' and `-r' are mutually exclusive. When used -# simultaneously, an error is displayed. -# -# Globals: -# output -# lines -# Options: -# -l - match against the -th element of `${lines[@]}' -# -l - search `${lines[@]}' for the unexpected line -# -p - partial matching -# -r - extended regular expression matching -# Arguments: -# $1 - unexpected output -# Returns: -# 0 - unexpected matches the actual output -# 1 - otherwise -# Outputs: -# STDERR - details, on failure -# error message, on error -refute_output() { - local -i is_match_line=0 - local -i is_match_contained=0 - local -i is_mode_partial=0 - local -i is_mode_regex=0 - - # Handle options. - while (( $# > 0 )); do - case "$1" in - -l) - if (( $# > 2 )) && [[ $2 =~ ^([0-9]|[1-9][0-9]+)$ ]]; then - is_match_line=1 - local -ri idx="$2" - shift - else - is_match_contained=1; - fi - shift - ;; - -L) is_match_contained=1; shift ;; - -p) is_mode_partial=1; shift ;; - -r) is_mode_regex=1; shift ;; - --) break ;; - *) break ;; - esac - done - - if (( is_match_line )) && (( is_match_contained )); then - echo "\`-l' and \`-l ' are mutually exclusive" \ - | batslib_decorate 'ERROR: refute_output' \ - | fail - return $? - fi - - if (( is_mode_partial )) && (( is_mode_regex )); then - echo "\`-p' and \`-r' are mutually exclusive" \ - | batslib_decorate 'ERROR: refute_output' \ - | fail - return $? - fi - - # Arguments. - local -r unexpected="$1" - - if (( is_mode_regex == 1 )) && [[ '' =~ $unexpected ]] || (( $? == 2 )); then - echo "Invalid extended regular expression: \`$unexpected'" \ - | batslib_decorate 'ERROR: refute_output' \ - | fail - return $? - fi - - # Matching. - if (( is_match_contained )); then - # Line contained in output. - if (( is_mode_regex )); then - local -i idx - for (( idx = 0; idx < ${#lines[@]}; ++idx )); do - if [[ ${lines[$idx]} =~ $unexpected ]]; then - { local -ar single=( - 'regex' "$unexpected" - 'index' "$idx" - ) - local -a may_be_multi=( - 'output' "$output" - ) - local -ir width="$( batslib_get_max_single_line_key_width \ - "${single[@]}" "${may_be_multi[@]}" )" - batslib_print_kv_single "$width" "${single[@]}" - if batslib_is_single_line "${may_be_multi[1]}"; then - batslib_print_kv_single "$width" "${may_be_multi[@]}" - else - may_be_multi[1]="$( printf '%s' "${may_be_multi[1]}" \ - | batslib_prefix \ - | batslib_mark '>' "$idx" )" - batslib_print_kv_multi "${may_be_multi[@]}" - fi - } | batslib_decorate 'no line should match the regular expression' \ - | fail - return $? - fi - done - elif (( is_mode_partial )); then - local -i idx - for (( idx = 0; idx < ${#lines[@]}; ++idx )); do - if [[ ${lines[$idx]} == *"$unexpected"* ]]; then - { local -ar single=( - 'substring' "$unexpected" - 'index' "$idx" - ) - local -a may_be_multi=( - 'output' "$output" - ) - local -ir width="$( batslib_get_max_single_line_key_width \ - "${single[@]}" "${may_be_multi[@]}" )" - batslib_print_kv_single "$width" "${single[@]}" - if batslib_is_single_line "${may_be_multi[1]}"; then - batslib_print_kv_single "$width" "${may_be_multi[@]}" - else - may_be_multi[1]="$( printf '%s' "${may_be_multi[1]}" \ - | batslib_prefix \ - | batslib_mark '>' "$idx" )" - batslib_print_kv_multi "${may_be_multi[@]}" - fi - } | batslib_decorate 'no line should contain substring' \ - | fail - return $? - fi - done - else - local -i idx - for (( idx = 0; idx < ${#lines[@]}; ++idx )); do - if [[ ${lines[$idx]} == "$unexpected" ]]; then - { local -ar single=( - 'line' "$unexpected" - 'index' "$idx" - ) - local -a may_be_multi=( - 'output' "$output" - ) - local -ir width="$( batslib_get_max_single_line_key_width \ - "${single[@]}" "${may_be_multi[@]}" )" - batslib_print_kv_single "$width" "${single[@]}" - if batslib_is_single_line "${may_be_multi[1]}"; then - batslib_print_kv_single "$width" "${may_be_multi[@]}" - else - may_be_multi[1]="$( printf '%s' "${may_be_multi[1]}" \ - | batslib_prefix \ - | batslib_mark '>' "$idx" )" - batslib_print_kv_multi "${may_be_multi[@]}" - fi - } | batslib_decorate 'line should not be in output' \ - | fail - return $? - fi - done - fi - elif (( is_match_line )); then - # Specific line. - if (( is_mode_regex )); then - if [[ ${lines[$idx]} =~ $unexpected ]] || (( $? == 0 )); then - batslib_print_kv_single 5 \ - 'index' "$idx" \ - 'regex' "$unexpected" \ - 'line' "${lines[$idx]}" \ - | batslib_decorate 'regular expression should not match line' \ - | fail - fi - elif (( is_mode_partial )); then - if [[ ${lines[$idx]} == *"$unexpected"* ]]; then - batslib_print_kv_single 9 \ - 'index' "$idx" \ - 'substring' "$unexpected" \ - 'line' "${lines[$idx]}" \ - | batslib_decorate 'line should not contain substring' \ - | fail - fi - else - if [[ ${lines[$idx]} == "$unexpected" ]]; then - batslib_print_kv_single 5 \ - 'index' "$idx" \ - 'line' "${lines[$idx]}" \ - | batslib_decorate 'line should differ' \ - | fail - fi - fi - else - # Entire output. - if (( is_mode_regex )); then - if [[ $output =~ $unexpected ]] || (( $? == 0 )); then - batslib_print_kv_single_or_multi 6 \ - 'regex' "$unexpected" \ - 'output' "$output" \ - | batslib_decorate 'regular expression should not match output' \ - | fail - fi - elif (( is_mode_partial )); then - if [[ $output == *"$unexpected"* ]]; then - batslib_print_kv_single_or_multi 9 \ - 'substring' "$unexpected" \ - 'output' "$output" \ - | batslib_decorate 'output should not contain substring' \ - | fail - fi - else - if [[ $output == "$unexpected" ]]; then - batslib_print_kv_single_or_multi 6 \ - 'output' "$output" \ - | batslib_decorate 'output equals, but it was expected to differ' \ - | fail - fi - fi - fi -} diff --git a/test/lib/output.bash b/test/lib/output.bash deleted file mode 100644 index aa9cb876..00000000 --- a/test/lib/output.bash +++ /dev/null @@ -1,264 +0,0 @@ -# -# output.bash -# ----------- -# -# Private functions implementing output formatting. Used by public -# helper functions. -# - -# Print a message to the standard error. When no parameters are -# specified, the message is read from the standard input. -# -# Globals: -# none -# Arguments: -# $@ - [=STDIN] message -# Returns: -# none -# Inputs: -# STDIN - [=$@] message -# Outputs: -# STDERR - message -batslib_err() { - { if (( $# > 0 )); then - echo "$@" - else - cat - - fi - } >&2 -} - -# Count the number of lines in the given string. -# -# TODO(ztombol): Fix tests and remove this note after #93 is resolved! -# NOTE: Due to a bug in Bats, `batslib_count_lines "$output"' does not -# give the same result as `${#lines[@]}' when the output contains -# empty lines. -# See PR #93 (https://github.com/sstephenson/bats/pull/93). -# -# Globals: -# none -# Arguments: -# $1 - string -# Returns: -# none -# Outputs: -# STDOUT - number of lines -batslib_count_lines() { - local -i n_lines=0 - local line - while IFS='' read -r line || [[ -n $line ]]; do - (( ++n_lines )) - done < <(printf '%s' "$1") - echo "$n_lines" -} - -# Determine whether all strings are single-line. -# -# Globals: -# none -# Arguments: -# $@ - strings -# Returns: -# 0 - all strings are single-line -# 1 - otherwise -batslib_is_single_line() { - for string in "$@"; do - (( $(batslib_count_lines "$string") > 1 )) && return 1 - done - return 0 -} - -# Determine the length of the longest key that has a single-line value. -# -# This function is useful in determining the correct width of the key -# column in two-column format when some keys may have multi-line values -# and thus should be excluded. -# -# Globals: -# none -# Arguments: -# $odd - key -# $even - value of the previous key -# Returns: -# none -# Outputs: -# STDOUT - length of longest key -batslib_get_max_single_line_key_width() { - local -i max_len=-1 - while (( $# != 0 )); do - local -i key_len="${#1}" - batslib_is_single_line "$2" && (( key_len > max_len )) && max_len="$key_len" - shift 2 - done - echo "$max_len" -} - -# Print key-value pairs in two-column format. -# -# Keys are displayed in the first column, and their corresponding values -# in the second. To evenly line up values, the key column is fixed-width -# and its width is specified with the first parameter (possibly computed -# using `batslib_get_max_single_line_key_width'). -# -# Globals: -# none -# Arguments: -# $1 - width of key column -# $even - key -# $odd - value of the previous key -# Returns: -# none -# Outputs: -# STDOUT - formatted key-value pairs -batslib_print_kv_single() { - local -ir col_width="$1"; shift - while (( $# != 0 )); do - printf '%-*s : %s\n' "$col_width" "$1" "$2" - shift 2 - done -} - -# Print key-value pairs in multi-line format. -# -# The key is displayed first with the number of lines of its -# corresponding value in parenthesis. Next, starting on the next line, -# the value is displayed. For better readability, it is recommended to -# indent values using `batslib_prefix'. -# -# Globals: -# none -# Arguments: -# $odd - key -# $even - value of the previous key -# Returns: -# none -# Outputs: -# STDOUT - formatted key-value pairs -batslib_print_kv_multi() { - while (( $# != 0 )); do - printf '%s (%d lines):\n' "$1" "$( batslib_count_lines "$2" )" - printf '%s\n' "$2" - shift 2 - done -} - -# Print all key-value pairs in either two-column or multi-line format -# depending on whether all values are single-line. -# -# If all values are single-line, print all pairs in two-column format -# with the specified key column width (identical to using -# `batslib_print_kv_single'). -# -# Otherwise, print all pairs in multi-line format after indenting values -# with two spaces for readability (identical to using `batslib_prefix' -# and `batslib_print_kv_multi') -# -# Globals: -# none -# Arguments: -# $1 - width of key column (for two-column format) -# $even - key -# $odd - value of the previous key -# Returns: -# none -# Outputs: -# STDOUT - formatted key-value pairs -batslib_print_kv_single_or_multi() { - local -ir width="$1"; shift - local -a pairs=( "$@" ) - - local -a values=() - local -i i - for (( i=1; i < ${#pairs[@]}; i+=2 )); do - values+=( "${pairs[$i]}" ) - done - - if batslib_is_single_line "${values[@]}"; then - batslib_print_kv_single "$width" "${pairs[@]}" - else - local -i i - for (( i=1; i < ${#pairs[@]}; i+=2 )); do - pairs[$i]="$( batslib_prefix < <(printf '%s' "${pairs[$i]}") )" - done - batslib_print_kv_multi "${pairs[@]}" - fi -} - -# Prefix each line read from the standard input with the given string. -# -# Globals: -# none -# Arguments: -# $1 - [= ] prefix string -# Returns: -# none -# Inputs: -# STDIN - lines -# Outputs: -# STDOUT - prefixed lines -batslib_prefix() { - local -r prefix="${1:- }" - local line - while IFS='' read -r line || [[ -n $line ]]; do - printf '%s%s\n' "$prefix" "$line" - done -} - -# Mark select lines of the text read from the standard input by -# overwriting their beginning with the given string. -# -# Usually the input is indented by a few spaces using `batslib_prefix' -# first. -# -# Globals: -# none -# Arguments: -# $1 - marking string -# $@ - indices (zero-based) of lines to mark -# Returns: -# none -# Inputs: -# STDIN - lines -# Outputs: -# STDOUT - lines after marking -batslib_mark() { - local -r symbol="$1"; shift - # Sort line numbers. - set -- $( sort -nu <<< "$( printf '%d\n' "$@" )" ) - - local line - local -i idx=0 - while IFS='' read -r line || [[ -n $line ]]; do - if (( ${1:--1} == idx )); then - printf '%s\n' "${symbol}${line:${#symbol}}" - shift - else - printf '%s\n' "$line" - fi - (( ++idx )) - done -} - -# Enclose the input text in header and footer lines. -# -# The header contains the given string as title. The output is preceded -# and followed by an additional newline to make it stand out more. -# -# Globals: -# none -# Arguments: -# $1 - title -# Returns: -# none -# Inputs: -# STDIN - text -# Outputs: -# STDOUT - decorated text -batslib_decorate() { - echo - echo "-- $1 --" - cat - - echo '--' - echo -} \ No newline at end of file diff --git a/test/test.full.bats b/test/test.full.bats deleted file mode 100755 index 38ba0e8e..00000000 --- a/test/test.full.bats +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/env bats -load test_helpers -load docker_helpers -load "lib/batslib" -load "lib/output" - -@test "[$TEST_FILE] testing Cachet Docker image build" { - command docker-compose build --no-cache cachet -} - -@test "[$TEST_FILE] testing Cachet docker-compose up" { - command docker-compose up -d -} - -@test "[$TEST_FILE] check for container init" { - docker_wait_for_log docker_cachet_1 15 "Initializing Cachet container ..." -} - -@test "[$TEST_FILE] check for postgres database startup" { - docker_wait_for_log docker_postgres_1 120 "LOG: database system is ready to accept connections" -} - -@test "[$TEST_FILE] check for empty sessions table" { - docker_wait_for_log docker_cachet_1 15 "Table chq_sessions does not exist! ..." -} - -@test "[$TEST_FILE] check for DB init" { - docker_wait_for_log docker_cachet_1 15 "Initializing Cachet database ..." -} - -@test "[$TEST_FILE] check for populated sessions table" { - docker_wait_for_log docker_cachet_1 15 "Table chq_sessions exists! ..." -} - -@test "[$TEST_FILE] check for container start message" { - docker_wait_for_log docker_cachet_1 15 "Starting Cachet! ..." -} - -@test "[$TEST_FILE] check for nginx startup" { - docker_wait_for_log docker_cachet_1 15 "INFO success: nginx entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)" -} - -@test "[$TEST_FILE] check for php-fpm startup" { - docker_wait_for_log docker_cachet_1 15 "INFO success: php-fpm entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)" -} - -@test "[$TEST_FILE] check for queue-worker startup" { - docker_wait_for_log docker_cachet_1 15 "INFO success: queue-worker entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)" -} - -@test "[$TEST_FILE] php artisan cachet:seed" { - run docker exec docker_cachet_1 php artisan cachet:seed - assert_output -l 0 $'Database seeded with demo data successfully!' -} - -@test "[$TEST_FILE] curl 200 response test" { - run curl_container docker_cachet_1 :8000/auth/login --head - assert_output -l 0 $'HTTP/1.1 200 OK\r' -} - -@test "[$TEST_FILE] login test" { - run curl_container docker_cachet_1 :8000/auth/login --head --user test:test123 - assert_output -l 0 $'HTTP/1.1 200 OK\r' -} - -@test "[$TEST_FILE] check for curl API pong" { - run curl_container docker_cachet_1 :8000/api/v1/ping - assert_output -l 0 $'{"data":"Pong!"}' -} - -@test "[$TEST_FILE] check for pg_dump version mismatch" { - run docker exec docker_cachet_1 php artisan app:update - refute_output -l 5 $'pg_dump: aborting because of server version mismatch' -} - -@test "[$TEST_FILE] ensure there are no volumes" { - run docker inspect -f '{{ .Mounts }}' docker_cachet_1 - assert_output -l 0 $'[]' -} - -@test "[$TEST_FILE] restart cachet" { - command docker-compose stop cachet - command docker-compose rm -f cachet - command docker-compose up -d - docker_wait_for_log docker_cachet_1 15 "INFO success: nginx entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)" -} - -@test "[$TEST_FILE] post-restart API pong" { - run curl_container docker_cachet_1 :8000/api/v1/ping - assert_output -l 0 $'{"data":"Pong!"}' -} - -@test "[$TEST_FILE] post-restart login test" { - run curl_container docker_cachet_1 :8000/auth/login --head --user test:test123 - assert_output -l 0 $'HTTP/1.1 200 OK\r' -} - -@test "[$TEST_FILE] stop all test containers" { - stop_bats_containers -} - -@test "[$TEST_FILE] Cleanup test containers" { - docker_clean docker_cachet_1 - docker_clean docker_postgres_1 -} diff --git a/test/test.mysql.bats b/test/test.mysql.bats deleted file mode 100755 index 2ea73a96..00000000 --- a/test/test.mysql.bats +++ /dev/null @@ -1,87 +0,0 @@ -#!/usr/bin/env bats -load test_helpers -load docker_helpers -load "lib/batslib" -load "lib/output" - -@test "[$TEST_FILE] docker-compose up" { - command docker-compose -f test/docker-compose-mysql.yml up -d -} - -@test "[$TEST_FILE] check for container init" { - docker_wait_for_log test_cachet_1 15 "Initializing Cachet container ..." -} - -@test "[$TEST_FILE] check for database startup" { - docker_wait_for_log test_mysql_1 120 "mysqld: ready for connections." -} - -@test "[$TEST_FILE] check for empty sessions table" { - docker_wait_for_log test_cachet_1 15 "Table chq_sessions does not exist! ..." -} - -@test "[$TEST_FILE] check for DB init" { - docker_wait_for_log test_cachet_1 15 "Initializing Cachet database ..." -} - -@test "[$TEST_FILE] check for populated sessions table" { - docker_wait_for_log test_cachet_1 15 "Table chq_sessions exists! ..." -} - -@test "[$TEST_FILE] check for container start message" { - docker_wait_for_log test_cachet_1 15 "Starting Cachet! ..." -} - -@test "[$TEST_FILE] check for nginx startup" { - docker_wait_for_log test_cachet_1 15 "INFO success: nginx entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)" -} - -@test "[$TEST_FILE] check for php-fpm startup" { - docker_wait_for_log test_cachet_1 15 "INFO success: php-fpm entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)" -} - -@test "[$TEST_FILE] php artisan cachet:seed" { - run docker exec test_cachet_1 php artisan cachet:seed - assert_output -l 0 $'Database seeded with demo data successfully!' -} - -@test "[$TEST_FILE] curl 200 response test" { - run curl_container test_cachet_1 :8000/auth/login --head - assert_output -l 0 $'HTTP/1.1 200 OK\r' -} - -@test "[$TEST_FILE] login test" { - run curl_container test_cachet_1 :8000/auth/login --head --user test:test123 - assert_output -l 0 $'HTTP/1.1 200 OK\r' -} - -@test "[$TEST_FILE] check for curl API pong" { - run curl_container test_cachet_1 :8000/api/v1/ping - assert_output -l 0 $'{"data":"Pong!"}' -} - -@test "[$TEST_FILE] restart cachet" { - command docker-compose -f test/docker-compose-mysql.yml stop cachet - command docker-compose -f test/docker-compose-mysql.yml rm -f cachet - command docker-compose -f test/docker-compose-mysql.yml up -d - docker_wait_for_log test_cachet_1 15 "INFO success: nginx entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)" -} - -@test "[$TEST_FILE] post-restart API pong" { - run curl_container test_cachet_1 :8000/api/v1/ping - assert_output -l 0 $'{"data":"Pong!"}' -} - -@test "[$TEST_FILE] post-restart login test" { - run curl_container test_cachet_1 :8000/auth/login --head --user test:test123 - assert_output -l 0 $'HTTP/1.1 200 OK\r' -} - -@test "[$TEST_FILE] stop all test containers" { - stop_bats_containers -} - -@test "[$TEST_FILE] Cleanup test containers" { - docker_clean test_cachet_1 - docker_clean test_mysql_1 -} diff --git a/test/test.sqlite.bats b/test/test.sqlite.bats deleted file mode 100755 index f20153d2..00000000 --- a/test/test.sqlite.bats +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env bats -load test_helpers -load docker_helpers -load "lib/batslib" -load "lib/output" - -@test "[$TEST_FILE] docker-compose up" { - command docker-compose -f test/docker-compose-sqlite.yml up -d -} - -@test "[$TEST_FILE] check for container init" { - docker_wait_for_log test_cachet_1 15 "Initializing Cachet container ..." -} - -@test "[$TEST_FILE] check for container start message" { - docker_wait_for_log test_cachet_1 15 "Starting Cachet! ..." -} - -@test "[$TEST_FILE] check for nginx startup" { - docker_wait_for_log test_cachet_1 15 "INFO success: nginx entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)" -} - -@test "[$TEST_FILE] check for php-fpm startup" { - docker_wait_for_log test_cachet_1 15 "INFO success: php-fpm entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)" -} - -@test "[$TEST_FILE] php artisan cachet:seed" { - run docker exec test_cachet_1 php artisan cachet:seed - assert_output -l 0 $'Database seeded with demo data successfully!' -} - -@test "[$TEST_FILE] curl 200 response test" { - run curl_container test_cachet_1 :8000/auth/login --head - assert_output -l 0 $'HTTP/1.1 200 OK\r' -} - -@test "[$TEST_FILE] login test" { - run curl_container test_cachet_1 :8000/auth/login --head --user test:test123 - assert_output -l 0 $'HTTP/1.1 200 OK\r' -} - -@test "[$TEST_FILE] check for curl API pong" { - run curl_container test_cachet_1 :8000/api/v1/ping - assert_output -l 0 $'{"data":"Pong!"}' -} - -@test "[$TEST_FILE] restart cachet" { - command docker-compose -f test/docker-compose-sqlite.yml stop cachet - command docker-compose -f test/docker-compose-sqlite.yml rm -f cachet - command docker-compose -f test/docker-compose-sqlite.yml up -d - docker_wait_for_log test_cachet_1 15 "INFO success: nginx entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)" -} - -@test "[$TEST_FILE] post-restart API pong" { - run curl_container test_cachet_1 :8000/api/v1/ping - assert_output -l 0 $'{"data":"Pong!"}' -} - -@test "[$TEST_FILE] post-restart login test" { - run curl_container test_cachet_1 :8000/auth/login --head --user test:test123 - assert_output -l 0 $'HTTP/1.1 200 OK\r' -} - -@test "[$TEST_FILE] stop all test containers" { - stop_bats_containers -} - -@test "[$TEST_FILE] Cleanup test containers" { - docker_clean test_cachet_1 -} diff --git a/test/test_helpers.bash b/test/test_helpers.bash deleted file mode 100644 index 045cb450..00000000 --- a/test/test_helpers.bash +++ /dev/null @@ -1,61 +0,0 @@ -# Test if requirements are met -( - type docker &>/dev/null || ( echo "docker is not available"; exit 1 ) -)>&2 - -# ENV vars for tests -export APP_ENV=development -export APP_KEY="base64:v2LwHrdgnE+RavEXdnF8LgWIibjvEcFkU2qaX5Ji708=" - -TEST_FILE=$(basename $BATS_TEST_FILENAME .bats) - -# stop all containers with the "bats-type" label (matching the optionally supplied value) -# -# $1 optional label value -function stop_bats_containers { - docker-compose stop -} - -# delete all containers -docker_cleanup() { - docker-compose rm -af -} - -# Send a HTTP request to container $1 for path $2 and -# Additional curl options can be passed as $@ -# -# $1 container name -# $2 HTTP path to query -# $@ additional options to pass to the curl command -function curl_container { - local -r container=$1 - local -r path=$2 - shift 2 - docker run --rm --net=docker_default --label bats-type="curl" curlimages/curl --silent \ - --connect-timeout 5 \ - --max-time 20 \ - --retry 4 --retry-delay 5 \ - "$@" \ - http://$(docker_ip $container)${path} -} - -# Retry a command $1 times until it succeeds. Wait $2 seconds between retries. -function retry { - local attempts=$1 - shift - local delay=$1 - shift - local i - - for ((i=0; i < attempts; i++)); do - run "$@" - if [ "$status" -eq 0 ]; then - echo "$output" - return 0 - fi - sleep $delay - done - - echo "Command \"$@\" failed $attempts times. Status: $status. Output: $output" >&2 - false -}