diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 5d88d2a..93229ad 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,15 +1,19 @@ ## The Issue -- # +- Fixes #REPLACE_ME_WITH_RELATED_ISSUE_NUMBER ## How This PR Solves The Issue + + ## Manual Testing Instructions + + ```bash -ddev add-on get https://github.com///tarball/ +ddev add-on get https://github.com/ddev/ddev-varnish/tarball/refs/pull/REPLACE_ME_WITH_THIS_PR_NUMBER/head ddev restart ``` diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 54604ea..ffdc8a4 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,11 +1,15 @@ name: tests on: pull_request: + paths-ignore: + - "**.md" push: branches: [ main ] + paths-ignore: + - "**.md" schedule: - - cron: '25 08 * * *' + - cron: '25 08 * * *' workflow_dispatch: inputs: @@ -19,9 +23,8 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true -# This is required for "gautamkrishnar/keepalive-workflow", see "ddev/github-action-add-on-test" permissions: - actions: write + contents: read jobs: tests: diff --git a/README.md b/README.md index 09b425b..5872524 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ ddev restart ``` > [!NOTE] -> Run `ddev add-on get ddev/ddev-varnish` after changes to `name`, `additional_hostnames`, `additional_fqdns`, or `project_tld` in `.ddev/config.yaml` so that `.ddev/docker-compose.varnish_extras.yaml` is regenerated. +> Run `ddev add-on get ddev/ddev-varnish` after changes in the Mailpit ports or `web_extra_exposed_ports` in `.ddev/config.yaml` so that `.ddev/docker-compose.varnish_extras.yaml` is regenerated. After installation, make sure to commit the `.ddev` directory to version control. @@ -27,9 +27,7 @@ After installation, make sure to commit the `.ddev` directory to version control The Varnish service inserts itself between ddev-router and the web container, so that calls to the web container are routed through Varnish first. The [docker-compose.varnish.yaml](docker-compose.varnish.yaml) installs Varnish and uses the default domain as its own host name. -A `docker-compose.varnish_extras.yaml` file is generated on install which replaces the `VIRTUAL_HOST` variable of the web container with a sub-domain of the website URL. For example, `mysite.ddev.site`, would be accessible via Varnish on `mysite.ddev.site` and directly on `novarnish.mysite.ddev.site`. - -If you use a `project_tld` other than `ddev.site` or `additional_fqdns` DDEV will help add hosts entries for the hostnames automagically; however, you'll need to add entries for the `novarnish.*` sub-domains yourself, e.g. `ddev hostname novarnish.testaddfqdn.random.tld 127.0.0.1`. +A `docker-compose.varnish_extras.yaml` file is generated on install which replaces the `HTTP_EXPOSE` and `HTTPS_EXPOSE` variables of the web container to exclude non-webserver ports from Varnish. ## Helper Commands diff --git a/docker-compose.varnish.yaml b/docker-compose.varnish.yaml index a707e14..2e840e1 100644 --- a/docker-compose.varnish.yaml +++ b/docker-compose.varnish.yaml @@ -6,17 +6,11 @@ services: # These labels ensure this service is discoverable by ddev. labels: com.ddev.site-name: ${DDEV_SITENAME} - com.ddev.approot: $DDEV_APPROOT + com.ddev.approot: ${DDEV_APPROOT} environment: - # This defines the host name the service should be accessible from. This - # will be sitename.ddev.site. - # This is the first half of the trick that puts varnish "in front of" the - # web container, just by switching the names. - - VIRTUAL_HOST=$DDEV_HOSTNAME - # This defines the ports the service should be accessible from at - # sitename.ddev.site. - - HTTPS_EXPOSE=${DDEV_ROUTER_HTTPS_PORT}:80,${DDEV_MAILPIT_HTTPS_PORT}:8025 - - HTTP_EXPOSE=${DDEV_ROUTER_HTTP_PORT}:80,${DDEV_MAILPIT_PORT}:8025 + - VIRTUAL_HOST=${DDEV_HOSTNAME} + - HTTP_EXPOSE=${DDEV_ROUTER_HTTP_PORT}:80 + - HTTPS_EXPOSE=${DDEV_ROUTER_HTTPS_PORT}:80 volumes: # This exposes a mount to the host system `.ddev/varnish` directory where # your default.vcl should be. @@ -24,8 +18,4 @@ services: - ".:/mnt/ddev_config" depends_on: - web - # Add mailpit support - expose: - - "8025" - entrypoint: - /usr/local/bin/docker-varnish-entrypoint -a 0.0.0.0:8025 ${VARNISH_VARNISHD_PARAMS:--p http_max_hdr=1000 -p http_resp_hdr_len=1M -p http_resp_size=2M -p workspace_backend=3M -p workspace_client=3M} + entrypoint: docker-varnish-entrypoint ${VARNISH_VARNISHD_PARAMS:--p http_max_hdr=1000 -p http_resp_hdr_len=1M -p http_resp_size=2M -p workspace_backend=3M -p workspace_client=3M} diff --git a/install.yaml b/install.yaml index a4fbf02..d546f0b 100644 --- a/install.yaml +++ b/install.yaml @@ -9,7 +9,6 @@ ddev_version_constraint: '>= v1.24.3' pre_install_actions: - | - #ddev-nodisplay #ddev-description:Removing old docker-compose.varnish-extras.yaml if [ -f ${DDEV_APPROOT}/.ddev/docker-compose.varnish-extras.yaml ]; then if grep -q '#ddev-generated' ${DDEV_APPROOT}/.ddev/docker-compose.varnish-extras.yaml; then @@ -22,29 +21,36 @@ pre_install_actions: post_install_actions: - | - #ddev-nodisplay #ddev-description:Checking docker-compose.varnish_extras.yaml for changes if [ -f docker-compose.varnish_extras.yaml ] && ! grep -q '#ddev-generated' docker-compose.varnish_extras.yaml; then echo "Existing docker-compose.varnish_extras.yaml does not have #ddev-generated, so can't be updated" exit 2 fi - | - #ddev-nodisplay - #ddev-description:Replacing all hostnames in the web container by adding "novarnish" subdomain prefix - cat <<-END >docker-compose.varnish_extras.yaml + #ddev-description:Modify HTTP_EXPOSE and HTTPS_EXPOSE for web container + cat <<'EOF' > docker-compose.varnish_extras.yaml #ddev-generated - # This is the second half of the trick that puts varnish "in front of" the web - # container, by switching all hostnames with "novarnish" subdomain prefix. + # This configuration modifies HTTP_EXPOSE and HTTPS_EXPOSE for web container + # to exclude 80 port, which is handled by Varnish. services: web: environment: - {{- $novarnish_hostnames := splitList "," (env "DDEV_HOSTNAME") }} - - VIRTUAL_HOST={{ range $i, $n := $novarnish_hostnames }}novarnish.{{ replace (env "DDEV_TLD") "\\${DDEV_TLD}" (replace (env "DDEV_PROJECT") "\\${DDEV_PROJECT}" $n) }}{{ if lt (add1 $i) (len $novarnish_hostnames) }},{{ end }}{{ end }} - END + {{- $base_http_ports := list (printf "%s:8025" (or (index .DdevProjectConfig "mailpit_http_port") (env "DDEV_MAILPIT_PORT"))) }} + {{- $base_https_ports := list (printf "%s:8025" (or (index .DdevProjectConfig "mailpit_https_port") (env "DDEV_MAILPIT_HTTPS_PORT"))) }} + {{- $extra_http_ports := list }} + {{- $extra_https_ports := list }} + {{- range .DdevProjectConfig.web_extra_exposed_ports }} + {{- $extra_http_ports = append $extra_http_ports (printf "%d:%d" .http_port .container_port) }} + {{- $extra_https_ports = append $extra_https_ports (printf "%d:%d" .https_port .container_port) }} + {{- end }} + {{- $all_http_ports := concat $base_http_ports $extra_http_ports }} + {{- $all_https_ports := concat $base_https_ports $extra_https_ports }} + - HTTP_EXPOSE={{ range $i, $p := $all_http_ports }}{{ $p }}{{ if lt (add1 $i) (len $all_http_ports) }},{{ end }}{{ end }} + - HTTPS_EXPOSE={{ range $i, $p := $all_https_ports }}{{ $p }}{{ if lt (add1 $i) (len $all_https_ports) }},{{ end }}{{ end }} + EOF removal_actions: - | - #ddev-nodisplay #ddev-description:Remove docker-compose.varnish_extras.yaml file if [ -f docker-compose.varnish_extras.yaml ]; then if grep -q '#ddev-generated' docker-compose.varnish_extras.yaml; then diff --git a/tests/test.bats b/tests/test.bats index ffdc31c..aae2da3 100644 --- a/tests/test.bats +++ b/tests/test.bats @@ -42,27 +42,120 @@ setup() { run ddev start -y assert_success export CUSTOM_VARNISH_VARNISHD_PARAMS=false + export ROUTER_HTTP_PORT=80 + export ROUTER_HTTPS_PORT=443 + export MAILPIT_HTTP_PORT=8025 + export MAILPIT_HTTPS_PORT=8026 + export PHP_EXTRA_HTTP_PORT= + export PHP_EXTRA_HTTPS_PORT= } health_checks() { - for url in http://${PROJNAME}.ddev.site:${ROUTER_HTTP_PORT}/ http://extrahostname.ddev.site:${ROUTER_HTTP_PORT}/ http://extrafqdn.ddev.site:${ROUTER_HTTP_PORT}/ https://${PROJNAME}.ddev.site:${ROUTER_HTTPS_PORT}/ https://extrahostname.ddev.site:${ROUTER_HTTPS_PORT}/ https://extrafqdn.ddev.site:${ROUTER_HTTPS_PORT}/ ; do - # It's "Via:" with http and "via:" with https. Tell me why. - echo "# test $url for via:.*varnish header" >&3 - curl -sfI $url | grep -i "Via:.*varnish" >/dev/null || (echo "# varnish headers not shown for $url" >&3 && exit 1); - echo "# test $url for phpinfo content" >&3 - curl -sf $url | grep "allow_url_fopen" >/dev/null || (echo "# phpinfo information not shown in curl for $url" >&3 && exit 1); + # Test that .ddev/docker-compose.varnish_extra.yaml created correct env vars + run ddev exec echo "\$HTTP_EXPOSE" + assert_success + if [[ ${PHP_EXTRA_HTTP_PORT} != "" ]]; then + assert_output "${MAILPIT_HTTP_PORT}:8025,${PHP_EXTRA_HTTP_PORT}:20080" + else + assert_output "${MAILPIT_HTTP_PORT}:8025" + fi + + run ddev exec echo "\$HTTPS_EXPOSE" + assert_success + if [[ ${PHP_EXTRA_HTTPS_PORT} != "" ]]; then + assert_output "${MAILPIT_HTTPS_PORT}:8025,${PHP_EXTRA_HTTPS_PORT}:20080" + else + assert_output "${MAILPIT_HTTPS_PORT}:8025" + fi + + local varnish_urls=( + "http://${PROJNAME}.ddev.site:${ROUTER_HTTP_PORT}" + "http://extrahostname.ddev.site:${ROUTER_HTTP_PORT}" + "http://extrafqdn.ddev.site:${ROUTER_HTTP_PORT}" + "https://${PROJNAME}.ddev.site:${ROUTER_HTTPS_PORT}" + "https://extrahostname.ddev.site:${ROUTER_HTTPS_PORT}" + "https://extrafqdn.ddev.site:${ROUTER_HTTPS_PORT}" + ) + + for url in "${varnish_urls[@]}"; do + # Test for Varnish headers (case-sensitive: "Via:" for HTTP, "via:" for HTTPS) + run curl -sfI "$url" + assert_success + if [[ "$url" == https://* ]]; then + assert_output --partial "via: 1.1 varnish (Varnish/6.0)" + assert_output --partial "x-varnish:" + else + assert_output --partial "Via: 1.1 varnish (Varnish/6.0)" + assert_output --partial "X-Varnish:" + fi + + # Test for phpinfo content + run curl -sf "$url" + assert_success + assert_output --partial "allow_url_fopen" done - for url in http://novarnish.${PROJNAME}.ddev.site:${ROUTER_HTTP_PORT}/ http://novarnish.extrahostname.ddev.site:${ROUTER_HTTP_PORT}/ http://novarnish.extrafqdn.ddev.site:${ROUTER_HTTP_PORT}/ https://novarnish.${PROJNAME}.ddev.site:${ROUTER_HTTPS_PORT}/ https://novarnish.extrahostname.ddev.site:${ROUTER_HTTPS_PORT}/ https://novarnish.extrafqdn.ddev.site:${ROUTER_HTTPS_PORT}/ ; do - echo "# test $url for phpinfo content" >&3 - curl -sf $url | grep "allow_url_fopen" >/dev/null || (echo "# phpinfo information not shown in curl for $url" >&3 && exit 1); + local mailpit_urls=( + "http://${PROJNAME}.ddev.site:${MAILPIT_HTTP_PORT}" + "http://extrahostname.ddev.site:${MAILPIT_HTTP_PORT}" + "http://extrafqdn.ddev.site:${MAILPIT_HTTP_PORT}" + "https://${PROJNAME}.ddev.site:${MAILPIT_HTTPS_PORT}" + "https://extrahostname.ddev.site:${MAILPIT_HTTPS_PORT}" + "https://extrafqdn.ddev.site:${MAILPIT_HTTPS_PORT}" + ) + + for url in "${mailpit_urls[@]}"; do + # Test that there are no Varnish headers for Mailpit + run curl -sfI "$url" + assert_failure + if [[ "$url" == https://* ]]; then + assert_output --partial "HTTP/2 405" + refute_output --partial "via: 1.1 varnish (Varnish/6.0)" + refute_output --partial "x-varnish:" + else + assert_output --partial "HTTP/1.1 405" + refute_output --partial "Via: 1.1 varnish (Varnish/6.0)" + refute_output --partial "X-Varnish:" + fi + + run curl -sf "$url" + assert_success + assert_output --partial "You need a browser with JavaScript enabled to use Mailpit" + + run curl -sf "$url/api/v1/info" + assert_success + assert_output --partial "Messages" + assert_output --partial "Unread" + assert_output --partial "RuntimeStats" done - echo "# test http://${PROJNAME}.ddev.site:${MAILPIT_HTTP_PORT}/ for http novarnish redirect" >&3 - curl -sfI "http://${PROJNAME}.ddev.site:${MAILPIT_HTTP_PORT}/" | grep -i "http://novarnish.${PROJNAME}.ddev.site:${MAILPIT_HTTP_PORT}/" >/dev/null || (echo "# http://${PROJNAME}.ddev.site:${MAILPIT_HTTP_PORT} did not redirect" >&3 && exit 1); - echo "# test https://${PROJNAME}.ddev.site:${MAILPIT_HTTPS_PORT}/ for https novarnish redirect" >&3 - curl -sfI "https://${PROJNAME}.ddev.site:${MAILPIT_HTTPS_PORT}/" | grep -i "https://novarnish.${PROJNAME}.ddev.site:${MAILPIT_HTTPS_PORT}/" >/dev/null || (echo "# https://${PROJNAME}.ddev.site:${MAILPIT_HTTPS_PORT} did not redirect" >&3 && exit 1); - + if [[ ${PHP_EXTRA_HTTP_PORT} != "" && ${PHP_EXTRA_HTTPS_PORT} != "" ]]; then + local php_extra_urls=( + "http://${PROJNAME}.ddev.site:${PHP_EXTRA_HTTP_PORT}" + "http://extrahostname.ddev.site:${PHP_EXTRA_HTTP_PORT}" + "http://extrafqdn.ddev.site:${PHP_EXTRA_HTTP_PORT}" + "https://${PROJNAME}.ddev.site:${PHP_EXTRA_HTTPS_PORT}" + "https://extrahostname.ddev.site:${PHP_EXTRA_HTTPS_PORT}" + "https://extrafqdn.ddev.site:${PHP_EXTRA_HTTPS_PORT}" + ) + + for url in "${php_extra_urls[@]}"; do + # Test that there are no Varnish headers for web_extra_exposed_ports + run curl -sfI "$url" + assert_success + if [[ "$url" == https://* ]]; then + refute_output --partial "via: 1.1 varnish (Varnish/6.0)" + refute_output --partial "x-varnish:" + else + refute_output --partial "Via: 1.1 varnish (Varnish/6.0)" + refute_output --partial "X-Varnish:" + fi + + run curl -sf "$url" + assert_output "php-extra" + done + fi + if [ "${CUSTOM_VARNISH_VARNISHD_PARAMS}" = "true" ]; then run ddev varnishadm param.show http_max_hdr assert_success @@ -96,8 +189,14 @@ health_checks() { teardown() { set -eu -o pipefail - ddev delete -Oy ${PROJNAME} >/dev/null 2>&1 - [ "${TESTDIR}" != "" ] && rm -rf ${TESTDIR} + ddev delete -Oy "${PROJNAME}" >/dev/null 2>&1 + # Persist TESTDIR if running inside GitHub Actions. Useful for uploading test result artifacts + # See example at https://github.com/ddev/github-action-add-on-test#preserving-artifacts + if [ -n "${GITHUB_ENV:-}" ]; then + [ -e "${GITHUB_ENV:-}" ] && echo "TESTDIR=${HOME}/tmp/${PROJNAME}" >> "${GITHUB_ENV}" + else + [ "${TESTDIR}" != "" ] && rm -rf "${TESTDIR}" + fi } @test "install from directory" { @@ -107,7 +206,6 @@ teardown() { assert_success run ddev restart -y assert_success - export ROUTER_HTTP_PORT=80 ROUTER_HTTPS_PORT=443 MAILPIT_HTTP_PORT=8025 MAILPIT_HTTPS_PORT=8026 health_checks } @@ -119,20 +217,44 @@ teardown() { assert_success run ddev restart -y assert_success - export ROUTER_HTTP_PORT=80 ROUTER_HTTPS_PORT=443 MAILPIT_HTTP_PORT=8025 MAILPIT_HTTPS_PORT=8026 health_checks } @test "install from directory with nonstandard port" { set -eu -o pipefail - run ddev config --router-http-port=8080 --router-https-port=8443 --mailpit-http-port=18025 --mailpit-https-port=18026 + export ROUTER_HTTP_PORT=8080 + export ROUTER_HTTPS_PORT=8443 + export MAILPIT_HTTP_PORT=18025 + export MAILPIT_HTTPS_PORT=18026 + export PHP_EXTRA_HTTP_PORT=20080 + export PHP_EXTRA_HTTPS_PORT=20443 + + run ddev config --router-http-port="${ROUTER_HTTP_PORT}" --router-https-port="${ROUTER_HTTPS_PORT}" --mailpit-http-port="${MAILPIT_HTTP_PORT}" --mailpit-https-port="${MAILPIT_HTTPS_PORT}" assert_success + + mkdir -p php-extra + assert_dir_exist php-extra + + printf "php-extra/index.php + assert_file_exists php-extra/index.php + + cat >>.ddev/config.yaml <<'EOF' +web_extra_daemons: + - name: "php-extra" + command: "php -S 0.0.0.0:20080" + directory: /var/www/html/php-extra +web_extra_exposed_ports: + - name: "php-extra" + container_port: 20080 + http_port: 20080 + https_port: 20443 +EOF + echo "# ddev add-on get ${DIR} with project ${PROJNAME} in $(pwd)" >&3 run ddev add-on get "${DIR}" assert_success run ddev restart -y assert_success - export ROUTER_HTTP_PORT=8080 ROUTER_HTTPS_PORT=8443 MAILPIT_HTTP_PORT=18025 MAILPIT_HTTPS_PORT=18026 health_checks } @@ -148,7 +270,6 @@ teardown() { assert_output 'VARNISH_VARNISHD_PARAMS="-p http_max_hdr=123 -p http_resp_hdr_len=16k"' run ddev restart -y assert_success - export ROUTER_HTTP_PORT=80 ROUTER_HTTPS_PORT=443 MAILPIT_HTTP_PORT=8025 MAILPIT_HTTPS_PORT=8026 export CUSTOM_VARNISH_VARNISHD_PARAMS=true health_checks } @@ -160,7 +281,6 @@ teardown() { assert_success run ddev restart -y assert_success - export ROUTER_HTTP_PORT=80 ROUTER_HTTPS_PORT=443 MAILPIT_HTTP_PORT=8025 MAILPIT_HTTPS_PORT=8026 health_checks run ddev varnish-config-reload assert_success diff --git a/varnish/default.vcl b/varnish/default.vcl index 8d3dd4e..2af751f 100644 --- a/varnish/default.vcl +++ b/varnish/default.vcl @@ -3,25 +3,8 @@ # For a more advanced example see https://github.com/mattiasgeniar/varnish-6.0-configuration-templates vcl 4.1; -import std; backend default { .host = "web"; .port = "80"; } - - -sub vcl_recv { - if (std.port(server.ip) == 8025) { - return (synth(750)); - } -} - -sub vcl_synth { - if (resp.status == 750) { - set resp.status = 301; - set resp.http.location = req.http.X-Forwarded-Proto + "://novarnish." + req.http.Host + req.url; - set resp.reason = "Moved"; - return (deliver); - } -}