Compare commits

...

66 Commits

Author SHA1 Message Date
Dokku Bot
ccf713c104 Release 0.33.4
# History

## 0.33.4

Install/update via the bootstrap script:

```shell
wget -NP . https://dokku.com/install/v0.33.4/bootstrap.sh
sudo DOKKU_TAG=v0.33.4 bash bootstrap.sh
```

### Bug Fixes

- #6564: @josegonzalez Ignore link and volume flags for nixpacks builds
- #6563: @josegonzalez Wait until initial k3s node is ready before installing manifests
- #6561: @josegonzalez Index the process map to ensure the correct config is pulled
- #6560: @josegonzalez Skip app stop/delete when k3s is not installed
- #6559: @josegonzalez Check errors when writing properties
- #6556: @josegonzalez Respect the release namespace when creating resources for a k3s deploy

### New Features

- #6562: @josegonzalez Add support for specifying annotations on kubernetes resources
- #6555: @josegonzalez Add support for app-specific service accounts when deploying via k3s
- #6546: @josegonzalez Install kubectx and kubens helper binaries when setting up a k3s cluster

### Refactors

- #6558: @josegonzalez Use type-specific functions for writing contents to a file
- #6557: @josegonzalez Consolidate property fetching for nginx plugin into golang codebase
- #6536: @josegonzalez Manage vector container via compose

### Documentation

- #6545: @josegonzalez Migrate markdown doc rewriting into mkdocs hooks

### Tests

- #6554: @josegonzalez Fix issue where CI cannot install docker-buildx-plugin

### Dependencies

- #6568: @dependabot[bot] chore(deps): bump golang from 1.21.6 to 1.22.0 in /tests/apps/gogrpc
- #6567: @dependabot[bot] chore(deps): bump watchdog from 3.0.0 to 4.0.0 in /docs/_build
- #6566: @dependabot[bot] chore(deps): bump mkdocs-material from 9.5.7 to 9.5.8 in /docs/_build
- #6565: @dependabot[bot] chore(deps): bump golang from 1.21.6 to 1.22.0 in /tests/apps/zombies-dockerfile-no-tini
- #6551: @dependabot[bot] chore(deps): bump flask from 3.0.1 to 3.0.2 in /tests/apps/multi
- #6552: @dependabot[bot] chore(deps): bump mkdocs-material from 9.5.6 to 9.5.7 in /docs/_build
- #6553: @dependabot[bot] chore(deps): bump markupsafe from 2.1.4 to 2.1.5 in /docs/_build
- #6550: @dependabot[bot] chore(deps): bump flask from 3.0.1 to 3.0.2 in /tests/apps/python-flask
- #6548: @dependabot[bot] chore(deps): bump org.eclipse.jetty:jetty-servlet from 11.0.19 to 11.0.20 in /tests/apps/java

### Other

