Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
b4a1dea
Ignore copilot file in .github
untergeek Sep 1, 2026
484b182
docs: 0.5.2 multi-database design spec
untergeek Sep 1, 2026
be2aa89
docs: 0.5.2 multi-database implementation plan
untergeek Sep 1, 2026
f656719
build: add postgres/mysql/databases install extras
untergeek Sep 1, 2026
eb9dcba
test: specify multi-engine build_databases behavior
untergeek Sep 1, 2026
0727224
feat: wire PostgreSQL and MySQL Django backends from env
untergeek Sep 1, 2026
662af0b
fix: spoof PyMySQL version_info for Django 6 mysql backend
untergeek Sep 1, 2026
35c0c40
feat: gunicorn pidfile and SQLite scale warning
untergeek Sep 1, 2026
aa8349c
test: mock check_server_version in serve SNMP test
untergeek Sep 1, 2026
31a12a0
test: Docker Postgres/MariaDB/MySQL matrix script
untergeek Sep 2, 2026
3dfac3a
fix: propagate pytest failures from test_databases.bat
untergeek Sep 2, 2026
ad87c4f
feat: BETA migrate-engine sqlite to postgres/mysql
untergeek Sep 2, 2026
d0d3dbc
fix: unlink gunicorn pidfile after migrate-engine SIGTERM
untergeek Sep 2, 2026
2b63f0b
test: live sqlite dump/load onto Postgres MariaDB MySQL
untergeek Sep 2, 2026
24d8975
docs: multi-engine env, systemd, Docker extras, CI matrix
untergeek Sep 2, 2026
9331676
docs: dump sqlite before switching LOGSTASHUI_DB_*
untergeek Sep 2, 2026
470665a
fix: migrate-engine dump uses package path; reset sequences without psql
untergeek Sep 2, 2026
13f07a6
docs: expand 0.5.2 CHANGELOG for multi-database
untergeek Sep 2, 2026
d54f9f1
fix: fail-fast DB floors before migrate; review nits
untergeek Sep 2, 2026
52db963
docs: note BETA migrate-engine is not atomic on the target
untergeek Sep 2, 2026
fcacf59
Add examples & fix ALLOWED_HOSTS in k8s
untergeek Sep 2, 2026
0ef9af9
docs: air-gapped freeze design spec
untergeek Sep 2, 2026
90b3351
feat: optional air-gapped freeze script
untergeek Sep 2, 2026
ea65675
Moved these files to be not tracked
untergeek Sep 2, 2026
638acfb
feat: LS version pill, VERSION binary path, unreleased agent
untergeek Sep 3, 2026
ebc3305
Improve testing...
untergeek Sep 3, 2026
73dcc51
fix: CI database testing
untergeek Sep 3, 2026
d544581
fix: FULLY honor LOGSTASHUI_TLS env var
untergeek Sep 3, 2026
c396829
Migrated all tests to be in tests/
untergeek Sep 3, 2026
1fe7ef1
The tests that were relocated are now added
untergeek Sep 3, 2026
5016bab
Fixed bugs that came up during testing
untergeek Sep 3, 2026
8eaf014
Add basic API access to the apps
untergeek Sep 3, 2026
51c9e0a
Massive collection of repairs and new functionality
untergeek Sep 4, 2026
72ebfbf
Update version 0.5.1 to 0.5.2
untergeek Sep 4, 2026
121b2a4
fix: log ERROR when LOGSTASHUI_OTEL=true but [otel] is missing
untergeek Sep 4, 2026
89804eb
feat: install LogstashUI[otel] in the image and freeze artifacts
untergeek Sep 4, 2026
85b634f
docs: OTEL env table, commented ConfigMap keys, [otel] in the image
untergeek Sep 4, 2026
d57a705
fix: init OpenTelemetry before Django builds the WSGI handler
untergeek Sep 4, 2026
d8e8e57
test: mocked OTEL success path; setup Django before instrument()
untergeek Sep 4, 2026
4cc4edb
test: preferred LogstashAgent pin is 0.5.2 (lockstep with UI)
untergeek Sep 4, 2026
ff9dcf4
docs: LogstashUI and LogstashAgent current pins are 0.5.2
untergeek Sep 4, 2026
bd69cf8
test: smoke LogstashUI[otel] in wheelhouse and pre-built images
untergeek Sep 4, 2026
26ef681
feat: add LOGSTASHUI_INSECURE_HTTP helpers
untergeek Sep 4, 2026
f27b6af
feat: honor LOGSTASHUI_INSECURE_HTTP in Django settings
untergeek Sep 4, 2026
0872734
feat: skip gunicorn TLS when LOGSTASHUI_INSECURE_HTTP is set
untergeek Sep 4, 2026
e4d50c6
feat: skip product CA when LOGSTASHUI_INSECURE_HTTP is set
untergeek Sep 4, 2026
0179dbe
feat: skip CA download and CSR signing in insecure HTTP mode
untergeek Sep 4, 2026
63228cf
feat: force http:// on agent and UI callback URLs when insecure
untergeek Sep 4, 2026
0de91ae
feat: show insecure HTTP on Management TLS settings
untergeek Sep 4, 2026
16df62a
docs: warn that LOGSTASHUI_INSECURE_HTTP is a last-resort escape hatch
untergeek Sep 4, 2026
48da16a
fix: do not mutate UI certs when LOGSTASHUI_INSECURE_HTTP is set
untergeek Sep 4, 2026
e3b6af4
fix: 409 TLS upload and revert when insecure HTTP is on
untergeek Sep 4, 2026
52ce19f
fix: hide TLS upload and revert when insecure HTTP is on
untergeek Sep 4, 2026
6747611
docs: insecure HTTP is not the Compose or Kubernetes default
untergeek Sep 4, 2026
5a41499
docs: add Kubernetes embedded-agent overlay
untergeek Sep 4, 2026
1eba16a
docs: comment LOGSTASH_AGENT_URL on K8s example ConfigMaps
untergeek Sep 4, 2026
d10a2bc
docs: comment LOGSTASHUI_AGENT_CSR_SECRET on K8s example Secrets
untergeek Sep 4, 2026
d7cb471
docs: document Kubernetes embedded agent overlay
untergeek Sep 4, 2026
03c3e79
docs: point agent URL and CSR secret at K8s embedded overlay
untergeek Sep 4, 2026
8cb9ffb
docs: CHANGELOG Kubernetes embedded-agent overlay
untergeek Sep 4, 2026
cfb9eee
Update the k8s example values to be consistent.
untergeek Sep 5, 2026
9363561
Missed the README in the last commit
untergeek Sep 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .github/workflows/offline-freeze.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: Offline wheels freeze

