Compare commits
66 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ccf713c104 | ||
|
|
bc4c25dccf | ||
|
|
cd4f9db95b | ||
|
|
4cc52179b5 | ||
|
|
ceaab97d99 | ||
|
|
a8c1e8a88a | ||
|
|
2432ad49a5 | ||
|
|
814953358b | ||
|
|
0adaa90fbd | ||
|
|
8576a1280a | ||
|
|
6692beae11 | ||
|
|
41a89bbb5d | ||
|
|
afb4b50bb3 | ||
|
|
03ae424a27 | ||
|
|
e9986b5775 | ||
|
|
980979ef87 | ||
|
|
7dbd9adc33 | ||
|
|
416a119904 | ||
|
|
1af36b609d | ||
|
|
5674a29211 | ||
|
|
b35d6df67c | ||
|
|
81286a0673 | ||
|
|
ea4767f2d8 | ||
|
|
4c0359bb28 | ||
|
|
a59480f97d | ||
|
|
bacfbe328b | ||
|
|
e7a3766e60 | ||
|
|
8711a1c801 | ||
|
|
f5f583b12b | ||
|
|
8b2f37feea | ||
|
|
408acbe14e | ||
|
|
e08e1df026 | ||
|
|
77d5a27fda | ||
|
|
79c0caaa7b | ||
|
|
d7ff8552d1 | ||
|
|
96e79487b9 | ||
|
|
ea55071751 | ||
|
|
4abd1f6b24 | ||
|
|
5e1089973e | ||
|
|
666f953715 | ||
|
|
d4b445985b | ||
|
|
6625846ecc | ||
|
|
1689ddd6d3 | ||
|
|
fe8b7ec451 | ||
|
|
31a73e4dab | ||
|
|
238535be0c | ||
|
|
d02c04c2a5 | ||
|
|
ff319428bf | ||
|
|
41f5283b1a | ||
|
|
e30650a620 | ||
|
|
2e57199318 | ||
|
|
976be62a1b | ||
|
|
7d37466750 | ||
|
|
5b922a93c7 | ||
|
|
2703101068 | ||
|
|
28532a56a9 | ||
|
|
dce37fa140 | ||
|
|
3d99ea63bd | ||
|
|
60d9a3ca34 | ||
|
|
06429a384e | ||
|
|
a277c5da1b | ||
|
|
6622de5998 | ||
|
|
0c83356542 | ||
|
|
b333334e72 | ||
|
|
f287f32682 | ||
|
|
c49322bd34 |
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
54
HISTORY.md
54
HISTORY.md
@@ -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:
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
2
debian/control
vendored
@@ -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
284
docs/_build/hooks.py
vendored
@@ -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
|
||||
"""
|
||||
|
||||
6
docs/_build/requirements.txt
vendored
6
docs/_build/requirements.txt
vendored
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -30,6 +30,6 @@
|
||||
"0.30.11",
|
||||
"0.31.5",
|
||||
"0.32.4",
|
||||
"0.33.3"
|
||||
"0.33.4"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core standard plugin"
|
||||
version = "0.33.3"
|
||||
version = "0.33.4"
|
||||
[plugin.config]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core events logging plugin"
|
||||
version = "0.33.3"
|
||||
version = "0.33.4"
|
||||
[plugin.config]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core app-json plugin"
|
||||
version = "0.33.3"
|
||||
version = "0.33.4"
|
||||
[plugin.config]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core apps plugin"
|
||||
version = "0.33.3"
|
||||
version = "0.33.4"
|
||||
[plugin.config]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core builder-dockerfile plugin"
|
||||
version = "0.33.3"
|
||||
version = "0.33.4"
|
||||
[plugin.config]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core builder-herokuish plugin"
|
||||
version = "0.33.3"
|
||||
version = "0.33.4"
|
||||
[plugin.config]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core builder-lambda plugin"
|
||||
version = "0.33.3"
|
||||
version = "0.33.4"
|
||||
[plugin.config]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core builder-nixpacks plugin"
|
||||
version = "0.33.3"
|
||||
version = "0.33.4"
|
||||
[plugin.config]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core builder-null plugin"
|
||||
version = "0.33.3"
|
||||
version = "0.33.4"
|
||||
[plugin.config]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core builder-pack plugin"
|
||||
version = "0.33.3"
|
||||
version = "0.33.4"
|
||||
[plugin.config]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core builder plugin"
|
||||
version = "0.33.3"
|
||||
version = "0.33.4"
|
||||
[plugin.config]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core buildpacks plugin"
|
||||
version = "0.33.3"
|
||||
version = "0.33.4"
|
||||
[plugin.config]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core caddy-vhosts plugin"
|
||||
version = "0.33.3"
|
||||
version = "0.33.4"
|
||||
[plugin.config]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core certificate management plugin"
|
||||
version = "0.33.3"
|
||||
version = "0.33.4"
|
||||
[plugin.config]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core checks plugin"
|
||||
version = "0.33.3"
|
||||
version = "0.33.4"
|
||||
[plugin.config]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core common plugin"
|
||||
version = "0.33.3"
|
||||
version = "0.33.4"
|
||||
[plugin.config]
|
||||
|
||||
@@ -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),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core config plugin"
|
||||
version = "0.33.3"
|
||||
version = "0.33.4"
|
||||
[plugin.config]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core cron plugin"
|
||||
version = "0.33.3"
|
||||
version = "0.33.4"
|
||||
[plugin.config]
|
||||
|
||||
@@ -39,7 +39,7 @@ trigger-docker-options-docker-args() {
|
||||
;;
|
||||
*)
|
||||
case "$IMAGE_SOURCE_TYPE" in
|
||||
dockerfile)
|
||||
dockerfile | nixpacks)
|
||||
case "$line" in
|
||||
--link* | -v* | --volume*)
|
||||
continue
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core docker-options plugin"
|
||||
version = "0.33.3"
|
||||
version = "0.33.4"
|
||||
[plugin.config]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core domains plugin"
|
||||
version = "0.33.3"
|
||||
version = "0.33.4"
|
||||
[plugin.config]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core enter plugin"
|
||||
version = "0.33.3"
|
||||
version = "0.33.4"
|
||||
[plugin.config]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core git plugin"
|
||||
version = "0.33.3"
|
||||
version = "0.33.4"
|
||||
[plugin.config]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core haproxy-vhosts plugin"
|
||||
version = "0.33.3"
|
||||
version = "0.33.4"
|
||||
[plugin.config]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core logs plugin"
|
||||
version = "0.33.3"
|
||||
version = "0.33.4"
|
||||
[plugin.config]
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
25
plugins/logs/templates/compose.yml.tmpl
Normal file
25
plugins/logs/templates/compose.yml.tmpl
Normal 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"
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core network plugin"
|
||||
version = "0.33.3"
|
||||
version = "0.33.4"
|
||||
[plugin.config]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
26
plugins/nginx-vhosts/functions.go
Normal file
26
plugins/nginx-vhosts/functions.go
Normal 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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
367
plugins/nginx-vhosts/nginx_vhosts.go
Normal file
367
plugins/nginx-vhosts/nginx_vhosts.go
Normal 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", "")
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core nginx-vhosts plugin"
|
||||
version = "0.33.3"
|
||||
version = "0.33.4"
|
||||
[plugin.config]
|
||||
|
||||
183
plugins/nginx-vhosts/src/nginx-property/nginx-property.go
Normal file
183
plugins/nginx-vhosts/src/nginx-property/nginx-property.go
Normal 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
|
||||
}
|
||||
@@ -6,5 +6,5 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(os.Getpagesize())
|
||||
fmt.Println(os.Getpagesize() / 1024)
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core openresty-vhosts plugin"
|
||||
version = "0.33.3"
|
||||
version = "0.33.4"
|
||||
[plugin.config]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core plugin plugin"
|
||||
version = "0.33.3"
|
||||
version = "0.33.4"
|
||||
[plugin.config]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core ports plugin"
|
||||
version = "0.33.3"
|
||||
version = "0.33.4"
|
||||
[plugin.config]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core proxy plugin"
|
||||
version = "0.33.3"
|
||||
version = "0.33.4"
|
||||
[plugin.config]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core ps plugin"
|
||||
version = "0.33.3"
|
||||
version = "0.33.4"
|
||||
[plugin.config]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core registry plugin"
|
||||
version = "0.33.3"
|
||||
version = "0.33.4"
|
||||
[plugin.config]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core repo plugin"
|
||||
version = "0.33.3"
|
||||
version = "0.33.4"
|
||||
[plugin.config]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core resource plugin"
|
||||
version = "0.33.3"
|
||||
version = "0.33.4"
|
||||
[plugin.config]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core run plugin"
|
||||
version = "0.33.3"
|
||||
version = "0.33.4"
|
||||
[plugin.config]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core scheduler-docker-local plugin"
|
||||
version = "0.33.3"
|
||||
version = "0.33.4"
|
||||
[plugin.config]
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core scheduler-k3s plugin"
|
||||
version = "0.33.3"
|
||||
version = "0.33.4"
|
||||
[plugin.config]
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
51
plugins/scheduler-k3s/templates/chart/ingress.yaml
Normal file
51
plugins/scheduler-k3s/templates/chart/ingress.yaml
Normal 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 }}
|
||||
@@ -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 }}
|
||||
|
||||
16
plugins/scheduler-k3s/templates/chart/service-account.yaml
Normal file
16
plugins/scheduler-k3s/templates/chart/service-account.yaml
Normal 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
Reference in New Issue
Block a user