- #6547: @josegonzalez Add support for nginx as a k3s ingress implementation
2024-02-07 21:38:43 +00:00
Jose Diaz-Gonzalez
bc4c25dccf Merge pull request #6547 from dokku/proxy-implementation 2024-02-07 16:12:22 -05:00
Jose Diaz-Gonzalez
cd4f9db95b fix: use annotation instead of location snippet 2024-02-07 13:15:16 -05:00
Jose Diaz-Gonzalez
4cc52179b5 Merge pull request #6568 from dokku/dependabot/docker/tests/apps/gogrpc/golang-1.22.0 2024-02-07 12:26:54 -05:00
Jose Diaz-Gonzalez
ceaab97d99 Merge pull request #6567 from dokku/dependabot/pip/docs/_build/watchdog-4.0.0 2024-02-07 12:26:42 -05:00
Jose Diaz-Gonzalez
a8c1e8a88a Merge pull request #6566 from dokku/dependabot/pip/docs/_build/mkdocs-material-9.5.8 2024-02-07 12:26:27 -05:00
Jose Diaz-Gonzalez
2432ad49a5 Merge pull request #6565 from dokku/dependabot/docker/tests/apps/zombies-dockerfile-no-tini/golang-1.22.0 2024-02-07 12:26:15 -05:00
dependabot[bot]
814953358b chore(deps): bump golang from 1.21.6 to 1.22.0 in /tests/apps/gogrpc
Bumps golang from 1.21.6 to 1.22.0.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-07 14:00:18 +00:00
dependabot[bot]
0adaa90fbd chore(deps): bump watchdog from 3.0.0 to 4.0.0 in /docs/_build
Bumps [watchdog](https://github.com/gorakhargosh/watchdog) from 3.0.0 to 4.0.0.
- [Release notes](https://github.com/gorakhargosh/watchdog/releases)
- [Changelog](https://github.com/gorakhargosh/watchdog/blob/master/changelog.rst)
- [Commits](https://github.com/gorakhargosh/watchdog/compare/v3.0.0...v4.0.0)

---
updated-dependencies:
- dependency-name: watchdog
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-07 13:57:46 +00:00
dependabot[bot]
8576a1280a chore(deps): bump mkdocs-material from 9.5.7 to 9.5.8 in /docs/_build
Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.7 to 9.5.8.
- [Release notes](https://github.com/squidfunk/mkdocs-material/releases)
- [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG)
- [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.5.7...9.5.8)

---
updated-dependencies:
- dependency-name: mkdocs-material
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-07 13:57:42 +00:00
dependabot[bot]
6692beae11 chore(deps): bump golang in /tests/apps/zombies-dockerfile-no-tini
Bumps golang from 1.21.6 to 1.22.0.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-07 13:33:43 +00:00
Jose Diaz-Gonzalez
41a89bbb5d feat: add a test for ingress-nginx support 2024-02-07 02:20:49 -05:00
Jose Diaz-Gonzalez
afb4b50bb3 docs: document how to switch to nginx 2024-02-07 02:20:37 -05:00
Jose Diaz-Gonzalez
03ae424a27 fix: ensure map keys are unique 2024-02-07 00:01:57 -05:00
Jose Diaz-Gonzalez
e9986b5775 chore: unsort flags 2024-02-06 23:02:55 -05:00
Jose Diaz-Gonzalez
980979ef87 feat: properly handle nginx annotation merging 2024-02-06 22:48:22 -05:00
Jose Diaz-Gonzalez
7dbd9adc33 fix: set log mount dir to /var/log/nginx 2024-02-06 22:12:54 -05:00
Jose Diaz-Gonzalez
416a119904 feat: pull nginx properties as ingress annotations 2024-02-06 22:12:54 -05:00
Jose Diaz-Gonzalez
1af36b609d feat: add resource-specific annotations to ingress objects 2024-02-06 22:12:41 -05:00
Jose Diaz-Gonzalez
5674a29211 feat: add ability to use ingress-nginx for routing instead of traefik 2024-02-06 21:19:51 -05:00
Jose Diaz-Gonzalez
b35d6df67c feat: add support for displaying error logs 2024-02-06 21:19:14 -05:00
Jose Diaz-Gonzalez
81286a0673 feat: add ability to install nginx instead of traefik 2024-02-06 21:19:14 -05:00
Jose Diaz-Gonzalez
ea4767f2d8 Merge pull request #6564 from dokku/6416-nixpacks-no-link
Ignore link and volume flags for nixpacks builds
2024-02-06 17:27:54 -05:00
Jose Diaz-Gonzalez
4c0359bb28 Merge pull request #6562 from dokku/k3s-annotations
Add support for specifying annotations on kubernetes resources
2024-02-06 17:27:35 -05:00
Jose Diaz-Gonzalez
a59480f97d fix: ignore link and volume flags for nixpacks builds
Closes #6416
2024-02-06 17:27:10 -05:00
Jose Diaz-Gonzalez
bacfbe328b tests: set KUBECONFIG for kubectl commands 2024-02-06 16:33:55 -05:00
Jose Diaz-Gonzalez
e7a3766e60 tests: use correct namespace for image repo 2024-02-06 15:32:50 -05:00
Jose Diaz-Gonzalez
8711a1c801 fix: force set the kubectl path when running kubectl commands 2024-02-06 14:39:48 -05:00
Jose Diaz-Gonzalez
f5f583b12b feat: add support for specifying annotations
As the command contains a colon, it must be handled in the commands binary as opposed to subcommands.

Also include a simple bats test.
2024-02-06 13:54:53 -05:00
Jose Diaz-Gonzalez
8b2f37feea Merge pull request #6563 from dokku/wait-for-init
Wait until initial k3s node is ready before installing manifests
2024-02-06 13:53:54 -05:00
Jose Diaz-Gonzalez
408acbe14e feat: wait until initial k3s node is ready before installing manifests 2024-02-06 13:53:34 -05:00
Jose Diaz-Gonzalez
e08e1df026 Merge pull request #6561 from dokku/process-index
Index the process map to ensure the correct config is pulled
2024-02-06 05:16:56 -05:00
Jose Diaz-Gonzalez
77d5a27fda Merge pull request #6560 from dokku/josegonzalez-patch-2
Skip app stop/delete when k3s is not installed
2024-02-06 05:16:40 -05:00
Jose Diaz-Gonzalez
79c0caaa7b Merge pull request #6559 from dokku/josegonzalez-patch-1
Check errors when writing properties
2024-02-06 05:13:00 -05:00
Jose Diaz-Gonzalez
d7ff8552d1 fix: index the process map to ensure the correct config is pulled
Without this change, certain generated process names - like those for a cron job - may be incorrectly handled when indexing the process map.
2024-02-06 04:41:14 -05:00
Jose Diaz-Gonzalez
96e79487b9 fix: skip app stop/delete when k3s is not installed 2024-02-06 04:38:04 -05:00
Jose Diaz-Gonzalez
ea55071751 feat: check errors when writing files 2024-02-06 04:36:11 -05:00
Jose Diaz-Gonzalez
4abd1f6b24 Merge pull request #6558 from dokku/write-to-file
Use type-specific functions for writing contents to a file
2024-02-06 02:44:14 -05:00
Jose Diaz-Gonzalez
5e1089973e refactor: use type-specific functions for writing contents to a file 2024-02-06 01:39:44 -05:00
Jose Diaz-Gonzalez
666f953715 Merge pull request #6557 from dokku/nginx-go-properties
Consolidate property fetching for nginx plugin into golang codebase
2024-02-06 01:15:15 -05:00
Jose Diaz-Gonzalez
d4b445985b refactor: consolidate property fetching for nginx plugin into golang codebase
Also update the documentation to include docs for all properties.
2024-02-06 00:42:48 -05:00
Jose Diaz-Gonzalez
6625846ecc Merge pull request #6555 from dokku/service-account
Add support for app-specific service accounts when deploying via k3s
2024-02-05 17:18:20 -05:00
Jose Diaz-Gonzalez
1689ddd6d3 Merge pull request #6556 from dokku/k3s-namespace
Respect the release namespace when creating resources for a k3s deploy
2024-02-05 16:51:15 -05:00
Jose Diaz-Gonzalez
fe8b7ec451 Merge pull request #6551 from dokku/dependabot/pip/tests/apps/multi/flask-3.0.2
chore(deps): bump flask from 3.0.1 to 3.0.2 in /tests/apps/multi
2024-02-05 16:50:44 -05:00
Jose Diaz-Gonzalez
31a73e4dab Merge pull request #6552 from dokku/dependabot/pip/docs/_build/mkdocs-material-9.5.7
chore(deps): bump mkdocs-material from 9.5.6 to 9.5.7 in /docs/_build
2024-02-05 16:50:30 -05:00
Jose Diaz-Gonzalez
238535be0c fix: respect the release namespace when creating resources for a k3s deploy 2024-02-05 16:50:00 -05:00
Jose Diaz-Gonzalez
d02c04c2a5 feat: add support for app-specific service accounts when deploying via k3s
Service accounts must still be bound to a role, but this ensures apps do not get access to the default service account.
2024-02-05 16:47:57 -05:00
Jose Diaz-Gonzalez
ff319428bf Merge pull request #6553 from dokku/dependabot/pip/docs/_build/markupsafe-2.1.5
chore(deps): bump markupsafe from 2.1.4 to 2.1.5 in /docs/_build
2024-02-05 16:32:51 -05:00
Jose Diaz-Gonzalez
41f5283b1a Merge pull request #6550 from dokku/dependabot/pip/tests/apps/python-flask/flask-3.0.2
chore(deps): bump flask from 3.0.1 to 3.0.2 in /tests/apps/python-flask
2024-02-05 16:32:25 -05:00
dependabot[bot]
e30650a620 chore(deps): bump mkdocs-material from 9.5.6 to 9.5.7 in /docs/_build
Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.6 to 9.5.7.
- [Release notes](https://github.com/squidfunk/mkdocs-material/releases)
- [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG)
- [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.5.6...9.5.7)

---
updated-dependencies:
- dependency-name: mkdocs-material
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-05 20:27:06 +00:00
dependabot[bot]
2e57199318 chore(deps): bump flask from 3.0.1 to 3.0.2 in /tests/apps/multi
Bumps [flask](https://github.com/pallets/flask) from 3.0.1 to 3.0.2.
- [Release notes](https://github.com/pallets/flask/releases)
- [Changelog](https://github.com/pallets/flask/blob/main/CHANGES.rst)
- [Commits](https://github.com/pallets/flask/compare/3.0.1...3.0.2)

---
updated-dependencies:
- dependency-name: flask
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-05 20:27:03 +00:00
dependabot[bot]
976be62a1b chore(deps): bump markupsafe from 2.1.4 to 2.1.5 in /docs/_build
Bumps [markupsafe](https://github.com/pallets/markupsafe) from 2.1.4 to 2.1.5.
- [Release notes](https://github.com/pallets/markupsafe/releases)
- [Changelog](https://github.com/pallets/markupsafe/blob/main/CHANGES.rst)
- [Commits](https://github.com/pallets/markupsafe/compare/2.1.4...2.1.5)

---
updated-dependencies:
- dependency-name: markupsafe
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-05 20:26:57 +00:00
dependabot[bot]
7d37466750 chore(deps): bump flask from 3.0.1 to 3.0.2 in /tests/apps/python-flask
Bumps [flask](https://github.com/pallets/flask) from 3.0.1 to 3.0.2.
- [Release notes](https://github.com/pallets/flask/releases)
- [Changelog](https://github.com/pallets/flask/blob/main/CHANGES.rst)
- [Commits](https://github.com/pallets/flask/compare/3.0.1...3.0.2)

---
updated-dependencies:
- dependency-name: flask
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-05 20:26:52 +00:00
Jose Diaz-Gonzalez
5b922a93c7 Merge pull request #6548 from dokku/dependabot/maven/tests/apps/java/org.eclipse.jetty-jetty-servlet-11.0.20
chore(deps): bump org.eclipse.jetty:jetty-servlet from 11.0.19 to 11.0.20 in /tests/apps/java
2024-02-05 15:26:24 -05:00
Jose Diaz-Gonzalez
2703101068 Merge pull request #6554 from dokku/fix-test-runs
Fix issue where CI cannot install docker-buildx-plugin
2024-02-05 15:26:13 -05:00
Jose Diaz-Gonzalez
28532a56a9 tests: fix issue where CI cannot install docker-buildx-plugin 2024-02-05 14:59:20 -05:00
dependabot[bot]
dce37fa140 chore(deps): bump org.eclipse.jetty:jetty-servlet in /tests/apps/java
Bumps org.eclipse.jetty:jetty-servlet from 11.0.19 to 11.0.20.

---
updated-dependencies:
- dependency-name: org.eclipse.jetty:jetty-servlet
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-01 13:35:44 +00:00
Jose Diaz-Gonzalez
3d99ea63bd Merge pull request #6536 from dokku/vector-via-compose
Manage vector container via compose
2024-01-30 10:23:45 -05:00
Jose Diaz-Gonzalez
60d9a3ca34 Merge pull request #6546 from dokku/kubens-kubectx
Install kubectx and kubens helper binaries when setting up a k3s cluster
2024-01-30 08:02:28 -05:00
Jose Diaz-Gonzalez
06429a384e feat: install kubectx and kubens 2024-01-30 08:01:42 -05:00
Jose Diaz-Gonzalez
a277c5da1b refactor: allow setting more options when changing permissions on a file
Rather than hardcoding paths, just allow them to be options.
2024-01-30 07:31:02 -05:00
Jose Diaz-Gonzalez
6622de5998 Merge pull request #6545 from dokku/docs-cleanup
Migrate markdown doc rewriting into mkdocs hooks
2024-01-30 06:55:15 -05:00
Jose Diaz-Gonzalez
0c83356542 refactor: move docs rewrite into mkdocs hooks
This will ensure `mkdocs build` does not rewrite content, making contribution easier.
2024-01-30 06:53:14 -05:00
Jose Diaz-Gonzalez
b333334e72 chore: use new github-style admonition 2024-01-30 06:50:24 -05:00
Jose Diaz-Gonzalez
f287f32682 docs: allow hooks file to be added to docker builds 2024-01-29 13:20:02 -05:00
Jose Diaz-Gonzalez
c49322bd34 feat: manage vector container via compose
Using compose instead of manual docker calls allows folks to customize the vector container by using a custom compose.yml template file. This opens us up to more customizations while aligning container management with how we do other external containers (such as for the proxy plugins).

Refs #5784
2024-01-26 06:46:18 -05:00
119 changed files with 3445 additions and 1152 deletions

View File

@@ -8,6 +8,7 @@ docs
tests
!build/package/*
!docs/_build/entrypoint
!docs/_build/hooks.py
!docs/_build/requirements.txt
!tests/dhparam.pem
!contrib/bash-completion

View File

@@ -23,7 +23,7 @@ RUN mkdir -p /etc/apt/keyrings \
&& curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null \
&& apt-get update \
&& apt-get -y --no-install-recommends install docker-ce docker-ce-cli containerd.io docker-compose-plugin \
&& apt-get -y --no-install-recommends install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin \
&& echo "dokku dokku/hostname string $DOKKU_HOSTNAME" | debconf-set-selections \
&& echo "dokku dokku/skip_key_file boolean $DOKKU_SKIP_KEY_FILE" | debconf-set-selections \
&& echo "dokku dokku/vhost_enable boolean $DOKKU_VHOST_ENABLE" | debconf-set-selections \

View File

@@ -1,5 +1,59 @@
# History
## 0.33.4
Install/update via the bootstrap script:
```shell
wget -NP . https://dokku.com/install/v0.33.4/bootstrap.sh
sudo DOKKU_TAG=v0.33.4 bash bootstrap.sh
```
### Bug Fixes
- #6564: @josegonzalez Ignore link and volume flags for nixpacks builds
- #6563: @josegonzalez Wait until initial k3s node is ready before installing manifests
- #6561: @josegonzalez Index the process map to ensure the correct config is pulled
- #6560: @josegonzalez Skip app stop/delete when k3s is not installed
- #6559: @josegonzalez Check errors when writing properties
- #6556: @josegonzalez Respect the release namespace when creating resources for a k3s deploy
### New Features
- #6562: @josegonzalez Add support for specifying annotations on kubernetes resources
- #6555: @josegonzalez Add support for app-specific service accounts when deploying via k3s
- #6546: @josegonzalez Install kubectx and kubens helper binaries when setting up a k3s cluster
### Refactors
- #6558: @josegonzalez Use type-specific functions for writing contents to a file
- #6557: @josegonzalez Consolidate property fetching for nginx plugin into golang codebase
- #6536: @josegonzalez Manage vector container via compose
### Documentation
- #6545: @josegonzalez Migrate markdown doc rewriting into mkdocs hooks
### Tests
- #6554: @josegonzalez Fix issue where CI cannot install docker-buildx-plugin
### Dependencies
- #6568: @dependabot[bot] chore(deps): bump golang from 1.21.6 to 1.22.0 in /tests/apps/gogrpc
- #6567: @dependabot[bot] chore(deps): bump watchdog from 3.0.0 to 4.0.0 in /docs/_build
- #6566: @dependabot[bot] chore(deps): bump mkdocs-material from 9.5.7 to 9.5.8 in /docs/_build
- #6565: @dependabot[bot] chore(deps): bump golang from 1.21.6 to 1.22.0 in /tests/apps/zombies-dockerfile-no-tini
- #6551: @dependabot[bot] chore(deps): bump flask from 3.0.1 to 3.0.2 in /tests/apps/multi
- #6552: @dependabot[bot] chore(deps): bump mkdocs-material from 9.5.6 to 9.5.7 in /docs/_build
- #6553: @dependabot[bot] chore(deps): bump markupsafe from 2.1.4 to 2.1.5 in /docs/_build
- #6550: @dependabot[bot] chore(deps): bump flask from 3.0.1 to 3.0.2 in /tests/apps/python-flask
- #6548: @dependabot[bot] chore(deps): bump org.eclipse.jetty:jetty-servlet from 11.0.19 to 11.0.20 in /tests/apps/java
### Other
- #6547: @josegonzalez Add support for nginx as a k3s ingress implementation
## 0.33.3
Install/update via the bootstrap script:

View File

@@ -86,8 +86,8 @@ Otherwise, you will need to import the keypair manually after installation using
To install the latest stable release, run the following commands as a user who has access to `sudo`:
```shell
wget -NP . https://dokku.com/install/v0.33.3/bootstrap.sh
sudo DOKKU_TAG=v0.33.3 bash bootstrap.sh
wget -NP . https://dokku.com/install/v0.33.4/bootstrap.sh
sudo DOKKU_TAG=v0.33.4 bash bootstrap.sh
```
You can then proceed to configure your server domain (via `dokku domains:set-global`) and user access (via `dokku ssh-keys:add`) to complete the installation.

View File

@@ -30,7 +30,7 @@ EOM
echo '--> Installing docker-ce'
apt-get -y update
apt-get -y install docker-ce docker-compose-plugin
apt-get -y install docker-ce docker-buildx-plugin docker-compose-plugin
echo '--> Enabling docker-ce'
systemctl enable docker

View File

@@ -3,7 +3,6 @@ import os
import pathlib
import shutil
import sys
from collections.abc import Callable
import yaml
from bs4 import BeautifulSoup
@@ -128,308 +127,6 @@ def generate_nav(src: str, dest: str) -> None:
yaml.dump(data, handler)
def modify_content_noop(lines: list[str], _) -> tuple[list[str], bool]:
"""
Simply returns the lines as is
"""
modified = False
updated_lines = []
for line in lines:
updated_lines.append(line)
return updated_lines, modified
def modify_content_links(lines: list[str], filename: str) -> tuple[list[str], bool]:
"""
Modifies links to be relative instead of absolute
"""
filename = filename.replace("/usr/src/source/docs/", "")
parts = filename.split("/")
parts.pop()
replacement = "](" + "/".join([".." for _ in parts]) + "/"
modified = False
updated_lines = []
for line in lines:
if "](/docs/" in line:
line = line.replace("](/docs/", replacement)
modified = True
updated_lines.append(line)
return updated_lines, modified
def modify_content_stripspace(lines: list[str], _) -> tuple[list[str], bool]:
"""
Removes trailing whitespace from each line
"""
modified = False
updated_lines = []
for line in lines:
line = line.rstrip()
updated_lines.append(line)
return updated_lines, modified
def modify_content_inject_newlines(lines: list[str], _) -> tuple[list[str], bool]:
"""
Ensures every line has a trailing newline character
"""
modified = False
updated_lines = []
for line in lines:
updated_lines.append(line.rstrip() + "\n")
return updated_lines, modified
def is_github_new(line: str, next_line: str | None) -> bool:
"""
Checks if a given line is a github "new as of" admonition
"""
if not next_line:
return False
return line.startswith("> [!IMPORTANT]") and "new as of" in next_line.lower()
def is_github_note(line: str) -> bool:
"""
Checks if a given line is a github "note" admonition
"""
return line.startswith("> [!NOTE]")
def is_github_warning(line: str) -> bool:
"""
Checks if a given line is a github "warning" admonition
"""
return line.startswith("> [!WARNING]")
def is_info(line: str) -> bool:
"""
Checks if a given line is an "info" admonition
"""
return line.startswith("> ")
def is_new(line: str) -> bool:
"""
Checks if a given line is a "new as of" admonition
"""
return line.startswith("> ") and "new as of" in line.lower()
def is_note(line: str) -> bool:
"""
Checks if a given line is a "note" admonition
"""
return line.startswith("> Note:")
def is_warning(line: str) -> bool:
"""
Checks if a given line is a "warning" admonition
"""
return line.startswith("> Warning:")
def modify_content_admonition(lines: list[str], _) -> tuple[list[str], bool]:
"""
Applies adminition info to each line in the output
"""
modified = False
updated_lines = []
admonition_lines = []
is_admonition = False
replace_new_in_next_line = False
for index, line in enumerate(lines):
next_line: str | None = None
if index + 1 < len(lines):
next_line = lines[index + 1]
if replace_new_in_next_line:
line = line.replace("New as of", "Introduced in")
line = line.replace("new as of", "introduced in")
replace_new_in_next_line = False
if is_github_new(line, next_line):
line = line.replace("> [!IMPORTANT]", '!!! tip "New"')
is_admonition = True
admonition_lines.append(line)
admonition_lines.append("")
replace_new_in_next_line = True
elif is_github_note(line):
line = line.replace("> [!NOTE]", '!!! note "Note"')
is_admonition = True
admonition_lines.append(line)
admonition_lines.append("")
elif is_github_warning(line):
line = line.replace("> [!WARNING]", '!!! warning "Warning"')
is_admonition = True
admonition_lines.append(line)
admonition_lines.append("")
elif is_new(line):
print("is_new")
line = line.replace("> ", '!!! tip "New"\n\n ')
line = line.replace("New as of", "Introduced in")
line = line.replace("new as of", "introduced in")
is_admonition = True
admonition_lines.append(line)
elif is_note(line):
print("is_note")
line = line.replace("> Note: ", '!!! note "Note"\n\n ')
is_admonition = True
admonition_lines.append(line)
elif is_warning(line):
print("is_warning")
line = line.replace("> Warning: ", '!!! warning "Warning"\n\n ')
is_admonition = True
admonition_lines.append(line)
elif is_info(line):
if not is_admonition:
line = line.replace("> ", '!!! info "Info"\n\n ')
elif line in [">", "> "]:
line = ""
else:
line = " " + line.removeprefix("> ")
is_admonition = True
admonition_lines.append(line)
elif is_admonition and line in [">", "> "]:
line = ""
admonition_lines.append(line)
elif is_admonition and line.startswith("> "):
line = " " + line.removeprefix("> ")
admonition_lines.append(line)
elif line == "":
is_admonition = False
if len(admonition_lines) > 0:
modified = True
updated_lines.extend(admonition_lines)
admonition_lines = []
updated_lines.append("")
else:
updated_lines.append(line)
return updated_lines, modified
def is_shell_codeblock_start(line: str) -> bool:
"""
Checks to see if a line starts a codeblock
"""
return line == "```shell"
def modify_content_terminal_example(lines: list[str], _) -> tuple[list[str], bool]:
"""
Modifies content so that terminal output is shown appropriately
"""
modified = False
updated_lines = []
command_block = []
example_block = []
in_command_block = False
in_example_block = False
previous_block = ""
next_line_must_be = None
for line in lines:
if is_shell_codeblock_start(line):
command_block.append(line)
modified = True
in_command_block = True
continue
elif in_command_block:
command_block.append(line)
if line == "```":
in_command_block = False
previous_block = "command_block"
next_line_must_be = ""
continue
elif line == "```":
if previous_block == "":
updated_lines.append(line)
continue
if previous_block == "command_block":
example_block.append(line)
if in_example_block:
previous_block = ""
in_example_block = False
updated_lines.append('=== "Shell"')
updated_lines.append("")
for command_line in command_block:
updated_lines.append(f" {command_line}")
command_block = []
updated_lines.append("")
updated_lines.append('=== "Output"')
updated_lines.append("")
for example_line in example_block:
updated_lines.append(f" {example_line}")
example_block = []
else:
in_example_block = True
continue
elif previous_block == "command_block":
if next_line_must_be is None:
if in_example_block:
example_block.append(line)
else:
updated_lines.extend(command_block)
updated_lines.append("")
updated_lines.append(line)
command_block = []
previous_block = ""
continue
if next_line_must_be == "":
if line == "":
next_line_must_be = None
continue
updated_lines.append(line)
if len(command_block) > 0:
updated_lines.extend(command_block)
return updated_lines, modified
def update_markdown(src):
"""
Updates all markdown files to be mkdocs compatible
"""
markdown_files = []
allowed_extensions = [".md"]
for subdir, _, files in os.walk(src):
for file in files:
ext = os.path.splitext(file)[-1].lower()
file_path = os.path.join(subdir, file)
if ext in allowed_extensions:
markdown_files.append(file_path)
modifiers: list[Callable[[list[str], str], tuple[list[str], bool]]] = [
modify_content_noop,
modify_content_links,
modify_content_stripspace,
modify_content_admonition,
modify_content_terminal_example,
modify_content_inject_newlines,
]
for file in markdown_files:
is_modified = False
lines = []
with open(file, "r", encoding="utf-8") as handler:
lines = handler.readlines()
for modifier in modifiers:
lines, modified = modifier(lines, file)
if modified:
is_modified = True
if is_modified:
with open(file, "w", encoding="utf-8") as handler:
handler.writelines(lines)
def main():
"""
Main command that performs doc manipulation
@@ -438,11 +135,6 @@ def main():
print(" Generating navigation")
generate_nav("/usr/src/source/mkdocs.yml", "/usr/src/app/mkdocs.yml")
print("----> Copying docs folder")
update_markdown(
"/usr/src/source/docs",
)
if os.path.exists("/usr/src/app/docs"):
print(" Removing old docs folder")
shutil.rmtree("/usr/src/app/docs")

2
debian/control vendored
View File

@@ -1,5 +1,5 @@
Package: dokku
Version: 0.33.3
Version: 0.33.4
Section: web
Priority: optional
Architecture: amd64

284
docs/_build/hooks.py vendored
View File

@@ -3,13 +3,295 @@
import posixpath
import re
from collections.abc import Callable
from re import Match
from mkdocs.config.defaults import MkDocsConfig
from mkdocs.structure.files import File, Files
from mkdocs.structure.pages import Page
def on_page_markdown(markdown: str, *, page: Page, config: MkDocsConfig, files: Files):
def on_page_markdown(markdown: str, *, page: Page, config: MkDocsConfig, files: Files) -> str | None:
resp = re_on_page_markdown(markdown, page=page, config=config, files=files)
if not resp:
return resp
modifiers: list[Callable[[list[str], str], tuple[list[str], bool]]] = [
modify_content_noop,
modify_content_links,
modify_content_stripspace,
modify_content_admonition,
modify_content_terminal_example,
]
is_modified = False
lines = resp.split("\n")
for modifier in modifiers:
lines, modified = modifier(lines, page.file.src_path)
if modified:
is_modified = True
if is_modified:
resp = "\n".join(lines)
return resp
def modify_content_noop(lines: list[str], _) -> tuple[list[str], bool]:
"""
Simply returns the lines as is
"""
modified = False
updated_lines = []
for line in lines:
updated_lines.append(line)
return updated_lines, modified
def modify_content_links(lines: list[str], filename: str) -> tuple[list[str], bool]:
"""
Modifies links to be relative instead of absolute
"""
filename = filename.replace("/usr/src/source/docs/", "")
parts = filename.split("/")
parts.pop()
replacement = "](" + "/".join([".." for _ in parts]) + "/"
modified = False
updated_lines = []
for line in lines:
if "](/docs/" in line:
line = line.replace("](/docs/", replacement)
modified = True
updated_lines.append(line)
return updated_lines, modified
def modify_content_stripspace(lines: list[str], _) -> tuple[list[str], bool]:
"""
Removes trailing whitespace from each line
"""
modified = False
updated_lines = []
for line in lines:
line = line.rstrip()
updated_lines.append(line)
return updated_lines, modified
def is_github_new(line: str, next_line: str | None) -> bool:
"""
Checks if a given line is a github "new as of" admonition
"""
if not next_line:
return False
return line.startswith("> [!IMPORTANT]") and "new as of" in next_line.lower()
def is_github_note(line: str) -> bool:
"""
Checks if a given line is a github "note" admonition
"""
return line.startswith("> [!NOTE]")
def is_github_warning(line: str) -> bool:
"""
Checks if a given line is a github "warning" admonition
"""
return line.startswith("> [!WARNING]")
def is_info(line: str) -> bool:
"""
Checks if a given line is an "info" admonition
"""
return line.startswith("> ")
def is_new(line: str) -> bool:
"""
Checks if a given line is a "new as of" admonition
"""
return line.startswith("> ") and "new as of" in line.lower()
def is_note(line: str) -> bool:
"""
Checks if a given line is a "note" admonition
"""
return line.startswith("> Note:")
def is_warning(line: str) -> bool:
"""
Checks if a given line is a "warning" admonition
"""
return line.startswith("> Warning:")
def modify_content_admonition(lines: list[str], filename: str) -> tuple[list[str], bool]:
"""
Applies adminition info to each line in the output
"""
modified = False
updated_lines = []
admonition_lines = []
is_admonition = False
replace_new_in_next_line = False
for index, line in enumerate(lines):
next_line: str | None = None
if index + 1 < len(lines):
next_line = lines[index + 1]
if replace_new_in_next_line:
line = line.replace("New as of", "Introduced in")
line = line.replace("new as of", "introduced in")
replace_new_in_next_line = False
if is_github_new(line, next_line):
line = line.replace("> [!IMPORTANT]", '!!! tip "New"')
is_admonition = True
admonition_lines.append(line)
admonition_lines.append("")
replace_new_in_next_line = True
elif is_github_note(line):
line = line.replace("> [!NOTE]", '!!! note "Note"')
is_admonition = True
admonition_lines.append(line)
admonition_lines.append("")
elif is_github_warning(line):
line = line.replace("> [!WARNING]", '!!! warning "Warning"')
is_admonition = True
admonition_lines.append(line)
admonition_lines.append("")
elif is_new(line):
print("is_new")
line = line.replace("> ", '!!! tip "New"\n\n ')
line = line.replace("New as of", "Introduced in")
line = line.replace("new as of", "introduced in")
is_admonition = True
admonition_lines.append(line)
elif is_note(line):
print("is_note")
line = line.replace("> Note: ", '!!! note "Note"\n\n ')
is_admonition = True
admonition_lines.append(line)
elif is_warning(line):
print("is_warning")
line = line.replace("> Warning: ", '!!! warning "Warning"\n\n ')
is_admonition = True
admonition_lines.append(line)
elif is_info(line):
if not is_admonition:
line = line.replace("> ", '!!! info "Info"\n\n ')
elif line in [">", "> "]:
line = ""
else:
line = " " + line.removeprefix("> ")
is_admonition = True
admonition_lines.append(line)
elif is_admonition and line in [">", "> "]:
line = ""
admonition_lines.append(line)
elif is_admonition and line.startswith("> "):
line = " " + line.removeprefix("> ")
admonition_lines.append(line)
elif line == "":
is_admonition = False
if len(admonition_lines) > 0:
modified = True
updated_lines.extend(admonition_lines)
admonition_lines = []
updated_lines.append("")
else:
updated_lines.append(line)
return updated_lines, modified
def is_shell_codeblock_start(line: str) -> bool:
"""
Checks to see if a line starts a codeblock
"""
return line == "```shell"
def modify_content_terminal_example(lines: list[str], _) -> tuple[list[str], bool]:
"""
Modifies content so that terminal output is shown appropriately
"""
modified = False
updated_lines = []
command_block = []
example_block = []
in_command_block = False
in_example_block = False
previous_block = ""
next_line_must_be = None
for line in lines:
if is_shell_codeblock_start(line):
command_block.append(line)
modified = True
in_command_block = True
continue
elif in_command_block:
command_block.append(line)
if line == "```":
in_command_block = False
previous_block = "command_block"
next_line_must_be = ""
continue
elif line == "```":
if previous_block == "":
updated_lines.append(line)
continue
if previous_block == "command_block":
example_block.append(line)
if in_example_block:
previous_block = ""
in_example_block = False
updated_lines.append('=== "Shell"')
updated_lines.append("")
for command_line in command_block:
updated_lines.append(f" {command_line}")
command_block = []
updated_lines.append("")
updated_lines.append('=== "Output"')
updated_lines.append("")
for example_line in example_block:
updated_lines.append(f" {example_line}")
example_block = []
else:
in_example_block = True
continue
elif previous_block == "command_block":
if next_line_must_be is None:
if in_example_block:
example_block.append(line)
else:
updated_lines.extend(command_block)
updated_lines.append("")
updated_lines.append(line)
command_block = []
previous_block = ""
continue
if next_line_must_be == "":
if line == "":
next_line_must_be = None
continue
updated_lines.append(line)
if len(command_block) > 0:
updated_lines.extend(command_block)
return updated_lines, modified
def re_on_page_markdown(markdown: str, *, page: Page, config: MkDocsConfig, files: Files) -> str | None:
"""
This hook is called after the page's markdown is loaded from file and can be
"""

View File

@@ -4,11 +4,11 @@ ghp-import==2.1.0
importlib-metadata==7.0.1
Jinja2==3.1.3
Markdown>=3.2.1,<3.6
MarkupSafe==2.1.4
MarkupSafe==2.1.5
mergedeep==1.3.4
mkdocs==1.5.3
mkdocs-exclude==1.0.2
mkdocs-material==9.5.6
mkdocs-material==9.5.8
mkdocs-material-extensions==1.3.1
packaging==23.2
Pygments==2.17.2
@@ -19,5 +19,5 @@ PyYAML==6.0.1
pyyaml_env_tag==0.1
six==1.16.0
soupsieve==2.5
watchdog==3.0.0
watchdog==4.0.0
zipp==3.17.0

View File

@@ -36,35 +36,35 @@ dokku plugin:list
```
plugn: dev
00_dokku-standard 0.33.3 enabled dokku core standard plugin
20_events 0.33.3 enabled dokku core events logging plugin
app-json 0.33.3 enabled dokku core app-json plugin
apps 0.33.3 enabled dokku core apps plugin
build-env 0.33.3 enabled dokku core build-env plugin
buildpacks 0.33.3 enabled dokku core buildpacks plugin
certs 0.33.3 enabled dokku core certificate management plugin
checks 0.33.3 enabled dokku core checks plugin
common 0.33.3 enabled dokku core common plugin
config 0.33.3 enabled dokku core config plugin
docker-options 0.33.3 enabled dokku core docker-options plugin
domains 0.33.3 enabled dokku core domains plugin
enter 0.33.3 enabled dokku core enter plugin
git 0.33.3 enabled dokku core git plugin
logs 0.33.3 enabled dokku core logs plugin
network 0.33.3 enabled dokku core network plugin
nginx-vhosts 0.33.3 enabled dokku core nginx-vhosts plugin
plugin 0.33.3 enabled dokku core plugin plugin
proxy 0.33.3 enabled dokku core proxy plugin
ps 0.33.3 enabled dokku core ps plugin
repo 0.33.3 enabled dokku core repo plugin
resource 0.33.3 enabled dokku core resource plugin
scheduler-docker-local 0.33.3 enabled dokku core scheduler-docker-local plugin
shell 0.33.3 enabled dokku core shell plugin
ssh-keys 0.33.3 enabled dokku core ssh-keys plugin
storage 0.33.3 enabled dokku core storage plugin
tags 0.33.3 enabled dokku core tags plugin
tar 0.33.3 enabled dokku core tar plugin
trace 0.33.3 enabled dokku core trace plugin
00_dokku-standard 0.33.4 enabled dokku core standard plugin
20_events 0.33.4 enabled dokku core events logging plugin
app-json 0.33.4 enabled dokku core app-json plugin
apps 0.33.4 enabled dokku core apps plugin
build-env 0.33.4 enabled dokku core build-env plugin
buildpacks 0.33.4 enabled dokku core buildpacks plugin
certs 0.33.4 enabled dokku core certificate management plugin
checks 0.33.4 enabled dokku core checks plugin
common 0.33.4 enabled dokku core common plugin
config 0.33.4 enabled dokku core config plugin
docker-options 0.33.4 enabled dokku core docker-options plugin
domains 0.33.4 enabled dokku core domains plugin
enter 0.33.4 enabled dokku core enter plugin
git 0.33.4 enabled dokku core git plugin
logs 0.33.4 enabled dokku core logs plugin
network 0.33.4 enabled dokku core network plugin
nginx-vhosts 0.33.4 enabled dokku core nginx-vhosts plugin
plugin 0.33.4 enabled dokku core plugin plugin
proxy 0.33.4 enabled dokku core proxy plugin
ps 0.33.4 enabled dokku core ps plugin
repo 0.33.4 enabled dokku core repo plugin
resource 0.33.4 enabled dokku core resource plugin
scheduler-docker-local 0.33.4 enabled dokku core scheduler-docker-local plugin
shell 0.33.4 enabled dokku core shell plugin
ssh-keys 0.33.4 enabled dokku core ssh-keys plugin
storage 0.33.4 enabled dokku core storage plugin
tags 0.33.4 enabled dokku core tags plugin
tar 0.33.4 enabled dokku core tar plugin
trace 0.33.4 enabled dokku core trace plugin
```
> [!WARNING]

View File

@@ -2,10 +2,10 @@
<browserconfig>
<msapplication>
<tile>
<square70x70logo src="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/favicons/mstile-70x70.png"/>
<square150x150logo src="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/favicons/mstile-150x150.png"/>
<square310x310logo src="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/favicons/mstile-310x310.png"/>
<wide310x150logo src="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/favicons/mstile-310x150.png"/>
<square70x70logo src="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/favicons/mstile-70x70.png"/>
<square150x150logo src="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/favicons/mstile-150x150.png"/>
<square310x310logo src="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/favicons/mstile-310x310.png"/>
<wide310x150logo src="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/favicons/mstile-310x150.png"/>
<TileColor>#da532c</TileColor>
</tile>
</msapplication>

View File

@@ -2,37 +2,37 @@
"name": "Dokku",
"icons": [
{
"src": "https:\/\/cdn.jsdelivr.net\/dokku\/dokku@v0.33.3\/docs\/assets\/favicons\/android-chrome-36x36.png",
"src": "https:\/\/cdn.jsdelivr.net\/dokku\/dokku@v0.33.4\/docs\/assets\/favicons\/android-chrome-36x36.png",
"sizes": "36x36",
"type": "image\/png",
"density": "0.75"
},
{
"src": "https:\/\/cdn.jsdelivr.net\/dokku\/dokku@v0.33.3\/docs\/assets\/favicons\/android-chrome-48x48.png",
"src": "https:\/\/cdn.jsdelivr.net\/dokku\/dokku@v0.33.4\/docs\/assets\/favicons\/android-chrome-48x48.png",
"sizes": "48x48",
"type": "image\/png",
"density": "1.0"
},
{
"src": "https:\/\/cdn.jsdelivr.net\/dokku\/dokku@v0.33.3\/docs\/assets\/favicons\/android-chrome-72x72.png",
"src": "https:\/\/cdn.jsdelivr.net\/dokku\/dokku@v0.33.4\/docs\/assets\/favicons\/android-chrome-72x72.png",
"sizes": "72x72",
"type": "image\/png",
"density": "1.5"
},
{
"src": "https:\/\/cdn.jsdelivr.net\/dokku\/dokku@v0.33.3\/docs\/assets\/favicons\/android-chrome-96x96.png",
"src": "https:\/\/cdn.jsdelivr.net\/dokku\/dokku@v0.33.4\/docs\/assets\/favicons\/android-chrome-96x96.png",
"sizes": "96x96",
"type": "image\/png",
"density": "2.0"
},
{
"src": "https:\/\/cdn.jsdelivr.net\/dokku\/dokku@v0.33.3\/docs\/assets\/favicons\/android-chrome-144x144.png",
"src": "https:\/\/cdn.jsdelivr.net\/dokku\/dokku@v0.33.4\/docs\/assets\/favicons\/android-chrome-144x144.png",
"sizes": "144x144",
"type": "image\/png",
"density": "3.0"
},
{
"src": "https:\/\/cdn.jsdelivr.net\/dokku\/dokku@v0.33.3\/docs\/assets\/favicons\/android-chrome-192x192.png",
"src": "https:\/\/cdn.jsdelivr.net\/dokku\/dokku@v0.33.4\/docs\/assets\/favicons\/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image\/png",
"density": "4.0"

View File

@@ -30,6 +30,6 @@
"0.30.11",
"0.31.5",
"0.32.4",
"0.33.3"
"0.33.4"
]
}

View File

@@ -1,5 +1,6 @@
# Nixpacks
> [!IMPORTANT]
> New as of 0.32.0
The `nixpacks` builder builds apps via [Nixpacks](https://nixpacks.com/), a buildpack alternative.

View File

@@ -27,6 +27,7 @@ dokku scheduler:set node-js-app selected
### Deploying amd64 images on arm64
> [!IMPORTANT]
> New as of 0.33.0
Many builders only produce amd64-compatible images. The docker-local scheduler will automatically detect these and run them via the `--platform=linux/amd64` on arm64 deploy targets.

View File

@@ -4,6 +4,7 @@
> New as of 0.33.0
```
scheduler-k3s:annotations:set <app|--global> <property> (<value>) [--process-type PROCESS_TYPE] <--resource-type RESOURCE_TYPE>, Set or clear an annotation for a given app/process-type/resource-type combination
scheduler-k3s:cluster-add [ssh://user@host:port] # Adds a server node to a Dokku-managed cluster
scheduler-k3s:cluster-list # Lists all nodes in a Dokku-managed cluster
scheduler-k3s:cluster-remove [node-id] # Removes client node to a Dokku-managed cluster
@@ -54,6 +55,14 @@ By default, Dokku will attempt to auto-detect the IP address of the server. In c
dokku scheduler-k3s:initialize --server-ip 192.168.20.15
```
Dokku's k3s integration natively uses `Traefik` as it's ingress load balancer via [Traefik's CRDs](https://doc.traefik.io/traefik/providers/kubernetes-crd/), but a cluster can be set to use `nginx` via the [ingress-nginx](https://github.com/kubernetes/ingress-nginx) project. Switching to nginx will result in any `nginx` plugin settings being respected, either by turning them into annotations or creating a custom server snippet.
To change the ingress, set the `--ingress-class` flag:
```shell
dokku scheduler-k3s:initialize --ingress-class nginx
```
### Adding nodes to the cluster
> [!WARNING]
@@ -262,6 +271,44 @@ The default value may be set by passing an empty value for the option.
dokku scheduler-k3s:set --global letsencrypt-server staging
```
### Customizing Resource Annotations
Dokku injects certain resources into each created resource by default, but it may be necessary to inject others for tighter integration with third-party tools. The `scheduler-k3s:annotations:set` command can be used to perform this task. The command takes an app name and a required `--resource-type` flag.
```shell
dokku scheduler-k3s:annotations:set node-js-app annotation.key annotation.value --resource-type deployment
```
If not specified, the annotation will be applied to all processes within an app, though it may be further scoped to a specific process type via the `--process-type` flag.
> [!NOTE]
> The cron ID is used as the process type if your app deploys any cron tasks
```shell
dokku scheduler-k3s:annotations:set node-js-app annotation.key annotation.value --resource-type deployment --process-type web
```
To unset an annotation, pass an empty value:
```shell
dokku scheduler-k3s:annotations:set node-js-app annotation.key --resource-type deployment
dokku scheduler-k3s:annotations:set node-js-app annotation.key --resource-type deployment --process-type web
```
The following resource types are supported:
- `certificate`
- `cronjob`
- `deployment`
- `ingress`
- `job`
- `pod`
- `secret`
- `service`
- `serviceaccount`
- `traefik_ingressroute`
- `traefik_middleware`
### Using kubectl remotely
> [!WARNING]

View File

@@ -2950,3 +2950,18 @@ main() {
main "$@"
```
### `vector-template-source`
- Description: Retrieves an alternative template for the vector compose config
- Invoked by: caddy-vhosts
- Arguments:
- Example:
```shell
#!/usr/bin/env bash
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
# TODO
```

View File

@@ -29,7 +29,7 @@ The workflow looks like this:
```shell
# having dokku-arch in ../dokku-arch
vagrant up build-arch
# wait for "==> build-arch: ==> Finished making: dokku 0.33.3-2 (Mon Feb 22 23:20:37 CET 2016)"
# wait for "==> build-arch: ==> Finished making: dokku 0.33.4-2 (Mon Feb 22 23:20:37 CET 2016)"
cd ../dokku-arch
git add PKGBUILD .SRCINFO
git commit -m 'Update to dokku 0.9.9'

View File

@@ -3,7 +3,7 @@
Pull the dokku/dokku image:
```shell
docker pull dokku/dokku:0.33.3
docker pull dokku/dokku:0.33.4
```
Next, run the image.
@@ -19,7 +19,7 @@ docker container run \
--publish 8443:443 \
--volume /var/lib/dokku:/mnt/dokku \
--volume /var/run/docker.sock:/var/run/docker.sock \
dokku/dokku:0.33.3
dokku/dokku:0.33.4
```
The above command will start a new docker container that is ready when a message similar to `Runit started as PID 12345` appears.
@@ -61,7 +61,7 @@ redis: https://github.com/dokku/dokku-redis.git
The alternative is to build a custom docker image via a custom Dockerfile. This Dockerfile can run any `plugin:install` command. Note that the version installed at that time will be the one that persists. Below is an example Dockerfile showing this method.
```Dockerfile
FROM dokku/dokku:0.33.3
FROM dokku/dokku:0.33.4
RUN dokku plugin:install https://github.com/dokku/dokku-postgres.git postgres
RUN dokku plugin:install https://github.com/dokku/dokku-redis.git redis
```

View File

@@ -37,8 +37,8 @@ To install the latest stable version of Dokku, you can run the following shell c
```shell
# for debian systems, installs Dokku via apt-get
wget -NP . https://dokku.com/install/v0.33.3/bootstrap.sh
sudo DOKKU_TAG=v0.33.3 bash bootstrap.sh
wget -NP . https://dokku.com/install/v0.33.4/bootstrap.sh
sudo DOKKU_TAG=v0.33.4 bash bootstrap.sh
```
The installation process takes about 5-10 minutes, depending upon internet connection speed.

View File

@@ -10,26 +10,26 @@
<meta name="author" content="">
<title>Dokku - The smallest PaaS implementation you've ever seen</title>
<link rel="apple-touch-icon" sizes="57x57" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/favicons/apple-touch-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/favicons/apple-touch-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/favicons/apple-touch-icon-72x72.png">
<link rel="apple-touch-icon" sizes="76x76" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/favicons/apple-touch-icon-76x76.png">
<link rel="apple-touch-icon" sizes="114x114" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/favicons/apple-touch-icon-114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/favicons/apple-touch-icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/favicons/apple-touch-icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/favicons/apple-touch-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/favicons/apple-touch-icon-180x180.png">
<link rel="icon" type="image/png" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/favicons/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/favicons/android-chrome-192x192.png" sizes="192x192">
<link rel="icon" type="image/png" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/favicons/favicon-96x96.png" sizes="96x96">
<link rel="icon" type="image/png" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/favicons/favicon-16x16.png" sizes="16x16">
<link rel="manifest" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/favicons/manifest.json">
<link rel="shortcut icon" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/favicons/favicon.ico">
<link rel="apple-touch-icon" sizes="57x57" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/favicons/apple-touch-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/favicons/apple-touch-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/favicons/apple-touch-icon-72x72.png">
<link rel="apple-touch-icon" sizes="76x76" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/favicons/apple-touch-icon-76x76.png">
<link rel="apple-touch-icon" sizes="114x114" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/favicons/apple-touch-icon-114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/favicons/apple-touch-icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/favicons/apple-touch-icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/favicons/apple-touch-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/favicons/apple-touch-icon-180x180.png">
<link rel="icon" type="image/png" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/favicons/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/favicons/android-chrome-192x192.png" sizes="192x192">
<link rel="icon" type="image/png" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/favicons/favicon-96x96.png" sizes="96x96">
<link rel="icon" type="image/png" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/favicons/favicon-16x16.png" sizes="16x16">
<link rel="manifest" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/favicons/manifest.json">
<link rel="shortcut icon" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/favicons/favicon.ico">
<meta name="apple-mobile-web-app-title" content="Dokku">
<meta name="application-name" content="Dokku">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="msapplication-TileImage" content="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/favicons/mstile-144x144.png">
<meta name="msapplication-config" content="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/favicons/browserconfig.xml">
<meta name="msapplication-TileImage" content="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/favicons/mstile-144x144.png">
<meta name="msapplication-config" content="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/favicons/browserconfig.xml">
<meta name="theme-color" content="#ffffff">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor" crossorigin="anonymous">
@@ -38,7 +38,7 @@
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Lato:wght@400;700;900&display=swap" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/style.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/style.css" rel="stylesheet">
<!-- <link href="./assets/style.css" rel="stylesheet"> -->
</head>
@@ -98,7 +98,7 @@
<main class="container px-4 px-lg-5">
<div class="row">
<div class="col-lg-7">
<a class="badge-link" href="https://github.com/dokku/dokku/releases"><span class="badge py-2 d-flex fit-content"><div class="circle align-self-center me-2"></div>Latest release <span id="releaseVersion" class="ms-1">v0.33.3</span></span></a>
<a class="badge-link" href="https://github.com/dokku/dokku/releases"><span class="badge py-2 d-flex fit-content"><div class="circle align-self-center me-2"></div>Latest release <span id="releaseVersion" class="ms-1">v0.33.4</span></span></a>
<h1 class="text-white hero-heading mt-5">An open source PAAS alternative to Heroku.</h1>
<h4 class="text-white mt-5">Dokku helps you build and manage the lifecycle of applications from building to scaling.</h4>
<div class="d-flex mt-5">
@@ -125,7 +125,7 @@
</p>
<p class="line">
<span class="prompt">$</span>
<span class="command">sudo DOKKU_TAG=v0.33.3 bash bootstrap.sh</span>
<span class="command">sudo DOKKU_TAG=v0.33.4 bash bootstrap.sh</span>
</p>
<br>
@@ -226,8 +226,8 @@
</svg>
Create</a>
<a class="nav-link" id="nav-deploy-tab" data-bs-toggle="tab" href="#nav-deploy" role="tab" aria-controls="nav-deploy" aria-selected="false">
<svg width="32" height="32" viewBox="0 0.33.32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M29.8709 1.40551C29.8591 1.34642 29.8311 1.29205 29.7903 1.24881C29.7495 1.20556 29.6976 1.17524 29.6407 1.16141C25.8466 0.20375 17.082 3.61605 12.3285 8.51812C11.4813 9.38524 10.7088 10.3268 10.0198 11.3321C8.55457 11.1953 7.08939 11.3105 5.84189 11.8721C2.31916 13.4706 1.29423 17.6454 1.00817 19.4376C0.992669 19.5358 0.999288 19.6364 1.02751 19.7315C1.05574 19.8267 1.10481 19.9138 1.17093 19.9862C1.23705 20.0586 1.31844 20.1144 1.40878 20.1491C1.49913 20.1838 1.59601 20.1966 1.69192 20.1865L7.34824 19.5456C7.35215 19.9859 7.37777 20.4257 7.42498 20.8633C7.4545 21.1658 7.58527 21.4482 7.79477 21.6618L9.98487 23.9221C10.1921 24.1384 10.466 24.2734 10.7593 24.3037C11.1808 24.3522 11.6044 24.3786 12.0285 24.3829L11.4082 30.2153C11.3985 30.33.3 11.411 30.414 11.4446 30.507C11.4783 30.6001 11.5323 30.684 11.6023 30.7521C11.6724 30.8202 11.7567 30.8708 11.8487 30.9C11.9408 30.9291 12.0381 30.936 12.1331 30.9202C13.8704 30.6322 17.9241 29.5744 19.4639 25.9396C20.0081 24.6508 20.1198 23.1466 19.9942 21.6417C20.9712 20.9312 21.8861 20.134 22.7285 19.2591C27.4994 14.3627 30.7849 5.51625 29.8709 1.40551ZM18.4878 12.9162C18.0711 12.4862 17.7873 11.9384 17.6723 11.3419C17.5573 10.7455 17.6162 10.1273 17.8417 9.56541C18.0672 9.00356 18.449 8.52333 18.939 8.18546C19.4289 7.84759 20.005 7.66725 20.5942 7.66725C21.1835 7.66725 21.7595 7.84759 22.2494 8.18546C22.7394 8.52333 23.1212 9.00356 23.3467 9.56541C23.5722 10.1273 23.6311 10.7455 23.5161 11.3419C23.4011 11.9384 23.1173 12.4862 22.7006 12.9162C22.4242 13.202 22.0959 13.4288 21.7344 13.5835C21.373 13.7383 20.9855 13.8179 20.5942 13.8179C20.2029 13.8179 19.8154 13.7383 19.454 13.5835C19.0926 13.4288 18.7642 13.202 18.4878 12.9162Z" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
<svg width="32" height="32" viewBox="0 0.33.42" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M29.8709 1.40551C29.8591 1.34642 29.8311 1.29205 29.7903 1.24881C29.7495 1.20556 29.6976 1.17524 29.6407 1.16141C25.8466 0.20375 17.082 3.61605 12.3285 8.51812C11.4813 9.38524 10.7088 10.3268 10.0198 11.3321C8.55457 11.1953 7.08939 11.3105 5.84189 11.8721C2.31916 13.4706 1.29423 17.6454 1.00817 19.4376C0.992669 19.5358 0.999288 19.6364 1.02751 19.7315C1.05574 19.8267 1.10481 19.9138 1.17093 19.9862C1.23705 20.0586 1.31844 20.1144 1.40878 20.1491C1.49913 20.1838 1.59601 20.1966 1.69192 20.1865L7.34824 19.5456C7.35215 19.9859 7.37777 20.4257 7.42498 20.8633C7.4545 21.1658 7.58527 21.4482 7.79477 21.6618L9.98487 23.9221C10.1921 24.1384 10.466 24.2734 10.7593 24.3037C11.1808 24.3522 11.6044 24.3786 12.0285 24.3829L11.4082 30.2153C11.3985 30.33.4 11.411 30.414 11.4446 30.507C11.4783 30.6001 11.5323 30.684 11.6023 30.7521C11.6724 30.8202 11.7567 30.8708 11.8487 30.9C11.9408 30.9291 12.0381 30.936 12.1331 30.9202C13.8704 30.6322 17.9241 29.5744 19.4639 25.9396C20.0081 24.6508 20.1198 23.1466 19.9942 21.6417C20.9712 20.9312 21.8861 20.134 22.7285 19.2591C27.4994 14.3627 30.7849 5.51625 29.8709 1.40551ZM18.4878 12.9162C18.0711 12.4862 17.7873 11.9384 17.6723 11.3419C17.5573 10.7455 17.6162 10.1273 17.8417 9.56541C18.0672 9.00356 18.449 8.52333 18.939 8.18546C19.4289 7.84759 20.005 7.66725 20.5942 7.66725C21.1835 7.66725 21.7595 7.84759 22.2494 8.18546C22.7394 8.52333 23.1212 9.00356 23.3467 9.56541C23.5722 10.1273 23.6311 10.7455 23.5161 11.3419C23.4011 11.9384 23.1173 12.4862 22.7006 12.9162C22.4242 13.202 22.0959 13.4288 21.7344 13.5835C21.373 13.7383 20.9855 13.8179 20.5942 13.8179C20.2029 13.8179 19.8154 13.7383 19.454 13.5835C19.0926 13.4288 18.7642 13.202 18.4878 12.9162Z" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
<path d="M5.29982 22.8765C4.60428 22.9646 3.95721 23.29 3.46136 23.801C2.232 25.074 2.11548 29.7889 2.11548 29.7889C2.11548 29.7889 6.68686 29.6687 7.91692 28.3985C8.41409 27.8876 8.72993 27.2187 8.81347 26.4997" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
</svg>
Deploy</a>
@@ -382,11 +382,11 @@
<path d="M349.4,526.666C349.111,527.157,348.412,528.058,348.297,529.367C348.275,529.592,348.093,529.731,348.121,529.384C348.246,528.107,348.805,527.306,349.23,526.61C349.422,526.327,349.513,526.459,349.402,526.667L349.4,526.666Z" fill="#002B64" transform="translate(348.784,528.021) translate(-348.784,-528.021)"/>
<path d="M349.112,526.377C348.782,526.842,347.705,527.923,347.479,529.217C347.439,529.438,347.245,529.564,347.302,529.219C347.535,527.956,348.464,526.962,348.943,526.301C349.159,526.035,349.238,526.174,349.111,526.372L349.112,526.377Z" fill="#002B64" transform="translate(348.232,527.793) translate(-348.232,-527.793)"/>
<path d="M348.823,526.088C348.43,526.504,347.147,527.879,346.743,529.122C346.671,529.335,346.462,529.431,346.567,529.098C346.974,527.879,348.098,526.568,348.667,525.978C348.918,525.745,348.976,525.894,348.823,526.072L348.823,526.088Z" fill="#002B64" transform="translate(347.723,527.587) translate(-347.723,-527.587)"/>
<path d="M336.092,563.3L336.092,570.461L335.188,570.461L335.188,569.233C334.368,570.213,333.434,570.704,332.388,570.704C331.334,570.704,330.444,570.33,329.726,569.584C329.005,568.829,328.647,567.934,328.647,566.899C328.647,565.855,329.012,564.964,329.739,564.237C330.467,563.491,331.341,563.117,332.356,563.117C333.526,563.117,334.474,563.617,335.202,564.615L335.202,563.307L336.106,563.307L336.092,563.3ZM335.257,566.933C335.257,566.114,334.981,565.426,334.433,564.868C333.884,564.301,333.209,564.019,332.409,564.019C331.617,564.019,330.947,564.31,330.398,564.895C329.85,565.472,329.574,566.155,329.574,566.947C329.574,567.739,329.854,568.428,330.412,569.013C330.97,569.589,331.635,569.876,332.409,569.876C333.191,569.876,333.861,569.596,334.419,569.038C334.977,568.48,335.257,567.778,335.257,566.933" fill="#A57242" transform="translate(332.377,566.91) translate(-332.377,-566.91)"/>
<path d="M336.092,563.3L336.092,570.461L335.188,570.461L335.188,569.233C334.368,570.213,333.434,570.704,332.388,570.704C331.334,570.704,330.444,570.33.429.726,569.584C329.005,568.829,328.647,567.934,328.647,566.899C328.647,565.855,329.012,564.964,329.739,564.237C330.467,563.491,331.341,563.117,332.356,563.117C333.526,563.117,334.474,563.617,335.202,564.615L335.202,563.307L336.106,563.307L336.092,563.3ZM335.257,566.933C335.257,566.114,334.981,565.426,334.433,564.868C333.884,564.301,333.209,564.019,332.409,564.019C331.617,564.019,330.947,564.31,330.398,564.895C329.85,565.472,329.574,566.155,329.574,566.947C329.574,567.739,329.854,568.428,330.412,569.013C330.97,569.589,331.635,569.876,332.409,569.876C333.191,569.876,333.861,569.596,334.419,569.038C334.977,568.48,335.257,567.778,335.257,566.933" fill="#A57242" transform="translate(332.377,566.91) translate(-332.377,-566.91)"/>
<path d="M338.213,570.415L338.213,560.73L340.224,560.73C341.601,560.73,342.611,560.833,343.253,561.04C343.901,561.238,344.458,561.556,344.926,561.997C345.395,562.428,345.755,562.958,346.005,563.59C346.258,564.218,346.384,564.957,346.384,565.802C346.384,566.64,346.186,567.429,345.789,568.166C345.401,568.903,344.871,569.46,344.197,569.839C343.531,570.218,342.569,570.406,341.305,570.406L338.207,570.406L338.213,570.415ZM339.138,569.488L340.258,569.488C341.429,569.488,342.273,569.429,342.806,569.312C343.336,569.195,343.8,568.966,344.197,568.624C344.591,568.273,344.894,567.851,345.101,567.355C345.308,566.859,345.411,566.295,345.411,565.654C345.411,565.016,345.289,564.426,345.046,563.887C344.812,563.348,344.474,562.902,344.034,562.551C343.593,562.2,343.072,561.966,342.468,561.849C341.874,561.723,340.997,561.66,339.829,561.66L339.141,561.66L339.141,569.486" fill="#A57242" transform="translate(342.296,565.572) translate(-342.296,-565.572)"/>
<path d="M325.591,561.235C325.591,561.028,325.663,560.847,325.807,560.696C325.96,560.543,326.14,560.466,326.346,560.466C326.562,560.466,326.741,560.543,326.885,560.696C327.038,560.839,327.115,561.019,327.115,561.235C327.115,561.442,327.039,561.623,326.885,561.774C326.742,561.918,326.562,561.99,326.346,561.99C326.139,561.99,325.958,561.914,325.807,561.761C325.663,561.608,325.591,561.433,325.591,561.235L325.591,561.235ZM325.887,563.232L326.819,563.232L326.819,570.392L325.887,570.392L325.887,563.232Z" fill="#A57242" transform="translate(326.353,565.429) translate(-326.353,-565.429)"/>
<path d="M300,570.415L301.377,560.73L301.526,560.73L305.473,568.671L309.375,560.73L309.523,560.73L310.914,570.415L309.957,570.415L309.011,563.484L305.592,570.415L305.348,570.415L301.883,563.415L300.937,570.415L300.006,570.415" fill="#A57242" transform="translate(305.457,565.572) translate(-305.457,-565.572)"/>
<path d="M319.679,563.3L319.679,570.461L318.775,570.461L318.775,569.233C317.956,570.213,317.022,570.704,315.975,570.704C314.922,570.704,314.031,570.33,313.313,569.584C312.592,568.829,312.234,567.934,312.234,566.899C312.234,565.855,312.599,564.964,313.327,564.237C314.054,563.491,314.929,563.117,315.943,563.117C317.114,563.117,318.061,563.617,318.789,564.615L318.789,563.307L319.693,563.307L319.679,563.3ZM318.844,566.933C318.844,566.114,318.569,565.426,318.02,564.868C317.472,564.301,316.797,564.019,315.996,564.019C315.204,564.019,314.534,564.31,313.985,564.895C313.437,565.472,313.162,566.155,313.162,566.947C313.162,567.739,313.442,568.428,313.999,569.013C314.557,569.589,315.222,569.876,315.996,569.876C316.779,569.876,317.449,569.596,318.006,569.038C318.564,568.48,318.842,567.778,318.844,566.933" fill="#A57242" transform="translate(315.963,566.91) translate(-315.963,-566.91)"/>
<path d="M319.679,563.3L319.679,570.461L318.775,570.461L318.775,569.233C317.956,570.213,317.022,570.704,315.975,570.704C314.922,570.704,314.031,570.33.413.313,569.584C312.592,568.829,312.234,567.934,312.234,566.899C312.234,565.855,312.599,564.964,313.327,564.237C314.054,563.491,314.929,563.117,315.943,563.117C317.114,563.117,318.061,563.617,318.789,564.615L318.789,563.307L319.693,563.307L319.679,563.3ZM318.844,566.933C318.844,566.114,318.569,565.426,318.02,564.868C317.472,564.301,316.797,564.019,315.996,564.019C315.204,564.019,314.534,564.31,313.985,564.895C313.437,565.472,313.162,566.155,313.162,566.947C313.162,567.739,313.442,568.428,313.999,569.013C314.557,569.589,315.222,569.876,315.996,569.876C316.779,569.876,317.449,569.596,318.006,569.038C318.564,568.48,318.842,567.778,318.844,566.933" fill="#A57242" transform="translate(315.963,566.91) translate(-315.963,-566.91)"/>
<path d="M321.342,563.3L322.287,563.3L322.287,564.34C322.54,563.918,322.783,563.608,323.015,563.408C323.258,563.21,323.522,563.112,323.811,563.112C324.107,563.112,324.406,563.193,324.702,563.355L324.215,564.138C324.08,564.057,323.94,564.017,323.797,564.017C323.517,564.017,323.254,564.161,323.001,564.448C322.758,564.735,322.579,565.109,322.462,565.568C322.345,566.018,322.286,566.842,322.286,568.047L322.286,570.479L321.341,570.479L321.341,563.319" fill="#A57242" transform="translate(323.022,566.795) translate(-323.022,-566.795)"/>
<path d="M348.198,560.776L350.114,560.776C351.131,560.776,351.9,560.983,352.432,561.396C352.962,561.799,353.228,562.385,353.228,563.149C353.228,563.571,353.121,563.964,352.905,564.322C352.698,564.673,352.393,564.955,351.987,565.171C352.652,565.387,353.157,565.71,353.497,566.142C353.848,566.573,354.023,567.087,354.023,567.679C354.023,568.48,353.731,569.146,353.146,569.676C352.561,570.197,351.806,570.459,350.878,570.459L348.216,570.459L348.216,560.774L348.198,560.776ZM349.173,561.719L349.173,564.817L349.726,564.817C350.571,564.817,351.202,564.682,351.615,564.413C352.028,564.143,352.235,563.72,352.235,563.144C352.235,562.192,351.588,561.715,350.291,561.715L349.171,561.715L349.173,561.719ZM349.171,565.8L349.171,569.518L350.371,569.518C351.064,569.518,351.571,569.45,351.895,569.315C352.228,569.171,352.499,568.955,352.705,568.668C352.912,568.372,353.015,568.069,353.015,567.764C353.015,567.459,352.956,567.188,352.84,566.954C352.723,566.72,352.548,566.513,352.314,566.334C352.089,566.154,351.814,566.02,351.49,565.93C351.176,565.84,350.581,565.795,349.709,565.795L349.17,565.795" fill="#A57242" transform="translate(351.111,565.617) translate(-351.111,-565.617)"/>
</g>

View File

@@ -49,110 +49,6 @@ The nginx server can be stopped via `nginx:stop`.
dokku nginx:stop
```
### Binding to specific addresses
> [!IMPORTANT]
> New as of 0.19.2
> [!NOTE]
> Changing this value globally or on a per-app basis will require rebuilding the nginx config via the `proxy:build-config` command.
By default, nginx will listen to all interfaces (`[::]` for IPv6, `0.0.0.0` for IPv4) when proxying requests to applications. This may be changed using the `bind-address-ipv4` and `bind-address-ipv6` properties. This is useful in cases where the proxying should be internal to a network or if there are multiple network interfaces that should respond with different content.
```shell
dokku nginx:set node-js-app bind-address-ipv4 127.0.0.1
dokku nginx:set node-js-app bind-address-ipv6 ::1
```
This may be reverted by setting an empty bind address.
```shell
dokku nginx:set node-js-app bind-address-ipv4
dokku nginx:set node-js-app bind-address-ipv6
```
> [!WARNING]
> Validation is not performed on either value.
Users with apps that contain a custom `nginx.conf.sigil` file will need to modify the files to respect the new `NGINX_BIND_ADDRESS_IPV4` and `NGINX_BIND_ADDRESS_IPV6` variables.
### HSTS Header
> [!IMPORTANT]
> New as of 0.20.0
> [!NOTE]
> Changing this value globally or on a per-app basis will require rebuilding the nginx config via the `proxy:build-config` command.
If SSL certificates are present, HSTS will be automatically enabled. It can be toggled via `nginx:set`:
```shell
dokku nginx:set node-js-app hsts true
dokku nginx:set node-js-app hsts false
```
The following options are also available via the `nginx:set` command:
- `hsts` (type: boolean, default: `true`): Enables or disables HSTS for your application.
- `hsts-include-subdomains` (type: boolean, default: `true`): Tells the browser that the HSTS policy also applies to all subdomains of the current domain.
- `hsts-max-age` (type: integer, default: `15724800`): Time in seconds to cache HSTS configuration.
- `hsts-preload` (type: boolean, default: `false`): Tells most major web browsers to include the domain in their HSTS preload lists.
Beware that if you enable the header and a subsequent deploy of your application results in an HTTP deploy (for whatever reason), the way the header works means that a browser will not attempt to request the HTTP version of your site if the HTTPS version fails until the max-age is reached.
#### Globally disabling the HSTS Header
> [!NOTE]
> Changing this value globally or on a per-app basis will require rebuilding the nginx config via the `proxy:build-config` command.
HSTS Header can be disabled for all apps by setting the `hsts` property to false after passing the `--global` flag to `nginx:set`.
```shell
dokku nginx:set --global hsts false
```
Once the HSTS setting is disabled globally, it can be re-enabled on a per-app basis by setting the `hsts` property as normal.
```shell
dokku nginx:set node-js-app hsts true
```
### Running behind another proxy — configuring `X-Forwarded-*` headers
Dokku's default Nginx configuration passes the de-facto standard HTTP headers [`X-Forwarded-For`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For), [`X-Forwarded-Proto`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto), and `X-Forwarded-Port` to your application.
These headers indicate the IP address of the original client making the request, the protocol of the original request (HTTP or HTTPS), and the port number of the original request, respectively.
If you have another HTTP proxy sitting in between the end user and your server (for example, a load balancer, or a CDN), then the values of these headers will contain information about (e.g. the IP address of) the the closest proxy, and not the end user.
To fix this, assuming that the other proxy also passes `X-Forwarded-*` headers, which in turn contain information about the end user, you can tell Nginx include those values in the `X-Forwarded-*` headers that it sends to your application. You can do this via `nginx:set`, like so:
```shell
dokku nginx:set node-js-app x-forwarded-for-value '$http_x_forwarded_for'
dokku nginx:set node-js-app x-forwarded-port-value '$http_x_forwarded_port'
dokku nginx:set node-js-app x-forwarded-proto-value '$http_x_forwarded_proto'
```
However, note that you should only do this if:
1. Requests to your website always go through a trusted proxy.
2. That proxy is configured to send the aforementioned `X-Forwarded-*` headers.
Otherwise, if it's possible for clients to make HTTP requests directly against your server, bypassing the other proxy, or if the other proxy is not configured to set these headers, then a client can basically pass any arbitrary values for these headers (which your app then presumably reads) and thereby fake an IP address, for example.
There's also the `X-Forwarded-Ssl` header which a less common alternative to `X-Forwarded-Proto` — and because of that, isn't included in Dokku's default Nginx configuration.
But you can tell Nginx to send this header as well, if necessary.
```shell
# force-setting value to `on`
dokku nginx:set node-js-app x-forwarded-ssl on
# force-setting value to `off`
dokku nginx:set node-js-app x-forwarded-ssl off
# removing the value from nginx.conf (default)
dokku nginx:set node-js-app x-forwarded-ssl
```
### Checking access logs
> [!NOTE]
@@ -184,130 +80,6 @@ You may also follow the logs by specifying the `-t` flag.
dokku nginx:error-logs node-js-app -t
```
### Changing log path
> [!IMPORTANT]
> New as of 0.20.1
> [!NOTE]
> Changing this value globally or on a per-app basis will require rebuilding the nginx config via the `proxy:build-config` command.
The path to where log files are stored can be changed by calling the `nginx:set` command with the following options:
- `access-log-path` (type: string, default: `${NGINX_LOG_ROOT}/${APP}-access.log`): Log path for nginx access logs
- `error-log-path` (type: string, default: `${NGINX_LOG_ROOT}/${APP}-error.log`): Log path for nginx error logs
The defaults should not be changed without verifying that the paths will be writeable by nginx. However, this setting is useful for enabling or disabling logging by setting the values to `off`.
```shell
dokku nginx:set node-js-app access-log-path off
dokku nginx:set node-js-app error-log-path off
```
The default value may be set by passing an empty value for the option:
```shell
dokku nginx:set node-js-app access-log-path
dokku nginx:set node-js-app error-log-path
```
In all cases, the nginx config must be regenerated after setting the above values.
### Changing log format
> [!IMPORTANT]
> New as of 0.22.0
> [!NOTE]
> Changing this value globally or on a per-app basis will require rebuilding the nginx config via the `proxy:build-config` command.
The format of the access log can be changed by calling the `nginx:set` command as follows:
```shell
dokku nginx:set node-js-app access-log-format custom-format
```
Prior to changing the log-format, log formats should be specified at a file such as `/etc/nginx/conf.d/00-log-formats.conf`. This will ensure they are available within your app's nginx context. For instance, the following may be added to the above file. It only needs to be specified once to be used for all apps.
```nginx
# /etc/nginx/conf.d/00-log-formats.conf
# escape=json was added in nginx 1.11.8
log_format json_combined escape=json
'{'
'"time_local":"$time_local",'
'"remote_addr":"$remote_addr",'
'"remote_user":"$remote_user",'
'"request":"$request",'
'"status":"$status",'
'"body_bytes_sent":"$body_bytes_sent",'
'"request_time":"$request_time",'
'"http_referrer":"$http_referer",'
'"http_user_agent":"$http_user_agent"'
'}';
```
Next, the format should be set for the given app.
```shell
dokku nginx:set node-js-app access-log-format json_combined
```
Finally, a proxy rebuild will change the format as desired.
```shell
dokku proxy:build-config node-js-app
```
### Specifying a read timeout
> [!IMPORTANT]
> New as of 0.21.0
> [!NOTE]
> Changing this value globally or on a per-app basis will require rebuilding the nginx config via the `proxy:build-config` command.
When proxying requests to your applications, it may be useful to specify a proxy read timeout. This can be done via the `nginx:set` command as follows:
```shell
dokku nginx:set node-js-app proxy-read-timeout 120s
```
The default value is `60s`, and all numeric values _must_ have a trailing time value specified (`s` for seconds, `m` for minutes).
The default value may be set by passing an empty value for the option:
```shell
dokku nginx:set node-js-app proxy-read-timeout
```
In all cases, the nginx config must be regenerated after setting the above value.
### Specifying a custom client_max_body_size
> [!IMPORTANT]
> New as of 0.23.0
> [!NOTE]
> Changing this value globally or on a per-app basis will require rebuilding the nginx config via the `proxy:build-config` command.
Users can override the default `client_max_body_size` value - which limits file uploads - via `nginx:set`. Changing this value will only apply to every `server` stanza of the default `nginx.conf.sigil`; users of custom `nginx.conf.sigil` files must update their templates to support the new value.
```shell
dokku nginx:set node-js-app client-max-body-size 50m
```
The default value is empty string, which will result in nginx falling back to any configured, higher-level defaults (or `1m` if unconfigued; all numerical values _must_ have a size unit specified (`k` for kilobytes, `m` for megabytes).
The default value may be set by passing an empty value for the option:
```shell
dokku nginx:set node-js-app client-max-body-size
```
In all cases, the nginx config must be regenerated after setting the above value.
Changing this value when using the PHP buildpack (or any other buildpack that uses an intermediary server) will require changing the value in the server config shipped with that buildpack. Consult your buildpack documentation for further details.
### Showing the nginx config
For debugging purposes, it may be useful to show the nginx config. This can be achieved via the `nginx:show-config` command.
@@ -341,6 +113,65 @@ The `--clean` flag may also be specified for a given app:
dokku nginx:validate-config node-js-app --clean
```
### Custom Error Pages
By default, Dokku provides custom error pages for the following three categories of errors:
- 4xx: For all non-404 errors with a 4xx response code.
- 404: For "404 Not Found" errors.
- 5xx: For all 5xx error responses
These are provided as an alternative to the generic Nginx error page, are shared for _all_ applications, and their contents are located on disk at `/var/lib/dokku/data/nginx-vhosts/dokku-errors`. To customize them for a specific app, create a custom `nginx.conf.sigil` as described above and change the paths to point elsewhere.
### Default site
By default, Dokku will route any received request with an unknown HOST header value to the lexicographically first site in the nginx config stack. This means that accessing the dokku server via its IP address or a bogus domain name may return a seemingly random website.
> [!WARNING]
> Some versions of Nginx may create a default site when installed. This site is simply a static page which says "Welcome to Nginx", and if this default site is enabled, Nginx will not route any requests with an unknown HOST header to Dokku. If you want Dokku to receive all requests, run the following commands:
>
> ```
> rm /etc/nginx/sites-enabled/default
> dokku nginx:stop
> dokku nginx:start
> ```
If services should only be accessed via their domain name, you may want to disable the default site by adding the following configuration to the global nginx configuration.
Create the file at `/etc/nginx/conf.d/00-default-vhost.conf`:
```nginx
server {
listen 80 default_server;
listen [::]:80 default_server;
# If services hosted by dokku are available via HTTPS, it is recommended
# to also uncomment the following section.
#
# Please note that in order to let this work, you need an SSL certificate. However
# it does not need to be valid. Users of Debian-based distributions can install the
# `ssl-cert` package with `sudo apt install ssl-cert` to automatically generate
# a self-signed certificate that is stored at `/etc/ssl/certs/ssl-cert-snakeoil.pem`.
#
#listen 443 ssl;
#listen [::]:443 ssl;
#ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem;
#ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key;
server_name _;
access_log off;
return 444;
}
```
Make sure to reload nginx after creating this file by running `systemctl reload nginx.service`.
This will catch all unknown HOST header values and close the connection without responding. You can replace the `return 444;` with `return 410;` which will cause nginx to respond with an error page.
The configuration file must be loaded before `/etc/nginx/conf.d/dokku.conf`, so it can not be arranged as a vhost in `/etc/nginx/sites-enabled` that is only processed afterwards.
Alternatively, you may push an app to your Dokku host with a name like "00-default". As long as it lists first in `ls /home/dokku/*/nginx.conf | head`, it will be used as the default nginx vhost.
### Customizing the nginx configuration
> [!IMPORTANT]
@@ -445,64 +276,188 @@ location / {
Please adjust the `Procfile` and `nginx.conf` file as appropriate.
### Custom Error Pages
### Setting Properties for the nginx config
By default, Dokku provides custom error pages for the following three categories of errors:
The nginx plugin exposes a variety of properties configurable via the `nginx:set` command. The properties are used to configure the generated `nginx.conf` file from the `nginx.conf.sigil` template. The value precedence is app-specific, then global, and finally the Dokku default.
- 4xx: For all non-404 errors with a 4xx response code.
- 404: For "404 Not Found" errors.
- 5xx: For all 5xx error responses
The nginx:set command takes an app name or the `--global` flag.
These are provided as an alternative to the generic Nginx error page, are shared for _all_ applications, and their contents are located on disk at `/var/lib/dokku/data/nginx-vhosts/dokku-errors`. To customize them for a specific app, create a custom `nginx.conf.sigil` as described above and change the paths to point elsewhere.
```shell
# set a property for the node-js-app
dokku nginx:set node-js-app property-name some-value
### Default site
By default, Dokku will route any received request with an unknown HOST header value to the lexicographically first site in the nginx config stack. This means that accessing the dokku server via its IP address or a bogus domain name may return a seemingly random website.
> [!WARNING]
> some versions of Nginx may create a default site when installed. This site is simply a static page which says "Welcome to Nginx", and if this default site is enabled, Nginx will not route any requests with an unknown HOST header to Dokku. If you want Dokku to receive all requests, run the following commands:
>
> ```
> rm /etc/nginx/sites-enabled/default
> dokku nginx:stop
> dokku nginx:start
> ```
If services should only be accessed via their domain name, you may want to disable the default site by adding the following configuration to the global nginx configuration.
Create the file at `/etc/nginx/conf.d/00-default-vhost.conf`:
```nginx
server {
listen 80 default_server;
listen [::]:80 default_server;
# If services hosted by dokku are available via HTTPS, it is recommended
# to also uncomment the following section.
#
# Please note that in order to let this work, you need an SSL certificate. However
# it does not need to be valid. Users of Debian-based distributions can install the
# `ssl-cert` package with `sudo apt install ssl-cert` to automatically generate
# a self-signed certificate that is stored at `/etc/ssl/certs/ssl-cert-snakeoil.pem`.
#
#listen 443 ssl;
#listen [::]:443 ssl;
#ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem;
#ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key;
server_name _;
access_log off;
return 444;
}
# set a property globally
dokku nginx:set --global property-name some-value
```
Make sure to reload nginx after creating this file by running `systemctl reload nginx.service`.
Additionally, setting an empty value will result in reverting the value back to it's default. For app-specific values, this means that Dokku will revert to the globally specified (or global default) value.
This will catch all unknown HOST header values and close the connection without responding. You can replace the `return 444;` with `return 410;` which will cause nginx to respond with an error page.
```shell
# default the value back to the global value for node-js-app
dokku nginx:set node-js-app property-name
The configuration file must be loaded before `/etc/nginx/conf.d/dokku.conf`, so it can not be arranged as a vhost in `/etc/nginx/sites-enabled` that is only processed afterwards.
# use the dokku default as the global value
dokku nginx:set --global property-name
```
Changing these value globally or on a per-app basis will require rebuilding the nginx config via the `proxy:build-config` command.
> [!WARNING]
> Validation is not performed against the values, and they are used as is within Dokku.
| Property | Default | Type | Explanation |
| --------------------------|---------------------------------------|---------|-------------------------------------------------------------------------------------|
| access-log-format | empty string | string | Name of custom log format to use (log format must be specified elsewhere) |
| access-log-path | `${NGINX_LOG_ROOT}/${APP}-access.log` | string | Log path for nginx access logs (set to `off` to disable) |
| bind-address-ipv4 | `0.0.0.0` | string | Default IPv4 address to bind to |
| bind-address-ipv6 | `[::]` | string | Default IPv6 address to bind to |
| client-max-body-size | `1m` | string | Size (with units) of client request body (usually modified for file uploads) |
| error-log-path | `${NGINX_LOG_ROOT}/${APP}-error.log` | string | Log path for nginx error logs (set to `off` to disable) |
| hsts | `true` | boolean | Enables or disables HSTS for your application |
| hsts-include-subdomains | `true` | boolean | Forces the browser to apply the HSTS policy to all app subdomains |
| hsts-max-age | `15724800` | integer | Time in seconds to cache HSTS configuration |
| hsts-preload | `false` | boolean | Tells the browser to include the domain in their HSTS preload lists |
| nginx-conf-sigil-path | `nginx.conf.sigil` | string | Path in the repository to the `nginx.conf.sigil` file |
| proxy-buffer-size | `8k` (# is os pagesize) | string | Size of the buffer used for reading the first part of proxied server response |
| proxy-buffering | `on` | string | Enables or disables buffering of responses from the proxied server |
| proxy-buffers | `8 8k` | string | Number and size of the buffers used for reading the proxied server response, for a single connection |
| proxy-busy-buffers-size | `16k` | string | Limits the total size of buffers that can be busy sending a response to the client while the response is not yet fully read. |
| proxy-read-timeout | `60s` | string | Timeout (with units) for reading response from your backend server |
| x-forwarded-for-value | `$remote_addr` | string | Used for specifying the header value to set for the `X-Forwared-For` header |
| x-forwarded-port-value | `$server_port` | string | Used for specifying the header value to set for the `X-Forwared-Port` header |
| x-forwarded-proto-value | `$scheme` | string | Used for specifying the header value to set for the `X-Forwared-Proto` header |
| x-forwarded-ssl | empty string | string | Less commonly used alternative to `X-Forwarded-Proto` (valid values: `on` or `off`) |
#### Binding to specific addresses
> [!NOTE]
> Users with apps that contain a custom `nginx.conf.sigil` file will need to modify the files to respect the new `NGINX_BIND_ADDRESS_IPV4` and `NGINX_BIND_ADDRESS_IPV6` variables.
Properties:
- `bind-address-ipv4`
- `bind-address-ipv6`
This is useful in cases where the proxying should be internal to a network or if there are multiple network interfaces that should respond with different content.
#### HSTS Header
> [!WARNING]
> if you enable the header and a subsequent deploy of your application results in an HTTP deploy (for whatever reason), the way the header works means that a browser will not attempt to request the HTTP version of your site if the HTTPS version fails until the max-age is reached.
Properties:
- `hsts`
- `hsts-include-subdomains`
- `hsts-max-age`
- `hsts-preload`
If SSL certificates are present, HSTS will be automatically enabled.
#### Running behind another proxy — configuring `X-Forwarded-*` headers
> [!WARNING]
> These values should only be modified if there is an intermediate Load balancer or CDN between the user and the Dokku server hosting your application.
Properties:
- `x-forwarded-for-value`
- `x-forwarded-port-value`
- `x-forwarded-proto-value`
- `x-forwarded-ssl`
Dokku's default Nginx configuration passes the de-facto standard HTTP headers [`X-Forwarded-For`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For), [`X-Forwarded-Proto`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto), and `X-Forwarded-Port` to your application.
These headers indicate the IP address of the original client making the request, the protocol of the original request (HTTP or HTTPS), and the port number of the original request, respectively.
If you have another HTTP proxy sitting in between the end user and your server (for example, a load balancer, or a CDN), then the values of these headers will contain information about (e.g. the IP address of) the the closest proxy, and not the end user.
To fix this, assuming that the other proxy also passes `X-Forwarded-*` headers, which in turn contain information about the end user, you can tell Nginx include those values in the `X-Forwarded-*` headers that it sends to your application. You can do this via `nginx:set`, like so:
```shell
dokku nginx:set node-js-app x-forwarded-for-value '$http_x_forwarded_for'
dokku nginx:set node-js-app x-forwarded-port-value '$http_x_forwarded_port'
dokku nginx:set node-js-app x-forwarded-proto-value '$http_x_forwarded_proto'
```
However, note that you should only do this if:
1. Requests to your website always go through a trusted proxy.
2. That proxy is configured to send the aforementioned `X-Forwarded-*` headers.
Otherwise, if it's possible for clients to make HTTP requests directly against your server, bypassing the other proxy, or if the other proxy is not configured to set these headers, then a client can basically pass any arbitrary values for these headers (which your app then presumably reads) and thereby fake an IP address, for example.
There's also the `X-Forwarded-Ssl` header which a less common alternative to `X-Forwarded-Proto` — and because of that, isn't included in Dokku's default Nginx configuration. It can be turned `on` if need be:
```shell
# force-setting value to `on`
dokku nginx:set node-js-app x-forwarded-ssl on
```
#### Changing log path
> [!WARNING]
> The defaults should not be changed without verifying that the paths will be writeable by nginx.
Properties:
- `access-log-path`
- `error-log-path`
These setting can be useful for enabling or disabling logging by setting the values to `off`.
```shell
dokku nginx:set node-js-app access-log-path off
dokku nginx:set node-js-app error-log-path off
```
#### Changing log format
Properties:
- `acccess-log-format`
Prior to changing the log-format, log formats should be specified at a file such as `/etc/nginx/conf.d/00-log-formats.conf`. This will ensure they are available within your app's nginx context. For instance, the following may be added to the above file. It only needs to be specified once to be used for all apps.
```nginx
# /etc/nginx/conf.d/00-log-formats.conf
# escape=json was added in nginx 1.11.8
log_format json_combined escape=json
'{'
'"time_local":"$time_local",'
'"remote_addr":"$remote_addr",'
'"remote_user":"$remote_user",'
'"request":"$request",'
'"status":"$status",'
'"body_bytes_sent":"$body_bytes_sent",'
'"request_time":"$request_time",'
'"http_referrer":"$http_referer",'
'"http_user_agent":"$http_user_agent"'
'}';
```
#### Specifying a read timeout
> [!NOTE]
> All numeric values _must_ have a trailing time value specified (`s` for seconds, `m` for minutes).
Properties:
- `proxy-read-timeout`
#### Specifying a custom client_max_body_size
> [!NOTE]
> All numerical values _must_ have a trailing size unit specified (`k` for kilobytes, `m` for megabytes).
Properties:
- `client-max-body-size`
This property is commonly used to increase the max file upload size.
Changing this value when using the PHP buildpack (or any other buildpack that uses an intermediary server) will require changing the value in the server config shipped with that buildpack. Consult your buildpack documentation for further details.
Alternatively, you may push an app to your Dokku host with a name like "00-default". As long as it lists first in `ls /home/dokku/*/nginx.conf | head`, it will be used as the default nginx vhost.
## Other

View File

@@ -9,26 +9,26 @@
<meta name="author" content="">
<title>Dokku - {{DOC_TITLE}}</title>
<link rel="apple-touch-icon" sizes="57x57" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/favicons/apple-touch-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/favicons/apple-touch-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/favicons/apple-touch-icon-72x72.png">
<link rel="apple-touch-icon" sizes="76x76" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/favicons/apple-touch-icon-76x76.png">
<link rel="apple-touch-icon" sizes="114x114" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/favicons/apple-touch-icon-114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/favicons/apple-touch-icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/favicons/apple-touch-icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/favicons/apple-touch-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/favicons/apple-touch-icon-180x180.png">
<link rel="icon" type="image/png" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/favicons/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/favicons/android-chrome-192x192.png" sizes="192x192">
<link rel="icon" type="image/png" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/favicons/favicon-96x96.png" sizes="96x96">
<link rel="icon" type="image/png" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/favicons/favicon-16x16.png" sizes="16x16">
<link rel="manifest" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/favicons/manifest.json">
<link rel="shortcut icon" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/favicons/favicon.ico">
<link rel="apple-touch-icon" sizes="57x57" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/favicons/apple-touch-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/favicons/apple-touch-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/favicons/apple-touch-icon-72x72.png">
<link rel="apple-touch-icon" sizes="76x76" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/favicons/apple-touch-icon-76x76.png">
<link rel="apple-touch-icon" sizes="114x114" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/favicons/apple-touch-icon-114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/favicons/apple-touch-icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/favicons/apple-touch-icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/favicons/apple-touch-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/favicons/apple-touch-icon-180x180.png">
<link rel="icon" type="image/png" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/favicons/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/favicons/android-chrome-192x192.png" sizes="192x192">
<link rel="icon" type="image/png" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/favicons/favicon-96x96.png" sizes="96x96">
<link rel="icon" type="image/png" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/favicons/favicon-16x16.png" sizes="16x16">
<link rel="manifest" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/favicons/manifest.json">
<link rel="shortcut icon" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/favicons/favicon.ico">
<meta name="apple-mobile-web-app-title" content="Dokku">
<meta name="application-name" content="Dokku">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="msapplication-TileImage" content="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/favicons/mstile-144x144.png">
<meta name="msapplication-config" content="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/favicons/browserconfig.xml">
<meta name="msapplication-TileImage" content="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/favicons/mstile-144x144.png">
<meta name="msapplication-config" content="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/favicons/browserconfig.xml">
<meta name="theme-color" content="#ffffff">
<script>
@@ -48,7 +48,7 @@
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Lato:wght@400;700;900&display=swap" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.3/docs/assets/style.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.33.4/docs/assets/style.css" rel="stylesheet">
<!-- <link href="./assets/style.css" rel="stylesheet"> -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.5.0/css/font-awesome.min.css" rel="stylesheet">
</head>

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core standard plugin"
version = "0.33.3"
version = "0.33.4"
[plugin.config]

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core events logging plugin"
version = "0.33.3"
version = "0.33.4"
[plugin.config]

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core app-json plugin"
version = "0.33.3"
version = "0.33.4"
[plugin.config]

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core apps plugin"
version = "0.33.3"
version = "0.33.4"
[plugin.config]

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core builder-dockerfile plugin"
version = "0.33.3"
version = "0.33.4"
[plugin.config]

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core builder-herokuish plugin"
version = "0.33.3"
version = "0.33.4"
[plugin.config]

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core builder-lambda plugin"
version = "0.33.3"
version = "0.33.4"
[plugin.config]

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core builder-nixpacks plugin"
version = "0.33.3"
version = "0.33.4"
[plugin.config]

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core builder-null plugin"
version = "0.33.3"
version = "0.33.4"
[plugin.config]

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core builder-pack plugin"
version = "0.33.3"
version = "0.33.4"
[plugin.config]

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core builder plugin"
version = "0.33.3"
version = "0.33.4"
[plugin.config]

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core buildpacks plugin"
version = "0.33.3"
version = "0.33.4"
[plugin.config]

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core caddy-vhosts plugin"
version = "0.33.3"
version = "0.33.4"
[plugin.config]

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core certificate management plugin"
version = "0.33.3"
version = "0.33.4"
[plugin.config]

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core checks plugin"
version = "0.33.3"
version = "0.33.4"
[plugin.config]

View File

@@ -13,11 +13,10 @@ func CreateAppDataDirectory(pluginName, appName string) error {
return err
}
if err := SetPermissions(directory, 0755); err != nil {
return err
}
return nil
return SetPermissions(SetPermissionInput{
Filename: directory,
Mode: os.FileMode(0755),
})
}
// CreateDataDirectory creates a data directory for the given plugin/app combination with the correct permissions
@@ -27,11 +26,10 @@ func CreateDataDirectory(pluginName string) error {
return err
}
if err := SetPermissions(directory, 0755); err != nil {
return err
}
return nil
return SetPermissions(SetPermissionInput{
Filename: directory,
Mode: os.FileMode(0755),
})
}
// GetAppDataDirectory returns the path to the data directory for the given plugin/app combination

View File

@@ -33,6 +33,18 @@ func ContainerStart(containerID string) bool {
return true
}
// ContainerRemove runs 'docker container remove' against an existing container
func ContainerRemove(containerID string) bool {
cmd := sh.Command(DockerBin(), "container", "remove", "-f", containerID)
cmd.Stdout = nil
cmd.Stderr = nil
if err := cmd.Run(); err != nil {
return false
}
return true
}
// ContainerExists checks to see if a container exists
func ContainerExists(containerID string) bool {
cmd := sh.Command(DockerBin(), "container", "inspect", containerID)
@@ -259,6 +271,15 @@ func GetWorkingDir(appName string, image string) string {
return workDir
}
func IsComposeInstalled() bool {
result, err := CallExecCommand(ExecCommandInput{
Command: DockerBin(),
Args: []string{"info", "--format", "{{range .ClientInfo.Plugins}}{{if eq .Name \"compose\"}}true{{end}}{{end}}')"},
CaptureOutput: true,
})
return err == nil && result.ExitCode == 0
}
// IsImageCnbBased returns true if app image is based on cnb
func IsImageCnbBased(image string) bool {
if len(image) == 0 {

View File

@@ -151,24 +151,33 @@ func ReadFirstLine(filename string) (text string) {
return
}
// SetPermissionsInput is the input struct for SetPermissions
type SetPermissionInput struct {
Filename string
GroupName string
Mode os.FileMode
Username string
}
// SetPermissions sets the proper owner and filemode for a given file
func SetPermissions(path string, fileMode os.FileMode) error {
if err := os.Chmod(path, fileMode); err != nil {
func SetPermissions(input SetPermissionInput) error {
if err := os.Chmod(input.Filename, input.Mode); err != nil {
return err
}
systemGroup := GetenvWithDefault("DOKKU_SYSTEM_GROUP", "dokku")
systemUser := GetenvWithDefault("DOKKU_SYSTEM_USER", "dokku")
if strings.HasPrefix(path, "/etc/sudoers.d/") {
systemGroup = "root"
systemUser = "root"
if input.GroupName == "" {
input.GroupName = GetenvWithDefault("DOKKU_SYSTEM_GROUP", "dokku")
}
group, err := user.LookupGroup(systemGroup)
if input.Username == "" {
input.Username = GetenvWithDefault("DOKKU_SYSTEM_USER", "dokku")
}
group, err := user.LookupGroup(input.GroupName)
if err != nil {
return err
}
user, err := user.Lookup(systemUser)
user, err := user.Lookup(input.Username)
if err != nil {
return err
}
@@ -182,7 +191,7 @@ func SetPermissions(path string, fileMode os.FileMode) error {
if err != nil {
return err
}
return os.Chown(path, uid, gid)
return os.Chown(input.Filename, uid, gid)
}
// TouchFile creates an empty file at the specified path
@@ -198,31 +207,70 @@ func TouchFile(filename string) error {
return err
}
if err := SetPermissions(filename, mode); err != nil {
return err
}
return nil
return SetPermissions(SetPermissionInput{
Filename: filename,
Mode: mode,
})
}
// WriteSliceToFile writes a slice of strings to a file
type WriteSliceToFileInput struct {
Filename string
Lines []string
Mode os.FileMode
Filename string
GroupName string
Lines []string
Mode os.FileMode
Username string
}
// WriteSliceToFile writes a slice of strings to a file
func WriteSliceToFile(input WriteSliceToFileInput) error {
return WriteBytesToFile(WriteBytesToFileInput{
Bytes: []byte(strings.TrimSuffix(strings.Join(input.Lines, "\n"), "\n") + "\n"),
Filename: input.Filename,
GroupName: input.GroupName,
Mode: input.Mode,
Username: input.Username,
})
}
// WriteStringToFile writes a string to a file
type WriteStringToFileInput struct {
Content string
Filename string
GroupName string
Mode os.FileMode
Username string
}
// WriteStringToFile writes a string to a file
func WriteStringToFile(input WriteStringToFileInput) error {
return WriteBytesToFile(WriteBytesToFileInput{
Bytes: []byte(input.Content),
Filename: input.Filename,
GroupName: input.GroupName,
Mode: input.Mode,
Username: input.Username,
})
}
// WriteBytesToFileInput writes a byte array to a file
type WriteBytesToFileInput struct {
Bytes []byte
Filename string
GroupName string
Mode os.FileMode
Username string
}
// WriteBytesToFile writes a byte array to a file
func WriteBytesToFile(input WriteBytesToFileInput) error {
file, err := os.OpenFile(input.Filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, input.Mode)
if err != nil {
return err
}
defer file.Close()
w := bufio.NewWriter(file)
for _, line := range input.Lines {
fmt.Fprintln(w, line)
}
if err = w.Flush(); err != nil {
if _, err := file.Write(input.Bytes); err != nil {
return err
}
@@ -230,9 +278,17 @@ func WriteSliceToFile(input WriteSliceToFileInput) error {
return err
}
if err := SetPermissions(input.Filename, input.Mode); err != nil {
return err
permissionsInput := SetPermissionInput{
Filename: input.Filename,
Mode: input.Mode,
}
return nil
if input.GroupName != "" {
permissionsInput.GroupName = input.GroupName
}
if input.Username != "" {
permissionsInput.Username = input.Username
}
return SetPermissions(permissionsInput)
}

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core common plugin"
version = "0.33.3"
version = "0.33.4"
[plugin.config]

View File

@@ -44,7 +44,9 @@ func CommandPropertySet(pluginName, appName, property, value string, properties
if value != "" {
LogInfo2Quiet(fmt.Sprintf("Setting %s to %s", property, value))
PropertyWrite(pluginName, appName, property, value)
if err := PropertyWrite(pluginName, appName, property, value); err != nil {
LogFailWithError(err)
}
} else {
LogInfo2Quiet(fmt.Sprintf("Unsetting %s", property))
if err := PropertyDelete(pluginName, appName, property); err != nil {
@@ -201,8 +203,17 @@ func PropertyListWrite(pluginName string, appName string, property string, value
return fmt.Errorf("Unable to write %s config value %s.%s: %s", pluginName, appName, property, err.Error())
}
file.Chmod(0600)
SetPermissions(propertyPath, 0600)
if err := file.Close(); err != nil {
return fmt.Errorf("Unable to close %s config value %s.%s: %s", pluginName, appName, property, err.Error())
}
if err := SetPermissions(SetPermissionInput{
Filename: propertyPath,
Mode: os.FileMode(0600),
}); err != nil {
return fmt.Errorf("Unable to set permissions for %s config value %s.%s: %s", pluginName, appName, property, err.Error())
}
return nil
}
@@ -328,8 +339,16 @@ func PropertyListRemove(pluginName string, appName string, property string, valu
return fmt.Errorf("Unable to write %s config value %s.%s: %s", pluginName, appName, property, err.Error())
}
file.Chmod(0600)
SetPermissions(propertyPath, 0600)
if err := file.Close(); err != nil {
return fmt.Errorf("Unable to close %s config value %s.%s: %s", pluginName, appName, property, err.Error())
}
if err := SetPermissions(SetPermissionInput{
Filename: propertyPath,
Mode: os.FileMode(0600),
}); err != nil {
return fmt.Errorf("Unable to set permissions for %s config value %s.%s: %s", pluginName, appName, property, err.Error())
}
if !found {
return errors.New("Property not found, nothing was removed")
@@ -364,8 +383,16 @@ func PropertyListRemoveByPrefix(pluginName string, appName string, property stri
return fmt.Errorf("Unable to write %s config value %s.%s: %s", pluginName, appName, property, err.Error())
}
file.Chmod(0600)
SetPermissions(propertyPath, 0600)
if err := file.Close(); err != nil {
return fmt.Errorf("Unable to close %s config value %s.%s: %s", pluginName, appName, property, err.Error())
}
if err := SetPermissions(SetPermissionInput{
Filename: propertyPath,
Mode: os.FileMode(0600),
}); err != nil {
return fmt.Errorf("Unable to set permissions for %s config value %s.%s: %s", pluginName, appName, property, err.Error())
}
if !found {
return errors.New("Property not found, nothing was removed")
@@ -389,9 +416,7 @@ func PropertyListSet(pluginName string, appName string, property string, value s
var lines []string
if index >= len(scannedLines) {
for _, line := range scannedLines {
lines = append(lines, line)
}
lines = append(lines, scannedLines...)
lines = append(lines, value)
} else {
for i, line := range scannedLines {
@@ -440,8 +465,18 @@ func PropertyWrite(pluginName string, appName string, property string, value str
defer file.Close()
fmt.Fprint(file, value)
file.Chmod(0600)
SetPermissions(propertyPath, 0600)
if err := file.Close(); err != nil {
return fmt.Errorf("Unable to close %s config value %s.%s: %s", pluginName, appName, property, err.Error())
}
if err := SetPermissions(SetPermissionInput{
Filename: propertyPath,
Mode: os.FileMode(0600),
}); err != nil {
return fmt.Errorf("Unable to set permissions for %s config value %s.%s: %s", pluginName, appName, property, err.Error())
}
return nil
}
@@ -451,10 +486,19 @@ func PropertySetup(pluginName string) error {
if err := os.MkdirAll(pluginConfigRoot, 0755); err != nil {
return err
}
if err := SetPermissions(filepath.Join(MustGetEnv("DOKKU_LIB_ROOT"), "config"), 0755); err != nil {
input := SetPermissionInput{
Filename: filepath.Join(MustGetEnv("DOKKU_LIB_ROOT"), "config"),
Mode: os.FileMode(0755),
}
if err := SetPermissions(input); err != nil {
return err
}
return SetPermissions(pluginConfigRoot, 0755)
return SetPermissions(SetPermissionInput{
Filename: pluginConfigRoot,
Mode: os.FileMode(0755),
})
}
func getPropertyPath(pluginName string, appName string, property string) string {
@@ -478,5 +522,8 @@ func makePluginAppPropertyPath(pluginName string, appName string) error {
if err := os.MkdirAll(pluginAppConfigRoot, 0755); err != nil {
return err
}
return SetPermissions(pluginAppConfigRoot, 0755)
return SetPermissions(SetPermissionInput{
Filename: pluginAppConfigRoot,
Mode: os.FileMode(0755),
})
}

View File

@@ -8,7 +8,7 @@ import (
"github.com/dokku/dokku/plugins/common"
)
//Get retrieves a value from a config. If appName is empty the global config is used.
// Get retrieves a value from a config. If appName is empty the global config is used.
func Get(appName string, key string) (value string, ok bool) {
env, err := loadAppOrGlobalEnv(appName)
if err != nil {
@@ -20,7 +20,7 @@ func Get(appName string, key string) (value string, ok bool) {
return env.Get(key)
}
//GetWithDefault gets a value from a config. If appName is empty the global config is used. If the appName or key do not exist defaultValue is returned.
// GetWithDefault gets a value from a config. If appName is empty the global config is used. If the appName or key do not exist defaultValue is returned.
func GetWithDefault(appName string, key string, defaultValue string) (value string) {
value, ok := Get(appName, key)
if !ok {
@@ -29,7 +29,7 @@ func GetWithDefault(appName string, key string, defaultValue string) (value stri
return value
}
//SetMany variables in the environment. If appName is empty the global config is used. If restart is true the app is restarted.
// SetMany variables in the environment. If appName is empty the global config is used. If restart is true the app is restarted.
func SetMany(appName string, entries map[string]string, restart bool) (err error) {
global := appName == "" || appName == "--global"
env, err := loadAppOrGlobalEnv(appName)
@@ -52,7 +52,10 @@ func SetMany(appName string, entries map[string]string, restart bool) (err error
fmt.Println(prettyPrintEnvEntries(" ", entries))
}
env.Write()
common.SetPermissions(env.Filename(), 0600)
common.SetPermissions(common.SetPermissionInput{
Filename: env.Filename(),
Mode: os.FileMode(0600),
})
triggerUpdate(appName, "set", keys)
}
if !global && restart && env.GetBoolDefault("DOKKU_APP_RESTORE", true) {
@@ -61,7 +64,7 @@ func SetMany(appName string, entries map[string]string, restart bool) (err error
return
}
//UnsetMany a value in a config. If appName is empty the global config is used. If restart is true the app is restarted.
// UnsetMany a value in a config. If appName is empty the global config is used. If restart is true the app is restarted.
func UnsetMany(appName string, keys []string, restart bool) (err error) {
global := appName == "" || appName == "--global"
env, err := loadAppOrGlobalEnv(appName)
@@ -85,7 +88,10 @@ func UnsetMany(appName string, keys []string, restart bool) (err error) {
}
if changed {
env.Write()
common.SetPermissions(env.Filename(), 0600)
common.SetPermissions(common.SetPermissionInput{
Filename: env.Filename(),
Mode: os.FileMode(0600),
})
triggerUpdate(appName, "unset", keys)
}
if !global && restart && env.GetBoolDefault("DOKKU_APP_RESTORE", true) {
@@ -94,7 +100,7 @@ func UnsetMany(appName string, keys []string, restart bool) (err error) {
return
}
//UnsetAll removes all config keys
// UnsetAll removes all config keys
func UnsetAll(appName string, restart bool) (err error) {
global := appName == "" || appName == "--global"
env, err := loadAppOrGlobalEnv(appName)
@@ -109,7 +115,10 @@ func UnsetAll(appName string, restart bool) (err error) {
}
if changed {
env.Write()
common.SetPermissions(env.Filename(), 0600)
common.SetPermissions(common.SetPermissionInput{
Filename: env.Filename(),
Mode: os.FileMode(0600),
})
triggerUpdate(appName, "clear", []string{})
}
if !global && restart && env.GetBoolDefault("DOKKU_APP_RESTORE", true) {
@@ -132,7 +141,7 @@ func triggerUpdate(appName string, operation string, args []string) {
}
}
//getEnvironment for the given app (global config if appName is empty). Merge with global environment if merged is true.
// getEnvironment for the given app (global config if appName is empty). Merge with global environment if merged is true.
func getEnvironment(appName string, merged bool) (env *Env) {
var err error
if appName != "" && merged {

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core config plugin"
version = "0.33.3"
version = "0.33.4"
[plugin.config]

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core cron plugin"
version = "0.33.3"
version = "0.33.4"
[plugin.config]

View File

@@ -39,7 +39,7 @@ trigger-docker-options-docker-args() {
;;
*)
case "$IMAGE_SOURCE_TYPE" in
dockerfile)
dockerfile | nixpacks)
case "$line" in
--link* | -v* | --volume*)
continue

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core docker-options plugin"
version = "0.33.3"
version = "0.33.4"
[plugin.config]

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core domains plugin"
version = "0.33.3"
version = "0.33.4"
[plugin.config]

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core enter plugin"
version = "0.33.3"
version = "0.33.4"
[plugin.config]

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core git plugin"
version = "0.33.3"
version = "0.33.4"
[plugin.config]

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core haproxy-vhosts plugin"
version = "0.33.3"
version = "0.33.4"
[plugin.config]

View File

@@ -5,11 +5,11 @@ import (
"encoding/json"
"errors"
"fmt"
"html/template"
"net/url"
"os"
"path/filepath"
"strings"
"time"
"github.com/dokku/dokku/plugins/common"
"github.com/joncalhoun/qson"
@@ -25,92 +25,164 @@ type vectorSource struct {
IncludeLabels []string `json:"include_labels,omitempty"`
}
type vectorSink map[string]interface{}
const vectorContainerName = "vector"
func killVectorContainer() error {
if !common.ContainerExists(vectorContainerName) {
return nil
}
if err := stopVectorContainer(); err != nil {
return err
}
time.Sleep(10 * time.Second)
if err := removeVectorContainer(); err != nil {
return err
}
return nil
type vectorTemplateData struct {
DokkuLibRoot string
DokkuLogsDir string
VectorImage string
}
func removeVectorContainer() error {
if !common.ContainerExists(vectorContainerName) {
return nil
type vectorSink map[string]interface{}
const vectorContainerName = "vector-vector-1"
const vectorOldContainerName = "vector"
func getComposeFile() ([]byte, error) {
result, err := common.CallPlugnTrigger(common.PlugnTriggerInput{
Trigger: "vector-template-source",
CaptureOutput: true,
})
if err == nil && result.ExitCode == 0 && strings.TrimSpace(result.Stdout) != "" {
contents, err := os.ReadFile(strings.TrimSpace(result.Stdout))
if err != nil {
return []byte{}, fmt.Errorf("Unable to read compose template: %s", err)
}
return contents, nil
}
cmd := common.NewShellCmd(strings.Join([]string{
common.DockerBin(), "container", "rm", "-f", vectorContainerName}, " "))
contents, err := templates.ReadFile("templates/compose.yml.tmpl")
if err != nil {
return []byte{}, fmt.Errorf("Unable to read compose template: %s", err)
}
return common.SuppressOutput(func() error {
if cmd.Execute() {
return nil
}
if common.ContainerExists(vectorContainerName) {
return errors.New("Unable to remove vector container")
}
return nil
})
return contents, nil
}
func startVectorContainer(vectorImage string) error {
cmd := common.NewShellCmd(strings.Join([]string{
common.DockerBin(),
"container",
"run", "--detach", "--name", vectorContainerName, common.MustGetEnv("DOKKU_GLOBAL_RUN_ARGS"),
"--restart", "unless-stopped",
"--volume", "/var/lib/dokku/data/logs:/etc/vector",
"--volume", "/var/run/docker.sock:/var/run/docker.sock",
"--volume", common.MustGetEnv("DOKKU_LOGS_HOST_DIR") + ":/var/logs/dokku/apps",
"--volume", common.MustGetEnv("DOKKU_LOGS_HOST_DIR") + "/apps:/var/log/dokku/apps",
vectorImage,
"--config", "/etc/vector/vector.json", "--watch-config"}, " "))
cmd.ShowOutput = false
if !common.IsComposeInstalled() {
return errors.New("Required docker compose plugin is not installed")
}
if !cmd.Execute() {
return errors.New("Unable to start vector container")
if common.ContainerExists(vectorOldContainerName) {
return errors.New("Vector container %s already exists in old format, run 'dokku logs:vector-stop' once to remove it")
}
tmpFile, err := os.CreateTemp(os.TempDir(), "vector-compose-*.yml")
if err != nil {
return fmt.Errorf("Unable to create temporary file: %s", err)
}
defer os.Remove(tmpFile.Name())
contents, err := getComposeFile()
if err != nil {
return fmt.Errorf("Unable to read compose template: %s", err)
}
tmpl, err := template.New("compose.yml").Parse(string(contents))
if err != nil {
return fmt.Errorf("Unable to parse compose template: %s", err)
}
dokkuLibRoot := os.Getenv("DOKKU_LIB_HOST_ROOT")
if dokkuLibRoot == "" {
dokkuLibRoot = os.Getenv("DOKKU_LIB_ROOT")
}
dokkuLogsDir := os.Getenv("DOKKU_LOGS_HOST_DIR")
if dokkuLogsDir == "" {
dokkuLogsDir = os.Getenv("DOKKU_LOGS_DIR")
}
data := vectorTemplateData{
DokkuLibRoot: dokkuLibRoot,
DokkuLogsDir: dokkuLogsDir,
VectorImage: vectorImage,
}
if err := tmpl.Execute(tmpFile, data); err != nil {
return fmt.Errorf("Unable to execute compose template: %s", err)
}
result, err := common.CallExecCommand(common.ExecCommandInput{
Command: common.DockerBin(),
Args: []string{
"compose",
"--file", tmpFile.Name(),
"--project-name", "vector",
"up",
"--detach",
"--quiet-pull",
},
StreamStdio: true,
})
if err != nil || result.ExitCode != 0 {
return fmt.Errorf("Unable to start vector container: %s", result.Stderr)
}
return nil
}
func stopVectorContainer() error {
if !common.ContainerExists(vectorContainerName) {
return nil
if !common.IsComposeInstalled() {
return errors.New("Required docker compose plugin is not installed")
}
if !common.ContainerIsRunning(vectorContainerName) {
return nil
if common.ContainerExists(vectorOldContainerName) {
common.ContainerRemove(vectorOldContainerName)
}
cmd := common.NewShellCmd(strings.Join([]string{
common.DockerBin(), "container", "stop", vectorContainerName}, " "))
tmpFile, err := os.CreateTemp(os.TempDir(), "vector-compose-*.yml")
if err != nil {
return fmt.Errorf("Unable to create temporary file: %s", err)
}
defer os.Remove(tmpFile.Name())
return common.SuppressOutput(func() error {
if cmd.Execute() {
return nil
}
contents, err := getComposeFile()
if err != nil {
return fmt.Errorf("Unable to read compose template: %s", err)
}
if common.ContainerIsRunning(vectorContainerName) {
return errors.New("Unable to stop vector container")
}
tmpl, err := template.New("compose.yml").Parse(string(contents))
if err != nil {
return fmt.Errorf("Unable to parse compose template: %s", err)
}
return nil
dokkuLibRoot := os.Getenv("DOKKU_LIB_HOST_ROOT")
if dokkuLibRoot == "" {
dokkuLibRoot = os.Getenv("DOKKU_LIB_ROOT")
}
dokkuLogsDir := os.Getenv("DOKKU_LOGS_HOST_DIR")
if dokkuLogsDir == "" {
dokkuLogsDir = os.Getenv("DOKKU_LOGS_DIR")
}
data := vectorTemplateData{
DokkuLibRoot: dokkuLibRoot,
DokkuLogsDir: dokkuLogsDir,
VectorImage: VectorImage,
}
if err := tmpl.Execute(tmpFile, data); err != nil {
return fmt.Errorf("Unable to execute compose template: %s", err)
}
result, err := common.CallExecCommand(common.ExecCommandInput{
Command: common.DockerBin(),
Args: []string{
"compose",
"--file", tmpFile.Name(),
"--project-name", "vector",
"down",
"--remove-orphans",
},
StreamStdio: true,
})
if err != nil || result.ExitCode != 0 {
return fmt.Errorf("Unable to stop vector container: %s", result.Stderr)
}
return nil
}
func sinkValueToConfig(appName string, sinkValue string) (vectorSink, error) {
@@ -132,9 +204,7 @@ func sinkValueToConfig(appName string, sinkValue string) (vectorSink, error) {
u.Scheme = strings.ReplaceAll(u.Scheme, "-", "_")
query := u.RawQuery
if strings.HasPrefix(query, "&") {
query = strings.TrimPrefix(query, "&")
}
query = strings.TrimPrefix(query, "&")
b, err := qson.ToJSON(query)
if err != nil {
@@ -225,9 +295,9 @@ func writeVectorConfig() error {
b = bytes.Replace(b, []byte("\\u002B"), []byte("+"), -1)
vectorConfig := filepath.Join(common.GetDataDirectory("logs"), "vector.json")
if err := common.WriteSliceToFile(common.WriteSliceToFileInput{
if err := common.WriteBytesToFile(common.WriteBytesToFileInput{
Bytes: b,
Filename: vectorConfig,
Lines: []string{string(b)},
Mode: os.FileMode(0600),
}); err != nil {
return err

View File

@@ -1,6 +1,7 @@
package logs
import (
"embed"
"fmt"
"github.com/dokku/dokku/plugins/common"
@@ -30,6 +31,9 @@ const VectorImage = "timberio/vector:0.35.X-debian"
// VectorDefaultSink contains the default sink in use for vector log shipping
const VectorDefaultSink = "blackhole://?print_interval_secs=1"
//go:embed templates/*
var templates embed.FS
// GetFailedLogs outputs failed deploy logs for a given app
func GetFailedLogs(appName string) error {
common.LogInfo2Quiet(fmt.Sprintf("%s failed deploy logs", appName))

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core logs plugin"
version = "0.33.3"
version = "0.33.4"
[plugin.config]

View File

@@ -107,20 +107,9 @@ func CommandVectorStart(vectorImage string) error {
vectorImage = common.PropertyGetDefault("logs", "--global", "vector-image", VectorImage)
}
if common.ContainerExists(vectorContainerName) {
if common.ContainerIsRunning(vectorContainerName) {
common.LogVerbose("Vector container is running")
return nil
}
common.LogVerbose("Starting vector container")
if !common.ContainerStart(vectorContainerName) {
return errors.New("Unable to start vector container")
}
} else {
if err := startVectorContainer(vectorImage); err != nil {
return err
}
common.LogVerbose("Starting vector container")
if err := startVectorContainer(vectorImage); err != nil {
return err
}
common.LogVerbose("Waiting for 10 seconds")
@@ -134,6 +123,6 @@ func CommandVectorStart(vectorImage string) error {
// CommandVectorStop stops and removes an existing vector container
func CommandVectorStop() error {
common.LogInfo2Quiet("Stopping and removing vector container")
return killVectorContainer()
common.LogInfo2Quiet("Stopping and removing vector container")
return stopVectorContainer()
}

View File

@@ -0,0 +1,25 @@
---
version: "3.7"
services:
vector:
image: "{{ $.VectorImage }}"
command:
- "--config"
- "/etc/vector/vector.json"
- "--watch-config"
labels:
dokku: ""
org.label-schema.schema-version: "1.0"
org.label-schema.vendor: dokku
network_mode: bridge
restart: unless-stopped
volumes:
- "{{ $.DokkuLibRoot }}/data/logs:/etc/vector"
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "{{ $.DokkuLogsDir }}/apps:/var/log/dokku/apps"

View File

@@ -84,11 +84,10 @@ func TriggerInstall() error {
return err
}
if err := common.SetPermissions(logDirectory, 0755); err != nil {
return err
}
return nil
return common.SetPermissions(common.SetPermissionInput{
Filename: logDirectory,
Mode: os.FileMode(0755),
})
}
// TriggerLogsGetProperty writes the logs key to stdout for a given app container

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core network plugin"
version = "0.33.3"
version = "0.33.4"
[plugin.config]

View File

@@ -1,5 +1,5 @@
GOARCH ?= amd64
BUILD = pagesize
BUILD = pagesize nginx-property
PLUGIN_NAME = nginx-vhosts
clean-pagesize:
@@ -8,4 +8,10 @@ clean-pagesize:
pagesize: clean-pagesize **/**/pagesize.go
GOARCH=$(GOARCH) go build -ldflags="-s -w" $(GO_ARGS) -o pagesize src/pagesize/pagesize.go
clean-nginx-property:
rm -rf nginx-property
nginx-property: clean-nginx-property **/**/nginx-property.go
GOARCH=$(GOARCH) go build -ldflags="-s -w" $(GO_ARGS) -o nginx-property src/nginx-property/nginx-property.go
include ../../common.mk

View File

@@ -38,31 +38,69 @@ cmd-nginx-report-single() {
verify_app_name "$APP"
local flag_map=(
"--nginx-access-log-format: $(fn-nginx-access-log-format "$APP")"
"--nginx-computed-access-log-format: $(fn-nginx-computed-access-log-format "$APP")"
"--nginx-global-access-log-format: $(fn-nginx-global-access-log-format "$APP")"
"--nginx-access-log-path: $(fn-nginx-access-log-path "$APP")"
"--nginx-computed-access-log-path: $(fn-nginx-computed-access-log-path "$APP")"
"--nginx-global-access-log-path: $(fn-nginx-global-access-log-path "$APP")"
"--nginx-bind-address-ipv4: $(fn-nginx-bind-address-ipv4 "$APP")"
"--nginx-computed-bind-address-ipv4: $(fn-nginx-computed-bind-address-ipv4 "$APP")"
"--nginx-global-bind-address-ipv4: $(fn-nginx-global-bind-address-ipv4 "$APP")"
"--nginx-bind-address-ipv6: $(fn-nginx-bind-address-ipv6 "$APP")"
"--nginx-computed-bind-address-ipv6: $(fn-nginx-computed-bind-address-ipv6 "$APP")"
"--nginx-global-bind-address-ipv6: $(fn-nginx-global-bind-address-ipv6 "$APP")"
"--nginx-client-max-body-size: $(fn-nginx-client-max-body-size "$APP")"
"--nginx-disable-custom-config: $(fn-plugin-property-get-default "nginx" "$APP" "disable-custom-config" "false")"
"--nginx-computed-client-max-body-size: $(fn-nginx-computed-client-max-body-size "$APP")"
"--nginx-global-client-max-body-size: $(fn-nginx-global-client-max-body-size "$APP")"
"--nginx-disable-custom-config: $(fn-nginx-disable-custom-config "$APP")"
"--nginx-computed-disable-custom-config: $(fn-nginx-computed-disable-custom-config "$APP")"
"--nginx-global-disable-custom-config: $(fn-nginx-global-disable-custom-config "$APP")"
"--nginx-error-log-path: $(fn-nginx-error-log-path "$APP")"
"--nginx-global-hsts: $(fn-plugin-property-get-default "nginx" "--global" "hsts" "true")"
"--nginx-computed-hsts: $(fn-nginx-hsts-is-enabled "$APP")"
"--nginx-hsts: $(fn-plugin-property-get-default "nginx" "$APP" "hsts" "")"
"--nginx-computed-error-log-path: $(fn-nginx-computed-error-log-path "$APP")"
"--nginx-global-error-log-path: $(fn-nginx-global-error-log-path "$APP")"
"--nginx-hsts-include-subdomains: $(fn-nginx-hsts-include-subdomains "$APP")"
"--nginx-computed-hsts-include-subdomains: $(fn-nginx-computed-hsts-include-subdomains "$APP")"
"--nginx-global-hsts-include-subdomains: $(fn-nginx-global-hsts-include-subdomains "$APP")"
"--nginx-hsts-max-age: $(fn-nginx-hsts-max-age "$APP")"
"--nginx-computed-hsts-max-age: $(fn-nginx-computed-hsts-max-age "$APP")"
"--nginx-global-hsts-max-age: $(fn-nginx-global-hsts-max-age "$APP")"
"--nginx-hsts-preload: $(fn-nginx-hsts-preload "$APP")"
"--nginx-computed-nginx-conf-sigil-path: $(fn-nginx-computed-nginx-conf-sigil-path "$APP")"
"--nginx-global-nginx-conf-sigil-path: $(fn-nginx-global-nginx-conf-sigil-path)"
"--nginx-nginx-conf-sigil-path: $(fn-nginx-nginx-conf-sigil-path "$APP")"
"--nginx-proxy-buffer-size: $(fn-nginx-proxy-buffer-size "$APP")"
"--nginx-proxy-buffering: $(fn-nginx-proxy-buffering "$APP")"
"--nginx-proxy-buffers: $(fn-nginx-proxy-buffers "$APP")"
"--nginx-proxy-busy-buffers-size: $(fn-nginx-proxy-busy-buffers-size "$APP")"
"--nginx-proxy-read-timeout: $(fn-nginx-proxy-read-timeout "$APP")"
"--nginx-computed-hsts-preload: $(fn-nginx-computed-hsts-preload "$APP")"
"--nginx-global-hsts-preload: $(fn-nginx-global-hsts-preload "$APP")"
"--nginx-hsts: $(fn-nginx-hsts "$APP")"
"--nginx-computed-hsts: $(fn-nginx-computed-hsts "$APP")"
"--nginx-global-hsts: $(fn-nginx-global-hsts "$APP")"
"--nginx-last-visited-at: $(fn-nginx-vhosts-last-visited-at "$APP")"
"--nginx-nginx-conf-sigil-path: $(fn-nginx-nginx-conf-sigil-path "$APP")"
"--nginx-computed-nginx-conf-sigil-path: $(fn-nginx-computed-nginx-conf-sigil-path "$APP")"
"--nginx-global-nginx-conf-sigil-path: $(fn-nginx-global-nginx-conf-sigil-path "$APP")"
"--nginx-proxy-buffer-size: $(fn-nginx-proxy-buffer-size "$APP")"
"--nginx-computed-proxy-buffer-size: $(fn-nginx-computed-proxy-buffer-size "$APP")"
"--nginx-global-proxy-buffer-size: $(fn-nginx-global-proxy-buffer-size "$APP")"
"--nginx-proxy-buffering: $(fn-nginx-proxy-buffering "$APP")"
"--nginx-computed-proxy-buffering: $(fn-nginx-computed-proxy-buffering "$APP")"
"--nginx-global-proxy-buffering: $(fn-nginx-global-proxy-buffering "$APP")"
"--nginx-proxy-buffers: $(fn-nginx-proxy-buffers "$APP")"
"--nginx-computed-proxy-buffers: $(fn-nginx-computed-proxy-buffers "$APP")"
"--nginx-global-proxy-buffers: $(fn-nginx-global-proxy-buffers "$APP")"
"--nginx-proxy-busy-buffers-size: $(fn-nginx-proxy-busy-buffers-size "$APP")"
"--nginx-computed-proxy-busy-buffers-size: $(fn-nginx-computed-proxy-busy-buffers-size "$APP")"
"--nginx-global-proxy-busy-buffers-size: $(fn-nginx-global-proxy-busy-buffers-size "$APP")"
"--nginx-proxy-read-timeout: $(fn-nginx-proxy-read-timeout "$APP")"
"--nginx-computed-proxy-read-timeout: $(fn-nginx-computed-proxy-read-timeout "$APP")"
"--nginx-global-proxy-read-timeout: $(fn-nginx-global-proxy-read-timeout "$APP")"
"--nginx-x-forwarded-for-value: $(fn-nginx-x-forwarded-for-value "$APP")"
"--nginx-computed-x-forwarded-for-value: $(fn-nginx-computed-x-forwarded-for-value "$APP")"
"--nginx-global-x-forwarded-for-value: $(fn-nginx-global-x-forwarded-for-value "$APP")"
"--nginx-x-forwarded-port-value: $(fn-nginx-x-forwarded-port-value "$APP")"
"--nginx-computed-x-forwarded-port-value: $(fn-nginx-computed-x-forwarded-port-value "$APP")"
"--nginx-global-x-forwarded-port-value: $(fn-nginx-global-x-forwarded-port-value "$APP")"
"--nginx-x-forwarded-proto-value: $(fn-nginx-x-forwarded-proto-value "$APP")"
"--nginx-computed-x-forwarded-proto-value: $(fn-nginx-computed-x-forwarded-proto-value "$APP")"
"--nginx-global-x-forwarded-proto-value: $(fn-nginx-global-x-forwarded-proto-value "$APP")"
"--nginx-x-forwarded-ssl: $(fn-nginx-x-forwarded-ssl "$APP")"
"--nginx-computed-x-forwarded-ssl: $(fn-nginx-computed-x-forwarded-ssl "$APP")"
"--nginx-global-x-forwarded-ssl: $(fn-nginx-global-x-forwarded-ssl "$APP")"
)
if [[ -z "$INFO_FLAG" ]]; then

View File

@@ -6,120 +6,6 @@ source "$PLUGIN_AVAILABLE_PATH/certs/functions"
source "$PLUGIN_AVAILABLE_PATH/config/functions"
source "$PLUGIN_AVAILABLE_PATH/nginx-vhosts/internal-functions"
fn-nginx-log-root() {
declare desc="get the nginx log root"
local NGINX_LOG_ROOT="/var/log/nginx"
fn-nginx-vhosts-uses-openresty && NGINX_LOG_ROOT="/var/log/openresty"
echo "$NGINX_LOG_ROOT"
}
fn-nginx-access-log-format() {
declare desc="get the configured access log format"
declare APP="$1"
fn-plugin-property-get-default "nginx" "$APP" "access-log-format" ""
}
fn-nginx-access-log-path() {
declare desc="get the configured access log path"
declare APP="$1"
local NGINX_LOG_ROOT="$(fn-nginx-log-root)"
fn-plugin-property-get-default "nginx" "$APP" "access-log-path" "${NGINX_LOG_ROOT}/${APP}-access.log"
}
fn-nginx-bind-address-ipv4() {
declare desc="get the configured ipv4 bind address"
declare APP="$1"
fn-plugin-property-get-default "nginx" "$APP" "bind-address-ipv4" ""
}
fn-nginx-bind-address-ipv6() {
declare desc="get the configured ipv6 bind address"
declare APP="$1"
fn-plugin-property-get-default "nginx" "$APP" "bind-address-ipv6" "::"
}
fn-nginx-proxy-buffer-size() {
declare desc="get the configured proxy buffer size"
declare APP="$1"
fn-plugin-property-get-default "nginx" "$APP" "proxy-buffer-size" "$(fn-get-pagesize)"
}
fn-nginx-proxy-buffering() {
declare desc="get the configured proxy buffering"
declare APP="$1"
fn-plugin-property-get-default "nginx" "$APP" "proxy-buffering" "on"
}
fn-nginx-proxy-buffers() {
declare desc="get the configured proxy buffers"
declare APP="$1"
fn-plugin-property-get-default "nginx" "$APP" "proxy-buffers" "8 $(fn-get-pagesize)"
}
fn-nginx-proxy-busy-buffers-size() {
declare desc="get the configured proxy busy buffers size"
declare APP="$1"
fn-plugin-property-get-default "nginx" "$APP" "proxy-busy-buffers-size" "$(($(fn-get-pagesize) * 2))"
}
fn-nginx-proxy-read-timeout() {
declare desc="get the configured proxy read timeout"
declare APP="$1"
fn-plugin-property-get-default "nginx" "$APP" "proxy-read-timeout" "60s"
}
fn-nginx-client-max-body-size() {
declare desc="get the configured client max body size"
declare APP="$1"
fn-plugin-property-get-default "nginx" "$APP" "client-max-body-size" ""
}
fn-nginx-error-log-path() {
declare desc="get the configured access log path"
declare APP="$1"
local NGINX_LOG_ROOT="$(fn-nginx-log-root)"
fn-plugin-property-get-default "nginx" "$APP" "error-log-path" "${NGINX_LOG_ROOT}/${APP}-error.log"
}
fn-nginx-x-forwarded-for-value() {
declare desc="get the configured x-forwarded-for value"
declare APP="$1"
fn-plugin-property-get-default "nginx" "$APP" "x-forwarded-for-value" "\$remote_addr"
}
fn-nginx-x-forwarded-port-value() {
declare desc="get the configured x-forwarded-port value"
declare APP="$1"
fn-plugin-property-get-default "nginx" "$APP" "x-forwarded-port-value" "\$server_port"
}
fn-nginx-x-forwarded-proto-value() {
declare desc="get the configured x-forwarded-proto value"
declare APP="$1"
fn-plugin-property-get-default "nginx" "$APP" "x-forwarded-proto-value" "\$scheme"
}
fn-nginx-x-forwarded-ssl() {
declare desc="get the configured x-forwarded-ssl value"
declare APP="$1"
fn-plugin-property-get-default "nginx" "$APP" "x-forwarded-ssl" ""
}
get_nginx_location() {
declare desc="check that nginx is at the expected location and return it"
fn-nginx-vhosts-nginx-location
@@ -182,7 +68,7 @@ nginx_logs() {
declare NGINX_LOGS_TYPE="${1#nginx:}" APP="$2"
local NGINX_LOGS_TYPE=${NGINX_LOGS_TYPE%-logs}
local NGINX_LOGS_PATH="$("fn-nginx-${NGINX_LOGS_TYPE}-log-path" "$APP")"
local NGINX_LOGS_PATH="$("fn-nginx-computed-${NGINX_LOGS_TYPE}-log-path" "$APP")"
if [[ "$NGINX_LOGS_PATH" == "off" ]] || [[ "$NGINX_LOGS_PATH" == "/dev/null" ]]; then
dokku_log_fail "$NGINX_LOGS_TYPE logs are disabled for this app"
@@ -226,7 +112,7 @@ get_custom_nginx_template() {
local IMAGE_TAG="$(get_running_image_tag "$APP")"
local IMAGE=$(get_deploying_app_image_name "$APP" "$IMAGE_TAG")
local NGINX_TEMPLATE_NAME="nginx.conf.sigil"
local DISABLE_CUSTOM_CONFIG="$(fn-plugin-property-get-default "nginx" "$APP" "disable-custom-config" "false")"
local DISABLE_CUSTOM_CONFIG="$(fn-nginx-computed-disable-custom-config "$APP")"
if [[ "$DISABLE_CUSTOM_CONFIG" == "true" ]]; then
return
@@ -420,15 +306,15 @@ nginx_build_config() {
GRPC_SUPPORTED="$(is_grpc_enabled "$NGINX_VERSION")"
local NGINX_LOG_ROOT="$(fn-nginx-log-root)"
local NGINX_ACCESS_LOG_FORMAT="$(fn-nginx-access-log-format "$APP")"
local NGINX_ACCESS_LOG_PATH="$(fn-nginx-access-log-path "$APP")"
local NGINX_ERROR_LOG_PATH="$(fn-nginx-error-log-path "$APP")"
local CLIENT_MAX_BODY_SIZE="$(fn-nginx-client-max-body-size "$APP")"
local PROXY_READ_TIMEOUT="$(fn-nginx-proxy-read-timeout "$APP")"
local PROXY_BUFFER_SIZE="$(fn-nginx-proxy-buffer-size "$APP")"
local PROXY_BUFFERING="$(fn-nginx-proxy-buffering "$APP")"
local PROXY_BUFFERS="$(fn-nginx-proxy-buffers "$APP")"
local PROXY_BUSY_BUFFERS_SIZE="$(fn-nginx-proxy-busy-buffers-size "$APP")"
local NGINX_ACCESS_LOG_FORMAT="$(fn-nginx-computed-access-log-format "$APP")"
local NGINX_ACCESS_LOG_PATH="$(fn-nginx-computed-access-log-path "$APP")"
local NGINX_ERROR_LOG_PATH="$(fn-nginx-computed-error-log-path "$APP")"
local CLIENT_MAX_BODY_SIZE="$(fn-nginx-computed-client-max-body-size "$APP")"
local PROXY_READ_TIMEOUT="$(fn-nginx-computed-proxy-read-timeout "$APP")"
local PROXY_BUFFER_SIZE="$(fn-nginx-computed-proxy-buffer-size "$APP")"
local PROXY_BUFFERING="$(fn-nginx-computed-proxy-buffering "$APP")"
local PROXY_BUFFERS="$(fn-nginx-computed-proxy-buffers "$APP")"
local PROXY_BUSY_BUFFERS_SIZE="$(fn-nginx-computed-proxy-busy-buffers-size "$APP")"
if [[ -z "$DOKKU_APP_LISTENERS" ]]; then
dokku_log_warn_quiet "No web listeners specified for $APP"
@@ -453,14 +339,12 @@ nginx_build_config() {
local NGINX_TEMPLATE_SOURCE="app-supplied"
fi
local NGINX_BIND_ADDRESS_IP4="$(fn-nginx-bind-address-ipv4 "$APP")"
local NGINX_BIND_ADDRESS_IP6="$(fn-nginx-bind-address-ipv6 "$APP")"
[[ -z "$NGINX_BIND_ADDRESS_IP6" ]] && NGINX_BIND_ADDRESS_IP6="::"
local PROXY_X_FORWARDED_FOR="$(fn-nginx-x-forwarded-for-value "$APP")"
local PROXY_X_FORWARDED_PORT="$(fn-nginx-x-forwarded-port-value "$APP")"
local PROXY_X_FORWARDED_PROTO="$(fn-nginx-x-forwarded-proto-value "$APP")"
local PROXY_X_FORWARDED_SSL="$(fn-nginx-x-forwarded-ssl "$APP")"
local NGINX_BIND_ADDRESS_IP4="$(fn-nginx-computed-bind-address-ipv4 "$APP")"
local NGINX_BIND_ADDRESS_IP6="$(fn-nginx-computed-bind-address-ipv6 "$APP")"
local PROXY_X_FORWARDED_FOR="$(fn-nginx-computed-x-forwarded-for-value "$APP")"
local PROXY_X_FORWARDED_PORT="$(fn-nginx-computed-x-forwarded-port-value "$APP")"
local PROXY_X_FORWARDED_PROTO="$(fn-nginx-computed-x-forwarded-proto-value "$APP")"
local PROXY_X_FORWARDED_SSL="$(fn-nginx-computed-x-forwarded-ssl "$APP")"
eval "$(config_export app "$APP")"
local SIGIL_PARAMS=(-f "$NGINX_TEMPLATE" APP="$APP" DOKKU_ROOT="$DOKKU_ROOT"

View File

@@ -0,0 +1,26 @@
package nginxvhosts
import (
"os"
)
func getLogRoot() string {
logRoot := "/var/log/nginx"
if isOpenRestyInstalled() {
logRoot = "/var/log/openresty"
}
return logRoot
}
func isOpenRestyInstalled() bool {
fi, err := os.Stat("/usr/bin/openresty")
if err != nil {
return false
}
if fi.IsDir() {
return false
}
return true
}

View File

@@ -1,3 +1,30 @@
module github.com/dokku/dokku/plugins/nginx-vhosts
go 1.21
require (
github.com/dokku/dokku/plugins/common v0.0.0-00010101000000-000000000000
github.com/spf13/pflag v1.0.5
)
require (
github.com/alexellis/go-execute/v2 v2.2.1 // indirect
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 // indirect
github.com/codeskyblue/go-sh v0.0.0-20190412065543-76bd3d59ff27 // indirect
github.com/fatih/color v1.16.0 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/kr/fs v0.1.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/melbahja/goph v1.4.0 // indirect
github.com/otiai10/copy v1.14.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pkg/sftp v1.13.5 // indirect
github.com/ryanuber/columnize v2.1.2+incompatible // indirect
golang.org/x/crypto v0.18.0 // indirect
golang.org/x/sync v0.6.0 // indirect
golang.org/x/sys v0.16.0 // indirect
)
replace github.com/dokku/dokku/plugins/common => ../common

View File

@@ -0,0 +1,95 @@
github.com/alexellis/go-execute/v2 v2.2.1 h1:4Ye3jiCKQarstODOEmqDSRCqxMHLkC92Bhse743RdOI=
github.com/alexellis/go-execute/v2 v2.2.1/go.mod h1:FMdRnUTiFAmYXcv23txrp3VYZfLo24nMpiIneWgKHTQ=
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 h1:sDMmm+q/3+BukdIpxwO365v/Rbspp2Nt5XntgQRXq8Q=
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM=
github.com/codeskyblue/go-sh v0.0.0-20190412065543-76bd3d59ff27 h1:HHUr4P/aKh4quafGxDT9LDasjGdlGkzLbfmmrlng3kA=
github.com/codeskyblue/go-sh v0.0.0-20190412065543-76bd3d59ff27/go.mod h1:VQx0hjo2oUeQkQUET7wRwradO6f+fN5jzXgB/zROxxE=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/melbahja/goph v1.4.0 h1:z0PgDbBFe66lRYl3v5dGb9aFgPy0kotuQ37QOwSQFqs=
github.com/melbahja/goph v1.4.0/go.mod h1:uG+VfK2Dlhk+O32zFrRlc3kYKTlV6+BtvPWd/kK7U68=
github.com/onsi/gomega v1.31.1 h1:KYppCUK+bUgAZwHOu7EXVBKyQA6ILvOESHkn/tgoqvo=
github.com/onsi/gomega v1.31.1/go.mod h1:y40C95dwAD1Nz36SsEnxvfFe8FFfNxzI5eJ0EYGyAy0=
github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU=
github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w=
github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks=
github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.13.5 h1:a3RLUqkyjYRtBTZJZ1VRrKbN3zhuPLlUc3sphVz81go=
github.com/pkg/sftp v1.13.5/go.mod h1:wHDZ0IZX6JcBYRK1TH9bcVq8G7TLpVHYIGJRFnmPfxg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/ryanuber/columnize v2.1.2+incompatible h1:C89EOx/XBWwIXl8wm8OPJBd7kPF25UfsK2X7Ph/zCAk=
github.com/ryanuber/columnize v2.1.2+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -37,7 +37,7 @@ fn-nginx-vhosts-migrate-nginx-conf-sigil() {
fn-plugin-property-write "nginx" "--global" "nginx-conf-sigil-migrated" "true"
for APP in $(dokku_apps "false" 2>/dev/null); do
local DISABLE_CUSTOM_CONFIG="$(fn-plugin-property-get-default "nginx" "$APP" "disable-custom-config" "false")"
local DISABLE_CUSTOM_CONFIG="$(fn-nginx-computed-disable-custom-config "$APP")"
if [[ "$DISABLE_CUSTOM_CONFIG" == "true" ]]; then
continue
fi

View File

@@ -4,6 +4,213 @@ set -eo pipefail
source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
source "$PLUGIN_CORE_AVAILABLE_PATH/common/property-functions"
fn-nginx-access-log-format() {
declare desc="get the configured access log format"
declare APP="$1"
fn-get-property --app "$APP" "access-log-format"
}
fn-nginx-computed-access-log-format() {
declare desc="get the computed access log format"
declare APP="$1"
fn-get-property --app "$APP" --computed "access-log-format"
}
fn-nginx-global-access-log-format() {
declare desc="get the global access log format"
declare APP="$1"
fn-get-property --app "$APP" --global "access-log-format"
}
fn-nginx-access-log-path() {
declare desc="get the configured access log path"
declare APP="$1"
fn-get-property --app "$APP" "access-log-path"
}
fn-nginx-computed-access-log-path() {
declare desc="get the computed access log path"
declare APP="$1"
fn-get-property --app "$APP" --computed "access-log-path"
}
fn-nginx-global-access-log-path() {
declare desc="get the global access log path"
declare APP="$1"
fn-get-property --app "$APP" --global "access-log-path"
}
fn-nginx-bind-address-ipv4() {
declare desc="get the configured ipv4 bind address"
declare APP="$1"
fn-get-property --app "$APP" "bind-address-ipv4"
}
fn-nginx-computed-bind-address-ipv4() {
declare desc="get the computed ipv4 bind address"
declare APP="$1"
fn-get-property --app "$APP" --computed "bind-address-ipv4"
}
fn-nginx-global-bind-address-ipv4() {
declare desc="get the global ipv4 bind address"
declare APP="$1"
fn-get-property --app "$APP" --global "bind-address-ipv4"
}
fn-nginx-bind-address-ipv6() {
declare desc="get the configured ipv6 bind address"
declare APP="$1"
fn-get-property --app "$APP" "bind-address-ipv6"
}
fn-nginx-computed-bind-address-ipv6() {
declare desc="get the computed ipv6 bind address"
declare APP="$1"
fn-get-property --app "$APP" --computed "bind-address-ipv6"
}
fn-nginx-global-bind-address-ipv6() {
declare desc="get the global ipv6 bind address"
declare APP="$1"
fn-get-property --app "$APP" --global "bind-address-ipv6"
}
fn-nginx-client-max-body-size() {
declare desc="get the configured client max body size"
declare APP="$1"
fn-get-property --app "$APP" "client-max-body-size"
}
fn-nginx-computed-client-max-body-size() {
declare desc="get the computed client max body size"
declare APP="$1"
fn-get-property --app "$APP" --computed "client-max-body-size"
}
fn-nginx-global-client-max-body-size() {
declare desc="get the global client max body size"
declare APP="$1"
fn-get-property --app "$APP" --global "client-max-body-size"
}
fn-nginx-disable-custom-config() {
declare desc="get the configured disable-custom-config value"
declare APP="$1"
fn-get-property --app "$APP" "disable-custom-config"
}
fn-nginx-computed-disable-custom-config() {
declare desc="get the computed disable-custom-config value"
declare APP="$1"
fn-get-property --app "$APP" --computed "disable-custom-config"
}
fn-nginx-global-disable-custom-config() {
declare desc="get the global disable-custom-config value"
declare APP="$1"
fn-get-property --app "$APP" --global "disable-custom-config"
}
fn-nginx-error-log-path() {
declare desc="get the configured error log path"
declare APP="$1"
fn-get-property --app "$APP" "error-log-path"
}
fn-nginx-computed-error-log-path() {
declare desc="get the computed error log path"
declare APP="$1"
fn-get-property --app "$APP" --computed "error-log-path"
}
fn-nginx-global-error-log-path() {
declare desc="get the global error log path"
declare APP="$1"
fn-get-property --app "$APP" --global "error-log-path"
}
fn-nginx-hsts-include-subdomains() {
declare APP="$1"
fn-get-property --app "$APP" "hsts-include-subdomains"
}
fn-nginx-computed-hsts-include-subdomains() {
declare APP="$1"
fn-get-property --app "$APP" --computed "hsts-include-subdomains"
}
fn-nginx-global-hsts-include-subdomains() {
declare APP="$1"
fn-get-property --app "$APP" --global "hsts-include-subdomains"
}
fn-nginx-hsts-max-age() {
declare APP="$1"
fn-get-property --app "$APP" "hsts-max-age"
}
fn-nginx-computed-hsts-max-age() {
declare APP="$1"
fn-get-property --app "$APP" --computed "hsts-max-age"
}
fn-nginx-global-hsts-max-age() {
declare APP="$1"
fn-get-property --app "$APP" --global "hsts-max-age"
}
fn-nginx-hsts-preload() {
declare APP="$1"
fn-get-property --app "$APP" "hsts-preload"
}
fn-nginx-computed-hsts-preload() {
declare APP="$1"
fn-get-property --app "$APP" --computed "hsts-preload"
}
fn-nginx-global-hsts-preload() {
declare APP="$1"
fn-get-property --app "$APP" --global "hsts-preload"
}
fn-nginx-hsts() {
declare APP="$1"
fn-get-property --app "$APP" "hsts"
}
fn-nginx-computed-hsts() {
declare APP="$1"
fn-get-property --app "$APP" --computed "hsts"
}
fn-nginx-global-hsts() {
declare APP="$1"
fn-get-property --app "$APP" --global "hsts"
}
fn-nginx-vhosts-last-visited-at() {
declare APP="$1"
local LOG_PATH=$(fn-nginx-access-log-path "$APP")
@@ -13,60 +220,223 @@ fn-nginx-vhosts-last-visited-at() {
fi
}
fn-nginx-hsts-include-subdomains() {
declare APP="$1"
fn-plugin-property-get-default "nginx" "$APP" "hsts-include-subdomains" "true"
}
fn-nginx-log-root() {
declare desc="get the nginx log root"
local NGINX_LOG_ROOT="/var/log/nginx"
fn-nginx-hsts-max-age() {
declare APP="$1"
fn-plugin-property-get-default "nginx" "$APP" "hsts-max-age" "15724800"
}
fn-nginx-hsts-preload() {
declare APP="$1"
fn-plugin-property-get-default "nginx" "$APP" "hsts-preload" "false"
}
fn-nginx-hsts-is-enabled() {
declare APP="$1"
local hsts_is_enabled="$(fn-plugin-property-get-default "nginx" "$APP" "hsts" "")"
if [[ "$hsts_is_enabled" == "" ]]; then
hsts_is_enabled="$(fn-plugin-property-get-default "nginx" "--global" "hsts" "true")"
fi
echo "$hsts_is_enabled"
}
fn-nginx-computed-nginx-conf-sigil-path() {
declare APP="$1"
local conf_sigil_path="$(fn-nginx-nginx-conf-sigil-path "$APP")"
if [[ "$conf_sigil_path" == "" ]]; then
conf_sigil_path="$(fn-nginx-global-nginx-conf-sigil-path)"
fi
echo "$conf_sigil_path"
}
fn-nginx-global-nginx-conf-sigil-path() {
declare APP="$1"
fn-plugin-property-get-default "nginx" "--global" "nginx-conf-sigil-path" "nginx.conf.sigil"
fn-nginx-vhosts-uses-openresty && NGINX_LOG_ROOT="/var/log/openresty"
echo "$NGINX_LOG_ROOT"
}
fn-nginx-nginx-conf-sigil-path() {
declare APP="$1"
fn-plugin-property-get-default "nginx" "$APP" "nginx-conf-sigil-path" ""
fn-get-property --app "$APP" "nginx-conf-sigil-path"
}
fn-nginx-computed-nginx-conf-sigil-path() {
declare APP="$1"
fn-get-property --computed --app "$APP" "nginx-conf-sigil-path"
}
fn-nginx-global-nginx-conf-sigil-path() {
declare APP="$1"
fn-get-property --global "nginx-conf-sigil-path"
}
fn-nginx-proxy-buffer-size() {
declare desc="get the configured proxy buffer size"
declare APP="$1"
fn-get-property --app "$APP" "proxy-buffer-size"
}
fn-nginx-computed-proxy-buffer-size() {
declare desc="get the computed proxy buffer size"
declare APP="$1"
fn-get-property --app "$APP" --computed "proxy-buffer-size"
}
fn-nginx-global-proxy-buffer-size() {
declare desc="get the global proxy buffer size"
declare APP="$1"
fn-get-property --app "$APP" --global "proxy-buffer-size"
}
fn-nginx-proxy-buffering() {
declare desc="get the configured proxy buffering"
declare APP="$1"
fn-get-property --app "$APP" "proxy-buffering"
}
fn-nginx-computed-proxy-buffering() {
declare desc="get the computed proxy buffering"
declare APP="$1"
fn-get-property --app "$APP" --computed "proxy-buffering"
}
fn-nginx-global-proxy-buffering() {
declare desc="get the global proxy buffering"
declare APP="$1"
fn-get-property --app "$APP" --global "proxy-buffering"
}
fn-nginx-proxy-buffers() {
declare desc="get the configured proxy buffers"
declare APP="$1"
fn-get-property --app "$APP" "proxy-buffers"
}
fn-nginx-computed-proxy-buffers() {
declare desc="get the computed proxy buffers"
declare APP="$1"
fn-get-property --app "$APP" --computed "proxy-buffers"
}
fn-nginx-global-proxy-buffers() {
declare desc="get the global proxy buffers"
declare APP="$1"
fn-get-property --app "$APP" --global "proxy-buffers"
}
fn-nginx-proxy-busy-buffers-size() {
declare desc="get the configured proxy busy buffers size"
declare APP="$1"
fn-get-property --app "$APP" "proxy-busy-buffers-size"
}
fn-nginx-computed-proxy-busy-buffers-size() {
declare desc="get the computed proxy busy buffers size"
declare APP="$1"
fn-get-property --app "$APP" --computed "proxy-busy-buffers-size"
}
fn-nginx-global-proxy-busy-buffers-size() {
declare desc="get the global proxy busy buffers size"
declare APP="$1"
fn-get-property --app "$APP" --global "proxy-busy-buffers-size"
}
fn-nginx-proxy-read-timeout() {
declare desc="get the configured proxy read timeout"
declare APP="$1"
fn-get-property --app "$APP" "proxy-read-timeout"
}
fn-nginx-computed-proxy-read-timeout() {
declare desc="get the computed proxy read timeout"
declare APP="$1"
fn-get-property --app "$APP" --computed "proxy-read-timeout"
}
fn-nginx-global-proxy-read-timeout() {
declare desc="get the global proxy read timeout"
declare APP="$1"
fn-get-property --app "$APP" --global "proxy-read-timeout"
}
fn-nginx-x-forwarded-for-value() {
declare desc="get the configured x-forwarded-for value"
declare APP="$1"
fn-get-property --app "$APP" "x-forwarded-for-value"
}
fn-nginx-computed-x-forwarded-for-value() {
declare desc="get the computed x-forwarded-for value"
declare APP="$1"
fn-get-property --app "$APP" --computed "x-forwarded-for-value"
}
fn-nginx-global-x-forwarded-for-value() {
declare desc="get the global x-forwarded-for value"
declare APP="$1"
fn-get-property --app "$APP" --global "x-forwarded-for-value"
}
fn-nginx-x-forwarded-port-value() {
declare desc="get the configured x-forwarded-port value"
declare APP="$1"
fn-get-property --app "$APP" "x-forwarded-port-value"
}
fn-nginx-computed-x-forwarded-port-value() {
declare desc="get the computed x-forwarded-port value"
declare APP="$1"
fn-get-property --app "$APP" --computed "x-forwarded-port-value"
}
fn-nginx-global-x-forwarded-port-value() {
declare desc="get the global x-forwarded-port value"
declare APP="$1"
fn-get-property --app "$APP" --global "x-forwarded-port-value"
}
fn-nginx-x-forwarded-proto-value() {
declare desc="get the configured x-forwarded-proto value"
declare APP="$1"
fn-get-property --app "$APP" "x-forwarded-proto-value"
}
fn-nginx-computed-x-forwarded-proto-value() {
declare desc="get the computed x-forwarded-proto value"
declare APP="$1"
fn-get-property --app "$APP" --computed "x-forwarded-proto-value"
}
fn-nginx-global-x-forwarded-proto-value() {
declare desc="get the global x-forwarded-proto value"
declare APP="$1"
fn-get-property --app "$APP" --global "x-forwarded-proto-value"
}
fn-nginx-x-forwarded-ssl() {
declare desc="get the configured x-forwarded-ssl value"
declare APP="$1"
fn-get-property --app "$APP" "x-forwarded-ssl"
}
fn-nginx-computed-x-forwarded-ssl() {
declare desc="get the computed x-forwarded-ssl value"
declare APP="$1"
fn-get-property --app "$APP" --computed "x-forwarded-ssl"
}
fn-nginx-global-x-forwarded-ssl() {
declare desc="get the global x-forwarded-ssl value"
declare APP="$1"
fn-get-property --app "$APP" --global "x-forwarded-ssl"
}
fn-nginx-vhosts-manage-hsts() {
declare APP="$1" SSL_ENABLED="$2"
local HSTS="$(fn-nginx-hsts-is-enabled "$APP")"
local HSTS_INCLUDE_SUBDOMAINS="$(fn-nginx-hsts-include-subdomains "$APP")"
local HSTS_MAX_AGE="$(fn-nginx-hsts-max-age "$APP")"
local HSTS_PRELOAD="$(fn-nginx-hsts-preload "$APP")"
local HSTS="$(fn-nginx-computed-hsts "$APP")"
local HSTS_INCLUDE_SUBDOMAINS="$(fn-nginx-computed-hsts-include-subdomains "$APP")"
local HSTS_MAX_AGE="$(fn-nginx-computed-hsts-max-age "$APP")"
local HSTS_PRELOAD="$(fn-nginx-computed-hsts-preload "$APP")"
local NGINX_HSTS_CONF="$DOKKU_ROOT/$APP/nginx.conf.d/hsts.conf"
local HSTS_TEMPLATE="$PLUGIN_AVAILABLE_PATH/nginx-vhosts/templates/hsts.conf.sigil"
@@ -166,10 +536,10 @@ fn-nginx-vhosts-nginx-init-cmd() {
esac
}
fn-get-pagesize() {
declare desc="return the underlying system's memory page size"
fn-get-property() {
declare desc="get a property from the nginx plugin"
"$PLUGIN_CORE_AVAILABLE_PATH/nginx-vhosts/pagesize"
"$PLUGIN_CORE_AVAILABLE_PATH/nginx-vhosts/nginx-property" "$@"
}
nginx_vhosts_validate_single_func() {

View File

@@ -0,0 +1,367 @@
package nginxvhosts
import (
"fmt"
"os"
"github.com/dokku/dokku/plugins/common"
)
func AppAccessLogFormat(appName string) string {
return common.PropertyGet("nginx", appName, "access-log-format")
}
func ComputedAccessLogFormat(appName string) string {
appValue := AppAccessLogFormat(appName)
if appValue != "" {
return appValue
}
return GlobalAccessLogFormat()
}
func GlobalAccessLogFormat() string {
return common.PropertyGet("nginx", "--global", "access-log-format")
}
func AppAccessLogPath(appName string) string {
return common.PropertyGet("nginx", appName, "access-log-path")
}
func ComputedAccessLogPath(appName string) string {
appValue := AppAccessLogPath(appName)
if appValue != "" {
return appValue
}
return GlobalAccessLogPath(appName)
}
func GlobalAccessLogPath(appName string) string {
defaultLogPath := fmt.Sprintf("%s/%s-access.log", getLogRoot(), appName)
return common.PropertyGetDefault("nginx", "--global", "access-log-path", defaultLogPath)
}
func AppBindAddressIPv4(appName string) string {
return common.PropertyGet("nginx", appName, "bind-address-ipv4")
}
func ComputedBindAddressIPv4(appName string) string {
appValue := AppBindAddressIPv4(appName)
if appValue != "" {
return appValue
}
return GlobalBindAddressIPv4()
}
func GlobalBindAddressIPv4() string {
return common.PropertyGet("nginx", "--global", "bind-address-ipv4")
}
func AppBindAddressIPv6(appName string) string {
return common.PropertyGet("nginx", appName, "bind-address-ipv6")
}
func ComputedBindAddressIPv6(appName string) string {
appValue := AppBindAddressIPv6(appName)
if appValue != "" {
return appValue
}
return GlobalBindAddressIPv6()
}
func GlobalBindAddressIPv6() string {
return common.PropertyGetDefault("nginx", "--global", "bind-address-ipv6", "::")
}
func AppClientMaxBodySize(appName string) string {
return common.PropertyGet("nginx", appName, "client-max-body-size")
}
func ComputedClientMaxBodySize(appName string) string {
appValue := AppClientMaxBodySize(appName)
if appValue != "" {
return appValue
}
return GlobalClientMaxBodySize()
}
func GlobalClientMaxBodySize() string {
return common.PropertyGetDefault("nginx", "--global", "client-max-body-size", "1m")
}
func AppDisableCustomConfig(appName string) string {
return common.PropertyGet("nginx", appName, "disable-custom-config")
}
func ComputedDisableCustomConfig(appName string) string {
appValue := AppDisableCustomConfig(appName)
if appValue != "" {
return appValue
}
return GlobalDisableCustomConfig()
}
func GlobalDisableCustomConfig() string {
return common.PropertyGetDefault("nginx", "--global", "disable-custom-config", "false")
}
func AppErrorLogPath(appName string) string {
return common.PropertyGet("nginx", appName, "error-log-path")
}
func ComputedErrorLogPath(appName string) string {
appValue := AppErrorLogPath(appName)
if appValue != "" {
return appValue
}
return GlobalErrorLogPath(appName)
}
func GlobalErrorLogPath(appName string) string {
defaultLogPath := fmt.Sprintf("%s/%s-error.log", getLogRoot(), appName)
return common.PropertyGetDefault("nginx", "--global", "error-log-path", defaultLogPath)
}
func AppHSTSIncludeSubdomains(appName string) string {
return common.PropertyGet("nginx", appName, "hsts-include-subdomains")
}
func ComputedHSTSIncludeSubdomains(appName string) string {
appValue := AppHSTSIncludeSubdomains(appName)
if appValue != "" {
return appValue
}
return GlobalHSTSIncludeSubdomains()
}
func GlobalHSTSIncludeSubdomains() string {
return common.PropertyGetDefault("nginx", "--global", "hsts-include-subdomains", "true")
}
func AppHSTSMaxAge(appName string) string {
return common.PropertyGet("nginx", appName, "hsts-max-age")
}
func ComputedHSTSMaxAge(appName string) string {
appValue := AppHSTSMaxAge(appName)
if appValue != "" {
return appValue
}
return GlobalHSTSMaxAge()
}
func GlobalHSTSMaxAge() string {
return common.PropertyGetDefault("nginx", "--global", "hsts-max-age", "15724800")
}
func AppHSTSPreload(appName string) string {
return common.PropertyGet("nginx", appName, "hsts-preload")
}
func ComputedHSTSPreload(appName string) string {
appValue := AppHSTSPreload(appName)
if appValue != "" {
return appValue
}
return GlobalHSTSPreload()
}
func GlobalHSTSPreload() string {
return common.PropertyGetDefault("nginx", "--global", "hsts-preload", "false")
}
func AppHSTS(appName string) string {
return common.PropertyGet("nginx", appName, "hsts")
}
func ComputedHSTS(appName string) string {
appValue := AppHSTS(appName)
if appValue != "" {
return appValue
}
return GlobalHSTS()
}
func GlobalHSTS() string {
return common.PropertyGetDefault("nginx", "--global", "hsts", "true")
}
func AppNginxConfSigilPath(appName string) string {
return common.PropertyGet("nginx", appName, "nginx-conf-sigil-path")
}
func ComputedNginxConfSigilPath(appName string) string {
appValue := AppNginxConfSigilPath(appName)
if appValue != "" {
return appValue
}
return GlobalNginxConfSigilPath()
}
func GlobalNginxConfSigilPath() string {
return common.PropertyGetDefault("nginx", "--global", "nginx-conf-sigil-path", "nginx.conf.sigil")
}
func AppProxyBufferSize(appName string) string {
return common.PropertyGet("nginx", appName, "proxy-buffer-size")
}
func ComputedProxyBufferSize(appName string) string {
appValue := AppProxyBufferSize(appName)
if appValue != "" {
return appValue
}
return GlobalProxyBufferSize()
}
func GlobalProxyBufferSize() string {
return common.PropertyGetDefault("nginx", "--global", "proxy-buffer-size", fmt.Sprintf("%dk", os.Getpagesize()/1024))
}
func AppProxyBuffering(appName string) string {
return common.PropertyGet("nginx", appName, "proxy-buffering")
}
func ComputedProxyBuffering(appName string) string {
appValue := AppProxyBuffering(appName)
if appValue != "" {
return appValue
}
return GlobalProxyBuffering()
}
func GlobalProxyBuffering() string {
return common.PropertyGetDefault("nginx", "--global", "proxy-buffering", "on")
}
func AppProxyBuffers(appName string) string {
return common.PropertyGet("nginx", appName, "proxy-buffers")
}
func ComputedProxyBuffers(appName string) string {
appValue := AppProxyBuffers(appName)
if appValue != "" {
return appValue
}
return GlobalProxyBuffers()
}
func GlobalProxyBuffers() string {
return common.PropertyGetDefault("nginx", "--global", "proxy-buffers", fmt.Sprintf("8 %dk", os.Getpagesize()/1024))
}
func AppProxyBusyBuffersSize(appName string) string {
return common.PropertyGet("nginx", appName, "proxy-busy-buffers-size")
}
func ComputedProxyBusyBuffersSize(appName string) string {
appValue := AppProxyBusyBuffersSize(appName)
if appValue != "" {
return appValue
}
return GlobalProxyBusyBuffersSize()
}
func GlobalProxyBusyBuffersSize() string {
return common.PropertyGetDefault("nginx", "--global", "proxy-busy-buffers-size", fmt.Sprintf("%dk", (os.Getpagesize()/1024)*2))
}
func AppProxyReadTimeout(appName string) string {
return common.PropertyGet("nginx", appName, "proxy-read-timeout")
}
func ComputedProxyReadTimeout(appName string) string {
appValue := AppProxyReadTimeout(appName)
if appValue != "" {
return appValue
}
return GlobalProxyReadTimeout()
}
func GlobalProxyReadTimeout() string {
return common.PropertyGetDefault("nginx", "--global", "proxy-read-timeout", "60s")
}
func AppXForwardedForValue(appName string) string {
return common.PropertyGet("nginx", appName, "x-forwarded-for-value")
}
func ComputedXForwardedForValue(appName string) string {
appValue := AppXForwardedForValue(appName)
if appValue != "" {
return appValue
}
return GlobalXForwardedForValue()
}
func GlobalXForwardedForValue() string {
return common.PropertyGetDefault("nginx", "--global", "x-forwarded-for-value", "$remote_addr")
}
func AppXForwardedPortValue(appName string) string {
return common.PropertyGet("nginx", appName, "x-forwarded-port-value")
}
func ComputedXForwardedPortValue(appName string) string {
appValue := AppXForwardedPortValue(appName)
if appValue != "" {
return appValue
}
return GlobalXForwardedPortValue()
}
func GlobalXForwardedPortValue() string {
return common.PropertyGetDefault("nginx", "--global", "x-forwarded-port-value", "$server_port")
}
func AppXForwardedProtoValue(appName string) string {
return common.PropertyGet("nginx", appName, "x-forwarded-proto-value")
}
func ComputedXForwardedProtoValue(appName string) string {
appValue := AppXForwardedProtoValue(appName)
if appValue != "" {
return appValue
}
return GlobalXForwardedProtoValue()
}
func GlobalXForwardedProtoValue() string {
return common.PropertyGetDefault("nginx", "--global", "x-forwarded-proto-value", "$scheme")
}
func AppXForwardedSSL(appName string) string {
return common.PropertyGet("nginx", appName, "x-forwarded-ssl")
}
func ComputedXForwardedSSL(appName string) string {
appValue := AppXForwardedSSL(appName)
if appValue != "" {
return appValue
}
return GlobalXForwardedSSL()
}
func GlobalXForwardedSSL() string {
return common.PropertyGetDefault("nginx", "--global", "x-forwarded-ssl", "")
}

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core nginx-vhosts plugin"
version = "0.33.3"
version = "0.33.4"
[plugin.config]

View File

@@ -0,0 +1,183 @@
package main
import (
"fmt"
"os"
nginx_vhosts "github.com/dokku/dokku/plugins/nginx-vhosts"
flag "github.com/spf13/pflag"
)
func main() {
args := flag.NewFlagSet("ps:inspect", flag.ExitOnError)
appName := args.String("app", "", "app: the app to inspect")
global := args.Bool("global", false, "global: inspect global property")
computed := args.Bool("computed", false, "computed: inspect computed property")
err := args.Parse(os.Args[1:])
if err != nil {
os.Exit(1)
}
property := args.Arg(0)
var value string
if *computed {
value = computedValue(*appName, property)
} else if *global {
value = globalValue(*appName, property)
} else {
value = appValue(*appName, property)
}
fmt.Print(value)
}
func appValue(appName string, property string) string {
var value string
switch property {
case "access-log-format":
value = nginx_vhosts.AppAccessLogFormat(appName)
case "access-log-path":
value = nginx_vhosts.AppAccessLogPath(appName)
case "bind-address-ipv4":
value = nginx_vhosts.AppBindAddressIPv4(appName)
case "bind-address-ipv6":
value = nginx_vhosts.AppBindAddressIPv6(appName)
case "client-max-body-size":
value = nginx_vhosts.AppClientMaxBodySize(appName)
case "disable-custom-config":
value = nginx_vhosts.AppDisableCustomConfig(appName)
case "error-log-path":
value = nginx_vhosts.AppErrorLogPath(appName)
case "hsts-include-subdomains":
value = nginx_vhosts.AppHSTSIncludeSubdomains(appName)
case "hsts-max-age":
value = nginx_vhosts.AppHSTSMaxAge(appName)
case "hsts-preload":
value = nginx_vhosts.AppHSTSPreload(appName)
case "hsts":
value = nginx_vhosts.AppHSTS(appName)
case "nginx-conf-sigil-path":
value = nginx_vhosts.AppNginxConfSigilPath(appName)
case "proxy-buffer-size":
value = nginx_vhosts.AppProxyBufferSize(appName)
case "proxy-buffering":
value = nginx_vhosts.AppProxyBuffering(appName)
case "proxy-buffers":
value = nginx_vhosts.AppProxyBuffers(appName)
case "proxy-busy-buffers-size":
value = nginx_vhosts.AppProxyBusyBuffersSize(appName)
case "proxy-read-timeout":
value = nginx_vhosts.AppProxyReadTimeout(appName)
case "x-forwarded-for-value":
value = nginx_vhosts.AppXForwardedForValue(appName)
case "x-forwarded-port-value":
value = nginx_vhosts.AppXForwardedPortValue(appName)
case "x-forwarded-proto-value":
value = nginx_vhosts.AppXForwardedProtoValue(appName)
case "x-forwarded-ssl":
value = nginx_vhosts.AppXForwardedSSL(appName)
}
return value
}
func computedValue(appName string, property string) string {
var value string
switch property {
case "access-log-format":
value = nginx_vhosts.ComputedAccessLogFormat(appName)
case "access-log-path":
value = nginx_vhosts.ComputedAccessLogPath(appName)
case "bind-address-ipv4":
value = nginx_vhosts.ComputedBindAddressIPv4(appName)
case "bind-address-ipv6":
value = nginx_vhosts.ComputedBindAddressIPv6(appName)
case "client-max-body-size":
value = nginx_vhosts.ComputedClientMaxBodySize(appName)
case "disable-custom-config":
value = nginx_vhosts.ComputedDisableCustomConfig(appName)
case "error-log-path":
value = nginx_vhosts.ComputedErrorLogPath(appName)
case "hsts-include-subdomains":
value = nginx_vhosts.ComputedHSTSIncludeSubdomains(appName)
case "hsts-max-age":
value = nginx_vhosts.ComputedHSTSMaxAge(appName)
case "hsts-preload":
value = nginx_vhosts.ComputedHSTSPreload(appName)
case "hsts":
value = nginx_vhosts.ComputedHSTS(appName)
case "nginx-conf-sigil-path":
value = nginx_vhosts.ComputedNginxConfSigilPath(appName)
case "proxy-buffer-size":
value = nginx_vhosts.ComputedProxyBufferSize(appName)
case "proxy-buffering":
value = nginx_vhosts.ComputedProxyBuffering(appName)
case "proxy-buffers":
value = nginx_vhosts.ComputedProxyBuffers(appName)
case "proxy-busy-buffers-size":
value = nginx_vhosts.ComputedProxyBusyBuffersSize(appName)
case "proxy-read-timeout":
value = nginx_vhosts.ComputedProxyReadTimeout(appName)
case "x-forwarded-for-value":
value = nginx_vhosts.ComputedXForwardedForValue(appName)
case "x-forwarded-port-value":
value = nginx_vhosts.ComputedXForwardedPortValue(appName)
case "x-forwarded-proto-value":
value = nginx_vhosts.ComputedXForwardedProtoValue(appName)
case "x-forwarded-ssl":
value = nginx_vhosts.ComputedXForwardedSSL(appName)
}
return value
}
func globalValue(appName string, property string) string {
var value string
switch property {
case "access-log-format":
value = nginx_vhosts.GlobalAccessLogFormat()
case "access-log-path":
value = nginx_vhosts.GlobalAccessLogPath(appName)
case "bind-address-ipv4":
value = nginx_vhosts.GlobalBindAddressIPv4()
case "bind-address-ipv6":
value = nginx_vhosts.GlobalBindAddressIPv6()
case "client-max-body-size":
value = nginx_vhosts.GlobalClientMaxBodySize()
case "disable-custom-config":
value = nginx_vhosts.GlobalDisableCustomConfig()
case "error-log-path":
value = nginx_vhosts.GlobalErrorLogPath(appName)
case "hsts-include-subdomains":
value = nginx_vhosts.GlobalHSTSIncludeSubdomains()
case "hsts-max-age":
value = nginx_vhosts.GlobalHSTSMaxAge()
case "hsts-preload":
value = nginx_vhosts.GlobalHSTSPreload()
case "hsts":
value = nginx_vhosts.GlobalHSTS()
case "nginx-conf-sigil-path":
value = nginx_vhosts.GlobalNginxConfSigilPath()
case "proxy-buffer-size":
value = nginx_vhosts.GlobalProxyBufferSize()
case "proxy-buffering":
value = nginx_vhosts.GlobalProxyBuffering()
case "proxy-buffers":
value = nginx_vhosts.GlobalProxyBuffers()
case "proxy-busy-buffers-size":
value = nginx_vhosts.GlobalProxyBusyBuffersSize()
case "proxy-read-timeout":
value = nginx_vhosts.GlobalProxyReadTimeout()
case "x-forwarded-for-value":
value = nginx_vhosts.GlobalXForwardedForValue()
case "x-forwarded-port-value":
value = nginx_vhosts.GlobalXForwardedPortValue()
case "x-forwarded-proto-value":
value = nginx_vhosts.GlobalXForwardedProtoValue()
case "x-forwarded-ssl":
value = nginx_vhosts.GlobalXForwardedSSL()
}
return value
}

View File

@@ -6,5 +6,5 @@ import (
)
func main() {
fmt.Println(os.Getpagesize())
fmt.Println(os.Getpagesize() / 1024)
}

View File

@@ -154,7 +154,7 @@ fn-openresty-proxy-buffer-size() {
declare desc="get the configured proxy buffer size"
declare APP="$1"
fn-plugin-property-get-default "openresty" "$APP" "proxy-buffer-size" "$(fn-get-pagesize)"
fn-plugin-property-get-default "openresty" "$APP" "proxy-buffer-size" "$(fn-get-pagesize)k"
}
fn-openresty-proxy-buffering() {
@@ -168,14 +168,14 @@ fn-openresty-proxy-buffers() {
declare desc="get the configured proxy buffers"
declare APP="$1"
fn-plugin-property-get-default "openresty" "$APP" "proxy-buffers" "8 $(fn-get-pagesize)"
fn-plugin-property-get-default "openresty" "$APP" "proxy-buffers" "8 $(fn-get-pagesize)k"
}
fn-openresty-proxy-busy-buffers-size() {
declare desc="get the configured proxy busy buffers size"
declare APP="$1"
fn-plugin-property-get-default "openresty" "$APP" "proxy-busy-buffers-size" "$(($(fn-get-pagesize) * 2))"
fn-plugin-property-get-default "openresty" "$APP" "proxy-busy-buffers-size" "$(($(fn-get-pagesize) * 2))k"
}
fn-openresty-proxy-connect-timeout() {

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core openresty-vhosts plugin"
version = "0.33.3"
version = "0.33.4"
[plugin.config]

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core plugin plugin"
version = "0.33.3"
version = "0.33.4"
[plugin.config]

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core ports plugin"
version = "0.33.3"
version = "0.33.4"
[plugin.config]

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core proxy plugin"
version = "0.33.3"
version = "0.33.4"
[plugin.config]

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core ps plugin"
version = "0.33.3"
version = "0.33.4"
[plugin.config]

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core registry plugin"
version = "0.33.3"
version = "0.33.4"
[plugin.config]

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core repo plugin"
version = "0.33.3"
version = "0.33.4"
[plugin.config]

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core resource plugin"
version = "0.33.3"
version = "0.33.4"
[plugin.config]

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core run plugin"
version = "0.33.3"
version = "0.33.4"
[plugin.config]

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core scheduler-docker-local plugin"
version = "0.33.3"
version = "0.33.4"
[plugin.config]

View File

@@ -14,6 +14,9 @@ import (
appjson "github.com/dokku/dokku/plugins/app-json"
"github.com/dokku/dokku/plugins/common"
nginxvhosts "github.com/dokku/dokku/plugins/nginx-vhosts"
resty "github.com/go-resty/resty/v2"
"golang.org/x/sync/errgroup"
"gopkg.in/yaml.v3"
corev1 "k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
@@ -361,6 +364,103 @@ func extractStartCommand(input StartCommandInput) string {
return command
}
// getAnnotations retrieves annotations for a given app and process type
func getAnnotations(appName string, processType string) (ProcessAnnotations, error) {
annotations := ProcessAnnotations{}
certificateAnnotations, err := getAnnotation(appName, processType, "certificate")
if err != nil {
return annotations, err
}
annotations.CertificateAnnotations = certificateAnnotations
cronJobAnnotations, err := getAnnotation(appName, processType, "cronjob")
if err != nil {
return annotations, err
}
annotations.CronJobAnnotations = cronJobAnnotations
deploymentAnnotations, err := getAnnotation(appName, processType, "deployment")
if err != nil {
return annotations, err
}
annotations.DeploymentAnnotations = deploymentAnnotations
ingressAnnotations, err := getIngressAnnotations(appName, processType)
if err != nil {
return annotations, err
}
annotations.IngressAnnotations = ingressAnnotations
jobAnnotations, err := getAnnotation(appName, processType, "job")
if err != nil {
return annotations, err
}
annotations.JobAnnotations = jobAnnotations
podAnnotations, err := getAnnotation(appName, processType, "pod")
if err != nil {
return annotations, err
}
annotations.PodAnnotations = podAnnotations
secretAnnotations, err := getAnnotation(appName, processType, "secret")
if err != nil {
return annotations, err
}
annotations.SecretAnnotations = secretAnnotations
serviceAnnotations, err := getAnnotation(appName, processType, "service")
if err != nil {
return annotations, err
}
annotations.ServiceAnnotations = serviceAnnotations
serviceAccountAnnotations, err := getAnnotation(appName, processType, "serviceaccount")
if err != nil {
return annotations, err
}
annotations.ServiceAccountAnnotations = serviceAccountAnnotations
traefikIngressRouteAnnotations, err := getAnnotation(appName, processType, "traefik_ingressroute")
if err != nil {
return annotations, err
}
annotations.TraefikIngressRouteAnnotations = traefikIngressRouteAnnotations
traefikMiddlewareAnnotations, err := getAnnotation(appName, processType, "traefik_middleware")
if err != nil {
return annotations, err
}
annotations.TraefikMiddlewareAnnotations = traefikMiddlewareAnnotations
return annotations, nil
}
// getGlobalAnnotations retrieves global annotations for a given app
func getGlobalAnnotations(appName string) (ProcessAnnotations, error) {
return getAnnotations(appName, GlobalProcessType)
}
// getAnnotation retrieves an annotation for a given app, process type, and resource type
func getAnnotation(appName string, processType string, resourceType string) (map[string]string, error) {
annotations := map[string]string{}
annotationsList, err := common.PropertyListGet("scheduler-k3s", appName, fmt.Sprintf("%s.%s", processType, resourceType))
if err != nil {
return annotations, err
}
for _, annotation := range annotationsList {
parts := strings.SplitN(annotation, ": ", 2)
if len(parts) != 2 {
return annotations, fmt.Errorf("Invalid annotation format: %s", annotation)
}
annotations[parts[0]] = parts[1]
}
return annotations, nil
}
func getDeployTimeout(appName string) string {
return common.PropertyGetDefault("scheduler-k3s", appName, "deploy-timeout", "")
}
@@ -395,6 +495,179 @@ func getComputedImagePullSecrets(appName string) string {
return imagePullSecrets
}
func getGlobalIngressClass() string {
return common.PropertyGetDefault("scheduler-k3s", "global", "ingress-class", DefaultIngressClass)
}
func getIngressAnnotations(appName string, processType string) (map[string]string, error) {
type annotation struct {
annotation string
getter func(appName string) string
locationSnippet func(value string) string
serverSnippet func(value string) string
}
locationLines := []string{}
serverLines := []string{}
properties := map[string]annotation{
"access-log-path": {
getter: nginxvhosts.ComputedAccessLogPath,
serverSnippet: func(value string) string {
if value == "" {
return ""
}
return fmt.Sprintf("access_log %s;", value)
},
},
"bind-address-ipv4": {
getter: nginxvhosts.ComputedBindAddressIPv4,
},
"bind-address-ipv6": {
getter: nginxvhosts.ComputedBindAddressIPv6,
},
"client-max-body-size": {
annotation: "nginx.ingress.kubernetes.io/proxy-body-size",
getter: nginxvhosts.ComputedClientMaxBodySize,
},
"disable-custom-config": {
getter: nginxvhosts.ComputedDisableCustomConfig,
},
"error-log-path": {
getter: nginxvhosts.ComputedErrorLogPath,
serverSnippet: func(value string) string {
if value == "" {
return ""
}
return fmt.Sprintf("error_log %s;", value)
},
},
// todo: handle hsts properly
"hsts-include-subdomains": {
getter: nginxvhosts.ComputedHSTSIncludeSubdomains,
},
"hsts-max-age": {
getter: nginxvhosts.ComputedHSTSMaxAge,
},
"hsts-preload": {
getter: nginxvhosts.ComputedHSTSPreload,
},
"hsts": {
getter: nginxvhosts.ComputedHSTS,
},
"nginx-conf-sigil-path": {
getter: nginxvhosts.ComputedNginxConfSigilPath,
},
"proxy-buffer-size": {
annotation: "nginx.ingress.kubernetes.io/proxy-buffer-size",
getter: nginxvhosts.ComputedProxyBufferSize,
},
"proxy-buffering": {
annotation: "nginx.ingress.kubernetes.io/proxy-buffering",
getter: nginxvhosts.ComputedProxyBuffering,
},
"proxy-buffers": {
annotation: "nginx.ingress.kubernetes.io/proxy-buffers-number",
getter: nginxvhosts.ComputedProxyBuffers,
},
"proxy-busy-buffers-size": {
getter: nginxvhosts.ComputedProxyBusyBuffersSize,
locationSnippet: func(value string) string {
if value == "" {
return ""
}
return fmt.Sprintf("proxy_busy_buffers_size %s;", value)
},
},
"proxy-read-timeout": {
annotation: "nginx.ingress.kubernetes.io/proxy-read-timeout",
getter: nginxvhosts.ComputedProxyReadTimeout,
},
"x-forwarded-for-value": {
getter: nginxvhosts.ComputedXForwardedForValue,
locationSnippet: func(value string) string {
if value == "" {
return ""
}
return fmt.Sprintf("proxy_set_header X-Forwarded-For %s;", value)
},
},
"x-forwarded-port-value": {
getter: nginxvhosts.ComputedXForwardedPortValue,
locationSnippet: func(value string) string {
if value == "" {
return ""
}
return fmt.Sprintf("proxy_set_header X-Forwarded-Port %s;", value)
},
},
"x-forwarded-proto-value": {
getter: nginxvhosts.ComputedXForwardedProtoValue,
locationSnippet: func(value string) string {
if value == "" {
return ""
}
return fmt.Sprintf("proxy_set_header X-Forwarded-Proto %s;", value)
},
},
"x-forwarded-ssl": {
getter: nginxvhosts.ComputedXForwardedSSL,
locationSnippet: func(value string) string {
if value == "" {
return ""
}
return fmt.Sprintf("proxy_set_header X-Forwarded-SSL %s;", value)
},
},
}
annotations := map[string]string{}
for _, newKey := range properties {
if newKey.locationSnippet != nil {
locationLines = append(locationLines, newKey.locationSnippet(newKey.getter(appName)))
} else if newKey.serverSnippet != nil {
serverLines = append(serverLines, newKey.serverSnippet(newKey.getter(appName)))
} else if newKey.annotation != "" {
annotations[newKey.annotation] = newKey.getter(appName)
}
}
var locationSnippet string
for _, line := range locationLines {
if line != "" {
locationSnippet += line + "\n"
}
}
var serverSnippet string
for _, line := range serverLines {
if line != "" {
serverSnippet += line + "\n"
}
}
if locationSnippet != "" {
annotations["nginx.ingress.kubernetes.io/configuration-snippet"] = locationSnippet
}
if serverSnippet != "" {
annotations["nginx.ingress.kubernetes.io/server-snippet"] = serverSnippet
}
customAnnotations, err := getAnnotation(appName, processType, "deployment")
if err != nil {
return map[string]string{}, err
}
for key, value := range customAnnotations {
if _, ok := annotations[key]; ok {
common.LogWarn(fmt.Sprintf("Nginx-based annotation %s will be overwritten by custom annotation", key))
}
annotations[key] = value
}
return annotations, nil
}
func getLetsencryptServer(appName string) string {
return common.PropertyGetDefault("scheduler-k3s", appName, "letsencrypt-server", "")
}
@@ -655,7 +928,7 @@ func getStartCommand(input StartCommandInput) (StartCommandOutput, error) {
}, nil
}
func installHelmCharts(ctx context.Context, clientset KubernetesClient) error {
func installHelmCharts(ctx context.Context, clientset KubernetesClient, shouldInstall func(HelmChart) bool) error {
for _, repo := range HelmRepositories {
helmAgent, err := NewHelmAgent("default", DeployLogPrinter)
if err != nil {
@@ -669,6 +942,10 @@ func installHelmCharts(ctx context.Context, clientset KubernetesClient) error {
}
for _, chart := range HelmCharts {
if !shouldInstall(chart) {
continue
}
if chart.CreateNamespace {
namespace := corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
@@ -722,6 +999,120 @@ func installHelmCharts(ctx context.Context, clientset KubernetesClient) error {
return nil
}
func installHelperCommands(ctx context.Context) error {
urls := map[string]string{
"kubectx": "https://github.com/ahmetb/kubectx/releases/latest/download/kubectx",
"kubens": "https://github.com/ahmetb/kubectx/releases/latest/download/kubens",
}
client := resty.New()
for binaryName, url := range urls {
resp, err := client.R().
SetContext(ctx).
Get(url)
if err != nil {
return fmt.Errorf("Unable to download %s: %w", binaryName, err)
}
if resp == nil {
return fmt.Errorf("Missing response from %s download: %w", binaryName, err)
}
if resp.StatusCode() != 200 {
return fmt.Errorf("Invalid status code for %s: %d", binaryName, resp.StatusCode())
}
f, err := os.Create(filepath.Join("/usr/local/bin", binaryName))
if err != nil {
return fmt.Errorf("Unable to create %s: %w", binaryName, err)
}
if err := f.Close(); err != nil {
return fmt.Errorf("Unable to close %s file: %w", binaryName, err)
}
err = common.WriteStringToFile(common.WriteStringToFileInput{
Content: resp.String(),
Filename: f.Name(),
GroupName: "root",
Mode: os.FileMode(0755),
Username: "root",
})
if err != nil {
return fmt.Errorf("Unable to write %s to file: %w", binaryName, err)
}
fi, err := os.Stat(f.Name())
if err != nil {
return fmt.Errorf("Unable to get %s file size: %w", binaryName, err)
}
if fi.Size() == 0 {
return fmt.Errorf("Invalid %s filesize", binaryName)
}
}
return installHelm(ctx)
}
func installHelm(ctx context.Context) error {
client := resty.New()
resp, err := client.R().
SetContext(ctx).
Get("https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3")
if err != nil {
return fmt.Errorf("Unable to download helm installer: %w", err)
}
if resp == nil {
return fmt.Errorf("Missing response from helm installer download: %w", err)
}
if resp.StatusCode() != 200 {
return fmt.Errorf("Invalid status code for helm installer script: %d", resp.StatusCode())
}
f, err := os.CreateTemp("", "sample")
if err != nil {
return fmt.Errorf("Unable to create temporary file for helm installer: %w", err)
}
defer os.Remove(f.Name())
if err := f.Close(); err != nil {
return fmt.Errorf("Unable to close helm installer file: %w", err)
}
err = common.WriteStringToFile(common.WriteStringToFileInput{
Content: resp.String(),
Filename: f.Name(),
Mode: os.FileMode(0755),
})
if err != nil {
return fmt.Errorf("Unable to write helm installer to file: %w", err)
}
fi, err := os.Stat(f.Name())
if err != nil {
return fmt.Errorf("Unable to get helm installer file size: %w", err)
}
if fi.Size() == 0 {
return fmt.Errorf("Invalid helm installer filesize")
}
common.LogInfo2Quiet("Running helm installer")
installerCmd, err := common.CallExecCommand(common.ExecCommandInput{
Command: f.Name(),
StreamStdio: true,
})
if err != nil {
return fmt.Errorf("Unable to call helm installer command: %w", err)
}
if installerCmd.ExitCode != 0 {
return fmt.Errorf("Invalid exit code from helm installer command: %d", installerCmd.ExitCode)
}
return nil
}
func isK3sInstalled() error {
if !common.FileExists("/usr/local/bin/k3s") {
return fmt.Errorf("k3s binary is not available")
@@ -797,6 +1188,17 @@ func kubernetesNodeToNode(node v1.Node) Node {
}
}
func uninstallHelperCommands(ctx context.Context) error {
errs, _ := errgroup.WithContext(ctx)
errs.Go(func() error {
return os.RemoveAll("/usr/local/bin/kubectx")
})
errs.Go(func() error {
return os.RemoveAll("/usr/local/bin/kubens")
})
return errs.Wait()
}
func waitForPodBySelectorRunning(ctx context.Context, input WaitForPodBySelectorRunningInput) error {
pods, err := waitForPodToExist(ctx, WaitForPodToExistInput{
Clientset: input.Clientset,

View File

@@ -8,6 +8,7 @@ require (
github.com/dokku/dokku/plugins/common v0.0.0-00010101000000-000000000000
github.com/dokku/dokku/plugins/config v0.0.0-00010101000000-000000000000
github.com/dokku/dokku/plugins/cron v0.0.0-00010101000000-000000000000
github.com/dokku/dokku/plugins/nginx-vhosts v0.0.0-00010101000000-000000000000
github.com/fatih/color v1.16.0
github.com/go-openapi/jsonpointer v0.19.6
github.com/go-resty/resty/v2 v2.11.0
@@ -18,6 +19,7 @@ require (
github.com/ryanuber/columnize v2.1.2+incompatible
github.com/spf13/pflag v1.0.5
github.com/traefik/traefik/v2 v2.10.7
golang.org/x/sync v0.6.0
gopkg.in/yaml.v3 v3.0.1
helm.sh/helm/v3 v3.13.3
k8s.io/api v0.29.0
@@ -152,7 +154,6 @@ require (
golang.org/x/mod v0.14.0 // indirect
golang.org/x/net v0.19.0 // indirect
golang.org/x/oauth2 v0.12.0 // indirect
golang.org/x/sync v0.6.0 // indirect
golang.org/x/sys v0.16.0 // indirect
golang.org/x/term v0.16.0 // indirect
golang.org/x/text v0.14.0 // indirect
@@ -186,3 +187,5 @@ replace github.com/dokku/dokku/plugins/common => ../common
replace github.com/dokku/dokku/plugins/config => ../config
replace github.com/dokku/dokku/plugins/cron => ../cron
replace github.com/dokku/dokku/plugins/nginx-vhosts => ../nginx-vhosts

View File

@@ -117,6 +117,9 @@ func (k KubernetesClient) ApplyKubernetesManifest(ctx context.Context, input App
"-f",
input.Manifest,
},
Env: map[string]string{
"KUBECONFIG": KubeConfigPath,
},
StreamStdio: true,
})
if err != nil {

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core scheduler-k3s plugin"
version = "0.33.3"
version = "0.33.4"
[plugin.config]

View File

@@ -20,6 +20,7 @@ func ReportSingleApp(appName string, format string, infoFlag string) error {
"--scheduler-k3s-computed-letsencrypt-server": reportComputedLetsencryptServer,
"--scheduler-k3s-letsencrypt-server": reportLetsencryptServer,
"--scheduler-k3s-global-letsencrypt-server": reportGlobalLetsencryptServer,
"--scheduler-k3s-global-ingress-class": reportGlobalIngressClass,
"--scheduler-k3s-global-letsencrypt-email-prod": reportGlobalLetsencryptEmailProd,
"--scheduler-k3s-global-letsencrypt-email-stag": reportGlobalLetsencryptEmailStag,
"--scheduler-k3s-computed-namespace": reportComputedNamespace,
@@ -66,6 +67,10 @@ func reportGlobalImagePullSecrets(appName string) string {
return getGlobalImagePullSecrets()
}
func reportGlobalIngressClass(appName string) string {
return getGlobalIngressClass()
}
func reportComputedLetsencryptServer(appName string) string {
return getComputedLetsencryptServer(appName)
}

View File

@@ -28,6 +28,7 @@ var (
GlobalProperties = map[string]bool{
"deploy-timeout": true,
"image-pull-secrets": true,
"ingress-class": true,
"letsencrypt-server": true,
"letsencrypt-email-prod": true,
"letsencrypt-email-stag": true,
@@ -38,8 +39,9 @@ var (
}
)
const DefaultIngressClass = "traefik"
const GlobalProcessType = "--global"
const KubeConfigPath = "/etc/rancher/k3s/k3s.yaml"
const RegistryConfigPath = "/etc/rancher/k3s/registries.yaml"
var (
@@ -66,13 +68,13 @@ var KubernetesManifests = []Manifest{
}
type HelmChart struct {
ReleaseName string
ChartPath string
Namespace string
CreateNamespace bool
Version string
Namespace string
Path string
ReleaseName string
RepoURL string
Version string
}
var HelmCharts = []HelmChart{
@@ -100,6 +102,14 @@ var HelmCharts = []HelmChart{
RepoURL: "https://helm.traefik.io/traefik",
Version: "26.0.0",
},
{
ChartPath: "ingress-nginx",
CreateNamespace: true,
Namespace: "ingress-nginx",
ReleaseName: "ingress-nginx",
RepoURL: "https://kubernetes.github.io/ingress-nginx",
Version: "4.7.5",
},
}
type HelmRepository struct {

View File

@@ -1,13 +1,15 @@
package main
import (
"flag"
"fmt"
"os"
"strconv"
"strings"
"github.com/dokku/dokku/plugins/common"
scheduler_k3s "github.com/dokku/dokku/plugins/scheduler-k3s"
flag "github.com/spf13/pflag"
)
const (
@@ -18,6 +20,7 @@ Manage scheduler-k3s settings for an app
Additional commands:`
helpContent = `
scheduler-k3s:annotations:set <app|--global> <property> (<value>) [--process-type PROCESS_TYPE] <--resource-type RESOURCE_TYPE>, Set or clear an annotation for a given app/process-type/resource-type combination
scheduler-k3s:cluster-add [--insecure-allow-unknown-hosts] [--server-ip SERVER_IP] [--taint-scheduling] <ssh://user@host:port>, Adds a server node to a Dokku-managed cluster
scheduler-k3s:cluster-list [--format json|stdout], Lists all nodes in a Dokku-managed cluster
scheduler-k3s:cluster-remove [node-id], Removes client node to a Dokku-managed cluster
@@ -30,10 +33,11 @@ Additional commands:`
)
func main() {
flag.Usage = usage
flag.Parse()
cmd := ""
if len(os.Args) > 1 {
cmd = os.Args[1]
}
cmd := flag.Arg(0)
switch cmd {
case "scheduler-k3s", "scheduler-k3s:help":
usage()
@@ -47,6 +51,28 @@ func main() {
} else {
fmt.Print("\n scheduler-k3s, Manage scheduler-k3s settings for an app\n")
}
case "scheduler-k3s:annotations:set":
args := flag.NewFlagSet("scheduler-k3s:annotations:set", flag.ExitOnError)
global := args.Bool("global", false, "--global: set a global property")
processType := args.String("process-type", "", "--process-type: scope to process-type")
resourceType := args.String("resource-type", "", "--resource-type: scope to resource-type")
err := args.Parse(os.Args[2:])
if err != nil {
common.LogFailWithError(err)
}
appName := args.Arg(0)
property := args.Arg(1)
value := args.Arg(2)
if *global {
appName = "--global"
property = args.Arg(0)
value = args.Arg(1)
}
if err := scheduler_k3s.CommandAnnotationsSet(appName, *processType, *resourceType, property, value); err != nil {
common.LogFailWithError(err)
}
default:
dokkuNotImplementExitCode, err := strconv.Atoi(os.Getenv("DOKKU_NOT_IMPLEMENTED_EXIT"))
if err != nil {

View File

@@ -22,8 +22,9 @@ func main() {
args := flag.NewFlagSet("scheduler-k3s:initialize", flag.ExitOnError)
taintScheduling := args.Bool("taint-scheduling", false, "taint-scheduling: add a taint against scheduling app workloads")
serverIP := args.String("server-ip", "", "server-ip: IP address of the dokku server node")
ingressClass := args.String("ingress-class", "traefik", "ingress-class: ingress-class to use for all outbound traffic")
args.Parse(os.Args[2:])
err = scheduler_k3s.CommandInitialize(*serverIP, *taintScheduling)
err = scheduler_k3s.CommandInitialize(*ingressClass, *serverIP, *taintScheduling)
case "cluster-add":
args := flag.NewFlagSet("scheduler-k3s:cluster-add", flag.ExitOnError)
allowUknownHosts := args.Bool("insecure-allow-unknown-hosts", false, "insecure-allow-unknown-hosts: allow unknown hosts")

View File

@@ -8,6 +8,7 @@ import (
"net/url"
"os"
"os/signal"
"sort"
"strings"
"syscall"
@@ -16,8 +17,53 @@ import (
"github.com/ryanuber/columnize"
)
// CommandAnnotationsSet set or clear a scheduler-k3s annotation for an app
func CommandAnnotationsSet(appName string, processType string, resourceType string, key string, value string) error {
if resourceType == "" {
return fmt.Errorf("Missing resource-type")
}
if processType == "" {
processType = GlobalProcessType
}
property := fmt.Sprintf("%s.%s", processType, resourceType)
annotationsList, err := common.PropertyListGet("scheduler-k3s", appName, property)
if err != nil {
return fmt.Errorf("Unable to get property list: %w", err)
}
annotations := []string{}
for _, annotation := range annotationsList {
parts := strings.SplitN(annotation, ": ", 2)
if len(parts) != 2 {
return fmt.Errorf("Invalid annotation: %s", annotation)
}
if key == parts[0] {
continue
}
annotations = append(annotations, annotation)
}
if value != "" {
annotations = append(annotations, fmt.Sprintf("%s: %s", key, value))
}
sort.Strings(annotations)
if err := common.PropertyListWrite("scheduler-k3s", appName, property, annotations); err != nil {
return fmt.Errorf("Unable to write property list: %w", err)
}
return nil
}
// CommandInitialize initializes a k3s cluster on the local server
func CommandInitialize(serverIP string, taintScheduling bool) error {
func CommandInitialize(ingressClass string, serverIP string, taintScheduling bool) error {
if ingressClass != "nginx" && ingressClass != "traefik" {
return fmt.Errorf("Invalid ingress-class: %s", ingressClass)
}
if err := isK3sInstalled(); err == nil {
return fmt.Errorf("k3s already installed, cannot re-initialize k3s")
}
@@ -84,6 +130,7 @@ func CommandInitialize(serverIP string, taintScheduling bool) error {
common.LogInfo2Quiet("Downloading k3s installer")
client := resty.New()
resp, err := client.R().
SetContext(ctx).
Get("https://get.k3s.io")
if err != nil {
return fmt.Errorf("Unable to download k3s installer: %w", err)
@@ -106,9 +153,9 @@ func CommandInitialize(serverIP string, taintScheduling bool) error {
return fmt.Errorf("Unable to close k3s installer file: %w", err)
}
err = common.WriteSliceToFile(common.WriteSliceToFileInput{
err = common.WriteStringToFile(common.WriteStringToFileInput{
Content: resp.String(),
Filename: f.Name(),
Lines: strings.Split(resp.String(), "\n"),
Mode: os.FileMode(0755),
})
if err != nil {
@@ -176,6 +223,11 @@ func CommandInitialize(serverIP string, taintScheduling bool) error {
args = append(args, "--node-taint", "CriticalAddonsOnly=true:NoSchedule")
}
common.CommandPropertySet("scheduler-k3s", "--global", "ingress-class", ingressClass, DefaultProperties, GlobalProperties)
if ingressClass == "nginx" {
args = append(args, "--disable", "traefik")
}
common.LogInfo2Quiet("Running k3s installer")
installerCmd, err := common.CallExecCommand(common.ExecCommandInput{
Command: f.Name(),
@@ -215,6 +267,19 @@ func CommandInitialize(serverIP string, taintScheduling bool) error {
return fmt.Errorf("Unable to create kubernetes client: %w", err)
}
common.LogInfo2Quiet("Waiting for node to exist")
nodes, err := waitForNodeToExist(ctx, WaitForNodeToExistInput{
Clientset: clientset,
NodeName: nodeName,
RetryCount: 20,
})
if err != nil {
return fmt.Errorf("Error waiting for pod to exist: %w", err)
}
if len(nodes) == 0 {
return fmt.Errorf("Unable to find node after initializing cluster, node will not be annotated/labeled appropriately access registry secrets")
}
for _, manifest := range KubernetesManifests {
common.LogInfo2Quiet(fmt.Sprintf("Installing %s@%s", manifest.Name, manifest.Version))
err = clientset.ApplyKubernetesManifest(ctx, ApplyKubernetesManifestInput{
@@ -242,11 +307,27 @@ func CommandInitialize(serverIP string, taintScheduling bool) error {
}
common.LogInfo2Quiet("Installing helm charts")
err = installHelmCharts(ctx, clientset)
err = installHelmCharts(ctx, clientset, func(chart HelmChart) bool {
if chart.ChartPath == "traefik" && ingressClass == "nginx" {
return false
}
if chart.ChartPath == "nginx" && ingressClass == "traefik" {
return false
}
return true
})
if err != nil {
return fmt.Errorf("Unable to install helm charts: %w", err)
}
common.LogInfo2Quiet("Installing helper commands")
err = installHelperCommands(ctx)
if err != nil {
return fmt.Errorf("Unable to install helper commands: %w", err)
}
common.LogVerboseQuiet("Done")
return nil
@@ -709,5 +790,6 @@ func CommandUninstall() error {
return fmt.Errorf("Invalid exit code from k3s uninstaller command: %d", uninstallerCmd.ExitCode)
}
return nil
common.LogInfo2Quiet("Removing k3s dependencies")
return uninstallHelperCommands(context.Background())
}

View File

@@ -33,12 +33,13 @@ type AppValues struct {
}
type GlobalValues struct {
AppName string `yaml:"app_name"`
DeploymentID string `yaml:"deploment_id"`
Image GlobalImage `yaml:"image"`
Namespace string `yaml:"namespace"`
Network GlobalNetwork `yaml:"network"`
Secrets map[string]string `yaml:"secrets,omitempty"`
Annotations ProcessAnnotations `yaml:"annotations,omitempty"`
AppName string `yaml:"app_name"`
DeploymentID string `yaml:"deploment_id"`
Image GlobalImage `yaml:"image"`
Namespace string `yaml:"namespace"`
Network GlobalNetwork `yaml:"network"`
Secrets map[string]string `yaml:"secrets,omitempty"`
}
type GlobalImage struct {
@@ -54,6 +55,7 @@ type GlobalNetwork struct {
}
type ProcessValues struct {
Annotations ProcessAnnotations `yaml:"annotations,omitempty"`
Args []string `yaml:"args,omitempty"`
Cron ProcessCron `yaml:"cron,omitempty"`
Healthchecks ProcessHealthchecks `yaml:"healthchecks,omitempty"`
@@ -63,6 +65,20 @@ type ProcessValues struct {
Web ProcessWeb `yaml:"web,omitempty"`
}
type ProcessAnnotations struct {
CertificateAnnotations map[string]string `yaml:"certificate,omitempty"`
CronJobAnnotations map[string]string `yaml:"cronjob,omitempty"`
DeploymentAnnotations map[string]string `yaml:"deployment,omitempty"`
IngressAnnotations map[string]string `yaml:"ingress,omitempty"`
JobAnnotations map[string]string `yaml:"job,omitempty"`
PodAnnotations map[string]string `yaml:"pod,omitempty"`
SecretAnnotations map[string]string `yaml:"secret,omitempty"`
ServiceAccountAnnotations map[string]string `yaml:"serviceaccount,omitempty"`
ServiceAnnotations map[string]string `yaml:"service,omitempty"`
TraefikIngressRouteAnnotations map[string]string `yaml:"traefik_ingressroute,omitempty"`
TraefikMiddlewareAnnotations map[string]string `yaml:"traefik_middleware,omitempty"`
}
type ProcessHealthchecks struct {
Liveness ProcessHealthcheck `yaml:"liveness,omitempty"`
Readiness ProcessHealthcheck `yaml:"readiness,omitempty"`
@@ -229,7 +245,7 @@ func templateKubernetesJob(input Job) (batchv1.Job, error) {
n := 5
b := make([]byte, n)
if _, err := rand.Read(b); err != nil {
panic(err)
return batchv1.Job{}, fmt.Errorf("Error generating random suffix: %w", err)
}
suffix = strings.ToLower(fmt.Sprintf("%X", b))
}
@@ -238,6 +254,28 @@ func templateKubernetesJob(input Job) (batchv1.Job, error) {
podAnnotations := annotations
podAnnotations["kubectl.kubernetes.io/default-container"] = fmt.Sprintf("%s-%s", input.AppName, input.ProcessType)
globalAnnotations, err := getGlobalAnnotations(input.AppName)
if err != nil {
return batchv1.Job{}, fmt.Errorf("Error getting global annotations: %w", err)
}
for key, value := range globalAnnotations.JobAnnotations {
annotations[key] = value
}
for key, value := range globalAnnotations.PodAnnotations {
podAnnotations[key] = value
}
processAnnotations, err := getAnnotations(input.AppName, input.ProcessType)
if err != nil {
return batchv1.Job{}, fmt.Errorf("Error getting process annotations: %w", err)
}
for key, value := range processAnnotations.JobAnnotations {
annotations[key] = value
}
for key, value := range processAnnotations.PodAnnotations {
podAnnotations[key] = value
}
job := batchv1.Job{
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("%s-%s-%s", input.AppName, input.ProcessType, suffix),
@@ -277,7 +315,8 @@ func templateKubernetesJob(input Job) (batchv1.Job, error) {
WorkingDir: input.WorkingDir,
},
},
RestartPolicy: corev1.RestartPolicyNever,
RestartPolicy: corev1.RestartPolicyNever,
ServiceAccountName: input.AppName,
},
},
},

View File

@@ -1,5 +1,5 @@
{{- $processName := "PROCESS_NAME" }}
{{- $config := .Values.processes.PROCESS_NAME }}
{{- $config := index .Values.processes "PROCESS_NAME" }}
{{- if and $config.web.tls.enabled $config.web.domains }}
---
apiVersion: cert-manager.io/v1
@@ -7,6 +7,16 @@ kind: Certificate
metadata:
annotations:
dokku.com/managed: "true"
{{- if and $.Values.global.annotations $.Values.global.annotations.certificate }}
{{- range $k, $v := $.Values.global.annotations.certificate }}
{{ $k }}: {{ $v | quote }}
{{- end }}
{{- end }}
{{- if and $config.annotations $config.annotations.certificate }}
{{- range $k, $v := $config.annotations.certificate }}
{{ $k }}: {{ $v | quote }}
{{- end }}
{{- end }}
labels:
app.kubernetes.io/name: {{ $.Values.global.app_name }}-{{ $processName }}
app.kubernetes.io/part-of: {{ $.Values.global.app_name }}

View File

@@ -1,5 +1,5 @@
{{- $processName := "CRON_ID" }}
{{- $config := .Values.processes.CRON_ID }}
{{- $config := index .Values.processes "CRON_ID" }}
---
apiVersion: batch/v1
kind: CronJob
@@ -11,13 +11,23 @@ metadata:
dokku.com/job-suffix: {{ $config.cron.suffix }}
dokku.com/managed: "true"
kubectl.kubernetes.io/default-container: {{ $.Values.global.app_name }}-cron
{{- if and $.Values.global.annotations $.Values.global.annotations.cronjob }}
{{- range $k, $v := $.Values.global.annotations.cronjob }}
{{ $k }}: {{ $v | quote }}
{{- end }}
{{- end }}
{{- if and $config.annotations $config.annotations.cronjob }}
{{- range $k, $v := $config.annotations.cronjob }}
{{ $k }}: {{ $v | quote }}
{{- end }}
{{- end }}
labels:
app.kubernetes.io/instance: {{ $.Values.global.app_name }}-cron-{{ $config.cron.suffix }}
app.kubernetes.io/name: cron
app.kubernetes.io/part-of: {{ $.Values.global.app_name }}
dokku.com/cron-id: {{ $config.cron.id }}
name: {{ $.Values.global.app_name }}-cron-{{ $config.cron.suffix }}
namespace: default
namespace: {{ $.Values.global.namespace }}
spec:
concurrencyPolicy: Allow
failedJobsHistoryLimit: 10
@@ -30,6 +40,16 @@ spec:
dokku.com/job-suffix: {{ $config.cron.suffix }}
dokku.com/managed: "true"
kubectl.kubernetes.io/default-container: {{ $.Values.global.app_name }}-cron
{{- if and $.Values.global.annotations $.Values.global.annotations.pod }}
{{- range $k, $v := $.Values.global.annotations.pod }}
{{ $k }}: {{ $v | quote }}
{{- end }}
{{- end }}
{{- if and $config.annotations $config.annotations.pod }}
{{- range $k, $v := $config.annotations.pod }}
{{ $k }}: {{ $v | quote }}
{{- end }}
{{- end }}
labels:
app.kubernetes.io/instance: {{ $.Values.global.app_name }}-cron-{{ $config.cron.suffix }}
app.kubernetes.io/name: cron
@@ -94,6 +114,7 @@ spec:
imagePullSecrets: {{ $.Values.global.image.image_pull_secrets }}
{{- end }}
restartPolicy: Never
serviceAccountName: {{ $.Values.global.app_name }}
schedule: {{ $config.cron.schedule }}
startingDeadlineSeconds: 60
successfulJobsHistoryLimit: 10

View File

@@ -1,5 +1,5 @@
{{- $processName := "PROCESS_NAME" }}
{{- $config := .Values.processes.PROCESS_NAME }}
{{- $config := index .Values.processes "PROCESS_NAME" }}
---
apiVersion: apps/v1
kind: Deployment
@@ -9,12 +9,22 @@ metadata:
dokku.com/builder-type: {{ $.Values.global.image.type }}
dokku.com/managed: "true"
kubectl.kubernetes.io/default-container: {{ $.Values.global.app_name }}-{{ $processName }}
{{- if and $.Values.global.annotations $.Values.global.annotations.deployment }}
{{- range $k, $v := $.Values.global.annotations.deployment }}
{{ $k }}: {{ $v | quote }}
{{- end }}
{{- end }}
{{- if and $config.annotations $config.annotations.deployment }}
{{- range $k, $v := $config.annotations.deployment }}
{{ $k }}: {{ $v | quote }}
{{- end }}
{{- end }}
labels:
app.kubernetes.io/instance: {{ $.Values.global.app_name }}-{{ $processName }}
app.kubernetes.io/name: {{ $processName }}
app.kubernetes.io/part-of: {{ $.Values.global.app_name }}
name: {{ $.Values.global.app_name }}-{{ $processName }}
namespace: default
namespace: {{ $.Values.global.namespace }}
spec:
replicas: {{ $config.replicas }}
revisionHistoryLimit: 5
@@ -31,6 +41,16 @@ spec:
dokku.com/builder-type: {{ $.Values.global.image.type }}
dokku.com/managed: "true"
kubectl.kubernetes.io/default-container: {{ $.Values.global.app_name }}-{{ $processName }}
{{- if and $.Values.global.annotations $.Values.global.annotations.pod }}
{{- range $k, $v := $.Values.global.annotations.pod }}
{{ $k }}: {{ $v | quote }}
{{- end }}
{{- end }}
{{- if and $config.annotations $config.annotations.pod }}
{{- range $k, $v := $config.annotations.pod }}
{{ $k }}: {{ $v | quote }}
{{- end }}
{{- end }}
labels:
app.kubernetes.io/instance: {{ $.Values.global.app_name }}-{{ $processName }}
app.kubernetes.io/name: {{ $processName }}
@@ -100,3 +120,4 @@ spec:
{{- if $.Values.global.image.image_pull_secrets }}
imagePullSecrets: {{ $.Values.global.image.image_pull_secrets }}
{{- end }}
serviceAccountName: {{ $.Values.global.app_name }}

View File

@@ -1,5 +1,5 @@
{{- $processName := "PROCESS_NAME" }}
{{- $config := .Values.processes.PROCESS_NAME }}
{{- $config := index .Values.processes "PROCESS_NAME" }}
{{- if and $config.web.domains (eq $.Values.global.network.ingress_class "traefik") }}
---
apiVersion: traefik.io/v1alpha1
@@ -7,6 +7,16 @@ kind: Middleware
metadata:
annotations:
dokku.com/managed: "true"
{{- if and $.Values.global.annotations $.Values.global.annotations.traefik_middleware }}
{{- range $k, $v := $.Values.global.annotations.traefik_middleware }}
{{ $k }}: {{ $v | quote }}
{{- end }}
{{- end }}
{{- if and $config.annotations $config.annotations.traefik_middleware }}
{{- range $k, $v := $config.annotations.traefik_middleware }}
{{ $k }}: {{ $v | quote }}
{{- end }}
{{- end }}
labels:
app.kubernetes.io/instance: {{ $.Values.global.app_name }}-{{ $processName }}-redirect-to-https
app.kubernetes.io/name: {{ $processName }}

View File

@@ -1,5 +1,5 @@
{{- $processName := "PROCESS_NAME" }}
{{- $config := .Values.processes.PROCESS_NAME }}
{{- $config := index .Values.processes "PROCESS_NAME" }}
{{- if and $config.web.domains (eq $.Values.global.network.ingress_class "traefik") }}
{{- range $pdx, $port_map := $config.web.port_maps }}
---
@@ -8,6 +8,16 @@ kind: IngressRoute
metadata:
annotations:
dokku.com/managed: "true"
{{- if and $.Values.global.annotations $.Values.global.annotations.traefik_ingressroute }}
{{- range $k, $v := $.Values.global.annotations.traefik_ingressroute }}
{{ $k }}: {{ $v | quote }}
{{- end }}
{{- end }}
{{- if and $config.annotations $config.annotations.traefik_ingressroute }}
{{- range $k, $v := $config.annotations.traefik_ingressroute }}
{{ $k }}: {{ $v | quote }}
{{- end }}
{{- end }}
labels:
app.kubernetes.io/instance: {{ $.Values.global.app_name }}-{{ $processName }}
app.kubernetes.io/name: {{ $processName }}

View File

@@ -0,0 +1,51 @@
{{- $processName := "PROCESS_NAME" }}
{{- $config := index .Values.processes "PROCESS_NAME" }}
{{- if and $config.web.domains (eq $.Values.global.network.ingress_class "nginx") }}
{{- range $pdx, $port_map := $config.web.port_maps }}
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
dokku.com/managed: "true"
{{- if and $.Values.global.annotations $.Values.global.annotations.deployment }}
{{- range $k, $v := $.Values.global.annotations.deployment }}
{{ $k }}: {{ $v | quote }}
{{- end }}
{{- end }}
{{- if and $config.annotations $config.annotations.ingress }}
{{- range $k, $v := $config.annotations.ingress }}
{{ $k }}: {{ $v | quote }}
{{- end }}
{{- end }}
labels:
app.kubernetes.io/instance: {{ $.Values.global.app_name }}-{{ $processName }}
app.kubernetes.io/name: {{ $processName }}
app.kubernetes.io/part-of: {{ $.Values.global.app_name }}
name: {{ $.Values.global.app_name }}-{{ $processName }}-{{ $port_map.name }}
namespace: {{ $.Values.global.namespace }}
spec:
ingressClassName: nginx
{{- if and $config.web.tls.enabled (eq $port_map.scheme "https") }}
tls:
- hosts:
{{- range $ddx, $domain := $config.web.domains }}
- {{ $domain | quote }}
{{- end }}
secretName: tls-{{ $.Values.global.app_name }}-{{ $processName }}
{{- end }}
rules:
{{- range $ddx, $domain := $config.web.domains }}
- host: {{ $domain | quote }}
http:
paths:
- backend:
service:
name: {{ $.Values.global.app_name }}-{{ $processName }}
port:
name: {{ $port_map.name }}
pathType: ImplementationSpecific
path: /
{{- end }}
{{- end }}
{{- end }}

View File

@@ -4,6 +4,11 @@ metadata:
annotations:
app.kubernetes.io/version: {{ $.Values.global.deploment_id | quote }}
dokku.com/managed: "true"
{{- if and $.Values.global.annotations $.Values.global.annotations.secret }}
{{- range $k, $v := $.Values.global.annotations.secret }}
{{ $k }}: {{ $v | quote }}
{{- end }}
{{- end }}
labels:
app.kubernetes.io/instance: env-{{ $.Values.global.app_name }}.{{ $.Values.global.deploment_id }}
app.kubernetes.io/name: env-{{ $.Values.global.app_name }}
@@ -13,4 +18,4 @@ metadata:
{{- with .Values.global.secrets }}
data:
{{- toYaml . | nindent 2 }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,16 @@
apiVersion: v1
kind: ServiceAccount
metadata:
annotations:
dokku.com/managed: "true"
{{- if and $.Values.global.annotations $.Values.global.annotations.serviceaccount }}
{{- range $k, $v := $.Values.global.annotations.serviceaccount }}
{{ $k }}: {{ $v | quote }}
{{- end }}
{{- end }}
labels:
app.kubernetes.io/instance: service-account
app.kubernetes.io/name: service-account
app.kubernetes.io/part-of: "{{ $.Values.global.app_name }}"
name: "{{ $.Values.global.app_name }}"
namespace: "{{ $.Values.global.namespace }}"

Some files were not shown because too many files have changed in this diff Show More