on:
workflow_dispatch:

jobs:
wheels:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v4
with:
python-version: "3.12"
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Freeze wheelhouse
run: bash bin/freeze_logstashui.sh --wheels
- name: Network-none install smoke
run: bash bin/test_freeze_wheels.sh
20 changes: 20 additions & 0 deletions .github/workflows/test-databases.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: Database matrix

on:
pull_request:
push:
branches: [main, master]

jobs:
db:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v4
with:
python-version: "3.12"
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Run database matrix
run: bash bin/test_databases.sh
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,6 @@ __marimo__/
# LogstashUI
/src/logstashui/staticfiles/
/src/logstashui/data/
/.plans/

# Local config (use logstashui.example.yml)
/src/logstashui/logstashui.yml
Expand All @@ -233,6 +232,7 @@ __marimo__/
/docker/logstashui_data/

# Agentic / local tooling scratch (not product docs)
/.plans/
.grokignore
/docs/superpowers/
.hermes.md
Expand All @@ -242,6 +242,8 @@ CLAUDE.md
.cursorrules
IDEA.md
graphify-out/
.github/copilot-instructions.md
/scratch/

# LogstashAgent (cloned from GitHub in host mode)
/LogstashAgent/
Expand Down
97 changes: 97 additions & 0 deletions CHANGELOG.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
>
> ⚠️ **Beta Release** - This project is under active development. Features may change.
>
> **Current package version: 0.5.1** — see [CHANGELOG.md](CHANGELOG.md). Pair with **LogstashAgent 0.5.1**. Operator guide: [agent roles / ports / coexistence / VERSION](docs/docs/logstashagent/general/roles.md).
> **Current package version: 0.5.2** — see [CHANGELOG.md](CHANGELOG.md). Pair with **LogstashAgent 0.5.2**. Operator guide: [agent roles / ports / coexistence / VERSION](docs/docs/logstashagent/general/roles.md).

