This commit is contained in:
Mateusz Gruszczyński committed 2026-10-22 19:20:57 +02:00
1 parent 58c7d63194
commit 5a0a063031
15 files changed
+124 -53

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
# RustProxy source and documentation maintenance # RustProxy source and documentation maintenance
Current source version: **0.34.7**. The Cargo package and binary are named `rust-proxy`. Current source version: **0.34.8**. The Cargo package and binary are named `rust-proxy`.
Before editing, read [README.md](README.md), [ROADMAP.md](ROADMAP.md), the relevant [documentation guide](docs/README.md) and the actual code and tests. Historical release experiments are not current specifications. Use the bundled `src/admin/openapi.json` for API routes and payloads; the source implementation is authoritative for runtime behavior. Before editing, read [README.md](README.md), [ROADMAP.md](ROADMAP.md), the relevant [documentation guide](docs/README.md) and the actual code and tests. Historical release experiments are not current specifications. Use the bundled `src/admin/openapi.json` for API routes and payloads; the source implementation is authoritative for runtime behavior.
+9 -2
View File
@@ -6,9 +6,16 @@
# Changelog # Changelog
Release history focused on **features, settings, default behavior, compatibility and fixes**. Changes to internal code structure and preliminary performance experiments are intentionally omitted. Historical descriptions reflect their respective releases; consult the current documentation for the settings available in 0.34.7. Release history focused on **features, settings, default behavior, compatibility and fixes**. Changes to internal code structure and preliminary performance experiments are intentionally omitted. Historical descriptions reflect their respective releases; consult the current documentation for the settings available in 0.34.8.
## 0.34.7 — Varnish-compatible cache lookup (source candidate) ## 0.34.8
- Docker vhosts now use container names by default instead of image/port names. Existing automatic hostnames are updated; manual hostnames and aliases stay unchanged. Docker Agent updated to 1.0.1.
## 0.34.7
- Docker discovery: default hostname now uses the Docker container name, without the image or port suffix. Previously generated hostnames are updated on discovery; manually configured hosts and aliases are retained.
— Varnish-compatible cache lookup (source candidate)
- **Settings → Cache → Varnish-compatible cache policy (`varnish_compatible`)** stays **ON by default**, including upgraded configurations without the field. An adjacent **i** popover (PL/EN) explains its effects, limitations and safety controls. OFF retains strict client hard-reload behavior. The live toggle invalidates existing entries across HOT/COLD. - **Settings → Cache → Varnish-compatible cache policy (`varnish_compatible`)** stays **ON by default**, including upgraded configurations without the field. An adjacent **i** popover (PL/EN) explains its effects, limitations and safety controls. OFF retains strict client hard-reload behavior. The live toggle invalidates existing entries across HOT/COLD.
- Only client hard-reload `Cache-Control: no-cache`, `max-age=0` and `Pragma: no-cache` are ignored when considering a **fresh HIT**, retaining `Age` and hit counters. Kept the original `Cache-Control: no-store` bypass, `only-if-cached` 504 on cache MISS and no background origin refresh on HIT, and `min-fresh`/`max-stale`/positive `max-age` freshness constraints (including explicit `max-stale=0` during grace). Original request headers, Vary, access/privacy rules and Web Server handling remain intact. - Only client hard-reload `Cache-Control: no-cache`, `max-age=0` and `Pragma: no-cache` are ignored when considering a **fresh HIT**, retaining `Age` and hit counters. Kept the original `Cache-Control: no-store` bypass, `only-if-cached` 504 on cache MISS and no background origin refresh on HIT, and `min-fresh`/`max-stale`/positive `max-age` freshness constraints (including explicit `max-stale=0` during grace). Original request headers, Vary, access/privacy rules and Web Server handling remain intact.
Generated
+1 -1
View File
@@ -1881,7 +1881,7 @@ dependencies = [
[[package]] [[package]]
name = "rust-proxy" name = "rust-proxy"
version = "0.34.7" version = "0.34.8"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"arc-swap", "arc-swap",
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "rust-proxy" name = "rust-proxy"
version = "0.34.7" version = "0.34.8"
edition = "2021" edition = "2021"
rust-version = "1.99" rust-version = "1.99"
description = "RustProxy - independent reverse proxy with local administration" description = "RustProxy - independent reverse proxy with local administration"
+6 -2
View File
@@ -1,4 +1,4 @@
# RustProxy 0.34.7 # RustProxy 0.34.8
RustProxy combines a reverse proxy, static/PHP web server, certificate management and traffic monitoring in one service with a browser administration panel. It supports several domains and backends without requiring a separate proxy configuration language. RustProxy combines a reverse proxy, static/PHP web server, certificate management and traffic monitoring in one service with a browser administration panel. It supports several domains and backends without requiring a separate proxy configuration language.
@@ -16,6 +16,10 @@ RustProxy combines a reverse proxy, static/PHP web server, certificate managemen
Features and default settings vary by component; configure only the capabilities you need. The [documentation index](docs/README.md) covers each workflow, including prerequisites, security boundaries, troubleshooting and API access. Features and default settings vary by component; configure only the capabilities you need. The [documentation index](docs/README.md) covers each workflow, including prerequisites, security boundaries, troubleshooting and API access.
## 0.34.8
- **Docker vhosts:** automatic hostname uses the container name, without image or port suffix; prior auto-generated names are updated while custom names and aliases are preserved. Docker Agent **1.0.1**.
## 0.34.7 ## 0.34.7
- **Settings → Cache:** Varnish-inspired cache policy (on by default) ignores hard-reload `no-cache` / `max-age=0` / `Pragma: no-cache` for fresh HITs, without disabling `only-if-cached`, `no-store`, `max-stale` or `min-fresh`. Public origin `no-cache` responses can use an explicit fallback TTL; other responses retain revalidation, `stale-if-error`, Surrogate-Control vetoes and privacy protections. The behavior is explained by an **i** icon next to the setting. See [Cache](docs/CACHE.md) for examples and safety implications. - **Settings → Cache:** Varnish-inspired cache policy (on by default) ignores hard-reload `no-cache` / `max-age=0` / `Pragma: no-cache` for fresh HITs, without disabling `only-if-cached`, `no-store`, `max-stale` or `min-fresh`. Public origin `no-cache` responses can use an explicit fallback TTL; other responses retain revalidation, `stale-if-error`, Surrogate-Control vetoes and privacy protections. The behavior is explained by an **i** icon next to the setting. See [Cache](docs/CACHE.md) for examples and safety implications.
@@ -99,4 +103,4 @@ The authenticated **`/docs`** page includes the guides and a separate interactiv
## Status of this source archive ## Status of this source archive
Version **0.34.7** is provided as a **source release candidate**. Configuration, ACL and TLS changes require native compilation and integration testing on the target systems before a production deployment is declared. See [Installation](docs/INSTALL.md) and [Roadmap](ROADMAP.md). Version **0.34.8** is provided as a **source release candidate**. Configuration, ACL and TLS changes require native compilation and integration testing on the target systems before a production deployment is declared. See [Installation](docs/INSTALL.md) and [Roadmap](ROADMAP.md).
+18 -18
View File
@@ -1,29 +1,29 @@
61bf40a0572889dc8b0dda9eae4e93e44b0de9364b3c798106c85700ffb37c23 .dockerignore 61bf40a0572889dc8b0dda9eae4e93e44b0de9364b3c798106c85700ffb37c23 .dockerignore
4140a2956264b44f259e6a37fc00ba02097bbe68b09296e04d3d243c1287129b .env.example 4140a2956264b44f259e6a37fc00ba02097bbe68b09296e04d3d243c1287129b .env.example
9e6356475bf587091bab72246ec9e325b498f7e48a6220e9a18baa50d0a385a6 .gitignore 9e6356475bf587091bab72246ec9e325b498f7e48a6220e9a18baa50d0a385a6 .gitignore
10e5382f535df61cc2e5905d83a61d40ac3e60572e7ff8e05bad6271d871b6c5 AGENTS.md e9aa7d23f867dabab4b254154dc81833511138f7679ef7d0583afcdff2ff231e AGENTS.md
71b520eb7169feb726ba057417156bd74faa71dc4cbd3e3f994bcfee19f851fd CHANGELOG.md e9f1223523e263960e5d1608c799ec4e25cd96a0a253a6b1ea1ad27e9b954d21 CHANGELOG.md
32a192c94173ec66cce81890c71f235febeda7cf59bc8581fc982e1a0759b296 Cargo.lock 3bce5792c1588ec7de8d97239d7e9e3304ce21eb76c619f48e6e62577c6cfac5 Cargo.lock
82922877469166aff8eec6805e56f9c7f07b95dba2dc02d17c9c9fd5f39747fa Cargo.toml 15821dc3b17d2da6d139b91a885269a817457d271fbeabb49c3d68bccc5efd22 Cargo.toml
25c7f3c8162cbf5cc156439774118a81dae9fdc52b611ec7e40c230fc99a772c Dockerfile 25c7f3c8162cbf5cc156439774118a81dae9fdc52b611ec7e40c230fc99a772c Dockerfile
b9a2fb9e884f26840fbd9c84b1564682ef2093a08b32a502b9b19711c19a3724 README.md ca24bbf88f886637f91735daba5a7eee83cac13dc3b49085ca9eb56cd102f789 README.md
ae6d14ada6fde7d0b8514840b821678418cf5b1816c18434e3b7fb2ae27a0b21 ROADMAP.md ae6d14ada6fde7d0b8514840b821678418cf5b1816c18434e3b7fb2ae27a0b21 ROADMAP.md
da8311e3db945290666324a2f24bf4521b365272d7ac3f862ded1be993eea646 UPDATE_CHECK.toml da8311e3db945290666324a2f24bf4521b365272d7ac3f862ded1be993eea646 UPDATE_CHECK.toml
7309cfaa188c7a1fcf30be1b8313ee99c00b7fe5d6d626ddefcc7345fa066112 build.rs 7309cfaa188c7a1fcf30be1b8313ee99c00b7fe5d6d626ddefcc7345fa066112 build.rs
2c6a0a5cb088399e2cc912073069eb334ec6615eee31734d2f0c161f1415fb6a deploy/README.md 2c6a0a5cb088399e2cc912073069eb334ec6615eee31734d2f0c161f1415fb6a deploy/README.md
13d514de5042a230cacf8c10abe1ef183c94ac799a89b8e2c111152ac7c69b48 deploy/rustproxy.service 13d514de5042a230cacf8c10abe1ef183c94ac799a89b8e2c111152ac7c69b48 deploy/rustproxy.service
c111f5b20484707b80efb7dd9176acbaa27b63ac78db497261ac234821e1fade dev.sh c111f5b20484707b80efb7dd9176acbaa27b63ac78db497261ac234821e1fade dev.sh
8a1ad36b0c7bda1fc6fe235c5a17becde8644600c1242b0d958d9eb57c2d6f62 docker-agent/README.md d213e774a9cd07bff0c79324722bd05c0aca18cc033d50b2c0558cdb72972bca docker-agent/README.md
e9f18b358a740f1fe16e369ed16ee37581b8ce4c94ddeb34a1a37b89a49b5a06 docker-agent/certificate.go e9f18b358a740f1fe16e369ed16ee37581b8ce4c94ddeb34a1a37b89a49b5a06 docker-agent/certificate.go
600d5aebc8557d6987158969d494bade1ba46ccf9d1cd573d357d0106db26b2b docker-agent/cli_test.go 600d5aebc8557d6987158969d494bade1ba46ccf9d1cd573d357d0106db26b2b docker-agent/cli_test.go
067793eee01fd284da66f79c6f8b6f19f8773e64f84af03168c8f751ff044685 docker-agent/credentials.go 067793eee01fd284da66f79c6f8b6f19f8773e64f84af03168c8f751ff044685 docker-agent/credentials.go
5e4f565c3c9de4dba897af2e801ea646a83395e72eae4717a6d502dff2fcec10 docker-agent/docker-agent.env.example 5e4f565c3c9de4dba897af2e801ea646a83395e72eae4717a6d502dff2fcec10 docker-agent/docker-agent.env.example
bf687a851374d8f1fa59d050f3f76532f7c12f16a6daa7d3c36102c2d9a8ec9c docker-agent/docker.go ae660c2a90f193167b166977277abfc86a25e043ab4def4c273b2a13d5a51b8b docker-agent/docker.go
9e9f3f17f38f9b0730f598f65988ce8ef562d2182ef6778fc7292b990e9cf22f docker-agent/go.mod 9e9f3f17f38f9b0730f598f65988ce8ef562d2182ef6778fc7292b990e9cf22f docker-agent/go.mod
3657c195ee6ff9c3c9f1e0d99a09b1ec2091edc61173098ef03db9b8dd7483fa docker-agent/install.sh 3657c195ee6ff9c3c9f1e0d99a09b1ec2091edc61173098ef03db9b8dd7483fa docker-agent/install.sh
b7d0466f70b541a705d234fc8a88b99b6197fdd5e682d3a288a78c76764358cf docker-agent/main.go b7d0466f70b541a705d234fc8a88b99b6197fdd5e682d3a288a78c76764358cf docker-agent/main.go
3d4051a1f805d145df3877476e4b87773e74be8e123045065955a5133afdc68c docker-agent/main_test.go d660f46f7ef8c539dd3f5486a31d5a1254b9737be8a278c7f9352c254558f8ac docker-agent/main_test.go
5778abe7a3bb0386ac7fa8929006dc719602135ab1a1bc9a47fa634092717185 docker-agent/model.go 351bcd0321c823694e12d578f96dc68ceb929979a70146d12af5394f639a486f docker-agent/model.go
e52b31d0abf65ae3034f6cb367cca113633014ce8d1d799ad4dd5624d2c7b8bb docker-agent/network.go e52b31d0abf65ae3034f6cb367cca113633014ce8d1d799ad4dd5624d2c7b8bb docker-agent/network.go
634790f35ff4627df2a78cec2ed371f6e9ec9df00afb386075595fc7f3ed72eb docker-agent/rustproxy-docker-agent.service 634790f35ff4627df2a78cec2ed371f6e9ec9df00afb386075595fc7f3ed72eb docker-agent/rustproxy-docker-agent.service
5220d669171ec3ad65f50e0bc8de5178dadaa4904def8e1a4f61002aa9e9a55b docker-agent/server.go 5220d669171ec3ad65f50e0bc8de5178dadaa4904def8e1a4f61002aa9e9a55b docker-agent/server.go
@@ -37,14 +37,14 @@ f24767c2f9d4db4db5005b700d03323dd20f0a1d180ae939be4fe54a126aaf32 docs/CONFIGURA
80bcc666736b10f6202386d0342e8d6feeba858d77aea310bf3ec6de01c2bfb3 docs/CONTAINER.md 80bcc666736b10f6202386d0342e8d6feeba858d77aea310bf3ec6de01c2bfb3 docs/CONTAINER.md
dc670d912a6a946160c2f8528c9bb710ec38ebe406dcb7dcaf18fb2653f667b6 docs/CUSTOM_LOCATIONS.md dc670d912a6a946160c2f8528c9bb710ec38ebe406dcb7dcaf18fb2653f667b6 docs/CUSTOM_LOCATIONS.md
9fb2d0bcac70d1883dd16557622851a1f0527b3d3569a4b5177f5e6dedd3c74d docs/DASHBOARD.md 9fb2d0bcac70d1883dd16557622851a1f0527b3d3569a4b5177f5e6dedd3c74d docs/DASHBOARD.md
d072d8cadd285d195e0f658e6a8a14ef6d49799ade9e453f01665d12a5135324 docs/DOCKER_DISCOVERY.md 3e2f7670d136911c1b7bfe8571c60590932f74a60bcac308add4ccb0446bf970 docs/DOCKER_DISCOVERY.md
f1baabe34e8a1b204d7c78e2f558c019c156b03adfa6ddecb931b4b8a5501f78 docs/INSTALL.md f1baabe34e8a1b204d7c78e2f558c019c156b03adfa6ddecb931b4b8a5501f78 docs/INSTALL.md
37001cbf8ed248ca05ec40f12dd9876d33f6e0c97dcb21eb2e6f53c3d97a60bb docs/INTEGRATIONS.md 37001cbf8ed248ca05ec40f12dd9876d33f6e0c97dcb21eb2e6f53c3d97a60bb docs/INTEGRATIONS.md
694cb7b15c5bbdc94cecd15dc516c05dcb3467849322cdd125b872d36dee4699 docs/LOGS.md 694cb7b15c5bbdc94cecd15dc516c05dcb3467849322cdd125b872d36dee4699 docs/LOGS.md
888cdc8c56e10b8a7615012e192e085bf2899d0ec9b3ddfb5731e7de6ab5bcde docs/MIGRATION.md 888cdc8c56e10b8a7615012e192e085bf2899d0ec9b3ddfb5731e7de6ab5bcde docs/MIGRATION.md
140b95ad778261455f269f2144a1c61c2315bd3b85300fb613822eacddf3cfc2 docs/MONITORING.md 140b95ad778261455f269f2144a1c61c2315bd3b85300fb613822eacddf3cfc2 docs/MONITORING.md
d5894c07c68ec4230987767198434abee13b0e525842fea21bcbe752fa82742b docs/PHP_FPM.md d5894c07c68ec4230987767198434abee13b0e525842fea21bcbe752fa82742b docs/PHP_FPM.md
6af7d306dc32ad03408a4bafcb819f7ef430fcc34fdbea460504483fea31b84f docs/README.md 0aa40fce348f18b4e9b319c25cd81d42f418502bba31511e349a8f82ab4992ce docs/README.md
490a6dbe870f2b1fd4ec7816768dde6c86fa7957fc9de2d2f075e123c9222222 docs/RESPONSE_DELIVERY.md 490a6dbe870f2b1fd4ec7816768dde6c86fa7957fc9de2d2f075e123c9222222 docs/RESPONSE_DELIVERY.md
e2b36152c13d65621a367c69dd400118dae1e4426a4d2b2139d2f63fa30442d6 docs/SECURITY.md e2b36152c13d65621a367c69dd400118dae1e4426a4d2b2139d2f63fa30442d6 docs/SECURITY.md
bbef60ff1771da9f48af812987766e1e479408873ae2f079b13f1d5c62ecc6ff docs/STREAMS.md bbef60ff1771da9f48af812987766e1e479408873ae2f079b13f1d5c62ecc6ff docs/STREAMS.md
@@ -143,7 +143,7 @@ d58a2ed83319176de4e66790ed292447c9497ae031464ec55cfe27174acb639a src/admin/file
282352eda7957477e7da44f16b90984b26a4a67a8755833bc0a6b34eaf93e3af src/admin/file_logs_query.rs 282352eda7957477e7da44f16b90984b26a4a67a8755833bc0a6b34eaf93e3af src/admin/file_logs_query.rs
f66ff718f82a326f80808b0a5088bd05a48625be802e5ba305fff3c344b59948 src/admin/logging_api.rs f66ff718f82a326f80808b0a5088bd05a48625be802e5ba305fff3c344b59948 src/admin/logging_api.rs
1ba0520c6348246cbb35b60e596e99dc600137c0696de86508cf06587bd86563 src/admin/mod.rs 1ba0520c6348246cbb35b60e596e99dc600137c0696de86508cf06587bd86563 src/admin/mod.rs
228560ed081bce7a490f401404083c79ecefcdfadac54a7256396ef2f40c0da6 src/admin/openapi.json 7bce0ba5a9fe3d1de2b10a29e58d88300e09ffcce50e6d94e68240c78db944b8 src/admin/openapi.json
7cfd6593ac9bcea3d1b9e61bbcb7ed98d65ed3190f54f94c1e104541f4fbb4f5 src/admin/operations.rs 7cfd6593ac9bcea3d1b9e61bbcb7ed98d65ed3190f54f94c1e104541f4fbb4f5 src/admin/operations.rs
f14afb6ae141053d83d0dc59df835b20e38a37a9e83f86f0f2053f2bd5d9ce57 src/admin/panel_assets.rs f14afb6ae141053d83d0dc59df835b20e38a37a9e83f86f0f2053f2bd5d9ce57 src/admin/panel_assets.rs
f5127855c2994d052678865df36d703b74f4a0afc633daf7718b10da18d26e37 src/admin/panel_content.rs f5127855c2994d052678865df36d703b74f4a0afc633daf7718b10da18d26e37 src/admin/panel_content.rs
@@ -187,7 +187,7 @@ fa00bce2651308a4e0b1303cc0146be17ed4794b67b2f0cebfaf861558d8c78c src/admin/web/
560a8e047182fbcee08aa4f8d999400a8a5ddca0b6a4640efd46f6b5c34fde91 src/admin/web/dashboard-defaults.json 560a8e047182fbcee08aa4f8d999400a8a5ddca0b6a4640efd46f6b5c34fde91 src/admin/web/dashboard-defaults.json
14213011cfb6ac334c8711b76bbdde97588a7b7ba4eb2592a1e387f877d6cbe0 src/admin/web/dashboard-widgets.json 14213011cfb6ac334c8711b76bbdde97588a7b7ba4eb2592a1e387f877d6cbe0 src/admin/web/dashboard-widgets.json
50d1ebcbaf4d0b2968da23bb943fc3f67131c5d32df3f61d23ff37aad9da5b81 src/admin/web/demo.html 50d1ebcbaf4d0b2968da23bb943fc3f67131c5d32df3f61d23ff37aad9da5b81 src/admin/web/demo.html
139872de552e73cc7d0263363e1c50656e9ff9054c4b5cb4dca0d8c423c78318 src/admin/web/docker.js afa6837d4cfa7feafeb4295563d0b4c31431cbb703d9096173e94ce93f851e34 src/admin/web/docker.js
1d5f8fbf81394a6a8c3c2eff78b48408ef7790c0613905c2c7f584f1548862c6 src/admin/web/docs.css 1d5f8fbf81394a6a8c3c2eff78b48408ef7790c0613905c2c7f584f1548862c6 src/admin/web/docs.css
aeffd403fa1823df7d9fa4c48280b4033b2d15e4cb9f66608caff89290de0a52 src/admin/web/docs.html aeffd403fa1823df7d9fa4c48280b4033b2d15e4cb9f66608caff89290de0a52 src/admin/web/docs.html
24b6d5676c2e0cd3862851a395c753dfd7e6fb022f7c69267c1e02b728f1d06f src/admin/web/docs.js 24b6d5676c2e0cd3862851a395c753dfd7e6fb022f7c69267c1e02b728f1d06f src/admin/web/docs.js
@@ -315,7 +315,7 @@ dada314e1709e960eb2e225b4d10c3cd12606c62c4bc2c0220aec1ba133e4ade src/demo.rs
045593a42a6e4f632e5052c655788f0843faa313fc929265b89509f6dfff91ed src/docker.rs 045593a42a6e4f632e5052c655788f0843faa313fc929265b89509f6dfff91ed src/docker.rs
93a8a3f6ba12761ddcbd19f22f32fb2c0e6948b660fb774ddd7ebdba9b6562a5 src/docker/client.rs 93a8a3f6ba12761ddcbd19f22f32fb2c0e6948b660fb774ddd7ebdba9b6562a5 src/docker/client.rs
f69087a82cf353e9a92fc97ee96510dbe0382efa1c5e53821d16c455a3eb95a4 src/docker/model.rs f69087a82cf353e9a92fc97ee96510dbe0382efa1c5e53821d16c455a3eb95a4 src/docker/model.rs
f095e5e3c564df409874019c647f7cec2156bf5a845b5b84864879fd07cf80ce src/docker/reconcile.rs e100739dcc720ea98ae2fdfec5133b9b1bc346d8546f18b26bb176c81f09f587 src/docker/reconcile.rs
2e8d4841665cc1d29e6262a1789bb64ced1cecd0036681cc1ef7b5d5bfe22065 src/docker/tests.rs 2e8d4841665cc1d29e6262a1789bb64ced1cecd0036681cc1ef7b5d5bfe22065 src/docker/tests.rs
e36db1ed3188ffe54df7fc4ef76c4b453f23683bb7b8cdf601046e2bd50e0403 src/forwarding.rs e36db1ed3188ffe54df7fc4ef76c4b453f23683bb7b8cdf601046e2bd50e0403 src/forwarding.rs
08d2e08a798abb944a53570f0bdaa08f1266b1107677ceda070091af4c2fc121 src/forwarding/peer_formatting.rs 08d2e08a798abb944a53570f0bdaa08f1266b1107677ceda070091af4c2fc121 src/forwarding/peer_formatting.rs
@@ -375,7 +375,7 @@ d134b0eb617f82f9971b3f3461379ea37c57bd0079bf54748d0c434b2ef1b475 src/proxy/cach
a03b1735daa8fa8e40cf5bd9c6e200d211baf19c30f62522d4237ed56642bce7 src/proxy/cache_plan/mod.rs a03b1735daa8fa8e40cf5bd9c6e200d211baf19c30f62522d4237ed56642bce7 src/proxy/cache_plan/mod.rs
3404b03096862de1050088f62cdd811ea1ab61d191f902501173107f81f23b9b src/proxy/cache_plan/tests.rs 3404b03096862de1050088f62cdd811ea1ab61d191f902501173107f81f23b9b src/proxy/cache_plan/tests.rs
a0b9e2e9ab0bc89c7b64bd19365198234b1c5c13f455ea47be122a5660639b17 src/proxy/error_fallback.rs a0b9e2e9ab0bc89c7b64bd19365198234b1c5c13f455ea47be122a5660639b17 src/proxy/error_fallback.rs
34a5c28cf8b719d39561bd0fc28f3ae40505d6ca8fc6b1532e5ea9ce318e7363 src/proxy/forward/cache_fast_path_tests.rs 60438601eea0888cd53c75cd94a3656f1eae5eaa700ebae171db2fe4f34eba13 src/proxy/forward/cache_fast_path_tests.rs
6b1ae807b0efa480207d9c5baf6e86b1f9dc6becb52600f14e4d1a84e8efcda1 src/proxy/forward/cache_lookup.rs 6b1ae807b0efa480207d9c5baf6e86b1f9dc6becb52600f14e4d1a84e8efcda1 src/proxy/forward/cache_lookup.rs
287012c81144d8f15af4a148ce3ca911d431bc59adff44e697567561aa372bb3 src/proxy/forward/h3_navigation_tests.rs 287012c81144d8f15af4a148ce3ca911d431bc59adff44e697567561aa372bb3 src/proxy/forward/h3_navigation_tests.rs
ce97cfcb07d90415937f0520ee7175502b22e60694c1ea8e3d2b47678d190e5f src/proxy/forward/response.rs ce97cfcb07d90415937f0520ee7175502b22e60694c1ea8e3d2b47678d190e5f src/proxy/forward/response.rs
@@ -396,8 +396,8 @@ de1763b89d98607e20f0f6e3c9b546483927cbe5450efc2e5634fe691fd52ad6 src/proxy/requ
b9a2307c6aa7f16613d935508a61574dfee964020b1b5be86293df2d684615d8 src/proxy/request_id/tests.rs b9a2307c6aa7f16613d935508a61574dfee964020b1b5be86293df2d684615d8 src/proxy/request_id/tests.rs
580fefdc7dfb27da73f86f4d2067d46a1f856d2f398b0598d37a743e880d3dac src/proxy/types.rs 580fefdc7dfb27da73f86f4d2067d46a1f856d2f398b0598d37a743e880d3dac src/proxy/types.rs
2ed96bc94003a881ab60f6871e3215598a3a519dc9f2480137f57292b70dc888 src/proxy/upstream_helpers.rs 2ed96bc94003a881ab60f6871e3215598a3a519dc9f2480137f57292b70dc888 src/proxy/upstream_helpers.rs
991bcc5caebc882849b6abc928230c0a6ec6e25f6aff6a8856e8a9807b9de72d src/proxy/web_cache_revalidation/mod.rs 4f2280d25eb8c08c08ad42832a12362d4fc399e8feedebe78112827d92399c95 src/proxy/web_cache_revalidation/mod.rs
c827ad43de9546dbc5d9744b1e8209da0f70d2be0304a60a776f9a71d75f57d8 src/proxy/web_cache_revalidation/tests.rs b9e2c34cfe0ac8627aca64b234e42024103b1bad6efc2f1bcc4347d929aee8f3 src/proxy/web_cache_revalidation/tests.rs
3d591201595a3b1e928ded20a8736477a47e5acc3bcbde158f7c17547ce155ba src/proxy/websocket.rs 3d591201595a3b1e928ded20a8736477a47e5acc3bcbde158f7c17547ce155ba src/proxy/websocket.rs
f8972bedb1a487fac86177774cd98cf2619fb25af97e57cfac2853db5cbf91a9 src/proxy/websocket/handshake.rs f8972bedb1a487fac86177774cd98cf2619fb25af97e57cfac2853db5cbf91a9 src/proxy/websocket/handshake.rs
c02d42b5c7eeedbc54c58e98691f7d8e39f8e9be63a48f6d6b4ad6b8ea431b7e src/proxy/websocket/http2_tests.rs c02d42b5c7eeedbc54c58e98691f7d8e39f8e9be63a48f6d6b4ad6b8ea431b7e src/proxy/websocket/http2_tests.rs
@@ -488,7 +488,7 @@ c95cc8f4549460917633a12ccb352a00a83c0f004b23c0b9ddecba42eee3ea17 src/vhost/vali
daeb5e05e4129764102b3f04e62b2dde38a8c7739bb475a13bc8c8f6d5844da1 src/web_server/config/model.rs daeb5e05e4129764102b3f04e62b2dde38a8c7739bb475a13bc8c8f6d5844da1 src/web_server/config/model.rs
a863ad1337fbc353c0f42caea4ac8b3644792e2aa08c37f992b7882b4b8dc6d5 src/web_server/config/validation.rs a863ad1337fbc353c0f42caea4ac8b3644792e2aa08c37f992b7882b4b8dc6d5 src/web_server/config/validation.rs
86c744216d7132d8294a6e90ba9be5fd871646006313d1c62506d40c81db9b12 src/web_server/fastcgi.rs 86c744216d7132d8294a6e90ba9be5fd871646006313d1c62506d40c81db9b12 src/web_server/fastcgi.rs
cd032866a9d74d6802c2d99f2799ce971c6a8400e39d7ceaf542cac8c4ee97fc src/web_server/fastcgi/protocol.rs 002071ef603f1d928a56226b011c76ac5ea2bc55521a0a3fd317c0ced0056c21 src/web_server/fastcgi/protocol.rs
e954308d94848471f6a9d70a50cd2181d17b9c41b2705a3e6c6c0e882a03733b src/web_server/fastcgi/response.rs e954308d94848471f6a9d70a50cd2181d17b9c41b2705a3e6c6c0e882a03733b src/web_server/fastcgi/response.rs
b98666353e4af560ab8423d4772d9ad26451dabb1c6d60f5faf547116a2bcd24 src/web_server/filesystem.rs b98666353e4af560ab8423d4772d9ad26451dabb1c6d60f5faf547116a2bcd24 src/web_server/filesystem.rs
d4fec3682532b0f2d11a0aefcd024f69fb623a39922ddb6c528a2d2de265347d src/web_server/htaccess.rs d4fec3682532b0f2d11a0aefcd024f69fb623a39922ddb6c528a2d2de265347d src/web_server/htaccess.rs
+2
View File
@@ -104,3 +104,5 @@ The API is `GET /v1/inventory` with `Authorization: Bearer <token>`; `GET /healt
- Cannot reach port `9876`: check `RUSTPROXY_DOCKER_AGENT_LISTEN`, listening socket, firewall, and that HTTPS is used. - Cannot reach port `9876`: check `RUSTPROXY_DOCKER_AGENT_LISTEN`, listening socket, firewall, and that HTTPS is used.
`RUSTPROXY_DOCKER_ADVERTISE_IP` adds a preferred host address without suppressing automatic address discovery. `RUSTPROXY_DOCKER_PUBLIC_IP_URL` controls the optional externally observed public IP. The agent's Docker socket access remains privileged; isolate the Docker host and network accordingly. `RUSTPROXY_DOCKER_ADVERTISE_IP` adds a preferred host address without suppressing automatic address discovery. `RUSTPROXY_DOCKER_PUBLIC_IP_URL` controls the optional externally observed public IP. The agent's Docker socket access remains privileged; isolate the Docker host and network accordingly.
Default `technical_name` uses the sanitized Docker container name (including Docker Compose container names), not the image or a port suffix. Each published TCP port still has an independent stable service key.
+6 -17
View File
@@ -54,13 +54,9 @@ func dockerContainers(ctx context.Context, sock string) ([]dockerContainer, erro
func cleanName(names []string) string { func cleanName(names []string) string {
if len(names) == 0 { if len(names) == 0 {
return "container" return ""
} }
name := strings.TrimPrefix(names[0], "/") return strings.TrimPrefix(names[0], "/")
if name == "" {
return "container"
}
return name
} }
func dnsLabel(value string) string { func dnsLabel(value string) string {
@@ -78,9 +74,6 @@ func dnsLabel(value string) string {
} }
} }
out := strings.Trim(b.String(), "-") out := strings.Trim(b.String(), "-")
if out == "" {
out = "app"
}
if len(out) > 48 { if len(out) > 48 {
out = strings.Trim(out[:48], "-") out = strings.Trim(out[:48], "-")
} }
@@ -92,22 +85,15 @@ func serviceIdentity(c dockerContainer, p dockerPort) (key, technical string) {
project := c.Labels["com.docker.compose.project"] project := c.Labels["com.docker.compose.project"]
composeService := c.Labels["com.docker.compose.service"] composeService := c.Labels["com.docker.compose.service"]
replica := c.Labels["com.docker.compose.container-number"] replica := c.Labels["com.docker.compose.container-number"]
suffix := strconv.Itoa(int(p.PrivatePort))
if project != "" && composeService != "" { if project != "" && composeService != "" {
if replica == "" { if replica == "" {
replica = name replica = name
} }
key = fmt.Sprintf("compose:%s/%s/%s:%d/%s", project, composeService, replica, p.PrivatePort, p.Type) key = fmt.Sprintf("compose:%s/%s/%s:%d/%s", project, composeService, replica, p.PrivatePort, p.Type)
technicalBase := project + "-" + composeService
if replica != "1" {
technicalBase += "-" + replica
}
technical = dnsLabel(technicalBase + "-" + suffix)
} else { } else {
key = fmt.Sprintf("container:%s:%d/%s", name, p.PrivatePort, p.Type) key = fmt.Sprintf("container:%s:%d/%s", name, p.PrivatePort, p.Type)
technical = dnsLabel(name + "-" + suffix)
} }
return key, technical return key, dnsLabel(name)
} }
func servicesFrom(containers []dockerContainer) []service { func servicesFrom(containers []dockerContainer) []service {
@@ -115,6 +101,9 @@ func servicesFrom(containers []dockerContainer) []service {
bindingSeen := make(map[string]map[string]struct{}) bindingSeen := make(map[string]map[string]struct{})
for _, c := range containers { for _, c := range containers {
name := cleanName(c.Names) name := cleanName(c.Names)
if dnsLabel(name) == "" {
continue
}
for _, p := range c.Ports { for _, p := range c.Ports {
if p.PublicPort == 0 || p.Type != "tcp" { if p.PublicPort == 0 || p.Type != "tcp" {
continue continue
+13 -1
View File
@@ -41,7 +41,7 @@ func TestServicesFromUsesStableComposeIdentity(t *testing.T) {
if len(got) != 1 { if len(got) != 1 {
t.Fatalf("got %d services", len(got)) t.Fatalf("got %d services", len(got))
} }
if got[0].Key != "compose:lab/web/1:8080/tcp" || got[0].TechnicalName != "lab-web-8080" { if got[0].Key != "compose:lab/web/1:8080/tcp" || got[0].TechnicalName != "web-1" {
t.Fatalf("unexpected service %#v", got[0]) t.Fatalf("unexpected service %#v", got[0])
} }
if len(got[0].Bindings) != 1 || got[0].Bindings[0].PublicPort != 18080 { if len(got[0].Bindings) != 1 || got[0].Bindings[0].PublicPort != 18080 {
@@ -64,6 +64,9 @@ func TestServicesFromKeepsComposeReplicasAndAllBindings(t *testing.T) {
if got[0].Key != "compose:lab/web/1:8080/tcp" || got[1].Key != "compose:lab/web/2:8080/tcp" { if got[0].Key != "compose:lab/web/1:8080/tcp" || got[1].Key != "compose:lab/web/2:8080/tcp" {
t.Fatalf("unexpected replica keys %#v", got) t.Fatalf("unexpected replica keys %#v", got)
} }
if got[0].TechnicalName != "lab-web-1" || got[1].TechnicalName != "lab-web-2" {
t.Fatalf("expected container-based hostnames, got %#v", got)
}
if len(got[0].Bindings) != 3 { if len(got[0].Bindings) != 3 {
t.Fatalf("expected all published bindings, got %#v", got[0].Bindings) t.Fatalf("expected all published bindings, got %#v", got[0].Bindings)
} }
@@ -243,3 +246,12 @@ func TestTLSInventoryReportsServingCertificateFingerprint(t *testing.T) {
t.Fatalf("inventory fingerprint = %q, want %q", got.CertificateSHA256, fingerprint) t.Fatalf("inventory fingerprint = %q, want %q", got.CertificateSHA256, fingerprint)
} }
} }
func TestTechnicalNameIgnoresImageAndPort(t *testing.T) {
c := dockerContainer{Names: []string{"/My_App"}, Image: "registry.example.com/image:latest"}
one := servicesFrom([]dockerContainer{{Names: c.Names, Image: c.Image, Ports: []dockerPort{{PrivatePort: 80, PublicPort: 8080, Type: "tcp"}}}})
two := servicesFrom([]dockerContainer{{Names: c.Names, Image: "changed", Ports: []dockerPort{{PrivatePort: 8080, PublicPort: 9000, Type: "tcp"}}}})
if len(one) != 1 || len(two) != 1 || one[0].TechnicalName != "my-app" || two[0].TechnicalName != "my-app" {
t.Fatalf("technical names must use the container name: %#v %#v", one, two)
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
package main package main
var version = "1.0.0" // overridden by the package builder using -ldflags -X main.version var version = "1.0.1" // overridden by the package builder using -ldflags -X main.version
type config struct { type config struct {
Listen string Listen string
+2 -2
View File
@@ -60,7 +60,7 @@ A token change invalidates the previous token after service restart. The agent l
- Running containers with **Docker-published, non-loopback TCP ports**; it does not turn an internal-only container port into a directly reachable service. - Running containers with **Docker-published, non-loopback TCP ports**; it does not turn an internal-only container port into a directly reachable service.
- Usable Docker-host interface/IP addresses and each published host binding, allowing an explicit backend address selection when the host has multiple networks. - Usable Docker-host interface/IP addresses and each published host binding, allowing an explicit backend address selection when the host has multiple networks.
- Current service status, container metadata and optional aliases. The agent does not install applications, change Docker network rules or deploy TLS on their behalf. - Default technical hostname from the Docker container name (including a Compose `container_name` or its generated container name), without image name or port suffix. Existing publications using the previous auto-generated port-suffixed name switch to the container name during reconciliation; manually selected hostnames and aliases are preserved. The agent does not install applications, change Docker network rules or deploy TLS on their behalf.
Agent inventory polling is separate from the panel's live status refresh. A new agent may initially show **WAITING** until the first poll, not a failure. **Refresh agents** requests an immediate agent update. Actual failures report a reason; a temporary agent outage can preserve the last successful inventory while a successful missing-service observation can lead to a generated route returning `503` until the service returns. Agent inventory polling is separate from the panel's live status refresh. A new agent may initially show **WAITING** until the first poll, not a failure. **Refresh agents** requests an immediate agent update. Actual failures report a reason; a temporary agent outage can preserve the last successful inventory while a successful missing-service observation can lead to a generated route returning `503` until the service returns.
@@ -70,7 +70,7 @@ In **Docker vhosts**, select the service and set:
| Option | Guidance | | Option | Guidance |
|---|---| |---|---|
| **Hostname / aliases** | A short name expands under the agent base domain; a full FQDN stays as entered. Configure public DNS separately. | | **Hostname / aliases** | By default, the sanitized Docker container name expands under the agent base domain, without an image or port suffix. A custom short name or full FQDN overrides it, and aliases remain configurable. Containers exposing multiple published TCP ports require distinct manually assigned hostnames to publish both ports. Configure public DNS separately. |
| **Backend IP/port** | Pick a detected, reachable Docker-host address or the configured automatic option. Docker host/firewall binding must permit RustProxy to connect. | | **Backend IP/port** | Pick a detected, reachable Docker-host address or the configured automatic option. Docker host/firewall binding must permit RustProxy to connect. |
| **HTTP / HTTPS upstream** | Match the container's actual service protocol; review origin TLS verification and Host behavior. | | **HTTP / HTTPS upstream** | Match the container's actual service protocol; review origin TLS verification and Host behavior. |
| **Frontend listeners / certificate** | Selected through the agent defaults and RustProxy configuration; ensure certificate covers the generated name. | | **Frontend listeners / certificate** | Selected through the agent defaults and RustProxy configuration; ensure certificate covers the generated name. |
+1 -1
View File
@@ -1,6 +1,6 @@
# RustProxy documentation # RustProxy documentation
Practical guides for **RustProxy 0.34.7**. This index and the listed pages are also available in the authenticated panel at **`/docs → Reader`**. The adjacent **API** tab provides the embedded OpenAPI reference. Paths below are links to actual files in the source archive. Practical guides for **RustProxy 0.34.8**. This index and the listed pages are also available in the authenticated panel at **`/docs → Reader`**. The adjacent **API** tab provides the embedded OpenAPI reference. Paths below are links to actual files in the source archive.
## Getting started ## Getting started
+1 -1
View File
@@ -2,7 +2,7 @@
"openapi": "3.0.3", "openapi": "3.0.3",
"info": { "info": {
"title": "RustProxy Administration API", "title": "RustProxy Administration API",
"version": "0.34.7", "version": "0.34.8",
"description": "Administration API for the active panel origin. Sign in through the panel; the same-origin HttpOnly cookie authenticates all private operations. Mutations also require the exact Origin, X-RustProxy-Request: 1 and a session-bound X-CSRF-Token. Loopback HTTP is the default; Settings can enable HTTPS before using a non-loopback administration bind. This document contains no live configuration or credentials. Object schemas marked extensible are summaries: full configuration validation is authoritative in Rust. The API also permits explicitly scoped GeneralApiBearer tokens on a narrow route allowlist, with RO/RW and source CIDR checks; API-token management and Prometheus-token management are browser-session-only." "description": "Administration API for the active panel origin. Sign in through the panel; the same-origin HttpOnly cookie authenticates all private operations. Mutations also require the exact Origin, X-RustProxy-Request: 1 and a session-bound X-CSRF-Token. Loopback HTTP is the default; Settings can enable HTTPS before using a non-loopback administration bind. This document contains no live configuration or credentials. Object schemas marked extensible are summaries: full configuration validation is authoritative in Rust. The API also permits explicitly scoped GeneralApiBearer tokens on a narrow route allowlist, with RO/RW and source CIDR checks; API-token management and Prometheus-token management are browser-session-only."
}, },
"servers": [ "servers": [
+15 -3
View File
@@ -97,6 +97,18 @@
return { node: field, read() { if (inputNode.value.trim()) consume(inputNode.value); return [...values]; }, count: () => values.length, input: inputNode }; return { node: field, read() { if (inputNode.value.trim()) consume(inputNode.value); return [...values]; }, count: () => values.length, input: inputNode };
} }
function legacyHostname(assignment) { return assignment?.hostname || assignment?.subdomain || ""; } function legacyHostname(assignment) { return assignment?.hostname || assignment?.subdomain || ""; }
function legacyDockerName(service) {
const key = service?.key || "";
const compose = /^compose:([^/]+)\/([^/]+)\/([^:]+):(\d+)\/tcp$/.exec(key);
const container = /^container:(.*):(\d+)\/tcp$/.exec(key);
if (!compose && !container) return null;
const base = compose ? `${compose[1]}-${compose[2]}${compose[3] === "1" ? "" : `-${compose[3]}`}-${compose[4]}` : `${container[1]}-${container[2]}`;
return base.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 48).replace(/-$/, "");
}
function displayHostname(assignment, service) {
const host = legacyHostname(assignment);
return service && host === legacyDockerName(service) ? service.technical_name : (host || service?.technical_name || "");
}
function serviceBindings(service) { function serviceBindings(service) {
if (!service) return []; if (!service) return [];
if (Array.isArray(service.bindings) && service.bindings.length) return service.bindings; if (Array.isArray(service.bindings) && service.bindings.length) return service.bindings;
@@ -384,7 +396,7 @@
const key = assignment?.service_key || service.key, row = el("article", undefined, "docker-service-row"); row.dataset.key = key; row.dataset.viewKey = viewKey(agent.id, key); const key = assignment?.service_key || service.key, row = el("article", undefined, "docker-service-row"); row.dataset.key = key; row.dataset.viewKey = viewKey(agent.id, key);
const runtime = runtimeFor(agent.id, key), member = runtime?.members?.[0], technicalName = service?.technical_name || assignment?.service_key || ""; const runtime = runtimeFor(agent.id, key), member = runtime?.members?.[0], technicalName = service?.technical_name || assignment?.service_key || "";
row.dockerSort = { row.dockerSort = {
service: technicalName.toLowerCase(), hostname: String(legacyHostname(assignment) || technicalName).toLowerCase(), service: technicalName.toLowerCase(), hostname: String(displayHostname(assignment, service) || technicalName).toLowerCase(),
status: stale ? 4 : assignment?.enabled === false || !assignment ? 3 : member?.status === "up" ? 0 : member?.status === "down" ? 1 : 2, status: stale ? 4 : assignment?.enabled === false || !assignment ? 3 : member?.status === "up" ? 0 : member?.status === "down" ? 1 : 2,
backend: String(assignment?.target_ip || agent.default_target_ip || inventory?.public_ip || "").toLowerCase(), scheme: String(assignment?.scheme || "http"), published: assignment && assignment.enabled !== false ? 0 : 1, backend: String(assignment?.target_ip || agent.default_target_ip || inventory?.public_ip || "").toLowerCase(), scheme: String(assignment?.scheme || "http"), published: assignment && assignment.enabled !== false ? 0 : 1,
}; };
@@ -411,12 +423,12 @@
liveLogs.disabled = !publish.checked || !poolRoutes.some(item => item.agent_id === agent.id && item.service_key === key && item.pool_id); liveLogs.disabled = !publish.checked || !poolRoutes.some(item => item.agent_id === agent.id && item.service_key === key && item.pool_id);
publish.setAttribute("aria-checked", String(publish.checked)); publish.setAttribute("aria-checked", String(publish.checked));
}; };
syncOpenActions(legacyHostname(assignment)); syncOpenActions(displayHostname(assignment, service));
const quickActions = el("div", undefined, "docker-service-quick-actions"); quickActions.append(openApp, liveLogs); const quickActions = el("div", undefined, "docker-service-quick-actions"); quickActions.append(openApp, liveLogs);
const headActions = el("div", undefined, "docker-service-head-actions"); headActions.append(runtimeSummary(runtime, assignment, stale), quickActions, visibility); top.append(publishLabel, meta, headActions); row.append(top); const headActions = el("div", undefined, "docker-service-head-actions"); headActions.append(runtimeSummary(runtime, assignment, stale), quickActions, visibility); top.append(publishLabel, meta, headActions); row.append(top);
const editor = el("details", undefined, "docker-service-editor"), editorSummary = el("summary"), editorTitle = el("span", t("dockerConfigureService")), editorHint = el("small", "", "fine docker-config-hint"); editorSummary.append(editorTitle, editorHint); editor.append(editorSummary); const editor = el("details", undefined, "docker-service-editor"), editorSummary = el("summary"), editorTitle = el("span", t("dockerConfigureService")), editorHint = el("small", "", "fine docker-config-hint"); editorSummary.append(editorTitle, editorHint); editor.append(editorSummary);
const route = el("div", undefined, "docker-route-grid"), host = input(legacyHostname(assignment) || service?.technical_name || "", "text", "docker-hostname"); host.maxLength = 253; host.placeholder = service?.technical_name || "app1"; const route = el("div", undefined, "docker-route-grid"), host = input(displayHostname(assignment, service), "text", "docker-hostname"); host.maxLength = 253; host.placeholder = service?.technical_name || "app1";
row.dockerSyncActions = () => syncOpenActions(host.value); row.dockerSyncActions = () => syncOpenActions(host.value);
let aliases; const aliasChanged = () => { updateHint?.(); row.dispatchEvent(new Event("input", { bubbles: true })); }; aliases = aliasEditor(assignment?.aliases || [], aliasChanged); row._readAliases = aliases.read; let aliases; const aliasChanged = () => { updateHint?.(); row.dispatchEvent(new Event("input", { bubbles: true })); }; aliases = aliasEditor(assignment?.aliases || [], aliasChanged); row._readAliases = aliases.read;
const target = el("select", undefined, "docker-target-ip"), auto = el("option", agent.default_target_ip ? `${t("dockerBackendIpAuto")} (${agent.default_target_ip})` : t("dockerBackendIpAuto")); auto.value = ""; target.append(auto); for (const [ip, label] of targetOptions(inventory, service, assignment, agent)) { const option = el("option", label); option.value = ip; target.append(option); } target.value = assignment?.target_ip || ""; const target = el("select", undefined, "docker-target-ip"), auto = el("option", agent.default_target_ip ? `${t("dockerBackendIpAuto")} (${agent.default_target_ip})` : t("dockerBackendIpAuto")); auto.value = ""; target.append(auto); for (const [ip, label] of targetOptions(inventory, service, assignment, agent)) { const option = el("option", label); option.value = ip; target.append(option); } target.value = assignment?.target_ip || "";
+47 -2
View File
@@ -55,6 +55,50 @@ pub(crate) fn generated_id(agent_id: &str, service_key: &str) -> String {
format!("docker-{readable}-{suffix}") format!("docker-{readable}-{suffix}")
} }
fn legacy_docker_name(service: &Service) -> Option<String> {
let (base, port) = if let Some(rest) = service.key.strip_prefix("compose:") {
let (path, port) = rest.rsplit_once(':')?;
let mut parts = path.split('/');
let project = parts.next()?;
let app = parts.next()?;
let replica = parts.next()?;
if parts.next().is_some() { return None; }
let mut base = format!("{project}-{app}");
if replica != "1" { base.push('-'); base.push_str(replica); }
(base, port)
} else {
let rest = service.key.strip_prefix("container:")?;
let (name, port) = rest.rsplit_once(':')?;
(name.to_owned(), port)
};
let (port, protocol) = port.split_once('/')?;
if protocol != "tcp" { return None; }
let source = format!("{base}-{port}").to_ascii_lowercase();
let mut name = String::new();
let mut dash = false;
for ch in source.chars() {
if ch.is_ascii_lowercase() || ch.is_ascii_digit() {
name.push(ch);
dash = false;
} else if !name.is_empty() && !dash {
name.push('-');
dash = true;
}
}
let name = name.trim_matches('-');
Some(name.chars().take(48).collect::<String>().trim_matches('-').to_owned())
}
fn effective_assignment(assignment: &Assignment, inventory: Option<&Inventory>) -> Assignment {
let mut result = assignment.clone();
if let Some(service) = inventory.and_then(|inventory| inventory.services.iter().find(|service| service.key == assignment.service_key)) {
if legacy_docker_name(service).as_deref() == Some(assignment.hostname.as_str()) {
result.hostname = service.technical_name.clone();
}
}
result
}
pub fn effective_config( pub fn effective_config(
mut desired: crate::config::Config, mut desired: crate::config::Config,
observed: &BTreeMap<String, Observation>, observed: &BTreeMap<String, Observation>,
@@ -73,14 +117,15 @@ pub fn effective_config(
.iter() .iter()
.filter(|assignment| assignment.enabled) .filter(|assignment| assignment.enabled)
{ {
let hosts = assignment_hosts(assignment, &agent.base_domain)?; let assignment = effective_assignment(assignment, inventory);
let hosts = assignment_hosts(&assignment, &agent.base_domain)?;
let target = inventory let target = inventory
.and_then(|inventory| { .and_then(|inventory| {
inventory inventory
.services .services
.iter() .iter()
.find(|service| service.key == assignment.service_key) .find(|service| service.key == assignment.service_key)
.and_then(|service| route_target(agent, assignment, inventory, service)) .and_then(|service| route_target(agent, &assignment, inventory, service))
}) })
.map(|(ip, port)| -> Result<_> { .map(|(ip, port)| -> Result<_> {
usable_target_ip(ip)?; usable_target_ip(ip)?;