<img src="docs/images/simulate.png" width="100%">

Expand Down
282 changes: 282 additions & 0 deletions bin/freeze_logstashui.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,282 @@
#!/bin/bash
#Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
#or more contributor license agreements. Licensed under the Elastic License;
#you may not use this file except in compliance with the Elastic License.

# Optional air-gapped freeze. Default `uv build` is unchanged.
# Usage:
# ./bin/freeze_logstashui.sh [--wheels] [--docker] [--standalone] [--all]
# [--output DIR] [--image NAME]
# No artifact flags → --all.

set -euo pipefail

ROOT=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)
TEMPLATES="$ROOT/packaging/offline"
DO_WHEELS=0
DO_DOCKER=0
DO_STANDALONE=0
EXPLICIT_STANDALONE=0
OUT=""
IMAGE_OVERRIDE=""

usage() {
cat <<'EOF'
Usage: freeze_logstashui.sh [--wheels] [--docker] [--standalone] [--all]
[--output DIR] [--image NAME]

Connected-builder freeze for air-gapped hosts. Default uv build is unchanged.

--wheels CPython 3.12 manylinux x86_64 wheelhouse zip
--docker docker save of a local image (never docker pull)
--standalone experimental PyInstaller onedir (Linux x86_64 only)
--all all three (default if no artifact flags)
--output DIR default: <repo>/dist/offline
--image NAME docker save this local tag instead of building

Isolated wheels host: CPython 3.12 x86_64 + python3.12-venv. No uv.
EOF
}

while [[ $# -gt 0 ]]; do
case "$1" in
--wheels) DO_WHEELS=1 ;;
--docker) DO_DOCKER=1 ;;
--standalone) DO_STANDALONE=1; EXPLICIT_STANDALONE=1 ;;
--all) DO_WHEELS=1; DO_DOCKER=1; DO_STANDALONE=1 ;;
--output)
OUT="${2:?--output requires a directory}"
shift
;;
--image)
IMAGE_OVERRIDE="${2:?--image requires a name}"
shift
;;
-h|--help)
usage
exit 0
;;
*)
echo "Unknown option: $1" >&2
usage >&2
exit 1
;;
esac
shift
done

if [[ $DO_WHEELS -eq 0 && $DO_DOCKER -eq 0 && $DO_STANDALONE -eq 0 ]]; then
DO_WHEELS=1
DO_DOCKER=1
DO_STANDALONE=1
fi

OUT="${OUT:-$ROOT/dist/offline}"
mkdir -p "$OUT"

die() { echo "ERROR: $*" >&2; exit 1; }

version_from_pyproject() {
sed -n 's/^version = "\([^"]*\)"/\1/p' "$ROOT/pyproject.toml" | head -n 1
}

subst() {
# $1 src $2 dest
local git_sha="$GIT_SHA"
local version="$VERSION"
local image="$IMAGE_NAME"
sed \
-e "s|__VERSION__|${version}|g" \
-e "s|__GIT_SHA__|${git_sha}|g" \
-e "s|__IMAGE_NAME__|${image}|g" \
"$1" > "$2"
}

sha256_tree() {
local dir="$1"
local out="$2"
(
cd "$dir"
if command -v sha256sum >/dev/null 2>&1; then
find . -type f ! -name SHA256SUMS.txt -print0 | sort -z | xargs -0 sha256sum
else
find . -type f ! -name SHA256SUMS.txt -print0 | sort -z | xargs -0 shasum -a 256
fi
) > "$out"
}

zip_dir() {
local src="$1"
local dest="$2"
rm -f "$dest"
(
cd "$(dirname "$src")"
zip -r -q "$dest" "$(basename "$src")"
)
}

require_uv() {
command -v uv >/dev/null 2>&1 || die "uv is required. Install: https://docs.astral.sh/uv/getting-started/installation/"
uv python find 3.12 >/dev/null 2>&1 || die "Need CPython 3.12 (uv python install 3.12)"
command -v zip >/dev/null 2>&1 || die "zip is required to pack freeze artifacts"
}

ensure_tailwind() {
local css="$ROOT/src/logstashui/theme/static/css/dist/styles.css"
if [[ -s "$css" ]]; then
return 0
fi
command -v npm >/dev/null 2>&1 || die "Tailwind CSS missing and npm is not installed"
(cd "$ROOT/src/logstashui/theme/static_src" && npm install && npm run build)
[[ -s "$css" ]] || die "Tailwind CSS build did not produce $css"
}

linux_x86_64() {
[[ "$(uname -s)" == Linux && "$(uname -m)" == x86_64 ]]
}

freeze_wheels() {
local stage="$OUT/logstashui-${VERSION}-offline-wheels-linux-x86_64-cp312"
local wheels="$stage/wheels"
local req="$OUT/requirements-offline.txt"
local zip="$OUT/logstashui-${VERSION}-offline-wheels-linux-x86_64-cp312.zip"

rm -rf "$stage"
mkdir -p "$wheels"

echo "==> uv build (normal wheel)"
(cd "$ROOT" && uv build)

local whl
whl=$(ls -1 "$ROOT/dist"/logstashui-"${VERSION}"-*.whl 2>/dev/null | head -n 1 || true)
[[ -n "$whl" && -f "$whl" ]] || die "uv build did not produce dist/logstashui-${VERSION}-*.whl"
cp "$whl" "$wheels/"

# otel rides along so an air-gapped site can still enable tracing with
# LOGSTASHUI_OTEL=true. It is inert until then.
echo "==> uv export --frozen --extra databases --extra otel"
(cd "$ROOT" && uv export --frozen --no-dev --extra databases --extra otel --no-emit-project \
-o "$req" >/dev/null)
local req_plain="$OUT/requirements-offline.nohash.txt"
(cd "$ROOT" && uv export --frozen --no-dev --extra databases --extra otel --no-emit-project --no-hashes \
-o "$req_plain" >/dev/null)

echo "==> download manylinux cp312 wheels (pure-python sdists → wheel on builder)"
(cd "$ROOT" && uv run --python 3.12 --with pip python \
"$TEMPLATES/download_wheels.py" "$req_plain" "$wheels")

subst "$TEMPLATES/wheels-install.sh" "$stage/install.sh"
subst "$TEMPLATES/wheels-README.md" "$stage/README.md"
chmod +x "$stage/install.sh"
cp "$ROOT/LICENSE.txt" "$stage/LICENSE.txt"
cp "$ROOT/NOTICE.txt" "$stage/NOTICE.txt"
cp "$req" "$stage/requirements-offline.txt"

{
echo "LogstashUI ${VERSION}"
echo "git ${GIT_SHA}"
echo "python CPython 3.12"
echo "platform linux-x86_64"
echo "extras databases otel"
echo
echo "wheels:"
(cd "$wheels" && ls -1 *.whl | sort)
} > "$stage/MANIFEST.txt"

sha256_tree "$stage" "$stage/SHA256SUMS.txt"
zip_dir "$stage" "$zip"
echo "Wrote $zip"
}

freeze_docker() {
local tag="${IMAGE_OVERRIDE:-logstashui:offline-${VERSION}}"
IMAGE_NAME="$tag"
local stage="$OUT/logstashui-${VERSION}-offline-docker-linux-x86_64"
local zip="$OUT/logstashui-${VERSION}-offline-docker-linux-x86_64.zip"

command -v docker >/dev/null 2>&1 || die "docker is required for --docker"

if [[ -n "$IMAGE_OVERRIDE" ]]; then
docker image inspect "$IMAGE_OVERRIDE" >/dev/null 2>&1 \
|| die "image ${IMAGE_OVERRIDE} is not local (never docker pull). Build it or omit --image."
else
echo "==> docker build --platform linux/amd64 ${tag}"
docker build --platform linux/amd64 -f "$ROOT/docker/Dockerfile" -t "$tag" "$ROOT"
fi

rm -rf "$stage"
mkdir -p "$stage"
echo "==> docker save ${tag}"
docker save "$tag" | gzip -c > "$stage/image.tar.gz"

subst "$TEMPLATES/docker-load.sh" "$stage/load.sh"
subst "$TEMPLATES/docker-README.md" "$stage/README.md"
subst "$TEMPLATES/compose.offline.yml" "$stage/compose.offline.yml"
chmod +x "$stage/load.sh"
cp "$ROOT/LICENSE.txt" "$stage/LICENSE.txt"
cp "$ROOT/NOTICE.txt" "$stage/NOTICE.txt"
sha256_tree "$stage" "$stage/SHA256SUMS.txt"
zip_dir "$stage" "$zip"
echo "Wrote $zip"
}

freeze_standalone() {
if ! linux_x86_64; then
if [[ "$EXPLICIT_STANDALONE" -eq 1 ]]; then
die "standalone freeze requires Linux x86_64 (PyInstaller binary is per-OS)"
fi
echo "WARNING: skipping --standalone (not Linux x86_64)" >&2
return 0
fi

local venv="$OUT/.standalone-venv"
local work="$OUT/pyinstaller-work"
local dist="$OUT/pyinstaller-dist"
local stage="$OUT/logstashui-${VERSION}-offline-standalone-linux-x86_64"
local zip="$OUT/logstashui-${VERSION}-offline-standalone-linux-x86_64.zip"
echo "==> throwaway venv + PyInstaller (not a project dependency)"
rm -rf "$venv" "$work" "$dist"
uv venv --python 3.12 "$venv"
(cd "$ROOT" && uv pip install --python "$venv" ".[databases,otel]")
uv pip install --python "$venv" pyinstaller
"$venv/bin/pyinstaller" \
--noconfirm \
--clean \
--workpath "$work" \
--distpath "$dist" \
"$TEMPLATES/logstashui.spec"

[[ -x "$dist/logstashui/logstashui" ]] || die "PyInstaller did not produce $dist/logstashui/logstashui"

rm -rf "$stage"
mkdir -p "$stage"
cp -a "$dist/logstashui" "$stage/logstashui"
subst "$TEMPLATES/standalone-run.sh" "$stage/run.sh"
subst "$TEMPLATES/standalone-README.md" "$stage/README.md"
chmod +x "$stage/run.sh"
cp "$ROOT/LICENSE.txt" "$stage/LICENSE.txt"
cp "$ROOT/NOTICE.txt" "$stage/NOTICE.txt"
sha256_tree "$stage" "$stage/SHA256SUMS.txt"
zip_dir "$stage" "$zip"
echo "Wrote $zip"
}

VERSION=$(version_from_pyproject)
[[ -n "$VERSION" ]] || die "could not read version from pyproject.toml"
GIT_SHA=$(git -C "$ROOT" rev-parse --short HEAD 2>/dev/null || echo unknown)
IMAGE_NAME="${IMAGE_OVERRIDE:-logstashui:offline-${VERSION}}"

require_uv
ensure_tailwind

echo "LogstashUI ${VERSION} git ${GIT_SHA} output ${OUT}"

if [[ $DO_WHEELS -eq 1 ]]; then
freeze_wheels
fi
if [[ $DO_DOCKER -eq 1 ]]; then
freeze_docker
fi
if [[ $DO_STANDALONE -eq 1 ]]; then
freeze_standalone
fi
Loading
Loading